Cplex Users Manual PDF
Cplex Users Manual PDF
Cplex Users Manual PDF
US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with
IBM Corp.
Trademarks
IBM, the IBM logo, and ibm.com are trademarks or registered trademarks of International Business Machines Corp.,
registered in many jurisdictions worldwide. Other product and service names might be trademarks of IBM or other
companies. A current list of IBM trademarks is available on the Web at "Copyright and trademark information" at
www.ibm.com/legal/copytrade.shtml.
Adobe, the Adobe logo, PostScript, and the PostScript logo are either registered trademarks or trademarks of Adobe
Systems Incorporated in the United States, and/or other countries.
Linux is a registered trademark of Linus Torvalds in the United States, other countries, or both.
UNIX is a registered trademark of The Open Group in the United States and other countries.
Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States,
other countries, or both.
Java and all Java-based trademarks and logos are trademarks or registered trademarks of Oracle and/or its affiliates.
Other company, product, or service names may be trademarks or service marks of others.
Contents v
Formulating a network problem . . . . . . . 177 Accessing dual values and reduced costs of SOCP
Example: network optimizer in the Interactive solutions . . . . . . . . . . . . . . . 211
Optimizer . . . . . . . . . . . . . . 178 Examples: SOCP . . . . . . . . . . . . 213
Network flow problem description . . . . . 178 Examples: QCP. . . . . . . . . . . . . 213
Understanding the network log file . . . . . 179
Tuning performance of the network optimizer 180
Part 4. Discrete optimization . . . 215
Solving problems with the network optimizer . . 180
Invoking the network optimizer . . . . . . 180
Network extraction . . . . . . . . . . 181 Chapter 15. Solving mixed integer
Preprocessing and the network optimizer . . . 181 programming problems (MIP) . . . . 217
Example: using the network optimizer with the Stating a MIP problem . . . . . . . . . . 217
Callable Library netex1.c . . . . . . . . . 181 Preliminary issues . . . . . . . . . . . . 218
Solving network-flow problems as LP problems 183 Entering MIP problems . . . . . . . . . 218
Example: network to LP transformation netex2.c 184 Displaying MIP problems . . . . . . . . 219
Changing problem type in MIPs . . . . . . 219
Chapter 13. Solving problems with a Changing variable type . . . . . . . . . 221
quadratic objective (QP) . . . . . . 185 Using the mixed integer optimizer . . . . . . 221
Invoking the optimizer for a MIP model. . . . 221
Distinguishing between convex and nonconvex
Emphasizing feasibility and optimality . . . . 222
QPs . . . . . . . . . . . . . . . . 185
Terminating MIP optimization . . . . . . . 223
Entering QPs . . . . . . . . . . . . . 187
Tuning performance features of the mixed integer
Matrix view . . . . . . . . . . . . . 187
optimizer . . . . . . . . . . . . . . . 225
Algebraic view . . . . . . . . . . . . 187
Branch & cut or dynamic search? . . . . . 225
Examples for entering QPs . . . . . . . . 187
Introducing performance features of the MIP
Reformulating QPs to save memory . . . . . 188
optimizer . . . . . . . . . . . . . . 225
Saving QP problems . . . . . . . . . . . 189
Applying cutoff values . . . . . . . . . 226
Changing problem type in QPs . . . . . . . 189
Applying tolerance parameters . . . . . . 226
Changing quadratic terms . . . . . . . . . 190
Applying heuristics . . . . . . . . . . 226
Optimizing QPs . . . . . . . . . . . . 191
When an integer solution is found: the
Diagnosing QP infeasibility. . . . . . . . . 192
incumbent . . . . . . . . . . . . . 226
Examples: creating a QP, optimizing, finding a
Controlling strategies: diving and backtracking 227
solution . . . . . . . . . . . . . . . 193
Selecting nodes . . . . . . . . . . . . 227
Problem description of a quadratic program . . 193
Selecting variables . . . . . . . . . . . 228
Example: iloqpex1.cpp . . . . . . . . . 193
Changing branching direction . . . . . . . 229
Example: QPex1.java . . . . . . . . . . 194
Solving subproblems . . . . . . . . . . 229
Example: qpex1.c . . . . . . . . . . . 194
Using node files . . . . . . . . . . . 230
Example: reading a QP from a file qpex2.c . . . 195
Probing . . . . . . . . . . . . . . 230
Cuts . . . . . . . . . . . . . . . . 231
Chapter 14. Solving problems with What are cuts? . . . . . . . . . . . . 231
quadratic constraints (QCP) . . . . . 197 Boolean Quadric Polytope (BQP) cuts . . . . 231
Identifying a quadratically constrained program Clique cuts . . . . . . . . . . . . . 232
(QCP) . . . . . . . . . . . . . . . . 197 Cover cuts . . . . . . . . . . . . . 232
Characteristics of a quadratically constrained Disjunctive cuts . . . . . . . . . . . 232
program . . . . . . . . . . . . . . 197 Flow cover cuts . . . . . . . . . . . 232
Convexity . . . . . . . . . . . . . 197 Flow path cuts . . . . . . . . . . . . 233
Semi-definiteness . . . . . . . . . . . 199 Gomory fractional cuts . . . . . . . . . 233
Second order cone programming (SOCP) and Generalized upper bound (GUB) cover cuts . . 233
non PSD . . . . . . . . . . . . . . 199 Implied bound cuts: global and local . . . . 233
Representing SOCP as Lagrangian . . . . . 200 Lift-and-project cuts . . . . . . . . . . 233
Detecting the problem type of a QCP or SOCP . . 202 Mixed integer rounding (MIR) cuts . . . . . 234
Overview. . . . . . . . . . . . . . 202 Multi-commodity flow (MCF) cuts . . . . . 234
Concert Technology and QCP problem type . . 202 Zero-half cuts . . . . . . . . . . . . 235
Callable Library and QCP problem type . . . 202 Adding cuts and re-optimizing . . . . . . 235
Interactive Optimizer and QCP problem type 203 Counting cuts . . . . . . . . . . . . 235
File formats and QCP problem type . . . . . 203 Parameters affecting cuts . . . . . . . . 236
Changing problem type in a QCP . . . . . . 206 Heuristics . . . . . . . . . . . . . . 237
Changing quadratic constraints . . . . . . . 207 What are heuristics? . . . . . . . . . . 237
Solving with quadratic constraints . . . . . . 208 Node heuristic . . . . . . . . . . . . 238
Numeric difficulties and quadratic constraints . . 208 Relaxation induced neighborhood search (RINS)
Accessing dual values and reduced costs of QCP heuristic . . . . . . . . . . . . . . 238
solutions . . . . . . . . . . . . . . . 208 Solution polishing . . . . . . . . . . . 238
Contents vii
Chapter 20. Using piecewise linear Describing the problem . . . . . . . . . . 338
functions in optimization: a transport Representing the data . . . . . . . . . . 339
Developing the model: building and modifying 340
example. . . . . . . . . . . . . . 311
The master model and column generator in this
What is a piecewise linear function? . . . . . . 311
application. . . . . . . . . . . . . . 340
Syntax of piecewise linear functions . . . . . . 312
Adding extractable objects: both ways . . . . 340
Discontinuous piecewise linear functions . . . . 313
Adding columns to a model . . . . . . . 341
Isolated points in piecewise linear functions . . . 315
Changing the type of a variable . . . . . . 342
Using IloPiecewiseLinear in expressions . . . . 315
Cut optimization model . . . . . . . . . 342
Describing the problem . . . . . . . . . . 315
Pattern generator model . . . . . . . . . 342
Problem statement. . . . . . . . . . . 315
Changing the objective function . . . . . . . 343
Variable shipping costs . . . . . . . . . 316
Solving the problem: using more than one
Model with varying costs . . . . . . . . 317
algorithm. . . . . . . . . . . . . . . 343
Developing a model . . . . . . . . . . . 317
Ending the program . . . . . . . . . . . 344
Creating the environment and model . . . . 317
Complete program . . . . . . . . . . . 344
Representing the data . . . . . . . . . 318
Adding constraints . . . . . . . . . . 318
Checking convexity and concavity . . . . . 318 Chapter 25. Early tardy scheduling 345
Adding an objective . . . . . . . . . . 319 Describing the problem . . . . . . . . . . 345
Solving the problem . . . . . . . . . . . 319 Understanding the data file . . . . . . . . 345
Displaying a solution . . . . . . . . . . . 319 Reading the data . . . . . . . . . . . . 346
Ending the application . . . . . . . . . . 320 Creating variables . . . . . . . . . . . . 346
Complete program: transport.cpp. . . . . . . 320 Stating precedence constraints . . . . . . . . 346
Stating resource constraints. . . . . . . . . 347
Representing the piecewise linear cost function . . 347
Chapter 21. Indicator constraints in
Transforming the problem . . . . . . . . . 348
optimization . . . . . . . . . . . . 321 Solving the problem . . . . . . . . . . . 348
What is an indicator constraint? . . . . . . . 321
Example: fixnet.c . . . . . . . . . . . . 322
Indicator constraints in the Interactive Optimizer 322 Part 5. Parallel optimization . . . . 351
What are indicator variables? . . . . . . . . 322
Restrictions on indicator constraints . . . . . . 322 Chapter 26. Multithreaded parallel
Best practices with indicator constraints . . . . 323 optimizers . . . . . . . . . . . . . 353
What are multithreaded parallel optimizers? . . . 353
Chapter 22. Logical constraints in Threads . . . . . . . . . . . . . . . 353
optimization . . . . . . . . . . . . 325 Thread safety . . . . . . . . . . . . 353
What are logical constraints? . . . . . . . . 325 Threads parameter . . . . . . . . . . 354
What can be extracted from a model with logical Threads and performance considerations . . . 355
constraints? . . . . . . . . . . . . . . 325 Determinism of results . . . . . . . . . . 355
Overview. . . . . . . . . . . . . . 325 Using parallel optimizers in the Interactive
Logical constraints in the C++ API . . . . . 326 Optimizer . . . . . . . . . . . . . . 356
Logical constraints in the Java API . . . . . 327 Using parallel optimizers in the Component
Logical constraints in the .NET API . . . . . 327 Libraries . . . . . . . . . . . . . . . 357
Which nonlinear expressions can be extracted? . . 327 Using the parallel barrier optimizer . . . . . . 358
Logical constraints for counting . . . . . . . 328 Concurrent optimizer in parallel . . . . . . . 358
Logical constraints as binary variables . . . . . 328 Determinism, parallelism, and optimization limits 359
How are logical constraints extracted? . . . . . 329 Parallel MIP optimizer . . . . . . . . . . 360
Introducing parallel MIP optimization . . . . 360
Chapter 23. Using logical constraints: Root relaxation and parallel MIP processing . . 360
Memory considerations and the parallel MIP
Food Manufacture 2 . . . . . . . . 331 optimizer . . . . . . . . . . . . . . 361
Introducing the example. . . . . . . . . . 331 Output from the parallel MIP optimizer . . . 361
Describing the problem . . . . . . . . . . 331 Clock settings and time measurement . . . . . 363
Representing the data . . . . . . . . . . 332
Developing the model . . . . . . . . . . 334
Formulating logical constraints . . . . . . . 335
Chapter 27. Remote object for
Solving the problem . . . . . . . . . . . 335 distributed parallel optimization . . . 365
CPLEX remote object for distributed parallel
Chapter 24. Using column generation: optimization. . . . . . . . . . . . . . 365
Application layout for the remote object . . . . 366
a cutting stock example . . . . . . . 337 Programming paradigm in C for the CPLEX
What is column generation? . . . . . . . . 337 remote object . . . . . . . . . . . . . 367
Column-wise models in Concert Technology . . . 337
Contents ix
Chapter 32. Repairing infeasibilities Reference documents about informational
with FeasOpt . . . . . . . . . . . 443 callbacks . . . . . . . . . . . . . . 479
What is FeasOpt? . . . . . . . . . . . . 443 Where to find examples of informational
Invoking FeasOpt . . . . . . . . . . . . 443 callbacks . . . . . . . . . . . . . . 479
Specifying preferences . . . . . . . . . . 444 Informational callbacks and distributed MIP:
Interpreting output from FeasOpt . . . . . . 444 some special considerations . . . . . . . 480
Example: FeasOpt in Concert Technology . . . . 445 What informational callbacks can return . . . 481
Query or diagnostic callbacks . . . . . . . . 482
What are query or diagnostic callbacks? . . . 482
Part 7. Advanced programming Where query callbacks are called . . . . . . 482
techniques . . . . . . . . . . . . 451 Query callbacks and dynamic search . . . . 484
Query callbacks and parallel search . . . . . 484
Chapter 33. User-cut and Control callbacks . . . . . . . . . . . . 484
What are control callbacks?. . . . . . . . 484
lazy-constraint pools . . . . . . . . 453 What control callbacks do . . . . . . . . 485
What are user cuts and lazy constraints? . . . . 453 Control callbacks and dynamic search . . . . 486
What are pools of user cuts or lazy constraints? 453 Control callbacks and parallel search . . . . 487
Differences between user cuts and lazy constraints 454 Implementing callbacks with Concert Technology 487
Identifying candidate constraints for lazy constraint How callback classes are organized . . . . . 487
pool . . . . . . . . . . . . . . . . 455 Writing callback classes by hand . . . . . . 488
Limitations on user-cut pools . . . . . . . . 456 Writing callbacks with macros in C++ . . . . 489
Adding user cuts and lazy constraints . . . . . 456 Callback interface . . . . . . . . . . . 490
Using the Component Libraries to add user cuts The continuous callback . . . . . . . . . 491
or lazy constraints . . . . . . . . . . . 456 Example: deriving the simplex callback
Using the Interactive Optimizer to add user cuts ilolpex4.cpp . . . . . . . . . . . . . . 491
or lazy constraints . . . . . . . . . . . 457 Implementing callbacks in the Callable Library . . 492
Reading and writing LP files . . . . . . . 457 Callable Library callback facilities . . . . . 492
Reading and writing SAV files. . . . . . . 458 Setting callbacks . . . . . . . . . . . 493
Reading and writing MPS files . . . . . . 458 Callbacks for continuous and discrete problems 493
Deleting user cuts and lazy constraints . . . . . 459 Example: using callbacks lpex4.c . . . . . . . 493
Example: controlling cuts iloadmipex5.cpp . . . 494
Chapter 34. Using goals . . . . . . . 461 Interaction between callbacks and parallel
Branch & cut with goals . . . . . . . . . . 461 optimizers . . . . . . . . . . . . . . 498
What is a goal? . . . . . . . . . . . . 461 Return values for callbacks . . . . . . . . . 499
Overview of goals in the search . . . . . . 461 Terminating without callbacks . . . . . . . . 499
How goals are implemented in branch & cut 462
About the method execute in a goal . . . . . 462 Chapter 36. Goals and callbacks: a
Special goals in branch & cut . . . . . . . . 462
comparison . . . . . . . . . . . . 501
Or goal . . . . . . . . . . . . . . 463
Overview. . . . . . . . . . . . . . . 501
And goal . . . . . . . . . . . . . . 463
Fail goal . . . . . . . . . . . . . . 463
Local cut goal . . . . . . . . . . . . 463 Chapter 37. Advanced presolve
Null goal . . . . . . . . . . . . . . 464 routines . . . . . . . . . . . . . . 503
Branch as CPLEX goal . . . . . . . . . 464 Introduction to presolve . . . . . . . . . . 503
Solution goal . . . . . . . . . . . . 464 A proposed example . . . . . . . . . . . 504
Aggregating goals . . . . . . . . . . . . 465 Restricting presolve reductions . . . . . . . 504
Example: goals in branch & cut . . . . . . . 465 When to alert presolve to modifications . . . 505
The goal stack . . . . . . . . . . . . . 467 Adding constraints to the first solution . . . . 505
Memory management and goals . . . . . . . 468 Primal and dual considerations in presolve
Cuts and goals . . . . . . . . . . . . . 469 reductions . . . . . . . . . . . . . 505
Injecting heuristic solutions. . . . . . . . . 471 Cuts and presolve reductions . . . . . . . 506
Controlling goal-defined search . . . . . . . 472 Infeasibility or unboundedness in presolve
Example: using node evaluators in a node selection reductions . . . . . . . . . . . . . 506
strategy . . . . . . . . . . . . . . . 474 Protected variables in presolve reductions . . . 507
Search limits. . . . . . . . . . . . . . 475 Manual control of presolve . . . . . . . . . 507
Modifying a problem . . . . . . . . . . . 509
Chapter 35. Using optimization
callbacks . . . . . . . . . . . . . 477 Chapter 38. Advanced MIP control
What are callbacks? . . . . . . . . . . . 477 interface . . . . . . . . . . . . . 511
Informational callbacks . . . . . . . . . . 478 Introducing the advanced MIP control interface . . 511
What is an informational callback? . . . . . 478 Introducing MIP control callbacks . . . . . . 511
Contents xi
xii CPLEX Users Manual
Meet CPLEX
Introduces CPLEX, explains what it does, suggests prerequisites, and offers advice
for using this documentation with it.
What is CPLEX?
Describes CPLEX Component Libraries.
IBM ILOG CPLEX offers C, C++, Java, .NET, and Python libraries that solve linear
programming (LP) and related problems. Specifically, it solves linearly or
quadratically constrained optimization problems where the objective to be
optimized can be expressed as a linear function or a convex quadratic function.
The variables in the model may be declared as continuous or further constrained
to take only integer values.
In this manual, the phrase CPLEX Component Libraries is used to refer equally to
any of these libraries. While all libraries are callable, the term Callable Library as
used here refers specifically to the C library.
CPLEX is a tool for solving, first of all, linear optimization problems. Such
problems are conventionally written like this:
where the relation ~ may be greater than or equal to, less than or equal to, or
simply equal to, and the upper bounds ui and lower bounds li may be positive
infinity, negative infinity, or any real number.
In the most basic linear optimization problem, the variables of the objective
function are continuous in the mathematical sense, with no gaps between real
values. To solve such linear programming problems, CPLEX implements
optimizers based on the simplex algorithms (both primal and dual simplex) as well
as primal-dual logarithmic barrier algorithms and a sifting algorithm. These
alternatives are explained more fully in Chapter 10, Solving LPs: simplex
optimizers, on page 133.
CPLEX can also handle certain problems in which the objective function is not
linear but quadratic. Such problems are known as quadratic programs or QPs.
Chapter 13, Solving problems with a quadratic objective (QP), on page 185,
covers those kinds of problems.
Within the category of mixed integer programs, there are two kinds of discrete
integer variables: if the integer values of the discrete variables must be either 0
(zero) or 1 (one), then they are known as binary; if the integer values are not
restricted in that way, they are known as general integer variables. This manual
CPLEX also offers a network optimizer aimed at a special class of linear problem
with network structures. CPLEX can optimize such problems as ordinary linear
programs, but if CPLEX can extract all or part of the problem as a network, then it
will apply its more efficient network optimizer to that part of your problem and
use the partial solution it finds there to construct an advanced starting point to
optimize the rest of the problem. Chapter 12, Solving network-flow problems, on
page 177 offers more detail about how the CPLEX network optimizer works.
The manual assumes that you are familiar with CPLEX from reading Getting
Started with CPLEX and from following the tutorials there. Before you begin using
CPLEX, it is a good idea to read Getting Started with CPLEX and to try the tutorials
in it. It is available in the standard distribution of the product.
In order to use CPLEX effectively, you need to be familiar with your operating
system, whether UNIX or Windows.
This manual assumes that you are familiar with the concepts of mathematical
programming, particularly linear programming. In case those concepts are new to
you, the bibliography in Further reading on page xx in this preface indicates
references to help you there.
This manual also assumes you already know how to create and manage files. In
addition, if you are building an application that uses the Component Libraries, this
manual assumes that you know how to compile, link, and execute programs
written in a high-level language. The Callable Library is written in the C
programming language, while Concert Technology is written in C++, Java, and
.NET. This manual also assumes that you already know how to program in the
appropriate language and that you will consult a programming guide when you
have questions in that area.
Examples online
Describes examples delivered with the product.
For the examples explained in the manual, you will find the complete code for the
solution in the examples subdirectory of the standard distribution of CPLEX, so
that you can see exactly how CPLEX fits into your own applications. Table 1 lists
the examples in this manual and indicates where to find them.
Table 1. Examples
Example Source File In This Manual
dietary optimization: ilodiet.cpp Example: optimizing the
building a model by rows diet problem in C++ on
(constraints) or by columns page 21
(variables), solving with
IloCplex in C++
Meet CPLEX xv
Table 1. Examples (continued)
Example Source File In This Manual
dietary optimization: Diet.java Example: optimizing the
building a model by rows diet problem in Java on
(constraints) or by columns page 45
(variables), solving with
IloCplex in Java
dietary optimization: Diet.cs Example: optimizing the
building a model by rows diet problem in C#.NET on
(constraints) or by columns page 56
(variables), solving with
Cplex in C#.NET
dietary optimization: diet.c Example: optimizing the
building a model by rows diet problem in the Callable
(constraints) or by columns Library on page 72
(variables), solving with the
Callable Library
linear programming: starting
from an advanced basis ilolpex6.cpp Example ilolpex6.cpp on
page 155
lpex6.c
Example lpex6.c on page
156
network optimization: using netex1.c Example: using the network
the Callable Library optimizer with the Callable
Library netex1.c on page
181
network optimization: netex2.c Example: network to LP
relaxing a network flow to transformation netex2.c on
an LP page 184
quadratic programming:
maximizing a QP iloqpex1.cpp Example: iloqpex1.cpp on
page 193
QPex1.java
Example: QPex1.java on
qpex1.c page 194
iloqcpex1.cpp
QCPex1.java
second order cone Examples: SOCP on page
programming: SOCP socpex1.c 213
ilosocpex1.cpp
SocpEx1.java
Like the reference manuals, this manual uses the following conventions:
v Important ideas are italicized the first time they appear.
Related documentation
Describes other available documentation of the product.
The online information files are distributed with the CPLEX libraries.
The complete documentation set for CPLEX consists of the following material:
v Getting Started: It is a good idea for new users of CPLEX to start with that
manual. It introduces CPLEX through the Interactive Optimizer, and contains
tutorials for CPLEX Concert Technology for C++, Java, and .NET applications as
well as the CPLEX Callable Library.
Getting Started is supplied in HTML and as an Eclipse plugin for use in IBM
Help System based on Eclipse.
v Users Manual: This manual explains the topics covered in the Getting Started
manual in greater depth, with individual chapters about:
LP (Linear Programming) problems;
Network-flow problems;
QP (Quadratic Programming) problems;
QCP (Quadratically Constrained Programming), including the special case of
second order cone programming (SOCP) problems, and
MIP (Mixed Integer Programming) problems.
There is also detailed information about:
tuning performance,
Online services
Describes the optimization forum.
Further reading
Recommends further reading about related topics.
In case you want to know more about optimization and mathematical or linear
programming, here is a brief selection of printed resources:
Chvatal, Vasek, Linear Programming, New York: W.H. Freeman and Company,
1983. This standard textbook for undergraduate students introduces both theory
and practice of linear programming.
Wolsey, Laurence A., Integer Programming, New York: John Wiley & Sons, 1998.
This book explains branch and cut, including cutting planes, in detail.
Gill, Philip E., Walter Murray, and Margaret H. Wright, Practical Optimization.
New York: Academic Press, 1982 reprint edition. This book covers, among other
topics, quadratic programming.
For more information about ill-conditioning and numerical difficulties, see also
these references:
v Higham, Nicholas J., Accuracy and Stability of Numerical Algorithms, Society
for Industrial and Applied Mathematics (SIAM), 2002.
v Duff, Iain S., A. M. Erisman, John Ker Reid, Direct Methods for Sparse Matrices
Clarendon Press, 1989.
v Gill, Philip E., Walter Murray, Margaret H. Wright, Practical Optimization,
Academic Press, 1981.
v Golub, Gene Howard, Charles F. Van Loan, Matrix Computations, The Johns
Hopkins University Press, 1983.
Overview
Highlights the design, architecture, modeling facilities for C++ users of CPLEX.
This topic explains the design of the library, explains modeling techniques, and
offers an example of programming with Concert Technology in C++. It also
provides information about controlling parameters in a C++ application. It shows
how to write C++ applications using CPLEX Concert Technology for C++ users. It
also includes information about compiling and linking your applications.
Compilation and linking instructions are provided with the files that come in the
standard distribution of CPLEX for your computer platform. Check the
readme.html file for details.
Procedure
1. First, create a model of your problem with the modeling facilities of Concert
Technology. Modeling an optimization problem with Concert Technology on
page 5 offers an introduction to creating a model.
2. When the model is ready to be solved, hand it over to CPLEX for solving.
Solving the model on page 9 explains how to do so. It includes a survey of
the IloCplex interface for controlling the optimization. Individual controls are
discussed in the chapters explaining the individual optimizers.
3. After CPLEX solves the model, Accessing solution information on page 14,
shows you how to access and interpret results from the optimization.
Results
Not covered in this chapter are advanced features, such as the use of goals or
callbacks for querying data about an ongoing optimization and for controlling the
optimization itself. Goals, callbacks, and other advanced features are discussed in
Part 7, Advanced programming techniques, on page 451.
Overview
Highlights the C++ classes for models in CPLEX.
A Concert Technology model consists of a set of C++ objects. Each variable, each
constraint, each special ordered set (SOS), and the objective function in a model are
all represented by objects of the appropriate Concert Technology class. These
objects are known as modeling objects. They are summarized in the table Table 2
on page 9.
Before you create modeling objects, you must construct an object of the class
IloEnv . This object known as the environment. It is constructed with the
statement:
IloEnv env;
Users familiar with the CPLEX Callable Library are cautioned not to confuse the
Concert Technology environment object with the CPLEX environment object of
type CPXENVptr, used to set CPLEX parameters. Such an object is not needed with
Concert Technology, as parameters are handled directly by each instance of the
class IloCplex. In other words, the environment in Concert Technology always
refers to the object of class IloEnv required for all other Concert Technology
objects.
Probably the first modeling class you will need is IloNumVar . Objects of this class
represent decision variables in a model. They are defined by the lower and upper
bound for the variable, and a type which can be one of ILOFLOAT , ILOINT , or
ILOBOOL for continuous, integer, or Boolean variables, respectively. The following
constructor creates an integer variable with bounds -1 and 10:
IloNumVar myIntVar(env, -1, 10, ILOINT);
The class IloNumVar provides methods that allow querying of the data needed to
specify a variable. However, only bounds can be modified. Concert Technology
provides a modeling object class IloConversion to change the type of a variable.
This conversion allows you to use the same variable with different types in
different models.
Variables are usually used to build up expressions, which in turn are used to
define the objective or constraints of the optimization problem. An expression can
be explicitly written, as in
1*x[1] + 2*x[2] + 3*x[3]
Whenever possible, build your expressions in terms of data that is either integer or
double-precision (64-bit) floating point. Single-precision (32-bit) floating point data
should be avoided, as it can result in unnecessarily ill conditioned problems. For
more information, refer to Numeric difficulties on page 146.
When you have finished using an expression (that is, you created a constraint with
it) you need to delete it by calling its method end , for example:
expr.end();
To formulate a full optimization problem, you need to create the objects that are
part of it and add them to an instance of IloModel, the class that represents
optimization problems. For example, these lines:
IloModel model(env);
model.add(obj);
model.add(r1);
define a model consisting of the objective obj , constraint r1 , and all the variables
they use. Notice that variables need not be added to a model explicitly, as they are
implicitly considered if any of the other modeling objects in the model use them.
(However, you may explicitly add variables to a model, for example, if you
consider a variable a part of the problem even though it does not appear in a
constraint or objective function.)
The class IloModel is itself a class of modeling objects. Thus, one model can be
added to another. A possible use of this feature is to capture different scenarios in
different models, all of which are extensions of a core model. The core model could
be represented as an IloModel object itself and added to the IloModel objects that
represent the individual scenarios.
Managing data
Describes C++ classes to manage data.
Usually the data of an optimization problem must be collected before or during the
creation of the Concert Technology representation of the model. Though, in
principle, modeling does not depend on how the data is generated and
represented, this task may be facilitated by the array classes or set classes, such
asIloNumSet, provided by Concert Technology.
For example, objects of class IloNumArray can be used to store numeric data in
arrays. Elements of the class IloNumArray can be accessed like elements of standard
C++ arrays, but the class also offers a wealth of additional features. For example,
Concert Technology arrays are extensible; in other words, they transparently adapt
to the required size when new elements are added using the method add .
Conversely, elements can be removed from anywhere in the array with the method
remove . Concert Technology arrays also provide debugging support when
compiled in debug mode by using assert statements to make sure that no element
beyond the array bounds is accessed. Input and output operators (that is,
operator << and operator >>) are provided for arrays. For example, the code:
IloNumArray data(env, 3, 1.0, 2.0, 3.0);
cout << data << endl;
When you have finished using an array and want to reclaim its memory, call the
method end; for example, data.end. When the environment ends, all memory of
arrays belonging to the same environment is returned to the system as well. Thus,
in practice you do not need to call end on an array (or any other Concert
Technology object) just before calling env.end.
The constructor for arrays specifies that an array of size 3 with elements 1.0, 2.0,
and 3.0 is constructed. This output format can be read back in with, for example:
cin >> data;
The Example: optimizing the diet problem in C++ on page 21 takes advantage of
this function and reads the problem data from a file.
Finally, Concert Technology provides the template class IloArray<X> to create array
classes for your own type X. This technique can be used to generate
multidimensional arrays. All the functions mentioned here are supported for
IloArray classes except for input/output, which depends on the input and output
operator being defined for type X.
Overview
Introduces the C++ class IloCplex.
CPLEX generally does not need to be involved while you create your model.
However, after the model is set up, it is time to create your cplex object, that is, an
instance of the class IloCplex, to be used to solve the model. IloCplex is a class
derived from IloAlgorithm. There are other Concert Technology algorithm classes,
also derived from IloAlgorithm, as documented in the CPLEX C++ API Reference
Manual. Some models might also be solved by using other algorithms, such as the
class IloCP for constraint programming, or by using a hybrid algorithm consisting
of both CP Optimizer and CPLEX. Some models, on the other hand, cannot be
solved with CPLEX.
The makeup of the model determines whether or not CPLEX can solve it. More
precisely, in order to be handled by IloCplex objects, a model may only consist of
modeling objects of the classes listed in Table 2.
For more detail about solving problems with IloCplex , see the following sections
of this manual.
Table 2. Concert Technology modeling objects in C++
To model: Use:
numeric variables objects of the class IloNumVar , as long as
they are not constructed with a list of
feasible values
semi-continuous variables objects of the class IloSemiContVar
linear objective function an object of the class IloObjective with
linear or piecewise linear expressions
quadratic objective function an object of the class IloObjective with
quadratic expressions
or
For more information about quadratic objective functions, see Chapter 13, Solving
problems with a quadratic objective (QP), on page 185.
For examples of piecewise linear constraints, see Chapter 20, Using piecewise
linear functions in optimization: a transport example, on page 311.
For more about logical constraints, see Chapter 22, Logical constraints in
optimization, on page 325. This topic introduces the idea of generated ranges.
For a description of special ordered sets, see Chapter 18, Using special ordered
sets (SOS), on page 303.
Extracting a model
Describes the method that extracts a model for an algorithm in the C++ API.
This manual defines only one optimization model and uses only one instance of
IloCplex at a time to solve the model. Consequently, this manual talks about these
as the model and the cplex object. However, in Concert Technology, an arbitrary
number of models and algorithm-objects can be created. The cplex object can be
created by the constructor:
IloCplex cplex(env);
To use CPLEX to solve the model, the model must first be extracted to cplex by a
call like this:
This method copies the data from the model into the appropriate efficient data
structures, which CPLEX uses for solving the problem. It does so by extracting
each of the modeling objects added to the model and each of the objects referenced
by them. For every extracted modeling object, corresponding data structures are
created internally in the cplex object. For readers familiar with the sparse matrix
representation used internally by CPLEX, a variable becomes a column and a
constraint becomes a row. As discussed later, these data structures are
synchronized with the modeling objects even if the modeling objects are modified.
If you consider a variable to be part of your model, even though it is not (initially)
used in any constraint, you should add this variable explicitly to the model. This
practice makes sure that the variable will be extracted. This practice may also be
important if you query solution information for the variable, since solution
information is available only for modeling objects that are known to CPLEX
because they have been extracted from a model.
If you feel uncertain about whether or not an object will be extracted, you can add
it to the model to be sure. Even if an object is added multiple times, it will be
extracted only once and thus will not slow the solution process down.
Since the sequence of creating the cplex object and extracting the model to it is
such a common one, IloCplex provides the shortcut:
IloCplex cplex(model);
This shortcut is completely equivalent to separate calls and makes sure that the
environment used for the cplex object will be the same as that used for the model
when it is extracted, as required by Concert Technology. The shortcut uses the
environment from the model to construct the cplex object before extraction.
Invoking a solver
Describes the method that invokes a solver in the C++ API.
After the model is extracted to the cplex object, you are ready to solve it by calling
solve();
For most problems this is all that is needed for solving the model. Nonetheless,
CPLEX offers a variety of controls that allow you to tailor the solution process for
your specific needs.
Choosing an optimizer
Describes the optimizers available in the C++ API.
Solving the extracted model with CPLEX involves solving one or a series of
continuous relaxations:
v Only one continuous relaxation needs to be solved if the extracted model is
continuous itself, that is, if it does not contain integer variables, Boolean
variables, semi-continuous or semi-integer variables, logical constraints, special
ordered sets (SOS), or piecewise linear functions. Chapter 10, Solving LPs:
simplex optimizers, on page 133 and Chapter 11, Solving LPs: barrier
optimizer, on page 157 discuss the algorithms available for solving LPs.
Similarly, Chapter 13, Solving problems with a quadratic objective (QP), on
page 185, discusses the algorithms available for solving QPs. Chapter 14,
The optimizer option used for solving the first continuous relaxation (whether it is
the only one or just the first in a series of problems) is controlled by the root
algorithm parameter:
cplex.setParam(IloCplex::RootAlg, alg);
As a nested enumeration, the fully qualified names that must be used in the
program are IloCplex::Primal, IloCplex::Dual, and so on. Table 3 displays the
meaning of the optimizer options defined by IloCplex::Algorithm.
Tip:
The choice Sifting is not available for QP models. Only the Barrier option is
available for QCP models. Table 4 summarizes these options.
Table 3. Optimizer options in IloCplex::Algorithm
AutoAlg let CPLEX decide which algorithm to use
Primal use the primal simplex algorithm
Dual use the dual simplex algorithm
Network use the primal network simplex algorithm on
an embedded network followed by the dual
simplex algorithm for LPs and the primal
simplex algorithm for QPs on the entire
problem
Barrier use the barrier algorithm. The type of
crossover performed after the barrier
algorithm is set by the parameter
IloCplex::BarCrossAlg .
Sifting use the sifting algorithm
Concurrent use multiple algorithms concurrently on a
multiprocessor system
If the extracted model requires the solution of more than one continuous
relaxation, the algorithm for solving the first one at the root is controlled by the
RootAlg parameter. The algorithm at all other nodes except the root is controlled
by the NodeAlg parameter:
cplex.setParam(IloCplex::NodeAlg, alg)
Though CPLEX defaults will prove sufficient to solve most problems, CPLEX offers
a variety of parameters to control various algorithmic choices. CPLEX parameters
can assume values of type bool, num, int, and string. IloCplex provides four
categories of parameters that are listed in the nested enumeration types:
v IloCplex_BoolParam
v IloCplex_IntParam
v IloCplex_NumParam
v IloCplex_StringParam
To access the current value of a parameter that interests you from Concert
Technology, use the method getParam. To access the default value of a parameter,
use the method getDefault. Use the methods getMin and getMax to access the
minimum and maximum values of num and int type parameters.
Some integer parameters are tied to nested enumerations that define symbolic
constants for the values the parameter may assume. The table Table 6 summarizes
those parameters and their enumeration types.
Table 6. Nested enumerations for integer parameters
This Enumeration: Is Used for This Parameter:
IloCplex::Algorithm IloCplex::RootAlg
There are, of course, routines in Concert Technology to set these parameters. Use
the following methods to set the values of CPLEX parameters:
v setParam(BoolParam, value);
v setParam(IntParam, value);
v setParam(NumParam, value);
v setParam(StringParam, value);
setParam(IloCplex::EpOpt, 0.0001);
The CPLEX Parameters Reference Manual documents the type of each parameter
(bool, int, num, string) along with the Concert Technology enumeration value,
symbolic constant, and reference number representing the parameter.
The method setDefaults resets all parameters (except the log file) to their default
values, including the CPLEX callback functions. This routine resets the callback
functions to NULL.
When you are solving a MIP, CPLEX provides additional controls of the solution
process. Priority orders and branching directions can be used to control the
branching in a static way. These controls are discussed in Heuristics on page 237.
These controls are static in the sense that they allow you to control the solution
process based on data that does not change during the solution and can thus be set
up before you solve the model.
As you see, these statuses indicate information about the model that the CPLEX
optimizer was able to prove during the most recent call to the method solve.
For more information about those status codes, see the CPLEX Reference Manual.
If getValue returns IloTrue , a feasible solution has been found and solution values
for model variables are available to be queried. For example, the solution value for
the numeric variable var1 can be accessed like this:
However, querying solution values variable by variable may result in ugly code.
Here, the use of Concert Technology arrays provides a much more compact way of
accessing the solution values. Assuming your variables are stored in an array of
numeric variables (IloNumVarArray) named var , use lines like these to access the
solution values for all variables in var simultaneously:
Solution data is not restricted to the solution values of variables. It also includes
values of slack variables in constraints (whether the constraints are linear or
quadratic) and the objective value. If the extracted model does not contain an
objective object, IloCplex assumes a 0 expression objective. The objective value is
returned by calling method getObjValue. Slack values are accessed with the
methods getSlack and getSlacks, which take a linear constraint, a quadratic
constraint, or an array of constraints as a parameter.
For LPs and QPs, solution data includes information such as dual variables and
reduced cost. Such information can be queried with the methods, getDual,
getDuals, getReducedCost, and getReducedCosts.
When you solve LPs or QPs with either the simplex algorithm or the barrier
optimizer with crossover enabled, basis information is available as well. You can
consult basis information by means of the method getStatuses which returns basis
status information for variables and constraints.
The availability of a basis for an LP allows you to perform sensitivity analysis for
your model, if the model is an LP. Such analysis tells you by how much you can
modify your model without affecting the solution you found. The modifications
supported by the sensitivity analysis function include bound changes, changes of
the right hand side vector and changes of the objective function. They are analyzed
by the methods getBoundSA, getRHSSA, and getObjSA, respectively.
An important feature of CPLEX is that even if no feasible solution has been found,
(that is, if solve returns IloFalse), some information about the problem can be
queried. All the methods discussed so far may successfully return information
about the current (infeasible) solution which CPLEX maintains.
In another approach, CPLEX can detect a conflict among the constraints and
bounds of an infeasible model and refine the conflict to report to you a minimal
conflict to repair yourself. For more about this approach, see Chapter 31,
Diagnosing infeasibility by refining conflicts, on page 429.
For more about these and other ways of overcoming infeasibility, see Diagnosing
LP infeasibility on page 150.
If you are also interested in the variable or constraint where the maximum
violation occurs, call the same method with these arguments instead:
IloRange maxrange;
IloNumVar maxvar;
IloNum violation = cplex.getQuality(IloCplex::MaxPrimalInfeas, &maxrange, &maxvar);
CPLEX copies the variable or constraint handle in which the maximum violation
occurs to maxvar or maxrange , and then CPLEX makes the other handle an empty
one. The maximum primal infeasibility is only one example of a wealth of quality
measures. The full list is defined by the nested enumeration IloCplex::Quality. Each
of these measures of quality can be used as an argument for the methods
getQuality, though some measures are not available for all choices of all options of
all optimizers. A list of solution qualities appears in the CPLEX Reference Manual of
the Callable Library and C++ API, as the group optim.cplex.solutionquality .
Modifying a model
Describes methods in the C++ API to modify a model.
Not only does CPLEX track all modifications of the model it has extracted, but also
it tries to maintain as much solution information from a previous invocation of
solve as is possible and reasonable.
You have already encountered what is perhaps the most important modification
method, that is, the method IloModel::add for adding modeling objects to a model.
Conversely, you may call IloModel::remove to remove a modeling object from a
model.
Objective functions can be modified by changing their sense and by editing their
expression, or by changing their expression completely.
Concert Technology also provides a way to remove a modeling object from all
other modeling objects and algorithms exactly the same way as when deleting it,
yet without deleting the modeling object: call the method
IloExtractable::removeFromAll. This method may be helpful to temporarily
remove a variable (or even an array of objects) from your model while keeping the
option to add it back later.
Tip: The method removeFromAll is inherited by classes derived from the class
IloExtractable, such classes as IloObjective, IloConstraint, or IloModel and others.
Usually when a constraint is removed from the extracted model, the constraint is
also removed from CPLEX as well, unless it was added to the model more than
once.
Consider the case where a variable is removed from CPLEX after one of the end or
remove operations. If the cplex object contains a simplex basis, by default the status
for that variable is removed from the basis as well. If the variable happens to be
basic, the operation corrupts the basis. If this is not what you want, CPLEX
provides a delete mode that first pivots the variable out of the basis before
removing it. The resulting basis is not guaranteed to be feasible or optimal, but it
will still constitute a valid basis. To select this mode, call the method:
setDeleteMode(IloCplex::FixBasis);
Similarly, when removing a constraint with the FixBasis delete mode, CPLEX will
pivot the corresponding slack or artificial variable into the basis before removing it,
to make sure of maintaining a valid basis. In either case, if no valid basis was
available in the first place, no pivot operation is performed. To set the delete mode
back to its default setting, call:
setDeleteMode (IloCplex::LeaveBasis);
A conversion object, that is, an instance of IloConversion , can specify a type only
for a variable that is in a model. Converting the type more than once is an error,
because there is no rule about which would have precedence. However, this
convention is not too restrictive, since you can remove the conversion from a
model and add a new one. To remove a conversion from a model, use the method
Handling errors
Describes error handling in the C++ API.
That method is a virtual method inherited from the base class IloException. If you
want to access only the message for printing to a channel or output stream, it is
more convenient to use the overloaded output operator (operator<< ) provided by
Concert Technology for IloException.
In contrast to most other Concert Technology classes, exception classes are not
handle classes. Thus, the correct type of an exception is lost if it is caught by value
rather than by reference (that is, using catch(IloException& e) {...} ). This is one
reason that catching IloException objects by reference is a good idea, as
demonstrated in all examples. See, for example, ilodiet.cpp . Some derived
exceptions may carry information that would be lost if caught by value. So if you
output an exception caught by reference, you may get a more precise message than
when outputting the same exception caught by value.
Overview
Outlines how to solve the diet problem with CPLEX in a C++ application.
The optimization problem solved in this example is to compose a diet from a set of
foods, so that the nutritional requirements are satisfied and the total cost is
minimized. Problem representation describes the problem.
Notes about the application are available in Complete program on page 26.
Problem representation
Describes how the problem is represented in the application.
Concert Technology is equally suited for both kinds of modeling; in fact, you can
even mix both approaches in the same program. If a new food product is created,
you can create a new variable for it regardless of how the model was originally
built. Similarly, if a new nutrient is discovered, you can add a new constraint for it.
Now you get a nutrition book and look up which nutrients are known and
relevant for you. For each nutrient, you note the minimum and maximum amounts
that should be found in your diet. Also, you go through the list of foods and
decide how much a food item will contribute for each nutrient. This gives you one
constraint per nutrient, which can naturally be represented as a range constraint in
pseudo-code like this:
nutrMin[i] <= sum_j (nutrPer[i][j] * Buy[j]) <= nutrMax[i]
The loop in the example combines those two ideas and looks like this:
mod.add(IloMinimize(env, IloScalProd(Buy,foodCost)));
for (i = 0; i < m; i++) {
IloExpr expr(env);
for (j = 0; j < n; j++) {
expr += Buy[j] * nutrPer[i][j];
}
mod.add(nutrMin[i] <= expr <= nutrMax[i]);
expr.end();
This way of creating the model appears in the function buildModelByRow , in the
example ilodiet.cpp .
You start with the nutrition book where you compile the list of nutrients that you
want to make sure are properly represented in your diet. For each of the nutrients,
you create an empty constraint:
where ... is left to be filled in after you walk into the store. Also, you set up the
objective function to minimize the cost. Constraint i is referred to as range[i] and
to the objective as cost .
Now you walk into the store and, for each food, you check the price and
nutritional content. With this data you create a variable representing the amount
you want to buy of the food type and install the variable in the objective function
and constraints. That is, you create the following column in pseudo code, like this:
cost(foodCost[j]) "+" "sum_i" (range[i](nutrPer[i][j]))
where the notation + and sum indicate in pseudo code that you add the new
variable j to the objective cost and constraints range[i ]. The value in parentheses
is the linear coefficient that is used for the new variable. This notation is similar to
the syntax actually used in Concert Technology, as demonstrated in the function
buildModelByColumn , in the example ilodiet.cpp .
for (j = 0; j < n; j++) {
IloNumColumn col = cost(foodCost[j]);
for (i = 0; i < m; i++) {
col += range[i](nutrPer[i][j]);
}
Buy.add(IloNumVar(col, foodMin[j], foodMax[j], type));
col.end();
}
Application description
Describes the architecture of the application.
Note:
In such cases, an exception is thrown. This practice makes sure that env.end is
called before the program is terminated.
All data defining the problem are read from a file. The nutrients per food are
stored in a two-dimensional array, IloNumArray2 .
If all goes well, the input file is opened in the file ifstream . After that, the arrays
for storing the problem data are created by declaring the appropriate variables.
After the problem data has been read and verified, it is time to build the model. To
do so, construct the model object with this declaration:
IloModel mod(env);
The array Buy is created to store the modeling variables. Since the environment is
not passed to the constructor of Buy , an empty handle is constructed. So at this
point the variable Buy cannot be used.
The model is created by rows using the function buildModelByRow . It first gets the
environment from the model object passed to it. Then the modeling variables Buy
are created. Instead of calling the constructor for the variables individually for each
variable, create the full array of variables, with the array of lower and upper
bounds and the variable type as parameter. In this array, variable Buy[i] is created
such that it has lower bound foodMin[i] , upper bound foodMax[i] , and type
indicated by type .
The statement:
mod.add(IloMinimize(env, IloScalProd(Buy, foodCost)));
creates the objective function and adds it to the model. The IloScalProd function
creates the expression j (Buy[j] * foodCost[j]) which is then passed to the
function IloMinimize . That function creates and returns the actual IloObjective
object, which is added to the model with the call mod.add .
The following loop creates the constraints of the problem one by one and adds
them to the model. First the expression j (Buy[j] * nutrPer[i][j]) is created by
building a Concert Technology expression. An expression variable expr of type
IloExpr is created, and linear terms are added to it by using operator+= in a loop.
The expression is used with the overloaded operator<= to construct a range
constraint (an IloRange object) which is added to the model:
mod.add(nutrMin[i] <= expr <= nutrMax[i]);
After an expression has been used for creating a constraint, it is deleted by a call to
expr.end .
In the following loop, the variables of the model are created one by one in
columns; thus, the new variables are immediately installed in the model. An
IloNumColumn object col is created and initialized to define how each new variable
will be appended to the existing objective and constraints.
The IloNumColumn object col is initialized to contain the objective coefficient for the
new variable. This is created with cost(foodCost[j]) , that is using the overloaded
operator() for IloObjective . Next, an IloNumColumn object is created for every
constraint, representing the coefficient the new variable has in that constraint.
Again these IloNumColumn objects are created with the overloaded operator() , this
time of IloRange . The IloNumColumn objects are merged together to an aggregate
IloNumColumn object using operator += . The coefficient for row i is created with
range[i](nutrPer[i][j]) , which calls the overloaded operator() for IloRange
objects.
which creates the new variable with lower bound foodMin[j] , upper bound
foodMax[j] and type type , and adds it to the existing objective and ranges with
the coefficients specified in column col . After creating the variable for this
column, the IloColumn object is deleted by calling col.end .
After the model has been populated, it is time to create the cplex object and
extract the model to it by calling:
IloCplex(mod);
It is then ready to solve the model, but for demonstration purposes the extracted
model will first be written to the file diet.lp . Doing so can help you debug your
model, as the file contains exactly what CPLEX sees. If it does not match what you
expected, it will probably help you locate the code that generated the wrong part.
The model is then solved by calling method solve. Finally, the solution status and
solution vector are output to the output channel cplex.out . By default this
channel is initialized to cout. All logging during optimization is also output to this
channel. To turn off logging, you would set the out stream of cplex to a null
stream by calling cplex.setOut(env.getNullStream()).
Note:
v All the definitions needed for a CPLEX Concert Technology application in C++
are imported by including the file <ilcplex/ilocplex.h> .
v The line ILOSTLBEGIN is a macro that is needed for portability. Microsoft Visual
C++ code varies, depending on whether you use the STL or not. This macro
allows you to switch between both types of code without the need to otherwise
change your source code.
v The function usage is called in case the program is executed with incorrect
command line arguments.
Overview
Offers an overview of the architecture.
A user-written Java application and CPLEX internals use separate memory heaps.
Java supports two different command-line options of interest in this respect:
v -Xms sets the initial heap size for the Java-part of the application;
v -Xmx sets the maximum heap size for the Java part of the application.
For applications where more memory is needed, you must carefully assess whether
the Java application needs to increase or decrease its memory heap. In some cases,
the Java application needs more memory to create the model to be solved. If so,
use the -Xmx option to increase the maximum heap size. Keep in mind that
allocating more space in the heap to the Java application makes less memory
available to the CPLEX internals, so increase the maximum heap size only as
needed. Otherwise, the Java application will use too much memory, imposing an
unnecessary limitation on the memory available to the CPLEX internals.
In cases where insufficient memory is available for CPLEX internals, there are
remedies to consider with respect to the heap:
v make sure the maximum heap size has not been set to a value larger than
needed;
v consider reducing the default maximum heap size if the Java application can
operate with less memory.
For users familiar with object-oriented design patterns, this design is that of a
factory, where IloCplex is a factory for modeling objects. The advantage of such a
design is that code which creates a model using the Concert Technology modeling
Compilation and linking instructions are provided with the files that come in the
standard distribution of CPLEX for your computer platform. Check the file
readme.html for details.
First, create a model of your problem with the modeling facilities of Concert
Technology. Modeling an optimization problem with Concert Technology in the
Java API on page 29 offers an introduction to creating a model. Building the
model on page 34 goes into more detail.
When the model is ready to be solved, hand it over to CPLEX for solving. Solving
the model on page 35 explains how to do so. It includes a survey of the IloCplex
interface for controlling the optimization. Individual controls are discussed in the
chapters explaining the individual optimizers.
After analyzing the results, you may want to make changes to the model and
study their effect. Modifying the model on page 47 explains how to make
changes and how CPLEX deals with them in the context of the diet problem.
Not covered in this chapter are advanced features, such as the use of goals or
callbacks to query data about an ongoing optimization and for controlling the
optimization itself. Goals, callbacks, and other advanced features are discussed in
Part 7, Advanced programming techniques, on page 451.
Overview
Introduces classes to support models for CPLEX in Java applications.
Note:
The class IloCplex extends IloCplexModeler. All the modeling methods in IloCplex
derive from IloCplexModeler. IloCplex implements the solving methods.
The example CplexServer.java shows you how to write an optimization server that
accepts pure Java model taking advantage of the class IloCplexModeler in a native
J2EE client application.
Since class IloCplex implements IloMPModeler (and thus its parent interface
IloModeler ) all methods from IloMPModeler and IloModeler can be used for
building a model. IloModeler defines the methods to:
v create modeling variables of type integer, floating-point, or Boolean;
v construct simple expressions using modeling variables;
v create objective functions; and
v create ranged constraints, that is, constraints of the form:
lowerbound expression upperbound
Models that consist only of such constructs can be built and solved with any
optimizer implementing the IloModeler interface, including IloCplex, which
implements the IloMPModeler extension.
Table 8 summarizes those observations about the interfaces of CPLEX with Concert
Technology for Java users.
Table 8. Modeling classes of CPLEX with Concert Technology for Java users
To Model This Use an Object of This Class or Interface
variable IloNumVar and its extensions IloIntVar and
IloSemiContVar
range constraint IloRange with (piecewise) linear or quadratic
expressions
other relational constraint IloConstraint of the form expr1 relation
expr2, where both expressions are linear or
quadratic and may optionally contain
piecewise linear terms.
LP matrix IloLPMatrix
linear or quadratic objective IloObjective with (piecewise) linear or
quadratic expressions
variable type-conversion IloConversion
special ordered set IloSOS1 or IloSOS2
logical constraints IloOr, IloAnd, and methods such as not
Using IloModeler
Describes the class IloModeler in the Java API.
Variables in a model
This constructor method allows you to set all the attributes of a variable: its lower
and upper bounds, its type, and its name. Names are optional in the sense that
null strings are considered to be valid as well.
The other constructor methods for variables are provided mainly for ease of use.
For example, because names are not frequently assigned to variables, all variable
constructors come in pairs, where one variant requires a name string as the last
parameter and the other one does not (defaulting to a null string).
Integer variables can be created by the intVar methods, and do not require the
type IloNumVarType.Int to be passed, as this is implied by the method name. The
bound parameters are also specified more consistently as integers. These methods
return objects of type IloIntVar, an extension of interface IloNumVar that allows
you to query and set bounds consistently using integers, rather than doubles as
used for IloNumVar .
Frequently, integer variables with 0/1 bounds are used as decision variables. To
help create such variables, the boolVar methods are provided. In the Boolean type,
0 (zero) and 1 (one) are implied, so these methods do not need to accept any
bound values.
For all these constructive methods, there are also equivalent methods for creating a
complete array of modeling variables at one time. These methods are called
numVarArray, intVarArray, and boolVarArray .
The special case of the scalar product of an array of values with an array of
variables is directly supported through the method scalProd. Thus that loop can be
rewritten as:
IloLinearNumExpr lin = cplex.scalProd(value, variable);
It is recommended that you build expressions in terms of data that is either integer
or double-precision (64-bit) floating-point. Single-precision (32 bit) floating-point
data should be avoided as it can result in unnecessarily ill-conditioned problems.
For more information, refer to Numeric difficulties on page 146.
Ranged constraints
where lb and ub are double values, expr is of type IloNumExpr, and name is a
string.
where relation is the relation =, , or . The following table shows how to choose lb
and ub for modeling these relations:
relation lb ub method
= rhs rhs eq
-Double.MAX_VALUE rhs le
rhs Double.MAX_VALUE ge
Again, all constructors for ranged constraints come in pairs, one constructor with
and one without an argument for the name.
For convenience, the methods maximize and minimize are provided to create a
maximization or minimization objective respectively, without using an
IloObjectiveSense parameter. Names for objective function objects are optional, so
all constructor methods come in pairs, one with and one without the name
parameter.
Not only do the add ConstrucorName -methods simplify the program, they are also
more efficient than the two equivalent calls because an intermediate copy can be
avoided.
All the building blocks are now in place to implement a method that creates a
model. The diet problem consists of finding the least expensive diet using a set of
foods such that all nutritional requirements are satisfied. The example in this
chapter builds the specific diet model, chooses an optimizing algorithm, and shows
how to access more detailed information about the solution.
The example includes a set of foods, where food j has a unit cost of foodCost[j].
The minimum and maximum amount of food j which can be used in the diet is
designated foodMin[j] and foodMax[j], respectively. Each food j also has a
nutritional value nutrPerFood[i][j] for all possible nutrients i. The nutritional
requirement states that in the diet the amount of every nutrient i consumed must
be within the bounds nutrMin[i] and nutrMax[i].
Mathematically, this problem can be modeled using a variable Buy[j] for each food
j indicating the amount of food j to buy for the diet. Then the objective is:
minimize j (Buy[j] * foodCost[j])
The nutritional requirements mean that the following conditions must be observed;
that is, for all i :
nutriMin[i] i nutrPerFood[i][j] * Buy[j] nutriMax[i]
Finally, every food must be within its bounds; that is, for all j :
foodMin[j] Buy[j] foodMax[j]
With what you have learned so far, you can implement a method that creates such
a model.
static void buildModelByRow(IloModeler model,
Data data,
IloNumVar[] Buy,
IloNumVarType type)
throws IloException {
int nFoods = data.nFoods;
int nNutrs = data.nNutrs;
The function accepts several arguments. The argument model is used for two
purposes:
The argument data contains the data for the model to be built. The argument Buy
is an array, initialized to length data.nFoods, containing the model's variables.
Finally, the argument type is used to specify the type of the variables being
created.
The function starts by creating the modeling variables, one by one, and storing
them in the array Buy. Each variable j is initialized to have bounds
data.foodMin[j] and data.foodMax[j] and to be of type type.
The variables are first used to construct the objective function expression with the
method model.scalProd(foodCost, Buy). This expression is immediately used to
create the minimization objective which is directly added to the active model by
addMinimize.
In the loop that follows, the nutritional constraints are added. For each nutrient i
the expression representing the amount of nutrient in a diet with food levels Buy is
computed using model.scalProd(nutrPerFood[i], Buy). This amount of nutrient
must be within the ranged constraint bounds nutrMin[i] and nutrMax[i]. This
constraint is created and added to the active model with addRange.
Note that function buildModelByRow uses the interface IloModeler rather than
IloCplex. This convention allows the function to be called without change in
another implementation of IloModeler, such as IloCP.
After you have created an optimization problem in your active model, you solve it
by means of the IloCplex object. For an object named cplex, for example, you
solve by calling the method like this:
cplex.solve();
The solve method returns a Boolean value specifying whether or not a feasible
solution was found and can be queried. However, when true is returned, the
solution that was found may not be the optimal one; for example, the optimization
may have terminated prematurely because it reached an iteration limit.
For example, an Optimal status indicates that an optimal solution has been found
and can be queried, whereas an Infeasible status indicates that the active model
has been proven to be infeasible. See the online CPLEX Java API Reference Manual
for more information about these statuses.
More detailed information about the status of the optimizer can be queried with
method getCplexStatus returning an object corresponding to CPLEX status codes.
Again the online CPLEX Java API Reference Manual contains further information
about this.
If a solution has been found with the solve method, you access it and then query
it using a variety of methods. The objective function can be accessed by the call:
double objval = cplex.getObjValue();
The values of individual modeling variables for the solution are accessed by the
methods IloCplex.getValue, for example:
double x1 = cplex.getValue(var1);
Frequently, solution values for an array of variables are needed. Rather than your
having to implement a loop to query the solution values variable by variable, use
the method IloCplex.getValues to do so with only one function call, like this:
double[] x = cplex.getValues(vars);
These ideas apply to solvingand printing the solution to the diet problem as well.
IloCplex cplex = new IloCplex();
IloNumVar[] Buy = new IloNumVar[nFoods];
// Solve model
if ( cplex.solve() ) {
System.out.println();
System.out.println(Solution status = + cplex.getStatus());
System.out.println();
System.out.println( cost = + cplex.getObjValue());
for (int i = 0; i < nFoods; i++) {
System.out.println( Buy + i + = +
cplex.getValue(Buy[i]));
}
System.out.println();
}
These lines of code start by creating a new IloCplex object and passing it, along
with the raw data in another object, either to the method buildModelByColumn or to
the method buildModelByRow. The array of variables returned by it is saved as the
array Buy. Then the method solve optimizes the active model and, upon success,
the application prints solution information.
Choosing an optimizer
Describes algorithms implemented in the Java API for solving optimization
problems.
Overview
Introduces the Java methods that solve a problem in CPLEX.
To choose the optimizer to solve a single continous model, or the first continuous
relaxation in a series, use
IloCplex.setParam(IloCplex.IntParam.RootAlg, alg)
where alg is an integer specifying the algorithm type. Table 10 on page 39 shows
you the available types of algorithms.
You are not obliged to set this parameter. In fact, if you do not explicitly call
IloCplex.setParam(IloCplex.IntParam.RootAlg, alg), CPLEX will use the default:
IloCplex.Algorithm.Auto. In contrast, any invalid setting, such as a value other
than those of the enumeration, will produce an error message.
In other words, use the root algorithm parameter to choose the optimizer for
solving at the root, and use the node algorithm parameter to choose the optimizer
for solving at the subsequent nodes of the problem.
Though CPLEX defaults will prove sufficient to solve most problems, CPLEX offers
a variety of other parameters to control various algorithmic choices. CPLEX
parameters can take values of type boolean, int, double, and string. The
parameters are accessed via parameter names defined in classes
IloCplex.BooleanParam, IloCplex.IntParam, IloCplex.DoubleParam, and
IloCplex.StringParam corresponding to the parameter type.
Parameters
Introduces parameters as a means to control optimizers in the Java API.
For example:
cplex.setParam(IloCplex.BooleanParam.PreInd, false);
sets the Boolean parameter PreInd to false, instructing CPLEX not to apply
presolve before solving the problem.
Thus, the suggested method for setting steepest-edge pricing for use with the
primal simplex algorithm looks like this:
cplex.setParam(IloCplex.IntParam.PPriInd,
IloCplex.PrimalPricing.Steep);
When CPLEX is solving a MIP, another important way for you to control the
solution process is by providing priority orders and branching directions for
variables.
Priority orders and branch directions allow you to control the branching performed
during branch & cut in a static way.
Overview
Introduces methods in Java to access information about a solution in CPLEX.
Depending on the model being solved and the algorithm being used, more
solution information is generated in addition to the objective value and solution
values for variables and slacks.
The class IloCplex offers a variety of ways to write information about a solution
that it has found.
After solving, you can call the method IloCplex.writeMIPstart to write a MIP
solution suitable for a restart. The file it writes is in MST format. That format is
documented by MST file format: MIP starts in the CPLEX File Formats Reference
Manual.
When solving an LP or QP, all the algorithms also compute dual solution
information that your application can then query. (However, no dual information is
available for QCP models.) You can access reduced costs by calling the method
IloCplex.getReducedCost or IloCplex.getReducedCosts. Similarly, you can access
dual solution values for the ranged constraints of the active model by using the
methods IloCplex.getDual or IloCplex.getDuals.
Basis information
Describes methods of the Java API to access basis information.
IloCplex.BasisStatus.Basic ,
IloCplex.BasisStatus.AtLower ,
IloCplex.BasisStatus.AtUpper, and
IloCplex.BasisStatus.FreeOrSuperbasic .
The availability of a basis for an LP allows you to perform sensitivity analysis for
your model. Such analysis tells you by how much you can modify your model
without affecting the solution you found. The modifications supported by the
sensitivity analysis function include variable bound changes, changes to the
bounds of ranged constraints, and changes to the objective function. They are
analyzed by methods IloCplex.getBoundSA, IloCplex.getRangeSA,
IloCplex.getRHSSA and IloCplex.getObjSA, respectively.
An important feature of CPLEX is that even if no feasible solution has been found,
(that is, if cplex.solve returns false), some information about the problem can
still be queried. All the methods discussed so far may successfully return
information about the current (infeasible) solution that CPLEX maintains.
Solution quality
Describes methods of the Java API to access information about the quality of a
solution.
IloCplex provides the method getQuality to allow you to analyze the quality of
the solution. Several quality measures are defined in class IloCplex.QualityType.
For example, to query the maximal bound violation of variables or slacks of the
solution found by cplex.solve, call getQuality, like this:
IloCplex.QualityType inf = cplex.getQuality(IloCplex.QualityType.MaxPrimalInfeas);
The variable or constraint for which this maximum infeasibility occurs can be
queried by inf.getNumVar or inf.getRange, one of which returns null. Not all
quality measures are available for solutions generated by different optimizers. See
the CPLEX Java API Reference Manual for further details.
Objects of type IloLPMatrix are provided for use with IloCplex to express
constraint matrices rather than individual constraints. An IloLPMatrix object
allows you to view a set of ranged constraints and the variables used by them as a
matrix, that is, as: L Ax U
(or cplex.addLPMatrix to add it immediately to the active model). The rows and
columns are then added to it by specifying the non-zero matrix coefficients.
Alternatively, you can add complete IloRange and IloNumVar objects to it to create
new rows and columns. When adding ranged constraints, columns will be
implicitly added for all the variables in the constraint expression that do not
already correspond to a column of the IloLPMatrix. The IloLPMatrix object will
make sure of consistency between the mapping of rows to constraints and columns
to variables. For example, if a ranged constraint that uses variables not yet part of
the IloLPMatrix is added to the IloLPMatrix, new columns will automatically be
added and associated to those variables.
See the online CPLEX Java API Reference Manual for more information about
IloLPMatrix methods.
Modeling by column
Introduces modeling by columns as implemented in the Java API.
The concept of modeling by column modeling comes from the matrix view of
mathematical programming problems. Starting from a (degenerate) constraint
matrix with all its rows but no columns, you populate it by adding columns to it.
The columns of the constraint matrix correspond to variables.
can be used to create a new variable and install it in the objective function
represented by obj with a linear coefficient of 1.0 and in the ranged constraint rng
with a linear coefficient of 2.0 .
After you have created the proper column object, use it to create a new variable by
passing it as the first parameter to the variable constructor. The newly created
variable will be immediately installed in existing modeling objects as defined by
the IloColumn object that has been used. So this line:
IloNumVar var = cplex.numVar(col, 0.0, 1.0);
creates a new variable with bounds 0.0 and 1.0 and immediately installs it in the
objective obj with linear coefficient 1.0 and in the ranged constraint rng with
linear coefficient 2.0 .
All constructor methods for variables come in pairs, one with and one without a
first IloColumn parameter. Methods for constructing arrays of variables are also
provided for modeling by column. These methods take an IloColumnArray object
as a parameter that defines how each individual new variable is to be installed in
existing modeling objects.
The problem solved in this example is to minimize the cost of a diet that satisfies
certain nutritional constraints. You might also want to compare this approach
through the Java API of CPLEX with similar applications in other programming
languages:
v Example: optimizing the diet problem in C++ on page 21
v Example: optimizing the diet problem in C#.NET on page 56
v Example: optimizing the diet problem in the Callable Library on page 72
This example was chosen because it is simple enough to be viewed from a row as
well as from a column perspective. Both ways are shown in the example. In this
example, either perspective can be viewed as natural. Only one approach will seem
natural for many models, but there is no general way of deciding which is more
appropriate (rows or columns) in a particular case.
The example starts by evaluating the command line arguments and reading the
input data file. The input data of the diet problem is read from a file using an
object of the embedded class Diet.Data. Its constructor requires a file name as an
argument. Using the class InputDataReader, it reads the data from that file. This
class is distributed with the examples, but will not be considered here as it does
not use CPLEX or Concert Technology in any special way.
After the data has been read, the IloCplex modeler/optimizer is created.
IloCplex cplex = new IloCplex();
IloNumVar[] Buy = new IloNumVar[nFoods];
The array IloNumVar[] Buy is also created where the modeling variables will be
stored by buildModelByRow or buildModelByColumn .
You have already seen a method very similar to buildModelByRow. This function is
called when byColumn is false, which is the case when the example is executed
without the -c command line option; otherwise, buildModelByColumn is called.
Note that unlike buildModelByRow, this method requires IloMPModeler rather than
IloModeler as an argument since modeling by column is not available with
IloModeler .
First, the function creates an empty minimization objective and empty ranged
constraints, and adds them to the active model.
IloObjective cost = model.addMinimize();
IloRange[] constraint = new IloRange[nNutrs];
Empty means that they use a 0 expression. After that the variables are created one
by one, and installed in the objective and constraints modeling by column. For
each variable, a column object must be created. Start by creating a column object
for the objective by calling:
IloColumn col = model.column(cost, data.foodCost[j]);
The column is then expanded to include the coefficients for all the constraints
using col.and with the column objects that are created for each constraint, as in
the following loop:
for (int i = 0; i < nNutrs; i++) {
col = col.and(model.column(constraint[i], data.nutrPerFood[i][j]));
}
When the full column object has been constructed it is finally used to create and
install the new variable like this:
Buy[j] = model.numVar(col, data.foodMin[j], data.foodMax[j], type);
After the model has been created, solving it and querying the solution is
straightforward. What remains to be pointed out is the exception handling. In case
Since none of these three possible exceptions is handled elsewhere, the main
function ends by catching them and issuing appropriate error messages.
The call to the method cplex.end frees the memory that CPLEX uses.
The entire source code listing for the example is available as Diet.java in the
standard distribution at yourCPLEXinstallation /examples/src.
An important feature of CPLEX is that you can modify a previously created model
to consider different scenarios. Furthermore, depending on the optimization model
and algorithm used, CPLEX will save as much information from a previous
solution as possible when optimizing a modified model.
When you add a modeling object such as a ranged constraint to a model, all the
variables used by that modeling object implicitly become part of the model as well.
However, when you remove a modeling object, no variables are implicitly removed
from the model. Instead, variables can only be explicitly removed from a model by
calling IloMPModeler.delete. (The interface IloMPModeler derives from the class
IloModel, among others. It is implemented by the class IloCplex.) This call will
cause the specified variables to be deleted from the model, and thus from all
modeling objects in the model that are using these variables. In other words,
deleting variables from a model may implicitly modify other modeling objects in
that model.
The API of specific modeling objects may provide modification methods. For
example, you can change variable bounds by using the methods IloNumVar.setLB
and IloNumVar.setUB. Similarly, you can change the bounds of ranged constraints
by using IloRange.setLB and IloRange.setUB.
Because not all the optimizers that implement the IloModeler interface support the
ability to modify a model, modification methods are implemented in IloMPModeler.
These methods are for manipulating the linear expressions in ranged constraints
and objective functions used with IloCplex. The methods
IloMPModeler.setLinearCoef, IloMPModeler.setLinearCoefs, and
IloMPModeler.addToExpr apply in this situation.
A variable can be used only in at most one conversion object, or the model will no
longer be unambiguously defined. This convention does not imply that the type of
a variable can be changed only once and never again after that. Instead, you can
remove the conversion object and add a new one to implement consecutive
variable type changes. To remove the conversion object, use the method
IloModel.remove .
Prerequisites
Prepares the reader for this tutorial.
This tutorial walks you through an application based on the widely published diet
problem.
The .NET API can be used from any progamming language in the .NET
framework. This tutorial concentrates on an example using C#.NET. There are also
examples of VB.NET (Visual Basic in the .NET framework) delivered with IBM
ILOG CPLEX in yourCPLEXhome \examples\src. Because of their .NET framework,
those VB.NET examples differ from the traditional Visual Basic examples that may
already be familiar to some CPLEX users.
Note:
For hints about checking your installation of CPLEX and Concert Technology for
.NET users, see the online manual Getting Started. It is also a good idea to try the
tutorial for .NET users in that manual before beginning this one.
Describe
States the aim of the tutorial and describes the finished application.
The aim of this tutorial is to build a simple application with CPLEX and Concert
Technology for .NET users. The tutorial is based on the well known diet problem:
to minimize the cost of a daily diet that satisfies certain nutritional constraints. The
conventional statement of the problem assumes data indicating the cost and
nutritional value of each available food.
yourCPLEXhome \examples\data\diet.dat
Write a natural language description of the problem and answer these questions:
v What is known about this problem?
v What are the unknown pieces of information (the decision variables) in this
problem?
v What are the limitations (the constraints) on the decision variables?
v What is the purpose (the objective) of solving this problem?
What is known?
The upper and lower bounds on the foods to be purchased for the diet
The amount of each food purchased must not exceed what is available.
foodCost = reader.ReadDoubleArray();
foodMin = reader.ReadDoubleArray();
foodMax = reader.ReadDoubleArray();
nutrMin = reader.ReadDoubleArray();
nutrMax = reader.ReadDoubleArray();
nutrPerFood = reader.ReadDoubleArrayArray();
nFoods = foodMax.Length;
nNutrs = nutrMax.Length;
if ( nFoods != foodMin.Length ||
nFoods != foodMax.Length )
throw new ILOG.CONCERT.Exception("inconsistent data in file "
+ filename);
if ( nNutrs != nutrMin.Length ||
nNutrs != nutrPerFood.Length )
throw new ILOG.CONCERT.Exception("inconsistent data in file "
+ filename);
for (int i = 0; i < nNutrs; ++i) {
if ( nutrPerFood[i].Length != nFoods )
throw new ILOG.CONCERT.Exception("inconsistent data in file "
+ filename);
}
}
}
The input data of the diet problem is read from a file into an object of the nested
class Diet.Data. Its constructor requires a file name as an argument. Using an
object of the class InputDataReader, your application reads the data from that file.
Model
Describes the modeling step of this tutorial.
Go to the comment Step 3 in Dietlesson.cs, and add this statement to create the
Cplex model for your application.
Cplex cplex = new Cplex();
Go to the comment Step 4 in Dietlesson.cs, and add this statement to create the
array of numeric variables that will appear in the solution.
INumVar[] Buy = new INumVar[nFoods];
Go to the comment Step 5 in Dietlesson.cs, and add the following lines to specify
whether to build the problem by rows or by columns.
if ( byColumn ) BuildModelByColumn(cplex, data, Buy, varType);
else BuildModelByRow (cplex, data, Buy, varType);
The finished application interprets an option entered through the command line by
the user to apply this conditional statement.
Build by Rows
Introduces reason for a static method in this example.
Go to the comment Step 6 in Dietlesson.cs, and add the following lines to set up
your application to build the model by rows.
internal static void BuildModelByRow(IModeler model,
Data data,
INumVar[] Buy,
NumVarType type) {
int nFoods = data.nFoods;
int nNutrs = data.nNutrs;
Those lines begin the static method to build a model by rows. The next steps in
this tutorial show you the heart of that method.
Go to the comment Step 7 in Dietlesson.cs , and add the following lines to create
a loop that creates the variables of the problem with the bounds specified by the
input data.
for (int j = 0; j < nFoods; j++) {
Buy[j] = model.NumVar(data.foodMin[j], data.foodMax[j], type);
}
The objective function indicates that you want to minimize the cost of the diet
computed as the sum of the amount of each food to buy Buy[i] times the unit
price of that food data.foodCost[i].
Go to the comment Step 9 in Dietlesson.cs, and add the following lines to add
the ranged nutritional constraints to the model.
for (int i = 0; i < nNutrs; i++) {
model.AddRange(data.nutrMin[i],
model.ScalProd(data.nutrPerFood[i], Buy),
data.nutrMax[i]);
}
}
Build by Columns
Introduces the need for a static method in the example.
Go to the comment Step 10 in Dietlesson.cs, and add the following lines to set up
your application to build the problem by columns.
internal static void BuildModelByColumn(IMPModeler model,
Data data,
INumVar[] Buy,
NumVarType type) {
int nFoods = data.nFoods;
int nNutrs = data.nNutrs;
Those lines begin a static method that the next steps will complete.
Go to the comment Step 11 in Dietlesson.cs, and add the following lines to create
empty columns that will hold the objective and ranged constraints of your
problem.
Go to the comment Step 12 in Dietlesson.cs, and add the following lines to create
each of the variables.
for (int j = 0; j < nFoods; j++) {
}
}
For each food j, a column object col is first created to represent how the new
variable for that food is to be added to the objective function and constraints. Then
that column object is used to construct the variable Buy[j] that represents the
amount of food j to be purchased for the diet. At this time, the new variable will
be installed in the objective function and constraints as defined by the column
object col.
Solve
Describes steps in the tutorial to solve the problem.
After you have added lines to your application to build a model, you are ready for
the next steps: adding lines for solving and displaying the solution.
Go to the comment Step 13 in Dietlesson.cs, and add this statement to solve the
problem.
if ( cplex.Solve() ) {
The next steps of this tutorial show you how to add features to your application.
Go to the comment Step 16 in Dietlesson.cs, and add the following lines to read
the data entered by the user at the command line.
for (int i = 0; i < args.Length; i++) {
if ( args[i].ToCharArray()[0] == -) {
switch (args[i].ToCharArray()[1]) {
case c:
byColumn = true;
break;
case i:
varType = NumVarType.Int;
break;
default:
Usage();
return;
}
}
else {
filename = args[i];
break;
}
}
Go to the comment Step 17 in Dietlesson.cs, and add the following lines to show
the user how to use the command correctly (in case of inappropriate input from a
user).
The try part of that try and catch statement is already available in your original
copy of Dietlesson.cs. When you finish the steps of this tutorial, you will have a
complete application ready to compile and execute.
yourCPLEXhome\examples\src\Diet.cs
There is a project for this example, suitable for use in an integrated development
environment, such as Microsoft Visual Studio, at:
The empty lesson, suitable for interactively following this tutorial, is available at:
yourCPLEXhome\examples\tutorials\Dietlesson.cs
If you want to copy an existing model in a .NET application of CPLEX, you might
consider using the method MakeClone of the interface IModel. However, that
interface method must be implemented to copy any elements (such as an objective
function, rows (constraints), or columns (variables)) that you have added to the
original model as you built up the original model and populated it.
That example imports a model into a CPLEX object. In your application, you need
to change the class and location of the LP file to fit your actual situation.
Overview
Offers general remarks and a diagram of the architecture of a Callable Library
application.
IBM ILOG CPLEX includes a callable C library that makes it possible to develop
applications to optimize, to modify, and to interpret the results of mathematical
programming problems whether linear, mixed integer, or convex quadratic ones.
You can use the Callable Library to write applications that conform to many
modern computer programming paradigms, such as client-server applications
within distributed environments, multithreaded applications running on multiple
processors, applications linked to database managers, or applications using flexible
graphic user interface builders, just to name a few.
The Callable Library together with the CPLEX database make up the CPLEX core,
as you see in Figure 3. The CPLEX database includes the computing environment,
its communication channels, and your problem objects. You will associate the core
with your application by calling library routines.
The Callable Library itself contains routines organized into several categories:
Compilation and linking instructions are provided with the files that come in the
standard distribution of CPLEX for your computer platform. Check the
readme.html file for details.
Overview
Outlines steps to include in a Callable Library application.
This topic tells you how to use the Callable Library in your own applications.
Briefly, you must initialize the CPLEX environment, instantiate a problem object,
and fill it with data. Then your application calls one of the CPLEX optimizers to
optimize your problem. Optionally, your application can also modify the problem
object and re-optimize it. CPLEX is designed to support this sequence of
operationsmodification and re-optimization of linear, quadratic, or mixed integer
programming problems (LPs, QPs, or MIPs) efficiently by reusing the current
feasible solution (basis or incumbent) of a problem as its starting point (when
applicable). After it finishes using CPLEX, your application must free the problem
object and release the CPLEX environment it has been using. The following
sections explain these steps in greater detail.
This routine returns a C pointer to the CPLEX environment that it creates. Your
application then passes this C pointer to other CPLEX routines (except CPXmsg). As
a developer, you decide for yourself whether the variable containing this pointer
should be global or local in your application. This routine is time consuming, so
you should call the CPXopenCPLEX routine only once, or as infrequently as possible,
in a program that solves a sequence of problems.
After you have initialized a CPLEX environment, your next step is to instantiate
(that is, create and initialize) a problem object by calling CPXcreateprob. This
routine returns a C pointer to the problem object. Your application then passes this
pointer to other routines of the Callable Library.
Most applications will use only one problem object, though CPLEX allows you to
create multiple problem objects within a given CPLEX environment.
When you instantiate a problem object, it is originally empty. In other words, it has
no constraints, no variables, and no coefficient matrix. CPLEX offers you several
alternative ways to put data into an empty problem object (that is, to populate
your problem object).
v You can make a sequence of calls, in any convenient order, to these routines:
CPXaddcols
CPXaddqconstr
CPXaddrows
CPXchgcoeflist
CPXcopyctype
CPXcopyqsep
CPXcopyquad
CPXnewcols
CPXnewrows
v If data already exist in MPS, SAV, or LP format in a file, you can call
CPXreadcopyprob to read that file and copy the data into the problem object.
Mathematical Programming System (MPS) is an industry-standard format for
organizing data in mathematical programming problems. LP and SAV file
formats are CPLEX-specific formats for expressing linear programming problems
as equations or inequalities. Understanding file formats on page 107 explains
these formats briefly. They are documented in the reference manual CPLEX File
Formats.
v You can assemble arrays of data and then call CPXcopylp to copy the data into
the problem object.
Whenever possible, compute your problem data in double precision (64 bit).
Computers are finite-precision machines, and truncating your data to single
precision (32 bit) can result in unnecessarily ill-conditioned problems For more
information, refer to Numeric difficulties on page 146.
Call one of the CPLEX optimizers to solve the problem object that you have
instantiated and populated. Choosing an optimizer for your LP problem on page
133 explains in greater detail how to choose an appropriate optimizer for your
problem.
Use the routines whose names begin with CPXchg to modify existing objects in the
model, or use the routines CPXaddcols, CPXaddqconstr, CPXaddrows, CPXnewcols, and
CPXnewrows to add new constraints and new variables to the model.
For example, lets say a user has already solved a given LP problem and then
changes the upper bound on a variable by means of an appropriate call to the
Callable Library routine CPXchgbds. CPLEX will then begin any further
optimization from the previous optimal basis. If that basis is still optimal with
respect to the new bound, then CPLEX will return that information without even
needing to refactor the basis.
Use the routine CPXfreeprob to destroy a problem object when your application no
longer needs it. Doing so will free all memory required to solve that problem
instance.
After all the calls from your application to the Callable Library are complete, you
must release the CPLEX environment by calling the routine CPXcloseCPLEX. This
routine tells CPLEX that:
v all application calls to the Callable Library are complete;
v CPLEX should release any memory allocated by CPLEX for this environment.
Tip:
Thread-safety concerns reading as well as writing data. That is, if two threads
attempt to read or write to the same location, trouble may occur.
For example, lets look at the synopses for two routines, CPXgetobjval and CPXgetx,
as they are documented in the Callable Library Reference Manual to clarify this
calling convention. Here is the synopsis of the routine CPXgetobjval:
int CPXgetobjval (CPXCENVptr env, CPXCLPptr lp, double *objval_p);
In that routine, the third argument is a pointer to a variable of type double . To call
this routine from C, declare:
double objval;
The second method declares the array as a local variable, like this:
double x[100];
Then to see the optimal values for columns 5 through 104, for example, you could
write this:
status = CPXgetx (env, lp, x, 5, 104);
For guidance about how to pass values to CPLEX routines from application
languages such as FORTRAN or BASIC that conventionally call by reference, see
Call by reference on page 70 in this manual, and consult the documentation for
those languages.
Data types
Describes the data types available in the C API.
In the Callable Library, CPLEX defines a few special data types for specific CPLEX
objects, as you see in the table Table 14. The types starting with CPXC represent the
corresponding pointers to constant (const) objects.
Table 14. Special data types in the Callable Library.
Data type Is a pointer to Declaration Set by calling
CPXENVptr CPLEX environment CPXENVptr env; CPXopenCPLEX
CPXCENVptr
CPXLPptr CPXCLPptr problem object CPXLPptr lp; CPXcreateprob
CPXNETptr problem object CPXNETptr net; CPXNETcreateprob
CPXCNETptr
CPXCHANNELptr message channel CPXCHANNELptr CPXgetchannels
channel;
When any of these special variables are set to a value returned by an appropriate
routine, that value can be passed directly to other CPLEX routines that require
such arguments. The actual internal type of these variables is a memory address
(that is, a pointer); this address uniquely identifies the corresponding object. If you
are programming in a language other than C, you should choose an appropriate
integer type or pointer type to hold the values of these variables.
The Callable Library does not take ownership of user memory. All arguments are
copied from your user-defined arrays into CPLEX-allocated memory. CPLEX
manages all problem-related memory. After you call a routine that copies data into
a CPLEX problem object, you can free or reuse the memory you allocated as
arguments to the copying routine.
As indicated in Change the problem object on page 62, after you have created a
problem object by calling CPXcreateprob, you can modify the problem in various
ways through calls to routines from the Callable Library. There is no need for you
to allocate extra space in anticipation of future problem modifications. Any limit on
As you modify a problem object through calls to modification routines from the
Callable Library, CPLEX automatically handles memory allocations to
accommodate the increasing size of the problem. In other words, you do not have
to keep track of the problem size nor make corresponding memory allocations
yourself as long as you are using library modification routines such as CPXaddrows
or CPXaddcols.
Most routines in the Callable Library return an integer value, 0 (zero) indicating
success of the call. A nonzero return value indicates a failure. Each failure value is
unique and documented in the Callable Library Reference Manual. However, some
routines are exceptions to this general rule.
Some query routines in the Callable Library return a nonzero value when they are
successful. For example, CPXgetnumcols returns the number of columns in the
constraint matrix (that is, the number of variables in the problem object). However,
most query routines return 0 (zero) reporting success of the query and entail one
or more arguments (such as a buffer or character string) to contain the results of
the query. For example, CPXgetrowname returns the name of a row in its name
argument.
It is extremely important that your application check the status (whether the status
is indicated by the return value or by an argument) of the routine that it calls
before it proceeds.
Symbolic constants
Describes coding conventions about symbolic constants in the C API.
Most CPLEX routines return or require values that are defined as symbolic
constants in the header file (that is, the include file) cplex.h. This practice of using
symbolic constants, rather than hard-coded numeric values, is highly recommend.
Symbolic names improve the readability of calling applications. Moreover, if
numeric values happen to change in subsequent releases of the product, the
symbolic names will remain the same, thus making applications easier to maintain.
Parameter routines
Describes routines of the C API to control CPLEX parameters.
Null arguments
Describes coding conventions of the C API with respect to null arguments.
Certain CPLEX routines that accept optional arguments allow you to pass a NULL
pointer in place of the optional argument. The documentation of those routines in
the Callable Library Reference Manual shows explicitly whether NULL pointer
arguments are acceptable. (Passing NULL arguments is an effective way to avoid
allocating unnecessary arrays.)
Within the linear programming data structure, the rows and columns that
represent constraints and variables are referenced by an index number. Each row
and column may optionally have an associated name. If you add or delete rows,
the index numbers usually change:
v for deletions, CPLEX decrements each reference index above the deletion point;
and
v for additions, CPLEX makes all additions at the end of the existing range.
However, CPLEX updates the names so that each row or column index will
correspond to the correct row or column name. Double checking names against
index numbers is the only sure way to reveal which changes may have been made
to matrix indices in such a context. The routines CPXgetrowindex and
CPXgetcolindex translate names to indices.
Character strings
Describes coding conventions of the C API with respect to character strings.
If you inadvertently make an error entering problem data, the problem object will
not correspond to your intentions. One possible result may be a segmentation fault
or other disruption of your application. In other cases, CPLEX may solve a
different model from the one you intended, and that situation may or may not
result in error messages from CPLEX.
To help you detect this kind of error, you can set the data consistency checking
switch parameter CPX_PARAM_DATACHECK(int) to the value CPX_ON to activate
additional checking of array arguments for CPXcopyData, CPXreadData, and
CPXchgData routines (where Data varies). The additional checks include:
v invalid sense /ctype /sostype values
v indices out of range, for example, rowind numrows
v duplicate entries
v matbeg or sosbeg array with decreasing values
v NANs in double arrays
v NULLs in name arrays
This additional checking may entail overhead (time and memory). When the
parameter is set to CPX_OFF, only simple checks are performed, for example,
checking for the existence of the environment.
CPLEX also provides diagnostic routines to look for common errors in the
definition of problem data. In the standard distribution of CPLEX, the file check.c
contains the source code for these routines:
v CPXcheckcopylp
v CPXcheckcopylpwnames
v CPXcheckcopyqpsep
v CPXcheckcopyquad
v CPXcheckcopyaddrows
v CPXcheckaddcols
v CPXcheckchgcoeflist
v CPXcheckvals
v CPXcheckcopyctype
v CPXcheckcopysos
v CPXcheckcopynet
Each of those routines performs a series of diagnostic tests of the problem data and
issues warnings or error messages whenever it detects a potential error. To use
them, you must compile and link the file check.c. After compiling and linking that
file, you will be able to step through the source code of these routines with a
debugger to help isolate problems.
If you have observed anomalies in your application, you can exploit this diagnostic
capability by calling the appropriate routines just before a change or copy routine.
The diagnostic routine can then detect errors in the problem data that could
subsequently cause inexplicable behavior.
Those checking routines send all messages to one of the standard CPLEX message
channels. You capture that output by setting a parameter (that is, the messages to
screen switch CPX_PARAM_SCRIND) if you want messages directed to your screen or
by calling the routine CPXsetlogfile if you want to direct messages to a log file.
Callbacks
Describes coding conventions of the C API with respect to callbacks.
The Callable Library supports callbacks so that you can define functions that will
be called at crucial points in your application:
v during the presolve process;
v once per iteration in a linear programming or quadratic programming routine;
and
v at various points, such as before node processing, in a mixed integer
optimization.
Portability
Describes coding conventions of the C API to make portable applications.
CPXPUBLIC
All Callable Library routines except CPXmsg have the word CPXPUBLIC as part of
their prototype. On UNIX platforms, this has no effect. On Win32 platforms, the
CPXPUBLIC designation tells the compiler that all of the CPLEX functions are
compiled with the Microsoft __stdcall calling convention. The exception CPXmsg
cannot be called by __stdcall because it takes a variable number of arguments.
Consequently, CPXmsg is declared as CPXPUBVARARGS; that calling convention is
defined as __cdecl for Win32 systems.
Function pointers
All Callable Library routines that require pointers to functions expect the passed-in
pointers to be declared as CPXPUBLIC. Consequently, when your application uses
such routines as CPXaddfuncdest , CPXsetlpcallbackfunc, and
CPXsetmipcallbackfunc, it must declare the user-written callback functions with the
CPXPUBLIC designation. For UNIX systems, this has no effect. For Win32 systems,
this will cause the callback functions to be declared with the __stdcall calling
convention. For examples of function pointers and callbacks, see Example: using
callbacks lpex4.c on page 493 and Example: Callable Library message channels
on page 115.
File pointers
File pointer arguments for Callable Library routines should be declared with the
type CPXFILEptr . On UNIX platforms, this practice is equivalent to using the file
pointer type. On Win32 platforms, the file pointers declared this way will
correspond to the environment of the CPLEX DLL. Any file pointer passed to a
Callable Library routine should be obtained with a call to CPXfopen and closed
with CPXfclose. Callable Library routines with file pointer arguments include
CPXsetlogfile, CPXaddfpdest , CPXdelfpdest , and CPXfputs . Callable Library
routines for message channels on page 114 discusses most of those routines.
String functions
Several routines in the Callable Library make it easier to work with strings. These
functions are helpful when you are writing applications in a language, such as
Visual Basic, that does not allow you to de-reference a pointer. The string routines
in the Callable Library are CPXstrlen, CPXstrcpy, and CPXmsgstr.
The Callable Library can be interfaced with FORTRAN applications. Although they
are no longer distributed with the product, you can download examples of a
FORTRAN application from the technotes of the product support web site. At the
portal of your product, search for CPLEX and FORTRAN to locate relevant
technotes, such as "Calling CPLEX from FORTRAN" or "Calling CPLEX C API
from Fortran using Open Watcom"
Those examples were compiled with CPLEX versions 7.0 and earlier on a
particular platform. Since C-to-FORTRAN interfaces vary across platforms
(operating system, hardware, compilers, etc.), you may need to modify the
examples for your own system.
Whether you need intermediate routines for the interface depends on your
operating system. As a first step in building such an interface, it is a good idea to
study your system documentation about C-to-FORTRAN interfaces. In that context,
this section lists a few considerations particular to CPLEX in building a FORTRAN
interface.
Case-sensitivity
Underscore
On some systems, all FORTRAN external symbols are created with an underscore
character (that is, _) added to the end of the symbol name. Some systems have an
option to turn off this feature. If you are able to turn off those postpended
underscores, you may not need other glue routines.
Six-character identifiers
Call by reference
Pointers
Strings
When you pass strings to routines of the Callable Library, they expect C strings;
that is, strings terminated by an ASCII NULL character, denoted \0 in C.
Consequently, when you pass a FORTRAN string, you must add a terminating
NULL character; you do so by means of the FORTRAN intrinsic function CHAR(0) .
C++ interface
Describes conventions of the C API for interface with C++ applications.
The CPLEX header file, cplex.h , includes the extern C statements necessary for
use with C++. If you wish to call the CPLEX C interface from a C++ application,
rather than using Concert Technology, you can include cplex.h in your C++
source.
Some CPLEX parameters assume values of type double; others assume values of
type int; others are strings (that is, C-type char*). Consequently, in the Callable
Library, there are sets of routines (one for int, one for double, one for char*) to
access and to change parameters that control the CPLEX environment and guide
optimization.
For example, the routine CPXinfointparam shows you the default, the maximum,
and the minimum values of a given parameter of type int , whereas the routine
CPXinfodblparam shows you the default, the maximum, and the minimum values
of a given parameter of type double , and the routine CPXinfostrparam shows you
the default value of a given string parameter. Those three Callable Library routines
observe the same conventions: they return 0 (zero) from a successful call and a
nonzero value in case of error.
The routine CPXinfostrparam differs slightly in that it does not expect pointers to
variables to hold the minimum and maximum values as those concepts do not
apply to a string parameter.
To access the current value of a parameter that interests you from the Callable
Library, use the routine CPXgetintparam for parameters of type int ,
CPXgetdblparam for parameters of type double , and CPXgetstrparam for string
parameters. These routines also expect arguments to indicate the environment, the
parameter you want to check, and a pointer to a variable to hold that current
value.
No doubt you have noticed in other chapters of this manual that you can set
parameters from the Callable Library. There are, of course, routines in the Callable
Library to set such parameters: one sets parameters of type int ; another sets
parameters of type double ; another sets string parameters.
v CPXsetintparam accepts arguments to indicate:
the environment; that is, a pointer of type CPXENVptr returned by
CPXopenCPLEX;
the parameter to set; this routine sets parameters of type int ;
the value you want the parameter to assume.
v CPXsetdblparam accepts arguments to indicate:
the environment; that is, a pointer of type CPXENVptr returned by
CPXopenCPLEX;
the parameter to set; this routine sets parameters of type double ;
the value you want the parameter to assume.
v CPXsetstrparam accepts arguments to indicate:
the environment; that is, a pointer of type CPXENVptr returned by
CPXopenCPLEX;
the parameter to set; this routine sets parameters of type const char* ;
the value you want the parameter to assume.
The Parameters Reference Manual documents the type of each parameter (int,
double, char*) along with the symbolic constant and reference number representing
the parameter.
The routine CPXsetdefaults resets all parameters (except the log file) to their
default values, including the CPLEX callback functions. This routine resets the
callback functions to NULL. Like other Callable Library routines to manage
parameters, this one accepts an argument specifying the environment, and it
returns 0 for success or a nonzero value in case of error.
The optimization problem solved in this example is to compose a diet from a set of
foods, so that the nutritional requirements are satisfied and the total cost is
minimized. The example diet.c illustrates these points.
Problem representation
Describes how to represent the problem by means of the C API.
The problem contains a set of foods, which are the modeling variables; a set of
nutritional requirements to be satisfied, which are the constraints; and an objective
of minimizing the total cost of the food. There are two ways to look at this
problem:
v The problem can be modeled in a row-wise fashion, by entering the variables
first and then adding the constraints on the variables and the objective function.
v The problem can be modeled in a column-wise fashion, by constructing a series
of empty constraints and then inserting the variables into the constraints and the
objective function.
The diet problem is equally suited for both kinds of modeling. In fact you can even
mix both approaches in the same program: If a new food product is introduced,
you can create a new variable for it, regardless of how the model was originally
built. Similarly, is a new nutrient is discovered, you can add a new constraint for
it.
You walk into the store and compile a list of foods that are offered. For each food,
you store the price per unit and the amount they have in stock. For some foods
that you particularly like, you also set a minimum amount you would like to use
in your diet. Then for each of the foods you create a modeling variable to represent
the quantity to be purchased for your diet.
Now you get a medical book and look up which nutrients are known and relevant
for you. For each nutrient, you note the minimum and maximum amount that
should be found in your diet. Also, you go through the list of foods and decide
how much a food item will contribute for each nutrient. This gives you one
constraint per nutrient, which can naturally be represented as a range constraint
where i represents the index of the nutrient under consideration, nutrmin[i] and
nutrmax[i] the minimum and maximum amount of nutrient i and nutrper[i][j]
the amount of nutrient i in food j . Finally, you specify your objective function to
minimize, like this:
You start with the medical book where you compile the list of nutrients that you
want to make sure are properly represented in your diet. For each of the nutrients,
you create an empty constraint:
where ... is left to be filled after you walk into your store. You also set up the
objective function to minimize the cost. Constraint i is referred to as rng[i] and
the objective is referred to as cost .
Now you walk into the store and, for each food, you check its price and nutritional
content. With this data you create a variable representing the amount you want to
buy of the food type and install it in the objective function and constraints. That is
you create the following column:
IloObjective obj = cplex.objective(sense, expr, name);
cplex.setParam(IloCplex.IntParam.PPriInd,
IloCplex.PrimalPricing.Steep);
IloColumn col = cplex.column(obj, 1.0).and(cplex.column(rng, 2.0));
cost(foodCost[j]) "+" "sum_i" (rng[i](nutrper[i][j]))
where the notation "+ " and "sum " indicates that you add the new variable j to
the objective cost and constraints rng[i] . The value in parentheses is the linear
coefficient that is used for the new variable.
Program description
Describes the architecture of an application from the C API solving the diet
problem.
All definitions needed for a Callable Library application are imported when your
application includes the file <ilcplex/cplex.h> at the beginning of the application.
After a number of lines that establish the calling sequences for the routines that are
to be used, the main function of the application begins by checking for correct
command line arguments, printing a usage reminder and exiting in case of errors.
Next, the data defining the problem are read from a file specified in the command
line at run time. The details of this are handled in the routine readdata. In this file,
cost, lower bound, and upper bound are specified for each type of food; then
minimum and maximum levels of several nutrients needed in the diet are
specified; finally, a table giving levels of each nutrient found in each unit of food is
given. The result of a successful call to this routine is two variables nfoods and
nnutr containing the number of foods and nutrients in the data file, arrays cost,
lb, ub containing the information about the foods, arrays nutrmin, nutrmax
containing nutritional requirements for the proposed diet, and array nutrper
containing the nutritional value of the foods.
Preparations to build and solve the model with CPLEX begin with the call to
CPXopenCPLEX. This establishes a CPLEX environment to contain the LP problem.
Two alternative approaches to filling this problem object are implemented in this
program, populatebyrow and populatebycolumn, and which one is executed is set at
run time by an argument on the command line. The routine populatebyrow
operates by first defining all the columns through a call to CPXnewcols and then
repeatedly calls CPXaddrows to enter the data of the constraints. The routine
populatebycolumn takes the complementary approach of establishing all the rows
first with a call to CPXnewrows and then sequentially adds the column data by calls
to CPXaddcols.
The model is at this point ready to be solved, and this is accomplished through the
call to CPXlpopt, which by default uses the dual simplex optimizer.
After this, the program finishes by making a call to CPXsolution to obtain the
values for each variable in this optimal solution, printing these values, and writing
the problem to a disk file (for possible evaluation by the user) via the call to
CPXwriteprob. It then terminates after freeing all the arrays that have been
allocated along the way.
Complete program
Tells where to find the C implementation of the diet problem online.
Most of the query routines in the Callable Library require your application to
allocate memory for one or more arrays that will contain the results of the query.
In many cases, your applicationthe calling programdoes not know the size of
these arrays in advance. For example, in a call to CPXgetcolsrequesting the matrix
data for a range of columns, your application needs to pass the arrays cmatind and
cmatval for CPLEX to populate with matrix coefficients and row indices. However,
unless your application has carefully kept track of the number of nonzeros in each
column throughout the problem specification and, if applicable, throughout its
modification, the actual length of these arrays remains unknown.
The arrays cmatind and cmatval require one element for each nonzero matrix
coefficient in the requested range of columns. The required length of these arrays,
specified in cmatspace , remains unknown at the time of the query. Your
applicationthe calling programcan discover the length of these arrays by first
calling CPXgetcols with a value of 0 for cmatspace . This call will return an error
status of CPXERR_NEGATIVE_SURPLUS indicating a shortfall of the array length
specified in cmatspace (in this case, 0 ); it will also return the actual number of
matrix nonzeros in the requested range of columns. CPXgetcols deposits this
shortfall as a negative number in the integer pointed to by surplus_p . Your
application can then negate this shortfall and allocate the arrays cmatind and
cmatval sufficiently long to contain all the requested matrix elements.
The following sample of code illustrates this procedure. The first call to CPXgetcols
passes a value of 0 (zero) for cmatspace in order to obtain the shortfall in cmatsz .
The sample then uses the shortfall to allocate the arrays cmatind and cmatval
properly; then it calls CPXgetcols again to obtain the actual matrix coefficients and
row indices.
status = CPXgetcols (env, lp, &nzcnt, cmatbeg, NULL, NULL,
0, &cmatsz, 0, numcols - 1);
if ( status != CPXERR_NEGATIVE_SURPLUS ) {
if ( status != 0 ) {
CPXmsg (cpxerror,
"CPXgetcols for surplus failed, status = %d\n", status);
goto TERMINATE;
}
CPXmsg (cpxwarning,
"All columns in range [%d, %d] are empty.\n",
0, (numcols - 1));
}
cmatsz = -cmatsz;
cmatind = (int *) malloc ((unsigned) (1 + cmatsz)*sizeof(int));
cmatval = (double *) malloc ((unsigned) (1 + cmatsz)*sizeof(double));
if ( cmatind == NULL || cmatval == NULL ) {
CPXmsg (cpxerror, "CPXgetcol mallocs failed\n");
status = 1;
goto TERMINATE;
}
status = CPXgetcols (env, lp, &nzcnt, cmatbeg, cmatind, cmatval,
cmatsz, &surplus, 0, numcols - 1);
if ( status ) {
CPXmsg (cpxerror, "CPXgetcols failed, status = %d\n", status);
goto TERMINATE;
}
That sample code (or your application) does not need to set the length of the array
cmatbeg . The array cmatbeg has one element for each column in the requested
range. Since this length is known ahead of time, your application does not need to
call a query routine to calculate it. More generally, query routines use surplus
This example uses the Callable Library query routine CPXgetcolname to get the
column names from a problem object. To do so, it applies the programming pattern
just outlined in Using surplus arguments for array allocations on page 75. It
derives from the example lpex2.c from the manual Getting Started. This
query-routine example differs from that simpler example in several ways:
v The example calls CPXgetcolname twice after optimization: the first call discovers
how much space to allocate to hold the names; the second call gets the names
and stores them in the arrays cur_colname and cur_colnamestore.
v When the example prints its answer, it uses the names as stored in cur_colname.
If no names exist there, the example creates generic names.
This example assumes that the current problem has been read from a file by
CPXreadcopyprob. You can adapt the example to use other query routines to get
information about any problem read from a file.
Before you begin working with the Python API of CPLEX, consider the topic
Setting up the Python API of CPLEX and the tutorial Python tutorial, both in
Getting Started with CPLEX.
Why Python?
Introduces the Python API of CPLEX.
The Python application programming interface (API) of CPLEX supports the full
functionality of CPLEX. Like other APIs, it enables a user to design models and
solve problems in these disciplines:
v linear programming (LP),
v mixed integer programming (MIP),
v quadratic programming (QP),
v quadratically constrained programming (QCP),
v second-order cone programming (SOCP),
v mixed integer quadratic programming (MIQP),
v mixed integer quadratically constrained programming (MIQCP).
With those advantages of Python in general, you can use the Python API of CPLEX
in a variety of ways. For example, you can use it to write scripts and applications
that call CPLEX. Because of its interpreter, you can also use the Python API
interactively. Like the Interactive Optimizer, the Python API of CPLEX supports
important features of CPLEX, such as:
v Improving performance with the Chapter 9, Tuning tool, on page 121
In the Python API of CPLEX, the class Cplex encapsulates an optimization problem
and provides methods to create and modify the model, to solve the problem, and
to query the solution. The class Cplex also offers methods to read data, write
results, or populate a model. Also within this class, there are groups of methods
that facilitate your management of variables, linear constraints, and the objective
function of your model as well as your analysis of the solution. For samples of
these methods at work, see the examples diet.py and rates.py distributed with
the product.
You can use either object names or indices as handles to modify and query
problem data. For example, assume that you have already created c, an instance of
the class Cplex, and that it contains ten variables in this session of Python. Then
the following line adds a new variable to that model and sets the upper bound of
the new variable as 1.0.
>>> c.variables.add(names = ["new_var"], ub = [1.0])
Afterwards, the following lines in the same session query that new variable by its
name and return its index and upper bound, like this:
>>> c.variables.get_index("new_var")
10
>>> c.variables.get_upper_bounds("new_var")
1.0
Likewise, you can query the new variable by its index to return its upper bound.
>>> c.variables.get_upper_bounds(10)
1.0
In fact, you can modify the upper bound of that variable, accessing it either by its
name or by its index, as you see in the following lines of the same session:
>>> c.variables.set_upper_bounds("new_var", 2.0)
>>> c.variables.get_upper_bounds(10)
2.0
>>> c.variables.set_upper_bounds(10, 3.0)
>>> c.variables.get_upper_bounds("new_var")
3.0
Likewise, to modify multiple pieces of data, you can call the method with a list of
pairs of handles and data, like this:
>>> c.variables.set_upper_bounds([("var0", 3.0), ("var1", 4.0)])
>>> c.variables.get_upper_bounds("var0")
3.0
>>> c.variables.get_upper_bounds("var1")
4.0
Methods available to query problem data are four-way polymorphic. You can call
them with a single handle, with two handles, with a list of handles, or with no
handle at all. For example, the following line queries the upper bound of a given
variable by name; that is, the method uses one argument.
>>> c.variables.get_upper_bounds("new_var")
3.0
The following example queries an inclusive range of data by calling the method
with two handles (the first and last index) to return the upper bounds of all the
variables in that range of indices.
>>> c.variables.get_upper_bounds(0, 3)
[1.0, 2.0, 1.0, 1.0]
Indeed, you can query data by calling a method with no arguments at all. For
example, the following line shows a query that returns a list equal in length to the
number of variables.
>>> c.variables.get_upper_bounds()
[1.0, 2.0, 1.0, 1.0, . . ., 3.0]
The Python API also generates formatted reports that include histograms. These
reports are available either interactively in a Python session or programmatically in
a Python application.
To generate a histogram, use methods of the class Cplex. If you are interested in a
histogram based on the rows (constraints) of your model, consider the method
get_histogram of the LinearConstraintInterface. Similarly, if you are interested in
a histogram based on the columns (variables) of your model, consider the method
get_histogram of the VariablesInterface.
Additionally, you can query the histogram object about the number of rows or
columns with a given nonzero count. That is, how many rows have N nonzeros?
Or, how many columns have K nonzeros? Here is a sample interactive session
querying nonzero columns and generating a histogram of them:
>>> import cplex
>>> c = cplex.Cplex("ind.lp")
>>> histogram = c.variables.get_histogram()
>>> print histogram
Nonzero Count: 1 2 3
Number of Columns: 1 6 36
>>> histogram[2]
6
>>> histogram[0:4]
[0, 1, 6, 36]
Continuing in the same session, the following lines use the histogram to report
more elaborate information about nonzeros in that problem:
>>> maxh = max(histogram)
>>> for i, count in histogram:
>>> if histogram[i] == maxh:
>>> print "most common nz count is", maxh, "appearing", count, "times"
most common nz count is 3 appearing 36 times
Tip:
The argument you supply to those methods may be the identifier of a single
quality or it may be a list of identifiers of qualities.
The same approach works for querying the quality of a solution in the solution
pool, but of course you invoke methods of the solution pool object in that case,
like this:
c.solution.pool.get_integer_quality(soln, quality_ID)
c.solution.pool.get_float_quality(soln, quality_ID)
Rather than retrieving information about the quality of a solution one metric at a
time, you can extract the most commonly used metrics with a single line of code.
To do so, use the methods Cplex.solution.get_quality_metrics or
Cplex.solution.pool.get_quality_metrics. You can display the objects returned
by these methods interactively, or you can use those returned objects to query
individual solution metrics.
For information about the quality of a solution in the solution pool, you specify
the solution of interest either by its index or by its name in the solution pool, like
this:
>>> print c.solution.pool.get_quality_metrics(soln)
The object returned by this method get_quality_metrics has many data members
that you can inspect individually. For the names and the conditions under which
they are defined, see the documentation of the class QualityMetrics in the
reference manual of the Python API.
To examine the nonzero values of variables in a solution, the Python API of CPLEX
offers several alternatives.
v You can enter a for-loop interactively to print the solution, like this:
>>> for i, x in enumerate(c.solution.get_values()):
... if (x!=0): #leading spaces for indention
... print "Solution value of ", c.variables.get_names(i), \
... " is ", x
Solution value of Open(1) is 1.0
Solution value of Open(2) is 1.0 ...
v You can enter a lambda expression interactively to print the solution, like this:
>>> print zip([1,2],[3,4]); # built-in function zip
[(1, 3), (2, 4)]
>>> print filter(lambda x:x[1]!=0,
zip(c.variables.get_names(),c.solution.get_values()))
[(Open(1), 1.0), (Open(2), 1.0), ...
v You can write a reusable function to print the solution, like this:
>>> def display_solution_nonzero_values (c):
... for i, x in enumerate(c.solution.get_values()):
... if (x!=0):
... print "Solution value of ",c.variables.get_names(i),\
... " is ", x
>>> display_solution_nonzero_values(c)
Solution value of Open(1) is 1.0
Solution value of Open(2) is 1.0 ...
The Python API of CPLEX also supports display at high precision of nonzero
values in a solution. To take advantage of this feature in your Python applications
of CPLEX, you can define your own function, like this:
>>> def display_solution_nonzero_values_highprecision(c):
... for i, x in enumerate(c.solution.get_values()):
... if (x!=0):
... print "Solution value of ",c.variables.get_names(i),\
Tip:
For users familiar with the hierarchy of parameters in the Interactive Optimizer or
CPLEX MATLAB toolbox, the parameters of the Python API are organized in the
same way.
As objects of the Python API, parameters have methods to access their minimum,
maximum, and default values, as the following lines illustrate.
>>> c.parameters.simplex.tolerances.markowitz.min()
0.0001
>>> c.parameters.simplex.tolerances.markowitz.default()
0.01
>>> c.parameters.simplex.tolerances.markowitz.max()
0.99999000000000005
>>> c.parameters.simplex.tolerances.markowitz.set(2.0)
Traceback ... cplex.exceptions.CplexError: Invalid argument
In the Python API, the callback classes are defined in the module callbacks of the
package cplex. They occupy a hierarchy of classes similar to that of the other
object-oriented APIs, such as C++ or Java.
callbacks.PresolveCallback
callbacks.SimplexCallback
callbacks.BarrierCallback
callbacks.CrossoverCallback
callbacks.TuningCallback
callbacks.MIPInfoCallback
callbacks.MIPCallback
callbacks.BranchCallback
callbacks.UserCutCallback
callbacks.LazyConstraintCallback
callbacks.HeuristicCallback
callbacks.SolveCallback
callbacks.IncumbentCallback
callbacks.NodeCallback
The topic Chapter 35, Using optimization callbacks, on page 477 offers general
guidance about writing and using your own callbacks.
This example shows how to read a problem from a file, myprob.lp, in LP format.
(For more information about LP, the linear programming format, see that topic in
the reference manual, File formats supported by CPLEX.) The example prints the
solution in double precision. It also displays nonzero reduced costs with the names
of the associated variables.
>>> c = cplex.Cplex("myprob.lp")
>>> c.solve()
[. . . CPLEX log . . .]
>>> # print the solution vector in double precision
>>> for val in c.solution.get_values():
... print " %+18.16e" % val
...
+2.5000000000000000e+000
+1.7865900324417692e+002
+1.9999999999999999e-001
>>> # print the nonzero reduced costs and their variable names
>>> for i, dj in enumerate(c.solution.get_reduced_costs()):
... if dj != 0.:
... print "red. cost of ", c.variables.get_names(i), " is ", dj
...
red. cost of x1 is 3.04
This example reads a problem from a file, myprob.mps, formatted in MPS. (For
more information about the math programming standard format MPS, see that
topic in the reference manual, File formats supported by CPLEX.) After setting a
limit on the number of iterations, and selecting primal simplex as the optimizer, it
then loops through the simplex iterations as CPLEX solves the problem, printing
rows (constraints).
>>> c = cplex.Cplex("myprob.mps")
>>> c.parameters.simplex.limits.iterations.set(1)
>>> c.parameters.lpmethod.set(c.parameters.lpmethod.values.primal)
>>> # this while loop will print the tableau after each
>>> # simplex iteration
>>> while c.solution.get_status() != c.solution.status.optimal:
... c.solve()
... print " CURRENT TABLEAU "
... for tableau_row in c.solution.advanced.binvarow():
... print tableau_row
... print
...
You can print only selected rows of the tableau by passing the names of rows or
the indices of rows to the method binvarow. Another method, binvacol, returns
columns of a tableau. The methods binvrow and binvcol return the inverted basis
matrix.
Consider, for example, a sequence of related problems. The sequence begins with a
model read from a formatted file, myprob.mps. (For more information about the
math programming standard format MPS, see that topic in the reference manual,
File formats supported by CPLEX.) Successive problems in the sequence reset the
lower bound of the variable x0 and solve the model again with the new lower
bound. The example prints the CPLEX log for each solution in a sequence of files
named lb_set_to_0.log, lb_set_to_1.log, and so forth.
>>> c = cplex.Cplex("myprob.mps")
>>> for i in range(10):
... c.set_results_stream("lb_set_to_" + str(i) + ".log")
... c.variables.set_lower_bounds("x0", 1.0 * i)
... c.solve()
... if c.solution.get_status() == c.solution.status.infeasible:
... break
...
This example uses the class of informational MIP callbacks, MIPInfoCallback, to set
multiple criteria for termination of a Python application.
Tip:
The class MIPInfoCallback can also query the incumbent solution vector, slacks on
linear constraints, slacks on quadratic constraints, and other measures of progress.
>>> class StopCriterion(cplex.callbacks.MIPInfoCallback):
... def __call__(self):
... if self.get_num_nodes() > 1000:
... if self.get_MIP_relative_gap() < 0.1:
... self.terminate()
... return
... else: # we've processed fewer than 1000 nodes
... if self.get_MIP_relative_gap() < 0.001:
... self.terminate()
... return
>>> c = cplex.Cplex("myprob.mps")
>>> c.register_callback(StopCriterion)
>>> c.solve()
[. . . CPLEX log . . .]
>>> c.solution.MIP.get_mip_relative_gap()
0.093
>>> c.solution.progress.get_num_nodes_processed()
223
Begin by creating a small-scale version of the model for your problem. This
prototype model can serve as a test-bed for your application and a point of
reference during development.
If you decompose your application into manageable components, you can more
easily identify the tools you will need to complete the application. Part of this
decomposition consists of deciding which methods or routines from the CPLEX
Component Libraries your application will call. Such a decomposition will assist
you in testing for completeness; it may also help you isolate troublesome areas of
the application during development; and it will aid you in measuring how much
work is already done and how much remains.
Test interactively
Introduces Interactive Optimizer as debugger.
As indicated in previous topics, CPLEX offers several ways of putting data into
your problem or (more formally) populating the problem object. You must decide
which approach is best adapted to your application, based on your knowledge of
the problem data and application specifications. These considerations may enter
into your decision:
v If your Callable Library application builds the arrays of the problem in memory
and then calls CPXcopylp, it avoids time-consuming reads from disk files.
v In the Callable Library, using the routines CPXnewcols, CPXnewrows, CPXaddcols,
CPXaddrows, and CPXchgcoeflist may help you build modular code that will be
more easily modified and maintained than code that assembles all problem data
in one step.
v An application that reads an MPS or LP file may reduce the coding effort but,
on the other hand, may increase runtime and disk space requirements.
Keep in mind that if an application using the CPLEX Component Libraries reads
an MPS or LP file, then some other program must generate that formatted file. The
data structures used to generate the file can almost certainly be used directly to
build the problem-populating arrays for CPXcopylp or CPXaddrows, a choice
resulting in less coding and a faster, more efficient application.
In short, formatted files are useful for prototyping your application. For production
purposes, assembly of data arrays in memory may be a better enhancement.
Test data
Presents a data checking tool.
Chapter 32, Repairing infeasibilities with FeasOpt, on page 443 introduces a tool
that attempts to repair infeasibility in a model by modifying the model according
to preferences that you express.
Choose an optimizer
Describes optimizer choice in terms of problem type.
After you have instantiated and populated a problem object, you solve it by calling
one of the optimizers available in the CPLEX Component Libraries. Your choice of
optimizer depends on the type of problem:
v Use the primal simplex, dual simplex, or primal-dual barrier optimizers to solve
linear and quadratic programs.
v Use the barrier optimizer to solve quadratically constrained programming
problems.
v The network optimizer is appropriate for solving linear and quadratic programs
with large embedded networks.
v Use the MIP optimizer if the problem contains discrete components (binary,
integer, or semi-continuous variables, piecewise linear objective, or SOS sets).
In CPLEX, there are many possible parameter settings for each optimizer.
Generally, the default parameter settings are best for linear programming and
quadratic programming problems, but Chapter 10, Solving LPs: simplex
optimizers, on page 133 and Chapter 13, Solving problems with a
quadratic objective (QP), on page 185 offer more detail about improving
performance with respect to these problems. Integer programming problems are
more sensitive to specific parameter settings, so you may need to experiment with
them, as suggested in Chapter 15, Solving mixed integer programming problems
(MIP), on page 217.
In either case, the Interactive Optimizer in CPLEX lets you try different parameter
settings and different optimizers to decide the best optimization procedure for
your particular application. From what you learn by experimenting with
commands in the Interactive Optimizer, you can more readily choose which
method or routine from the Component Libraries to call in your application.
Comments, written in mixed upper- and lower-case, will prove useful to you at a
later date when you stare at code written months ago and try to figure out what it
does. They will also prove useful to CPLEX team, should you need to send CPLEX
your application for customer support.
Follow conventional formatting practices so that your code will be easier to read,
both for you and for others. Use fewer than 80 characters per line. Put each
statement on a separate line. Use white space (for example, space, blank lines, tabs)
to distinguish logical blocks of code. Display compound loops with clearly
indented bodies. Display if statements like combs; that is, align if and else in the
same column and then indent the corresponding block. Likewise, it is a good idea
to indent the body of compound statements, loops, and other structures distinctly
from their corresponding headers and closing brackets. Use uniform indentation
(for example, three to five spaces). Put at least one space before and after each
relational operator, as well as before and after each binary plus (+ ) and minus (- ).
Use space as you do in normal a natural language, such as English.
Avoid side-effects
A user-defined function should not change the values of its arguments. Do not use
an argument to a function on the lefthand side of an assignment statement in that
function. Since C and C++ pass arguments by value, treat the arguments strictly as
values; do not change them inside a function.
Always declare the return type of functions explicitly. Though C has a historical
tradition of making the default return type of all functions int , it is a good idea
to declare explicitly the return type of functions that return a value, and to use
void for procedures that do not return a value.
Use only one return statement in any function. Limit your use of break statements
to the inside of switch statements. In C, do not use continue statements and limit
your use of goto statements to exit conditions that branch to the end of a function.
Handle error conditions in C++ with a try /catch block and in C with a goto
statement that transfers control to the end of the function so that your functions
have only one exit point.
In other words, control the flow of your functions so that each block has one entry
point and one exit point. This one way in, one way out rule makes code easier to
read and debug.
Localize variables
Avoid global variables at all costs. Code that exploits global variables invariably
produces side-effects which in turn make the code harder to debug. Global
variables also set up peculiar reactions that make it difficult to include your code
successfully within other applications. Also global variables preclude
multithreading unless you invoke locking techniques. As an alternative to global
Scalars (both numbers and characters) that remain constant throughout your
application should be named. For example, if your application includes a value
such as 1000, create a constant with the #define statement to name it. If the value
ever changes in the future, its occurrences will be easy to find and modify as a
named constant.
Code first for clarity. Get your code working accurately first so that you maintain a
good understanding of what it is doing. Then, after it works correctly, look for
opportunities to improve performance.
Debug effectively
Using diagnostic routines for debugging on page 68, contains tips and guidelines
for debugging an application that uses the CPLEX Callable Library. In that context,
a symbolic debugger as well as other widely available development tools are quite
helpful to produce error-free code.
Even a program that has been carefully debugged so that it runs correctly may still
contain errors or features that inhibit its performance with respect to execution
speed, memory use, and so forth. Just as the CPLEX Interactive Optimizer can aid
in your tests for correctness, it can also help you improve performance. It uses the
same routines as the Component Libraries; consequently, it requires the same
amount of time to solve a problem created by a Concert or Callable Library
application.
Use one of these methods, specifying a file type of SAV, to create a binary
representation of the problem object from your application in a SAV file.
v IloCplex::exportModel in the C++ API
v IloCplex.exportModel in the Java API
v Cplex.ExportModel in the .NET API
v CPXwriteprob in the Callable Library (C API)
v cplex.model.write(my_model.sav) in the Python API
Then read that representation into the Interactive Optimizer, and solve it there.
If your application sets parameters, use the same settings in the Interactive
Optimizer.
If you find that your application takes significantly longer to solve the problem
than does the Interactive Optimizer, then you can probably improve the
performance of your application. In such a case, look closely at issues like memory
fragmentation, unnecessary compiler options, inappropriate linker options, and
programming practices that slow the application without causing incorrect results
(such as operations within a loop that should be outside the loop).
The CPLEX Interactive Optimizer distributed with the Component Libraries offers
a way to see what is going on within the CPLEX part of your application when
you observe peculiar behavior in your optimization application. The commands of
the Interactive Optimizer correspond exactly to routines of the Component
Libraries, so anomalies due to the CPLEX-part of your application will manifest
themselves in the Interactive Optimizer as well, and contrariwise, if the Interactive
Optimizer behaves appropriately on your problem, you can be reasonably sure that
routines you call in your application from the Component Libraries work in the
same appropriate way.
With respect to parameter settings, you can write a parameter file with the file
extension .prm from your application by means of one of these methods:
v IloCplex::writeParam in the C++ API
v IloCplex.writeParam in the Java API
v Cplex.WriteParam in the .NET API
v CPXwriteparam in the Callable Library
v write file .prm in the Interactive Optimizer
The Interactive Optimizer can read a .prm file and then set parameters exactly as
they are in your application.
In the other direction, you can use the display command in the Interactive
Optimizer to show the nondefault parameter settings; you can then save those
settings in a .prm file for re-use later. See the topic Saving a parameter specification
file in the reference manual of the Interactive Optimizer for more detail about
using a parameter file in this way.
To use the Interactive Optimizer for debugging, you first need to write a version of
the problem from the application into a formatted file that can then be loaded into
the Interactive Optimizer. To do so, insert a call to the method exportModel or to
the routine CPXwriteprob into your application. Use that call to create a file,
whether an LP, SAV, or MPS formatted problem file. (Understanding file formats
on page 107 briefly describes these file formats.) Then read that file into the
Interactive Optimizer and optimize the problem there.
Note that MPS, LP and SAV files have differences that influence how to interpret
the results of the Interactive Optimizer for debugging. SAV files contain the exact
binary representation of the problem as it appears in your program, while MPS
and LP files are text files containing possibly less precision for numeric data. And,
unless every variable appears on the objective function, CPLEX will probably order
the variables differently when it reads the problem from an LP file than from an
MPS or SAV file. With this in mind, SAV files are the most useful for debugging
using the Interactive Optimizer, followed by MPS files, then finally LP files, in
terms of the change in behavior you might see by use of explicit files. On the other
hand, LP files are often quite helpful when you want to examine the problem,
more so than as input for the Interactive Optimizer. Furthermore, try solving both
the SAV and MPS files of the same problem using the Interactive Optimizer.
Different results may provide additional insight into the source of the difficulty. In
particular, use the following guidelines with respect to reproducing your programs
behavior in the Interactive Optimizer.
If the peculiar behavior that you observed in your application persists in the
Interactive Optimizer, then you must examine the LP or MPS or SAV problem file
to discover whether the problem file actually defines the problem you intended. If
it does not define the problem you intended to optimize, then the problem is being
passed incorrectly from your application to CPLEX, so you need to look at that
part of your application.
Make sure the problem statistics and matrix coefficients indicated by the
Interactive Optimizer match the ones for the intended model in your application.
Use the Interactive Optimizer command display problem stats to verify that the
size of the problem, the sense of the constraints, and the types of variables match
your expectations. For example, if your model is supposed to contain only general
integer variables, but the Interactive Optimizer indicates the presence of binary
variables, check the type variable passed to the constructor of the variable (Concert
Technology) or check the specification of the ctype array and the routine
CPXcopyctype (Callable Library). You can also examine the matrix, objective, and
righthand side coefficients in an LP or MPS file to see if they are consistent with
the values you expect in the model.
CPLEX detects many different kinds of errors and generates exception, warnings,
or error messages about them.
To view warnings and error messages in the Callable Library, you must direct
them either to your screen or to a log file.
v To direct all messages to your screen, use the routine CPXsetintparam to set the
messages to screen switch CPX_PARAM_SCRIND.
v To direct all messages to a log file, use the routine CPXsetlogfile.
Most methods and routines of the Component Libraries return a value that
indicates whether the routine failed, where it failed, and why it failed. This return
value can help you isolate the point in your application where an error occurs.
If you delete a portion of a problem, CPLEX changes not only the dimensions but
also the indices of the problem. If your application continues to use the former
dimensions and indices, errors will occur. Therefore, in parts of your application
that delete portions of the problem, look carefully at how dimensions and indices
are represented.
If you are having difficulty tracking down the source of an anomaly in the heap,
try making certain local variables temporarily global. This debugging trick may
prove useful after your application reads in a problem file or modifies a problem
object. If application behavior changes when you change a local variable to global,
then you may get from it a better idea of the source of the anomaly.
Your application may inadvertently alter the problem and thus produce
unexpected results. To check whether your application is solving the problem you
intended, use the Interactive Optimizer, as in Using the Interactive Optimizer for
debugging on page 96, and the diagnostic routines, as in Using diagnostic
routines for debugging on page 68.
You should not ignore any CPLEX warning message in this situation either, so read
your messages, as in Read your messages on page 98.
If you are working in the Interactive Optimizer, you can use the command
display problem stats to check the problem dimensions.
Check row and column indices. FORTRAN conventionally numbers from one (1 ),
whereas C, C++, Java, and other languages number from zero (0 ). This difference
in numbering conventions can lead to unexpected results with regard to row and
column indices when your application modifies a problem or exercises query
routines.
It is important that you use the FORTRAN declaration IMPLICIT NONE to help you
detect any unintended type conversions, because such inadvertent conversions
frequently lead to strange application behavior.
Tell us
Tells where to report problems.
Finally, if your problem remains unsolved by CPLEX, or if you believe you have
discovered a bug in CPLEX, the team would appreciate hearing from you about it,
through IBM customer support or the IBM ILOG CPLEX users forum.
Note:
There are platforms that limit the size of files that they can read. For example, a
32-bit operating system typically has a smaller addressable memory than a
comparable 64-bit operating system and consequently limits the size of a file there.
If you have created a problem file on one platform, and you find that you are
unable to read the problem on another platform, consider whether the platform
where you are trying to read the file suffers from such a limit on file size. IBM
ILOG CPLEX may be unable to open your problem file due to the size of the file
being greater than the platform limit.
For clarity in this topic, 32-bit API refers to the legacy interface of the Callable
Library (C API) of previous versions of CPLEX. Likewise, 64-bit API refers to the
implementation of this feature supporting very large models in the C, C++, Java,
.NET, and Python APIs.
Certain parameters of CPLEX control very large integers, such as integers relating
to the number of nodes explored during the search for a solution, the number of
nonzero coefficients in a model, the number of iterations during optimization, and
so forth. These parameters are designated by their type CPX_PARAMTYPE_LONG.
Code that you write using this symbol takes advantage fully of the address space
on your platform, whether your platform is 32-bit or 64-bit.
In the CPLEX Callable Library (C API), the legacy 32-bit API is still available, as
well as its familiar include file cplex.h. Each of the routines in the 32-bit API is
duplicated by a corresponding routine, prefixed by CPXX (for CPLEX extended) and
declared in a separate header file, cplexx.h.
For example, the existing 32-bit routine CPXsolution declared in cplex.h has a
corresponding 64-bit routine CPXXsolution declared in cplexx.h. Another include
file, cpxconst.h, declares the common constants that routines declared in either
cplex.h or cplexx.h require; both cplex.h and cplexx.h implicitly include
cpxconst.h.
When you query the number of nonzero coefficients in a model by means of the
64-bit routine CPXXgetnumnz, the routine returns a value of type CPXNNZ. This
symbol, defined in cplexx.h, depends on the current setting of CPX_APIMODEL.
Code written with this interface takes best advantage of the address space
available on your platform, whether 32-bit or 64-bit. In contrast, when you query
the number of nonzero coefficients in a model by means of the 32-bit routine
CPXgetnumnz, the routine returns a value of type int. This query routine will not
fail with very large models, but silent truncation can occur if the return value is in
fact of type LONG and the result cannot be represented by a 32-bit integer.
To take advantage of the 64-bit API of CPLEX in your applications of the Callable
Library (C API), you simply include the header file cplexx.h, which implicitly
includes cpxconst.h; then use routines declared in these header files; that is,
routines prefixed by CPXX in your application.
However, the API does not oblige you to use those types directly. Instead, the API
introduces types that are more descriptive of how the corresponding typed data is
applied.
v CPXSIZE represent the size in bytes (not characters) of names associated with
the model, its variables, and its constraints. Technically, CPXSIZE is a signed
integer type of the same width as size_t. It is defined in cpxconst.h, which in
turn is included by cplex.h and cplexx.h appropriately.
v CPXCNT represents counters. CPXCNT specifies the integer type that cplexx.h
uses to pass potentially large counters, such as number of nodes, number of
iterations, and so forth, to the CPLEX Callable Library (C API) or to read such
data from the library. The type of CPXCNT is 64-bit integer by default.
v CPXDIM represents dimensions. CPXDIM specifies the integer type that
cplexx.h uses to pass dimensions of a model, such as row indices, column
counts, and so forth, to the CPLEX Callable Library (C API) or to read such data
from the library. The type of CPXDIM is 32-bit integer by default.
Tip: CPX_APIMODEL affects only the API, not the library. Internally, the library
always uses 32-bit nonzero counters on 32-bit ports and 64-bit nonzero counters on
64-bit ports. CPX_APIMODEL allows you to specify which types to use when your
application interacts with the library. If the data types requested by
CPX_APIMODEL are different from the types used internally, then the library will
perform appropriate transformations.
If you want to adapt your existing Callable Library (C API) applications to take
advantage of 64-bit counters, for example, to solve models with more than two
billion nonzero coefficients, and you want to do so in a way that is portable in
future applications, use those symbols (typedefs and macros) in your application to
support such portability.
Parameters of this 64-bit type can still be controlled through the 32-bit API as long
as the values do not exceed INT_MAX. If you set a LONG parameter by means of
the 32-bit API to a value greater than or equal to CPX_BIGINT as a representation
of infinity, then CPLEX will quietly transform this symbol into CPX_BIGLONG, the
value that denotes infinity (approximately 9e+18).
To use parameters that accept values greater than INT_MAX in your legacy
application of the 32-bit API, use these corresponding routines from the 64-bit API
instead:
v CPXXsetlongparam
v CPXXgetlongparam
v CPXXinfolongparam
In the Callable Library (C API), you can still invoke these existing 32-bit routines
on all parameter types.
v CPXsetintparam
v CPXgetintparam
v CPXinfointparam
You can, if necessary, (in legacy applications, for example) also invoke these
routines for long parameters on parameters of type CPX_PARAMTYPE_INT. These
long parameter routines work correctly there. This practice of invoking the long
routines for long parameters on parameters of type CPX_PARAMTYPE_INT is not
recommended, but the practice is allowed for convenience with respect to
parameters in Table 17 on page 102. For parameters in that table, this practice
(allowed but not recommended) treats each parameter in a way that does not
depend on the actual port (combination of compiler and operating system) in use.
In the C++ API of CPLEX, the parameters listed in Table 16 on page 102 and
Table 17 on page 102 have migrated from the legacy enumeration
IloCplex::IntParam to the recommended enumeration IloCplex::LongParam. The
parameters listed in the table of port-dependent parameters are managed correctly
and transparently by the C++ API. That is, the user does not need to worry about
the actual type of the parameter. Existing code still compiles without change.
However, CPLEX silently truncates a long parameter value when it is obliged to
assign the value to a variable of type int. Consequently, you should carefully
check your code in this respect and migrate legacy applications as appropriate to
the 64-bit API.
In the Java API of CPLEX, the parameters listed in Table 16 on page 102 and
Table 17 on page 102 have migrated from the legacy class IloCplex.IntParam to the
recommended class IloCplex.LongParam. Existing code still compiles without
change. However, CPLEX silently truncates a long parameter value when it is
obliged to assign the value to a variable of type int. Consequently, it is a good
idea to migrate from those legacy members to new members as appropriate in
your legacy applications. For example, if your legacy application uses a very large
value for any of the parameters listed in those two tables, then you should migrate
to the longer type of value for that parameter.
In the .NET API of CPLEX, the parameters listed in Table 16 on page 102 and
Table 17 on page 102 have migrated from the legacy class Cplex.IntParam to the
recommended class Cplex.LongParam. Existing code still compiles without change.
However, CPLEX silently truncates a long parameter value when it is obliged to
assign the value to a variable of type int. Consequently, it is a good idea to
migrate from those legacy members to new members as appropriate in your legacy
applications. For example, if your legacy application uses a very large value for
any of the parameters listed in those two tables, then you should migrate to the
longer type of value for that parameter.
In the Python API of CPLEX, the parameters listed in Table 16 on page 102 and
Table 17 on page 102 are managed appropriately and transparently without change
in your legacy applications. For new applications of the Python API, use the 64-bit
API for future portability and correct treatment of large integers.
Selecting an encoding
Explains special considerations about encoding, also known as code pages.
CPLEX offers parameters that specify the encoding (also known as the code page)
for CPLEX to use in the representation of data, whether as input or output. For
details about these encoding parameters, see also the documentation of the API
string encoding switch and the file encoding switch in the CPLEX Parameters
Reference Manual.
Tip:
These encoding parameters have no effect on IBM CPLEX Optimizer for z/OS,
where only EBCDIC IBM-1047 encoding is available.
However, the user must append the option swaplfnl to the encoding name, like
this:
"IBM1047,swaplfnl"
in order to avoid anomalies due to a difference in the way that the IBM Java
Virtual Machine and Runtime Environment interprets newline characters, and the
way International Components for Unicode (ICU) interprets newline characters.
Default encoding
By default, CPLEX uses the encoding ISO-8859-1 (also known as Latin-1). The
familiar encoding known as ASCII is a subset of ISO-8859-1. In fact, ISO-8859-1
supports a wide variety of character sets, so this default is a reasonable choice for
many users.
Multi-byte encoding
However, the encoding ISO-8859-1 cannot represent multi-byte character sets, such
as Chinese, Japanese, Korean, Indian, or Vietnamese characters, for example. If you
want to represent a character set that requires multiple bytes per character, then a
better choice than the default is the encoding UTF-8. UTF-8 is a multi-byte
character encoding that can represent every character in the Unicode character set;
that is, it is sufficiently comprehensive for many purposes. It is compatible with
ASCII. It does not require byte-order marks (also known as BOM) nor specification
of big-end or little-end byte-order. It does not include multi-byte characters that
contain a NULL byte in their multi-byte encoding. In short, it is a serviceable if
bulky encoding for many users whose needs reach beyond ASCII or Latin-1.
If you choose another multi-byte encoding, such as UTF-32 or UTF-16, for example,
rather than UTF-8, be sure to specify the encoding fully by including the byte
order, like this: UTF-32LE or UTF-32BE.
Also take care if you choose another multi-byte encoding, such as UTF-16 or
UTF-32, instead of UTF-8: CPLEX routines such as CPXmsg do not work well with
Advice
Overview
Introduces the reference manual about file formats supported by CPLEX.
The CPLEX File Formats Reference Manual documents the file formats that CPLEX
supports more fully. The following topics cover programming considerations about
widely used file formats.
Since CPLEX reads the objective function as the first row, the two columns
appearing there will become the first two variables. When the problem is displayed
or rewritten into another LP file, the variables there will appear in a different order
within each row. In this example, if you execute the command
display problem all, you see this:
Maximize
obj: 2 x2 + 3 x3
Subject To
c1: x2 + x3 - x1 <= 20
c2: - 3 x2 + x3 + x1 <= 30
Bounds
0 <= x1 <= 40
All other variables are >= 0.
That is, x1 appears at the end of each constraint in which it has a nonzero
coefficient. Also, while re-ordering like this does not affect the optimal objective
function value of the problem, if there exist alternate optimal solutions at this
value, then the different order of the variables could result in a change in the
solution path of the algorithm, and there may be noticeable variation in the
solution values of the individual variables.
The CPLEX MPS file reader is highly compatible with files created by other
modeling systems that respect the MPS format. There is generally no need to
modify existing problem files to use them with CPLEX. However, there are
CPLEX-specific conventions that may be useful for you to know. This section
explains those conventions, and the CPLEX File Formats Reference Manual
documents the MPS format more fully.
In an MPS file, CPLEX selects the first free row or N-type row as the objective
function, and it discards all subsequent free rows unless it is instructed otherwise
by an OBJNAME section in the file. To retain free rows in an MPS file, reformulate
them as equality rows with an additional free variable. For example, replace the
The MPS format allows multiple righthand sides (RHSs), multiple bounds, and
multiple range vectors. It also allows extra free rows. Together, these features are
known as extra rim vectors. By default, the CPLEX MPS reader selects the first
RHS, bound, and range definitions that it finds. The first free row (that is, N-type
row) becomes the objective function, and the remaining free rows are discarded.
The extra rim data are also discarded.
Fairly common problems in MPS files include split vectors, unnamed columns, and
duplicated names. CPLEX checks for these conditions and reports them. If repeated
rows or columns occur in an MPS file, CPLEX reports an error and stops reading
the file. You can then edit the MPS file to correct the source of the problem.
You may often want to save a modified MPS file for later use. To that end, CPLEX
writes out a problem exactly as it appears in memory. All your revisions of that
problem will appear in the new file. One potential area for confusion occurs when
a maximization problem is saved. Since MPS conventionally represents all
problems as minimizations, CPLEX reverses the sign of the objective-function
coefficients when it writes a maximization problem to an MPS file. When you read
and optimize this new problem, the values of the variables will be valid for the
original model. However, since the problem has been converted from a
maximization to the equivalent minimization, the objective, dual, and reduced-cost
values will have reversed signs.
Most MPS files in fixed format conform to the CPLEX extensions and thus can be
read by the CPLEX MPS reader without error. However, the CPLEX MPS reader
will not accept the following conventions:
v blank space within a name;
v blank lines;
v missing fields (such as bound names and righthand side names);
v extraneous, uncommented characters;
v blanks in lieu of repeated name fields, such as bound vector names and
righthand side names.
You can convert fixed-format MPS files that contain those conventions into
acceptable CPLEX-extended MPS files. To do so, use the convert utility supplied in
the standard distribution of CPLEX. The convert utility removes unreadable
features from fixed-format MPS, BAS, and ORD files. It runs from the operating
system prompt of your platform. Here is the syntax of the convert utility:
Your command must include an input-file name and an output-file name; they
must be different from each other. The options, summarized in Table 18, indicate
the file type. You may specify only one option. If you do not specify an option,
CPLEX attempts to deduce the file type from the extension in the file name.
Table 18. Options for the convert utility and corresponding file extensions
Option File type File extension
-m MPS (Mathematical .mps
Programming System)
-b BAS (basis file according to .bas
MPS conventions)
-o ORD (priority orders) .ord
Concert Technology for C++ users offers a suite of classes for serializing CPLEX
models (that is, instances of IloModel ) and solutions (that is, instances of
IloSolution ) through XML. The CPLEX C++ API Reference Manual documents the
XML serialization API in the group optim.concert.xml. That group includes these
classes:
v IloXmlContext allows you to serialize an instance of IloModel or IloSolution .
This class offers methods for reading and writing a model, a solution, or both a
model and a solution together. There are examples of how to use this class in the
reference manual.
v IloXmlInfo offers methods that enable you to validate the XML serialization of
elements, such as numeric arrays, integer arrays, variables, and other
extractables from your model or solution.
Note:
Overview
Introduces log files from CPLEX.
Overview
Introduces message channels for CPLEX.
In both the Interactive Optimizer and the Callable Library, there are message
channels that enable you to direct output from your application as you prefer. In
the Interactive Optimizer, these channels are defined by the command
set output channel with its options as listed in Table 19 on page 113.
In the Python API, the class Cplex provides the methods set_results_stream,
set_warning_stream, set_error_stream, and set_log_stream to control output
channels.
In the Callable Library, there are routines for managing message channels, in
addition to parameters that you can set. In the C++ and Java APIs, the class
Besides the log-file parameter, the Interactive Optimizer offers you output-channel
parameters to give you finer control over when and where messages appear.
Output-channel parameters indicate whether output should or should not appear
on screen in the Interactive Optimizer. They also allow you to designate log files
for message channels. The output-channel parameters do not affect the log-file
parameter, so it is customary to use the command set logfile before the
command set output channel value1 value2.
The option value2 lets you specify a file name to redirect output from a channel.
Also in that command, value1 allows you to turn on or off output to the screen.
When value1 is y, output is directed to the screen; when its value is n, output is
not directed to the screen. The table Table 20 summarizes which channels direct
output to the screen by default. If a channel directs output to the screen by default,
you can leave value1 blank to get the same effect as set output channel y.
Table 20. Channels directing output to the screen or to a file
Channel Default value 1 Meaning
dialog y blank directs output to
screen but not to a file
errors y blank directs output to
screen and to a file
The Callable Library (C API) defines several message channels for flexible control
over message output:
v cpxresults for messages containing status and progress information;
v cpxerror for messages issued when a task cannot be completed;
v cpxwarning for messages issued when a nonfatal difficulty is encountered; or
when an action taken may have side-effects; or when an assumption made may
have side-effects;
v cpxlog for messages containing information that would not conventionally be
displayed on screen but could be useful in a log file. In other words, this
message channel displays information that is not displayed elsewhere, such
information as branching information in the MIP log or basis-change information
in the Simplex log. Because this information complements the information
printed through the channel declared by cpxresults, this channel is most useful
when the output destinations for the cpxlog channel are also connected to the
channel declared by cpxresults.
Messages from multiple channels may be sent to one destination. All predefined
CPLEX channels can be directed to a single file by a call to CPXsetlogfile.
Similarly, all predefined CPLEX channels except cpxlog can be directed to the
screen by the messages to screen switch CPX_PARAM_SCRIND. For a finer level of
control, or to define destinations for application-specific messages, use the
following message handling routines, all documented in the Callable Library
Reference Manual:
v CPXmsg writes a message to a predefined channel;
v CPXflushchannel flushes a channel to its associated destination;
v CPXdisconnectchannel flushes a channel and clears its destination list;
This example shows you how to use the CPLEX message handler from the Callable
Library. It captures all messages generated by CPLEX and displays them on screen
along with a label indicating which channel sent the message. It also creates a user
channel to receive output generated by the program itself. The user channel
accepts user-generated messages, displays them on screen with a label, and records
them in a file without the label.
In the C++ API of Concert Technology, the class IloEnv initializes output streams
for general information, for error messages, and for warnings. The class
IloAlgorithm supports these communication streams, and the class IloCplex
inherits its methods. For general output, there is the method IloAlgorithm ::out.
For warnings and nonfatal conditions, there is the method IloAlgorithm::warning.
For errors, there is the method IloAlgorithm::error.
Determinism means that repeated solving of the same model with the same
parameter settings, including limits, on the same computing platform will follow
exactly the same solution path, yielding the same level of performance and the
same values in the solution.
System time (such as CPU time measured in seconds or wall clock time measured
in seconds) is not deterministic; in other words, it may vary from one run to
another. For example, the load of other applications on a system can impact
performance and thus influence system time. Consequently, two consecutive runs
even with the same time limit may yield results that are not deterministic.
For situations where the user requires deterministic results, CPLEX offers a choice
between deterministic and opportunistic algorithms. For a comparison and contrast
of determinism and opportunism in the context of parallel algorithms, for example,
see the topic Determinism of results on page 355.
CPLEX also offers the user a choice between time limits measured in seconds (that
is, dependent on system time) and time limits measured deterministically. The
options of system time measured in seconds and deterministic time measured in
ticks (on the one hand) or parallelism and opportunism (on the other) are
orthogonal to one another. That is, you can set a system time limit on a
deterministic parallel algorithm, but of course then the termination criterion will
not be deterministic because it depends on nondeterministic system time measured
in seconds. You can likewise set a deterministic time limit on an opportunistic
parallel optimization. In that case, the termination criterion, indeed, the entire run
will not be deterministic because of the opportunism of the parallel optimization.
In other words, you can mix and match these options, but you must give careful
consideration to the effect you want to achieve in doing so.
You can even set both a deterministic and a system time limit simultaneously. In
such a case, CPLEX stops the optimization as soon as it reaches one of the limits
(similar to what happens when you set both a MIP gap and a node limit in a MIP
optimization).
Tip: If you want a fully deterministic optimization, then you need to apply a
deterministic algorithm, and you must not use a wall-clock time limit nor a CPU
time limit.
There are methods and routines in IBM ILOG CPLEX that provide a time stamp to
enable you to measure computational time. These methods and routines are
adapted for use either with opportunistic search or with the default deterministic
search. Some of the methods and routines measure time in seconds of wall-clock
time; others measure time in deterministic ticks. An application can invoke one of
these methods or routines at the beginning and end of an operation, and then
compare the two time stamps to compute elapsed time (either in seconds or in
deterministic ticks, depending on the respective method or routine that you chose).
v In Concert Technology
In the C++ API, IloCplex::getCplexTime returns a time stamp that
applications may use to calculate elapsed time in seconds.
IloCplex::getDetTime returns a time stamp in deterministic ticks.
In the Java API, IloCplex.getCplexTime returns a time stamp that
applications may use to calculate elapsed time in seconds.
IloCplex.getDetTime returns a time stamp in deterministic ticks.
In the .NET API, the property Cplex.CplexTime accesses a time stamp that
applications may use to calculate elapsed time in seconds. The property
Cplex.DetTime accesses a time stamp in deterministic ticks.
v In the Callable Library, CPXXgettime returns a time stamp that applications may
use to calculate elapsed time in seconds. CPXXgetdettime returns a time stamp in
deterministic ticks.
v In the Python API, the method Cplex.get_time returns a time stamp to measure
elapsed time in seconds, and the method Cplex.get_dettime returns a time stamp
to measure elapsed time in deterministic ticks.
In addition, other methods and routines return a time stamp adapted to use in
callbacks. Again, there are methods and routines available either for measuring
time in seconds or for measuring time in deterministic ticks. For more information
about these callback methods and routines, see Using the timing interface in
callbacks on page 119.
For a sample of these timing features, see these examples among those distributed
with the product in yourCPLEXinstallation/examples:
v ilomipex4.cpp in C++ in Concert Technology
v MIPex4.java in Java in Concert Technology
v MIPex4.cs in C#.NET in Concert Technology
v MIPex4.vb in Visual Basic.NET in Concert Technology
v xmipex4.c in C in the Callable Library
In addition to the general purpose methods and routines that provide a time stamp
to enable you to measure computational time in your applications of IBM ILOG
CPLEX, there are methods and routines specifically for use in callbacks. Like the
general purpose methods and routines for time stamps, these callback methods
and routines are adapted for use either with the default opportunistic search or
with the deterministic search. An application can invoke one of these methods or
routines at the beginning and end of an operation, and then compare the two time
stamps to compute elapsed time (either in seconds or in deterministic ticks,
respectively).
v In Concert Technology
In the C++ API, CallbackI::getStartTime returns a time stamp in seconds at
the beginning of optimization. CallbackI::getStartDetTime returns a time
stamp in deterministic ticks at the beginning of optimization.
CallbackI::getEndTime returns a time stamp specifying in seconds when
CPLEX will reach a time limit. CallbackI::getEndDetTime returns a time
stamp specifying in deterministic ticks when CPLEX will reach a time limit.
CallbackI::getCplexTime returns the current time stamp in seconds.
CallbackI::getDetTime returns the current time stamp in deterministic ticks.
In the Java API, CpxCallback.getStartTime returns a time stamp in seconds at
the beginning of optimization. CpxCallback.getStartDetTime returns a time
stamp in deterministic ticks at the beginning of optimization.
CpxCallback.getEndTime returns a time stamp specifying in seconds when
CPLEX will reach a time limit. CpxCallback.getEndDetTime returns a time
stamp specifying in deterministic ticks when CPLEX will reach a time limit.
In the .NET API, the property Cplex.Callback.StartTime accesses a time
stamp in seconds at the beginning of optimization.
Cplex.Callback.StartDetTime accesses a time stamp in deterministic ticks at
the beginning of optimization. Cplex.Callback.EndTime accesses a time stamp
specifying in seconds when CPLEX will reach a time limit.
Cplex.Callback.EndDetTime accesses a time stamp specifying in deterministic
ticks when CPLEX will reach a time limit.
With those values, you can compute the time in seconds since the start of the
optimization: getCplexTime - getStartTime. Likewise, you can compute the
remaining time in seconds until CPLEX reaches the time limit: getEndTime -
getCplexTime. Analogously, you can compute the time in deterministic ticks
since the start of the optimization: getDetTime - getStartDetTime. Similarly, you
can compute the remaining time in determinitic ticks until CPLEX reaches the
time limit: getDetEndTime - getDetTime.
v In the Callable Library, CPX_CALLBACK_INFO_STARTTIME and
CPX_CALLBACK_INFO_ENDTIME are symbolic values that CPXgetcallbackinfo can
supply in its argument whichinfo. Those values are time stamps of the point in
time when optimization started and terminated (if optimization does not finish
before that point). In other words, those symbols are useful in measuring time in
seconds through information callbacks.
Likewise, CPX_CALLBACK_INFO_STARTDETTIME and CPX_CALLBACK_INFO_ENDDETTIME
are also symbolic values that CPXgetcallbackinfo can supply in its argument
whichinfo. Those values are time stamps of the point in time measured in
deterministic ticks when optimization started and terminated (if optimization
does not finish before that point). In other words, those symbols are useful in
measuring time in deterministic ticks through information callbacks.
The tuning tool looks at parameters to improve solving time, either in seconds of
system time or in deterministic ticks. If your model suffers from numeric
instability, the tuning tool will not attempt to correct that problem. Furthermore, if
there is insufficient memory to accommodate optimization of your model, the
tuning tool will not correct that problem either. In short, the tuning tool does not
magically eliminate all performance bottlenecks. However, if you understand the
performance issues of your model, the tuning tool can help you discern parameter
settings that lead to faster solving time.
The recommended practice with the tuning tool is to solve your model first with
default parameter settings and to consider the results before invoking the tuning
tool. Your analysis of those results directs you toward the next step. The following
topics sketch typical scenarios for working effectively with the tuning tool and
outline what to expect from the tuning tool.
If CPLEX solves your problem to optimality, you may still want to apply the
tuning tool to discover whether you can solve the model faster. In such a case,
bear in mind that the tuning tool performs several optimization runs as it goes
about its work. These optimization runs may take six to eight times longer than
the default run that produced your optimal results. If that projected time (six to
eight times longer than the initial default run) seems too long for your purpose,
then consider setting a general time limit or consider setting a specific tuning time
limit per problem, per optimization. As you set such time limits, keep in mind that
you can set time limits in terms of system time seconds or deterministic ticks. (For
information about these contrasting ways of measuring time, see the topic
Chapter 8, Timing interface, on page 117.)
To set a general time limit, either use the optimizer time limit in seconds
parameter (TiLim, CPX_PARAM_TILIM) to set a limit in seconds of system time, or use
the deterministic time limit parameter (DetTiLim, CPX_PARAM_DETTILIM) to set a
deterministic time limit in terms of deterministic ticks.
To set a specific tuning time limit per problem, per optimization, either use the
tuning time limit in seconds parameter (TuningTiLim, CPX_PARAM_TUNINGTILIM) to
In the case where CPLEX finds solutions for your model but does not prove
optimality in your initial run before invoking the tuning tool, you will likely want
to set the time limit per model. See Tuning and time limits for more about that
idea.
In situations where CPLEX does not solve your model to optimality for a reason
other than a time limit, you should address that reason before you apply the
tuning tool.
For example, if your initial run results in an out-of-memory error, consider setting
the memory emphasis parameter (memory reduction switch: MemoryEmphasis,
CPX_PARAM_MEMORYEMPHASIS). Then create a file in which you specify a fixed setting
of that parameter for the tuning tool to respect. Pass that file to the tuning tool
with your model. Fixing parameters and tuning multiple models in the Interactive
Optimizer on page 126 illustrates this approach.
The tuning process is affected by four time limit parameters, and these parameters
interact with one another.
v optimizer time limit in seconds: TiLim, CPX_PARAM_TILIM
v deterministic time limit: DetTiLim, CPX_PARAM_DETTILIM
v tuning time limit in seconds: TuningTiLim, CPX_PARAM_TUNINGTILIM
v deterministic tuning time limit: TuningDetTiLim, CPX_PARAM_TUNINGDETTILIM
For the tuning process, the overall time limit in seconds of system time is set with
the general time limit parameter (optimizer time limit in seconds: TiLim,
CPX_PARAM_TILIM).
The overall time limit in deterministic ticks is set with the deterministic time limit
parameter (deterministic time limit: DetTiLim, CPX_PARAM_DETTILIM).
Tip: You can set both the overall time limit in seconds (TiLim) and the overall
deterministic time limit (DetTiLim) to a finite value (less than 1e+75), and the entire
tuning session will terminate if CPLEX reaches either of those limits. In contrast,
you cannot set both the per-problem, per-optimization time limit in seconds
(TuningTiLim) and the deterministic per-problem, per-optimization time limit
(TuningDetTiLim) parameters to a finite value (less than 1e+75). At least one of
those two must be set to 1e+75 (infinity).
The per-problem, per-optimization time limit can also be set by means of the
general time limit parameter (optimizer time limit in seconds TiLim,
CPX_PARAM_TILIM) or the deterministic time limit parameter (deterministic time
limit DetTiLim, CPX_PARAM_DETTILIM) in a fixed parameter set. Fixing parameters
and tuning multiple models in the Interactive Optimizer on page 126 explains
more about that approach. If you are using a time limit in your usual (non-tuning)
runs of the models, you may want to set the per-problem, per-optimization time
limit in this way.
Values of the tuning time-limit parameters impact whether or not CPLEX conducts
the tuning session in deterministic mode.
v tuning time limit in seconds: TuningTiLim, CPX_PARAM_TUNINGTILIM
v deterministic tuning time limit: TuningDetTiLim, CPX_PARAM_TUNINGDETTILIM
If TuningTiLim < 1e+75, and TuningDetTiLim = 1e+75, then the tuning result will be
nondeterministic, as CPLEX uses a system time limit for individual tuning runs.
If TuningTiLim = 1e+75, and TuningDetTiLim < 1e+75, then the tuning result will be
deterministic, as CPLEX uses a deterministic time limit for individual tuning runs.
Of course, if you apply a finite overall system time limit (TiLim, CPX_PARAM_TILIM),
and the tuning process reaches that finite system time limit, then the tuning results
are not deterministic. (This principle is the same for optimization in deterministic
mode reaching a time limit: results are not deterministic then either.)
The overall system time limit (TiLim, CPX_PARAM_TILIM), the overall deterministic
time limit (DetTiLim, CPX_PARAM_DETTILIM), the tuning time limit in seconds
(TuningTiLim, CPX_PARAM_TUNINGTILIM), and the deterministic tuning time limit in
ticks (TuningDetTiLim, CPX_PARAM_TUNINGDETTILIM) are unrelated to the parallel
mode switch (ParallelMode, CPX_PARAM_PARALLELMODE). If the parallel mode switch
In other words, if you are using default values of all CPLEX parameters, a tuning
session will yield deterministic results.
Tuning results
Describes typical results of a tuning session.
The tuning tool selects the best suite of parameter settings based on the
performance times of the several optimizations of the model or models. Whether
your tuning results are deterministic depends on considerations outlined in the
topics Tuning and time limits on page 122 and Tuning time limits and
determinism on page 123.
In Concert Technology, you invoke the tuning tool through these methods:
v tuneParam in the C++ API; see also the example ilotuneset.cpp ;
v tuneParam in the Java API; see also the example TuneSet.java ;
v Cplex.TuneParam in the .NET API; see also the example TuneSet.cs or
TuneSet.vb .
The tool is also part of the Callable Library (C API) through separate routines for
tuning a single model or a group of models. For more detail about the C routine
for tuning a single model or a group of models, see Tuning models in the Callable
Library (C API) on page 128. For a sample application, see examples/src/cplex/
tuneset.c .
In the Interactive Optimizer, you can tune one model or a group of models with
the tune command. See the Examples: time limits on tuning in the Interactive
Optimizer on page 125, as well as the topic Fixing parameters and tuning
multiple models in the Interactive Optimizer on page 126.
You can specify whether you want the least worst performance or the best average
performance across a set of models with the tuning measure parameter, (tuning
measure: TuningMeasure, CPX_PARAM_TUNINGMEASURE), documented in the CPLEX
Parameters Reference Manual.
You can also set a time limit specific to tuning per problem and per optimization
run, as explained in the topic Tuning and time limits on page 122. The example
in Examples: time limits on tuning in the Interactive Optimizer. shows how to
set such a time limit.
Both overall system time limits (in seconds) and overall deterministic time limits
(in ticks) have an impact on tuning. Likewise, both overall time limits and
per-problem, per-optimization time limits also govern tuning. These examples in
the Interactive Optimizer illustrate key points about time limits and tuning.
First example: overall time limit and per-problem time limit in seconds
As a first example, suppose that you want to spend an overall amount of time
tuning the parameter settings for a given model, say, 1000 seconds. Also suppose
that you want CPLEX to make multiple attempts within that overall time limit to
tune the parameter settings for your model. Suppose further that you want to set a
time limit on each of those attempts, say, 200 seconds per attempt.
In the Interactive Optimizer, first enter your model into a session (for example, by
reading a formatted file), like this:
read model.mps
Then set the overall time limit through the environment with this command:
This series of commands tells CPLEX to tune the parameters of your model,
making multiple attempts of 200 seconds each (set by the tuning time limit in
seconds: TuningTiLim, CPX_PARAM_TUNINGTILIM), within an overall time limit of 1000
seconds (set by the optimizer time limit in seconds: TiLim, CPX_PARAM_TILIM) .
Tip: Remember that system time limits in seconds lead to nondeterministic results
in tuning, as explained in the topics Tuning and time limits on page 122 and
Tuning time limits and determinism on page 123. If your application requires
deterministic results, use deterministic time limits instead, as illustrated in the next
example.
As a second example, suppose that you want to spend an overall amount of time
deterministically tuning the parameter settings for a given model, say, 1000000
ticks. Also suppose that you want CPLEX to make multiple attempts within that
overall time limit to tune the parameter settings for your model. Suppose further
that you want to set a time limit on each of those attempts, say, 200 ticks per
attempt.
In the Interactive Optimizer, as in the previous example, first enter your model
into a session (for example, by reading a formatted file), like this:
read model.mps
Then set the overall deterministic time limit through the environment with this
command:
This series of commands tells CPLEX to tune the parameters of your model
deterministically, making multiple attempts of 200000 ticks each (set by the
deterministic tuning time limit: TuningDetTiLim, CPX_PARAM_TUNINGDETTILIM),
within an overall time limit of 1000000 ticks (set by the deterministic time limit:
DetTiLim, CPX_PARAM_DETTILIM).
Tip: You can use both overall time limits (both system in seconds and
deterministic in ticks) together; that is, you can set both TiLim and DetTiLim
simultaneously, as explained in the topic Tuning and time limits on page 122.
However, you must not mix a per-problem, per-optimization tuning time limit in
seconds with a deterministic per-problem, per-optimization tuning time limit in
ticks. That is, do not mix TuningTiLim, CPX_PARAM_TUNINGTILIM with
TuningDetTiLim, CPX_PARAM_TUNINGDETTILIM, again as explained in Tuning and
time limits on page 122.
Default behavior
At default settings, these four time limits are set to infinity (that is, 1e+75 on most
platforms). These default settings are equivalent to setting the deterministic tuning
time limit TuningDetTiLim, CPX_PARAM_TUNINGDETTILIM to 1e+7.
The command to invoke the tuning tool in the Interactive Optimizer is: tune
Optionally, that command may take one or two arguments, in either order, like
this:
or
If you supply an optional file name with the extension.prm , that file contains the
formatted list of parameters and their values that you do not want CPLEX to tune.
That is, you want CPLEX to respect those parameter settings. If you do not supply
a parameter file to the tune command, then all parameters are subject to tuning.
For example, if you have a model that you know has numerical issues, and you
have already determined that you want to set the numerical emphasis parameter
yourself, then a PRM file suitable for tuning your model would contain the
following heading and parameter setting:
CPLEX Parameter File Version 12.6.0.0
CPXPARAM_Emphasis_MIP 1
Tip:
A PRM file must have a correctly formatted header to let CPLEX know which
version to expect.
An easy way to generate a PRM file with a correct heading and fixed parameter
settings is to read your model, set the parameters you wish, and write a PRM file
from the Interactive Optimizer, with this command:
If you supply an optional file name, such as modelfile, that file contains a list of
files, one file name per line. Each file contains a model to tune. Each file must
specify its type, such as .mps, .lp, or .sav, as the extension in the file name.
Optionally, those cited files may be compressed, as specified by the extension .gz
or .bz2. Here is an example of the contents of such a file, specifying three
compressed files of type .mps available in the current working directory:
For models not in the current working directory, you can specify a path to them
with the usual syntax for your platform.
CPLEX will uncompress the model files, read them according to their type of
format, and apply the tuning tool to that suite of files. If you do not specify a list
of models to tune, then CPLEX tunes the model currently in the Interactive
Optimizer.
The routine CPXtuneparam tunes one existing model, and the routine
CPXtuneparamprobset tunes a group of models, specified by an array of file names.
Both these routines are applicable to models of one of these types: LP, QP, and
MIP. (Neither applies to networks. Neither applies to quadratically constrained
programs (QCP).) Advanced bases in .sav files are ignored by these routines.
Acceptable file formats for these routines are:
v .mps
v .lp
v .sav
Parameter settings in the environment control the resources for the tuning tool; the
parameter settings passed in the arguments of these routines are used by CPLEX
as a starting point for tuning. You can specify parameters and corresponding
values for CPLEX to use as a starting point. Bear in mind that the parameters you
specify at nondefault settings will be fixed during the tuning. That is, the tuning
tool will not attempt to modify them. In other words, when you set a parameter as
a starting point, you eliminate it from consideration by the tuning tool.
Callbacks (except the tuning callback) are ignored by these tuning routines in the
Callable Library (C API).
The tuning tool checks the termination signal (that is, the variable set by the
routine CPXsetterminate) and terminates the tuning process in compliance with
that termination signal. Tuning also respects time limits set in the environment.
After tuning, the return value of either routine specifies 0 (zero) when tuning
completed successfully and nonzero when it has not done so. A tuning status is
also returned as an argument to these routines. The possible nonzero values of the
tuning status argument are these:
v CPX_TUNE_ABORT specifies that abort occurred through CPXsetterminate.
v CPX_TUNE_TILIM specifies that tuning reached the time limit specified in the
environment by the overall optimizer time limit parameter CPX_PARAM_TILIM .
v CPX_TUNE_DETTILIM specifies that tuning reached the deterministic time limit
specified by the deterministic time limit parameter CPX_PARAM_DETTILIM.
A tuning callback is a user-written function that CPLEX calls before each trial run
during a tuning session. A tuning callback allows you to follow the progress of
tuning. It reports information that enables you to estimate how much more time
tuning needs to achieve results useful in your model.
To use a tuning callback in a tuning session, you must first write the callback
function, and then pass it to CPLEX. CPLEX will then execute your tuning callback
before it begins a trial run.
v In Concert Technology, you must implement your user-written function as an
instance of the tuning callback class.
IloCplex::TuningCallbackI in the C++ API
IloCplex.TuningCallback in the Java API
Cplex.TuningCallback in the .NET API
For details about writing your tuning callback, see Chapter 35, Using
optimization callbacks, on page 477, especially Implementing callbacks with
Concert Technology on page 487
v In the Callable Library (C API), use the routine CPXsettuningcallbackfunc . For
more about how to write a callback, see also Implementing callbacks in the
Callable Library on page 492.
To terminate a tuning session, you can use one of the following means:
v In the C++ API, pass an instance of the class IloCplex::Aborter to an instance
of IloCplex. Then call the method IloCplex::Aborter::abort to terminate the
tuning session.
v In the Java API, pass an instance of the class IloCplex.Aborter to an instance of
IloCplex. Then call the method IloCplex.Aborter.abort to terminate the tuning
session.
v In the .NET API, pass an instance of the class Cplex.Aborter to an instance of
Cplex . Then call the method Cplex.Aborter.Abort to terminate the tuning
session.
v In the Callable Library (C API), call the routine CPXsetterminate to set a pointer
to the termination signal. Initially, the value of the termination signal should be
zero. When your application sets the termination signal to a nonzero value, then
CPLEX will terminate the tuning session.
The preceding topics have focused on the details of writing applications that
model optimization problems and access the solutions to those problems, with
minimal attention to the optimizer that solves them, because most models are
solved well by the default optimizers provided by IBM ILOG CPLEX. For instances
where a user wants to exert more direct influence over the solution process,
CPLEX provides a number of features that may be of interest.
This topic and the following one tell you more about solving linear programs with
the LP optimizers of CPLEX. This topic emphasizes primal and dual simplex
optimizers.
Overview of LP optimizers
Introduces parameters to select LP optimizers.
CPLEX offers several different optimizers for linear programming problems. Each
of these optimizers is available whether you call CPLEX from within your own
application using Concert Technology or the Callable Library, or you use the
Interactive Optimizer.
The choice of LP optimizer in CPLEX can be specified using the algorithm for
continuous problems parameter, named RootAlg in the C++, Java, and .NET APIs,
CPX_PARAM_LPMETHOD in the Callable Library, and lpmethod in the Interactive
Optimizer. In Concert Technology, the LP method is controlled by the RootAlg
parameter (which also controls related aspects of QP and MIP solutions, as
explained in the corresponding chapters of this manual). In this chapter, this
parameter will be referred to uniformly as LPMethod.
The LPMethod parameter sets which optimizer will be used when you solve a
model in one of the following ways:
v cplex.solve (Concert Technology)
v CPXlpopt (Callable Library)
v optimize (Interactive Optimizer)
LPMethod
0 Default Setting Automatic selection of an
optimizer
1 Primal Simplex Primal simplex optimizer
on page 135
2 Dual Simplex Dual simplex optimizer on
page 135
3 Network Simplex Network optimizer on
page 135
4 Barrier Barrier optimizer on page
136
5 Sifting Sifting optimizer on page
136
6 Concurrent optimizer on
Concurrent Dual, Barrier, page 136
and Primal in opportunistic
parallel mode; Concurrent
Dual and Barrier in
deterministic parallel mode
The default Automatic setting of the LP method lets CPLEX decide which
algorithm to use to optimize your problem. Most models are solved well with this
setting, and this is the recommended option except when you have a compelling
reason to tune performance for a particular class of model.
If you are familiar with linear programming theory, then you recall that a linear
programming problem can be stated in primal or dual form, and an optimal
solution (if one exists) of the dual has a direct relationship to an optimal solution
of the primal model. CPLEX dual simplex optimizer makes use of this relationship,
but still reports the solution in terms of the primal model. The dual simplex
method is the first choice for optimizing a linear programming problem, especially
for primal-degenerate problems with little variability in the righthand side
coefficients but significant variability in the cost coefficients.
CPLEX primal simplex optimizer also can effectively solve a wide variety of linear
programming problems with its default parameter settings. The primal simplex
method is not the obvious choice for a first try at optimizing a linear programming
problem. However, this method will sometimes work better on problems where the
number of variables exceeds the number of constraints significantly, or on
problems that exhibit little variability in the cost coefficients. Few problems exhibit
poor numeric performance in both primal and dual form. Consequently, if you
have a problem where numeric difficulties occur when you use the dual simplex
optimizer, then consider using the primal simplex optimizer instead.
Network optimizer
Describes conditions favoring the network optimizer.
Sifting optimizer
Describes conditions favoring the sifting optimizer.
Sifting was developed to exploit the characteristics of models with large aspect
ratios (that is, a large ratio of the number of columns to the number of rows). In
particular, the method is well suited to large aspect ratio models where an optimal
solution can be expected to place most variables at their lower bounds. The sifting
algorithm can be thought of as an extension to the familiar simplex method. It
starts by solving a subproblem (known as the working problem) consisting of all
rows but only a small subset of the full set of columns, by assuming an arbitrary
value (such as its lower bound) for the solution value of each of the remaining
columns. This solution is then used to re-evaluate the reduced costs of the
remaining columns. Any columns whose reduced costs violate the optimality
criterion become candidates to be added to the working problem for the next
major sifting iteration. When no candidates are present, the solution of the working
problem is optimal for the full problem, and sifting terminates.
The choice of optimizer to solve the working problem is governed by the SiftAlg
parameter. You can set this parameter to any of the values accepted by the
LPMethod parameter, except for Concurrent and of course Sifting itself. At the
default SiftAlg setting, CPLEX chooses the optimizer automatically, typically
switching between barrier and primal simplex as the optimization proceeds. It is
recommended that you not turn off the barrier crossover step (that is, do not set
the parameter BarCrossAlg to -1) when you use the sifting optimizer, so that this
switching can be carried out as needed.
Tip:
Concurrent optimizer
Describes conditions favoring the concurrent optimizer.
When you are using parameter settings other than the default, consider the
algorithms that these settings will affect. Some parameters, such as the time limit,
will affect all the algorithms invoked by the concurrent optimizer. Others, such as
the refactoring frequency, will affect both the primal and dual simplex algorithms.
And some parameters, such as the primal gradient, dual gradient, or barrier
convergence tolerance, affect only a single algorithm.
Tuning LP performance
Documents tactics for tuning performance on LP models.
To help you decide whether default settings of parameters are best for your model,
or whether other parameter settings may improve performance, the tuning tool is
available. Chapter 9, Tuning tool, on page 121 explains more about this utility
and offers you examples of its use.
The following sections suggest other features of CPLEX to consider in tuning the
performance of your application.
Preprocessing
Documents preprocessing at default parameter settings in LP optimizers.
A useful preprocessing feature for performance tuning, one that is not always
activated by default, can be to convert the problem to its dual formulation. The
nature of the dual formulation is rooted in linear programming theory, beyond the
scope of this manual, but for the purposes of this preprocessing feature it is
sufficient to think of the roles of the rows and columns of the model's constraint
matrix as being switched. Thus the feature is especially applicable to models that
have many more rows than columns.
Conversely, to entirely inhibit the dual formulation for the barrier optimizer, you
can set the PreDual parameter to -1 . The default, automatic, setting is 0 .
It is worth emphasizing, to those familiar with linear programming theory, that the
decision to solve the dual formulation of your model, via this preprocessing
parameter, is not the same as the choice between using the dual simplex method or
the primal simplex method to perform the optimization. Although these two
concepts (dual formulation and dual simplex optimizer) have theoretical
foundations in common, it is valid to consider, for example, solving the dual
formulation of your model with the dual simplex method; this would not simply
result in the same computational path as solving the primal formulation with the
primal simplex method. However, with that distinction as background, it may be
worth knowing that when CPLEX generates the dual formulation, and a simplex
optimizer is to be used, CPLEX will in most cases automatically select the opposite
simplex optimizer to the one it would have selected for the primal formulation.
Thus, if you set the PreDual parameter to 1 (one), and also select LPMethod 1
(which normally invokes the primal simplex optimizer), the dual simplex optimizer
will be used in solving the dual formulation. Because solution status and the other
results of an optimization are the same regardless of these settings, no additional
steps need to be taken by the user to use and interpret the solution; but
examination of solution logs might prove confusing if this behavior is not taken
into account.
To reduce memory use, presolve may compress the arrays used for storage of the
original model. This compression can make more memory available for the
optimizer that the user has called. To conserve memory, you can also turn on the
memory emphasis parameter (memory reduction switch: MemoryEmphasis (bool),
CPX_PARAM_MEMORYEMPHASIS (int)) .
In rare instances, a user may wish to specify the number of analysis passes that the
presolver or the aggregator makes through the problem. The parameters PrePass
and AggInd , respectively, control these two preprocessing features; the default,
automatic , setting of -1 lets CPLEX decide the number of passes to make, while a
setting of 0 directs CPLEX not to use that preprocessing feature, and a positive
integer limits the number of passes to that value. At the automatic setting, CPLEX
applies the aggregator just once when it is solving the LP model; for some
problems, it may be worthwhile to increase the AggInd setting. The behavior under
the PrePass default is less easy to predict, but if the output log indicates it is
performing excessive analysis, you may wish to try a limit of five passes or some
other modest value.
Another parameter, which affects only the aggregator, is AggFill. Occasionally the
substitutions made by the aggregator will increase matrix density and thus make
each iteration too expensive to be advantageous. In such cases, try lowering
AggFill from its default value of 10. CPLEX may make fewer substitutions as a
consequence, and the resulting problem will be less dense.
Finally, if for some reason you wish to turn CPLEX preprocessing entirely off, set
the parameter PreInd to 0 (zero).
Another performance improvement to consider, unless you are using the barrier
optimizer, is starting from an advanced basis. If you can start a simplex optimizer
from an advanced basis, then there is the potential for the optimizer to perform
significantly fewer iterations, particularly when your current problem is similar to
a problem that you have solved previously. Even when problems are different,
starting from an advanced basis may possibly help performance. For example, if
your current problem is composed of several smaller problems, an optimal basis
from one of the component problems may significantly speed up solution of the
other components or even of the full problem.
In cases where models are solved in separate application calls, and thus the basis
will not be available in memory, you can communicate the final basis from one run
to the start of the next by first saving the basis to a file before the end of the first
run.
Tip:
A basis file, also known as a BAS file, is a formatted text file conforming to the
MPS standard. It relies on each variable (column) and each constraint (row) having
a name. If those names do not exist, names will be created automatically and
added during write operations of a basis file. If you anticipate the need to read
and write basis files, it is a good idea to assign a name yourself to every variable
and constraint when you create your model.
Make sure that the advanced start parameter, AdvInd , is set to either 1 (its default
value) or 2, and not 0 (zero), before calling the optimization routine that is to make
use of an advanced basis.
If you anticipate the advanced basis to be a close match for your problem, so that
relatively few iterations will be needed, or if you are unsure, then the default
setting of 1 is a good choice because it avoids some overhead processing. If you
anticipate that the simplex optimizer will require many iterations even with the
advanced basis, or if the model is large and preprocessing typically removes much
from the model, then the setting of 2 may give you a faster solution by giving you
the advantages of preprocessing. However, in such cases, you might also consider
not using the advanced basis, by setting this parameter to 0 instead, on the
grounds that the basis may not be giving you a helpful starting point after all.
Simplex parameters
Documents parameters settings that may improve performance of LP optimizers.
After you have chosen the right optimizer and, if appropriate, you have started
from an advanced basis, you may want to experiment with different parameter
settings to improve performance. This section documents parameters that are most
The parameters in Table 24 set the pricing algorithms that CPLEX uses.
Consequently, these are the algorithmic parameters most likely to affect simplex
linear programming performance. The default setting of these gradient parameters
chooses the pricing algorithms that are best for most problems. When you are
selecting alternate pricing algorithms, look at these values as guides:
v overall solution time;
v number of Phase I iterations (that is, iterations before CPLEX arrives at an initial
feasible solution);
v total number of iterations.
CPLEX records those values in the log file as it works. (By default, CPLEX creates
the log file in the directory where it is executing, and it names the log file
cplex.log. Managing log files on page 111 tells you how to rename and relocate
this log file.)
The symbolic names for the acceptable values for these parameters appear in
Table 24 and Table 25. The default value in both cases is 0 (zero).
Table 24. DPriInd parameter settings for dual simplex pricing algorithm
Description Concert Callable Library
0 set automatically DPriIndAuto CPX_DPRIIND_AUTO
1 standard dual pricing DPriIndFull CPX_DPRIIND_FULL
2 steepest-edge pricing DPriIndSteep CPX_DPRIIND_STEEP
3 steepest-edge in slack DPriIndFullSteep CPX_DPRIIND_FULLSTEEP
space
4 steepest-edge, unit DPriIndSteepQStart CPX_DPRIIND_STEEPQSTART
initial norms
5 devex pricing DPriIndDevex CPX_DPRIIND_DEVEX
Table 25. PPriInd parameter settings for primal simplex pricing algorithm
Description Concert Callable Library
-1 reduced-cost pricing PPriIndPartial CPX_PPRIIND_PARTIAL
For the dual simplex pricing parameter, the default value selects steepest-edge
pricing. That is, the default (0 or CPX_DPRIIND_AUTO) automatically selects 2 or
CPX_DPRIIND_STEEP.
For the primal simplex pricing parameter, reduced-cost pricing (-1 ) is less
computationally expensive, so you may prefer it for small or relatively easy
problems. Try reduced-cost pricing, and watch for faster solution times. Also if
your problem is dense (say, 20-30 nonzeros per column), reduced-cost pricing may
be advantageous.
If you observe that devex pricing helps, then you might also consider
steepest-edge pricing (2). Steepest-edge pricing is computationally more expensive
than reduced-cost pricing, but it may produce the best results on difficult
problems. One way of reducing the computational intensity of steepest-edge
pricing is to choose steepest-edge pricing with initial slack norms (3).
Scaling
Poorly conditioned problems (that is, problems in which even minor changes in
data result in major changes in solutions) may benefit from an alternative scaling
method. Scaling attempts to rectify poorly conditioned problems by multiplying
rows or columns by constants without changing the fundamental sense of the
problem. If you observe that your problem has difficulty staying feasible during its
solution, then you should consider an alternative scaling method.
Crash
It is possible to control the way CPLEX builds an initial (crash) basis through the
CraInd parameter.
In the dual simplex optimizer, the CraInd parameter sets whether CPLEX
aggressively uses primal variables instead of slack variables while it still tries to
preserve as much dual feasibility as possible. If its value is 1 (one), it specifies the
default starting basis; if its value is 0 (zero) or -1, it specifies an aggressive starting
basis. These settings are summarized in Table 27.
Table 27. CraInd parameter settings for the dual simplex optimizer
CraInd Setting Meaning for Dual Simplex Optimizer
1 Use default starting basis guided by
coefficients
0 Use an aggressive starting basis ignoring
coefficients
-1 Use an aggressive starting basis contrary to
coefficients
In the primal simplex optimizer, the CraInd setting sets how CPLEX uses the
coefficients of the objective function to select the starting basis. If its value is 1
(one), CPLEX uses the coefficients to guide its selection; if its value is 0 (zero),
CPLEX ignores the coefficients; if its value is -1, CPLEX does the opposite of what
the coefficients normally suggest. These settings are summarized in Table 28.
Table 28. CraInd parameter settings for the primal simplex optimizer
CraInd Setting Meaning for Primal Simplex Optimizer
1 Use coefficients of objective function to
select basis
0 Ignore coefficients of objective function
-1 Select basis contrary to one indicated by
coefficients of objective function
Lack of memory
Documents CPLEX behavior in limited memory for LP models.
If you set the memory emphasis parameter to its optional value of 1 (one), then
CPLEX adopts memory conservation tactics at the beginning of optimization rather
than only after the shortage becomes apparent. These tactics may still have a
noticeable impact on solution speed because these tactics change the emphasis
from speed to memory utilization, but they could give an improvement over the
default in the case where memory is insufficient.
Warning messages
The CPLEX primal and dual simplex optimizers refactor the problem basis at a
rate set by the ReInv parameter.
In an extreme case, lack of memory may force CPLEX to refactor at every iteration,
and the impact on performance will be dramatic. If you provide more memory in
such a situation, the benefit will be tremendous.
Ill conditioning
Introduces CPLEX behavior with respect to ill conditioning in LP models.
Even if you do not explicitly change the data in your model, finite precision
computers can introduce changes in the data for a variety of reasons.
v Most numbers (for example, 1/3, 5/12) cannot be represented exactly in finite
precision.
v Different chips employ different floating point representations.
v Different compilers can use the same floating point representation differently.
Not all kinds of ill-conditioning are easily measured. However, there are
conventions to measure ill-conditioning in square linear systems of equations.
These conventions calculate a value, known as the condition number or kappa,
based on measurable qualities of the solution of the square linear system. (For
more information about ill-conditioning and how it is conventionally calculated,
consult the references recommended in the topic Further reading on page xx in
the preface of this manual.)
Experts in the field say that a square linear system is ill-conditioned if the
condition number kappa is large. A square linear system is well conditioned if the
condition number kappa is small. Whether the condition number kappa is large or
small depends on the computer, the data, and the algorithmic tolerances. In
particular, ill conditioning can occur when the round off error associated with
finite precision is large enough to influence algorithmic decisions. In other words,
the basic question about both ill-conditioning and numerical difficulties is whether
the tolerances of the optimizer can misdirect it to make decisions about the model
Numeric difficulties
Documents CPLEX behavior with respect to numeric difficulties in LP models.
While CPLEX will usually achieve an optimal solution in spite of these difficulties,
you can help it do so more efficiently. This section characterizes situations in which
you can help.
Some problems will not be solvable even after you take the measures suggested
here. For example, problems can be so poorly conditioned that their optimal
solutions are beyond the numeric precision of your computer.
At its default setting, CPLEX employs ordinary caution in dealing with the
numerical properties of the computations it must perform. Under the optional
setting, CPLEX uses extreme caution.
This emphasis parameter is different in style from the various tolerance parameters
in CPLEX. The purpose of the emphasis parameter is to relieve the user of the
need to analyze which tolerances or other algorithmic controls to try. Instead, the
user tells CPLEX that the model about to be solved is known to be susceptible to
unstable numerical behavior and lets CPLEX make the decisions about how best to
proceed.
There may be a trade-off between solution speed and numerical caution. You
should not be surprised if your model solves less rapidly at the optional setting of
this parameter, because each iteration may potentially be noticeably slower than at
the default. On the other hand, if the numerical difficulty has been causing the
optimizer to proceed less directly to the optimal solution, it is possible that the
optional setting will reduce the number of iterations, thus leading to faster
solution. When the user chooses an emphasis on extreme numerical caution,
solution speed is in effect treated as no longer the primary emphasis.
There is no absolute link between the form of data in a model and the numeric
difficulty the problem poses. Nevertheless, certain choices in how you present the
data to CPLEX can have an adverse effect.
Placing large upper bounds (say, in the neighborhood of 1e9 to 1e12) on individual
variables can cause difficulty during Presolve. If you intend for such large bounds
to mean no bound is really in effect it is better to simply not include such
bounds in the first place.
Large coefficients anywhere in the model can likewise cause trouble at various
points in the solution process. Even if the coefficients are of more modest size, a
wide variation (say, six or more orders of magnitude) in coefficients found in the
objective function or right hand side, or in any given row or column of the matrix,
can cause difficulty either in Presolve when it makes substitutions, or in the
optimizer routines, particularly the barrier optimizer, as convergence is
approached.
With default numeric tolerances, this will deliver an optimal solution of x1=1.0
and x2=1.0 , giving an objective function value of 2.0 . Now, see what happens
when using slightly more accurate data (in terms of the fractional values that are
clearly intended to be expressed):
Maximize
obj: x1 + x2
Subject To
c1: 0.333333333 x1 + 0.666666667 x2 = 1
c2: x1 + 2 x2 = 3
End
The solution to this problem has x1=3.0 and x2=0.0, giving an optimal objective
function value of 3.0 , a result qualitatively different from that of the first model.
Since this latter result is the same as would be obtained by removing constraint c1
from the model entirely, this is a more satisfactory result. Moreover, the numeric
stability of the optimal basis (as indicated by the condition number, discussed in
the next section), is vastly improved.
Be particularly wary of data in your model that has been computed (within your
program, or transmitted to your program from another via an input file) using
single-precision (32-bit) arithmetic. For example, in C, this situation would arise
from using type float instead of double. Such data will be accurate only to about 8
decimal digits, so that (for example) if you print the data, you might see values
like 0.3333333432674408 instead of 0.3333333333333333. CPLEX uses
double-precision (64-bit) arithmetic in its computations, and truncated
single-precision data carries the risk that it will convey a different meaning than
the user intends.
The underlying principle behind all the cautions in this section is that information
contained in the data needs to reflect actual meaning or the optimizer may reach
unstable solutions or encounter algorithmic difficulties.
Ill-conditioned matrices are sensitive to minute changes in problem data. That is, in
such problems, small changes in data can lead to very large changes in the
reported problem solution. CPLEX provides a basis condition number to measure
the sensitivity of a linear system to the problem data. You might also think of the
basis condition number as the number of places in precision that can be lost.
For example, if the basis condition number at optimality is 1e+13, then a change in
a single matrix coefficient in the thirteenth place (counting from the right) may
dramatically alter the solution. Furthermore, since many computers provide about
16 places of accuracy in double precision, only three accurate places are left in such
a solution. Even if an answer is obtained, perhaps only the first three significant
digits are reliable.
More generally, for a given order of magnitude for the feasibility and optimality
tolerances, dividing the feasibility tolerance by the machine precision specifies the
lower threshold for the condition number at which point the potential for
numerical difficulties begins. For example, with the default feasibility of CPLEX
and optimality tolerances of 1e-6 and machine precision of 1e-16, 1e+10 is the
Because of this effective loss of precision for matrices with high basis condition
numbers, CPLEX may be unable to select an optimal basis. In other words, a high
basis condition number can make it impossible to find a solution.
v In the Interactive Optimizer, use the command display solution kappa in order
to see the basis condition number of a resident basis matrix.
v In Concert Technology, use the method:
IloCplex::getQuality(IloCplex::Kappa) (C++)
IloCplex.getQuality(IloCplex.QualityType.Kappa) (Java)
Cplex.GetQuality(Cplex.QualityType.Kappa) (.NET)
v In the Callable Library, use the routineCPXgetdblquality to access the condition
number in the double-precision variable dvalue, like this:
status = CPXgetdblquality(env, lp, &dvalue, CPX_KAPPA);
Repeated singularities
To save the best factorable basis found so far in the Interactive Optimizer, use the
write command with the file type bas. When using the Component Libraries, use
the method cplex.writeBasis or the routine CPXwriteprob.
If CPLEX encounters repeated singularities in your problem, you may want to try
alternative scaling on the problem (rather than simply increasing CPLEX tolerance
for singularities). Scaling on page 142 explains how to try alternative scaling.
If alternate scaling does not help, another tactic to try is to increase the Markowitz
tolerance. The Markowitz tolerance controls the kinds of pivots permitted. If you
set it near its maximum value of 0.99999 , it may make iterations slower but more
numerically stable. Inability to stay feasible on page 150 shows how to change
the Markowitz tolerance.
If none of these ideas help, you may need to alter the model of your problem.
Consider removing the offending variables manually from your model, and review
the model to find other ways to represent the functions of those variables.
In either case, perturbation creates a different but closely related problem. After
CPLEX has solved the perturbed problem, it removes the perturbation by resetting
problem data to their original values.
If CPLEX automatically perturbs your problem early in the solution process, you
should consider starting the solution process yourself with a perturbation. (Starting
in this way will save the time that would be wasted if you first allowed
optimization to stall and then let CPLEX perturb the problem automatically.)
To start perturbation yourself, set the parameter PerInd to 1 instead of its default
value of 0 (zero). The perturbation constant, EpPer, is usually appropriate at its
default value of 1e-6, but can be set to any value 1e-8 or larger.
If you observe that your problem has been perturbed more than once, then the
perturbed problem may differ too greatly from your original problem. In such a
case, consider reducing the value of the perturbation constant perturbation
constant (EpPer in Concert Technology, CPX_PARAM_EPPER in the Callable Library).
If a problem repeatedly becomes infeasible in Phase II (that is, after CPLEX has
achieved a feasible solution), then numeric difficulties may be occurring. It may
help to increase the Markowitz tolerance in such a case. By default, the value of
the parameter EpMrk is 0.01, and suitable values range from 0.0001 to 0.99999.
Sometimes slow progress in Phase I (the period when CPLEX calculates the first
feasible solution) is due to similar numeric difficulties, less obvious because
feasibility is not gained and lost. In the progress reported in the log file, an
increase in the printed sum of infeasibilities may be a symptom of this case. If so,
it may be worthwhile to set a higher Markowitz tolerance, just as in the more
obvious case of numeric difficulties in Phase II.
Diagnosing LP infeasibility
Documents ways to diagnose sources of infeasibility in LP models.
CPLEX reports statistics about any problem that it optimizes. For infeasible
solutions, it reports values that you can analyze to discover where your problem
formulation proved infeasible. In certain situations, you can then alter your
problem formulation or change CPLEX parameters to achieve a satisfactory
solution.
v When the CPLEX primal simplex optimizer terminates with an infeasible basic
solution, it calculates dual variables and reduced costs relative to the Phase I
objective function; that is, relative to the infeasibility function. The Phase I
objective function depends on the current basis. Consequently, if you use the
CPLEX provides tools to help you analyze the source of the infeasibility in your
model. Those tools include the conflict refiner and FeasOpt:
v The conflict refiner is invoked by the routine CPXrefineconflict in the Callable
Library or by the method refineConflict in Concert Technology. It finds a set of
conflicting constraints and bounds in a model and refines the set to be minimal
in a sense that you declare. It then reports its findings for you to take action to
repair that conflict in your infeasible model. For more about this feature, see
Chapter 31, Diagnosing infeasibility by refining conflicts, on page 429.
v FeasOpt is implemented in the Callable Library by the routine CPXfeasopt and
in Concert Technology by the method feasOpt. For more about this feature, see
Repairing infeasibility: FeasOpt on page 155.
With the help of those tools, you may be able to modify your problem to avoid
infeasibility.
By default, CPLEX scales a problem before attempting to solve it. After it finds an
optimal solution, it then checks for any violations of optimality or feasibility in the
original, unscaled problem. If there is a violation of reduced cost (indicating
nonoptimality) or of a bound (indicating infeasibility), CPLEX reports both the
maximum scaled and unscaled feasibility violations.
Unscaled infeasibilities are rare, but they may occur when a problem is
ill-conditioned. For example, a problem containing a row in which the coefficients
have vastly different magnitude is ill-conditioned in this sense and may result in
unscaled infeasibilities.
You may also be able to re-optimize the problem successfully after you reduce
optimality tolerance, as explained in Maximum reduced-cost infeasibility on
page 154, or after you reduce feasibility tolerance, as explained in Maximum
bound infeasibility: identifying largest bound violation on page 153. When you
change these tolerances, CPLEX may produce a better solution to your problem,
but lowering these tolerances sometimes produces erratic behavior or an unstable
optimal basis.
Tip:
If you restart CPLEX from a previously optimal or infeasible solution and use
reduced tolerance without making any other change to the problem, the previous
solution status remains valid. Consequently, no iterations will occur because
parameter settings (such as this reduced tolerance) are part of the environment in
which CPLEX operates, rather than part of a solution to one of possibly multiple
models in that environment. In other words, changing parameters does not alter
the solution status, but changing the model does. You can make CPLEX restart the
optimization using new tolerances by making a superfluous change in the model,
for example, by resetting the bound on a variable to its existing value.
Infeasibility and unboundedness in linear programs are closely related. (For more
about that idea, see the topics in Part 6, Infeasibility and unboundedness, on
page 421.) When the linear program CPLEX solves is infeasible, the associated dual
linear program has an unbounded ray. Similarly, when the dual linear program is
infeasible, the primal linear program has an unbounded ray. This relationship is
important for proper interpretation of infeasible solution output.
The treatment of models that are unbounded involves a few subtleties. Specifically,
a declaration of unboundedness means that CPLEX has detected that the model
has an unbounded ray. Given any feasible solution x with objective z, a multiple of
the unbounded ray can be added to x to give a feasible solution with objective z-1
(or z+1 for maximization models). Thus, if a feasible solution exists, then the
By default, individual infeasibilities are written to a log file but not displayed on
the screen. To display the infeasibilities on your screen, in Concert Technology, use
methods of the environment to direct the output stream to a log file; in the
Interactive Optimizer, use the command set output logonly y cplex.log.
For C++ applications, see Accessing solution information on page 14, and for
Java applications, see Accessing solution information on page 36. Those sections
highlight the application programming details of how to retrieve statistics about
the quality of a solution.
When the simplex optimizer detects infeasibility in the primal or dual linear
program (LP), parts of the solution it provides are relative to the Phase I linear
program it solved to conclude infeasibility. In other words, the result you see in
such a case is not the solution values computed relative to the original objective or
original righthand side vector. Keep this distinction in mind when you interpret
solution quality; otherwise, you may be surprised by the results. In particular,
when CPLEX detects that a linear program is infeasible using the primal simplex
method, the reduced costs and dual variables provided in the solution are relative
to the objective of the Phase I linear program it solved. Similarly, when CPLEX
detects that a linear program is unbounded because the dual simplex method
detected dual infeasibility, the primal and slack variables provided in the solution
are relative to the Phase I linear program created for the dual simplex optimizer.
The maximum bound infeasibility identifies the largest bound violation. This
information may help you discover the cause of infeasibility in your problem. If
the largest bound violation exceeds the feasibility tolerance of your problem by
only a small amount, then you may be able to get a feasible solution to the
problem by increasing the feasibility tolerance parameter (EpRHS in Concert
To change the optimality tolerance, set the optimality tolerance parameter (EpOpt in
Concert Technology, CPX_PARAM_EPOPT in the Callable Library). Its range is between
1e-9 and 0.1. Its default value is 1e-6.
The maximum dual residual indicates the numeric accuracy of the reduced costs in
the current solution. By construction, in exact arithmetic, the dual residual of a
basic solution is always 0 (zero). A nonzero value is thus the effect of roundoff
error due to finite-precision arithmetic in the computation of the dual solution
vector. Thus, a significant nonzero value indicates ill conditioning.
When you are trying to decide whether your problem is ill-conditioned, you also
need to consider the following maximum absolute values, all available in the
infeasibility analysis that CPLEX provides you:
v variables;
v slack variables;
v dual variables;
v reduced costs (that is, dual slack variables).
When using the Component Libraries, use the method getQuality or the routine
CPXgetdblquality to access the information provided by the command
display solution quality in the Interactive Optimizer.
If you discover from this analysis that your model is indeed ill-conditioned, then
you need to reformulate it. Coping with an ill-conditioned problem or handling
unscaled infeasibilities on page 151 outlines steps to follow in this situation.
If CPLEX reports that your problem is infeasible, then you can invoke tools of
CPLEX to help you analyze the source of the infeasibility. These diagnostic tools
compute a set of conflicting constraints and column bounds that would be feasible
if one of them (a constraint or variable) were removed. Such a set is known as a
conflict. For more about detecting conflicts, see Chapter 31, Diagnosing
infeasibility by refining conflicts, on page 429.
Overview
Introduces an example starting from an advanced basis.
Example ilolpex6.cpp
Shows an example of starting from in advanced basis in the C++ API.
The example, ilolpex6.cpp, resembles one you may have studied in the Getting
Started manual, ilolpex1.cpp. This example differs from that one in these ways:
v Arrays are constructed using the populatebycolumn method, and thus no
command line arguments are needed to select a construction method.
v In the main routine, the arrays cstat and rstat set the status of the initial basis.
v After the problem data has been copied into the problem object, the basis is
copied by a call to cplex.setBasisStatuses.
v After the problem has been optimized, the iteration count is printed. For the
given data and basis, the basis is optimal, so no iterations are required to
optimize the problem.
The call to cplex.solve optimizes the problem, and the subsequent print of the
iteration count demonstrates that the advanced basis took effect. In fact, this basis
is immediately detected as optimal, resulting in zero iterations being performed, in
contrast to the behavior seen in the example program ilolpex1.cpp where the
same model is solved without the use of an advanced basis.
Example lpex6.c
Shows an example of starting from an advanced basis in the C API.
The example, lpex6.c, resembles one you may have studied in the Getting Started
manual, lpex1.c. This example differs from that one in these ways:
v In the main routine, the arrays cstat and rstat set the status of the initial basis.
v After the problem data has been copied into the problem object, the basis is
copied by a call to CPXcopybase .
v After the problem has been optimized, the iteration count is printed. For the
given data and basis, the basis is optimal, so no iterations are required to
optimize the problem.
The application begins with declarations of arrays to store the solution of the
problem. Then, before it calls any other CPLEX routine, the application invokes the
Callable Library routine CPXopenCPLEX to initialize the CPLEX environment. After
the environment has been initialized, the application calls other Callable Library
routines, such as CPXsetintparam with the argument CPX_PARAM_SCRIND to direct
output to the screen and most importantly, CPXcreateprob to create the problem
object. The routine populatebycolumn builds the problem object, and as noted
earlier, CPXcopybase copies the advanced starting basis.
Before the application ends, it calls CPXfreeprob to free space allocated to the
problem object and CPXcloseCPLEX to free the environment.
The IBM ILOG CPLEX barrier optimizer is well suited to large, sparse problems.
An alternative to the simplex optimizers, which are also suitable to problems in
which the matrix representation is dense, the barrier optimizer exploits a
primal-dual logarithmic barrier algorithm to generate a sequence of strictly positive
primal and dual solutions to a problem. As with the simplex optimizers, it is not
really necessary to understand the internal workings of barrier in order to obtain
its performance benefits. However, for the interested reader, here is an outline of
how it works.
CPLEX finds the primal solutions, conventionally denoted (x, s), from the primal
formulation:
Minimize cTx
subject to Ax = b
where A is the constraint matrix, including slack and surplus variables; u is the
upper and l the lower bounds on the variables.
subject to ATy - w + z = c
CPLEX measures progress by considering the primal feasibility, dual feasibility, and
duality gap at each iteration. To measure feasibility, CPLEX considers the accuracy
with which the primal constraints (Ax = b, x + s = u) and dual constraints
(ATy + z - w = c) are satisfied. The optimizer stops when it finds feasible primal
and dual solutions that are complementary. A complementary solution is one
where the sums of the products (xj -lj)zj and (uj - xj)zj are within some tolerance
of 0 (zero). Since each (xj -lj), (uj - xj), and zj is strictly positive, the sum can be
near zero only if each of the individual products is near zero. The sum of these
products is known as the complementarity of the problem.
The CPLEX barrier optimizer is appropriate and often advantageous for large
problems, for example, those with more than 100 000 rows or columns. It is not
always the best choice, though, for sparse models with more than 100 000 rows. It
is effective on problems with staircase structures or banded structures in the
constraint matrix. It is also effective on problems with a small number of nonzeros
per column (perhaps no more than a dozen nonzero values per column).
In short, denseness or sparsity are not the deciding issues when you are deciding
whether to use the barrier optimizer. In fact, its performance is most dependent on
these characteristics:
v the number of floating-point operations required to compute the Cholesky
factor;
v the presence of dense columns, that is, columns with a relatively high number of
nonzero entries.
To decide whether to use the barrier optimizer on a given problem, you should
look at both these characteristics, not simply at denseness, sparseness, or problem
size. (How to check those characteristics is explained later in this chapter in
Cholesky factor in the log file on page 163, and Nonzeros in lower triangle of
A*A' in the log file on page 163).
Since many users prefer basic solutions because they can be used to restart simplex
optimization, the CPLEX barrier optimizer includes basis crossover algorithms. By
default, the barrier optimizer automatically invokes a primal crossover when the
barrier algorithm terminates (unless termination occurs abnormally because of
insufficient memory or numeric difficulties). Optionally, you can also execute
barrier optimization with a dual crossover or with no crossover at all. The section
Controlling crossover on page 160 explains how to control crossover in the
Interactive Optimizer.
The barrier optimizer and the simplex optimizers (primal and dual) are
fundamentally different approaches to solving linear programming problems. The
key differences between them have these implications:
v Simplex and barrier optimizers differ with respect to the nature of solutions.
Barrier solutions tend to be midface solutions. In cases where multiple optima
exist, barrier solutions tend to place the variables at values between their
bounds, whereas in basic solutions from a simplex technique, the values of the
variables are more likely to be at either their upper or their lower bound. While
objective values will be the same, the nature of the solutions can be very
different.
v By default, the barrier optimizer uses crossover to produce a basis. However,
you may choose to run the barrier optimizer without crossover. In such a case,
As you have read in Introducing the barrier optimizer on page 157, the CPLEX
barrier optimizer finds primal and dual solutions from the primal and dual
formulations of a model, but you do not have to reformulate the problem yourself.
The CPLEX barrier optimizer automatically creates the primal and dual
formulations of the problem for you after you enter or read in the problem.
Specify that you want to use the barrier optimizer by setting the parameter
LPMethod to one of the values in Table 29.
Table 29. Settings of LPMethod to invoke the barrier optimizer
Setting Context
IloCplex::Barrier Concert Technology for C++ users
IloCplex.Algorithm.Barrier Concert Technology for Java users
Cplex.Algorithm.Barrier Concert Technology for .NET users
CPX_ALG_BARRIER Callable Library
4 Interactive Optimizer
And then you call the solution routine just as for any other CPLEX optimizer, as
you see in Table 30.
Table 30. Calling the barrier optimizer
Call Context
cplex.solve Concert Technology for C++ users
cplex.solve Concert Technology for Java users
In addition to the parameters available for other CPLEX LP optimizers, there are
also parameters to control the CPLEX barrier optimizer. In the Interactive
Optimizer, to see a list of the parameters specific to the CPLEX barrier optimizer,
use the command set barrier.
Controlling crossover
Documents values of the parameter controlling barrier crossover.
The nature of the crossover step that follows barrier is controlled by the parameter
barrier crossover algorithm (CPX_PARAM_BARCROSSALG, BarCrossAlg). Under the
default Automatic setting, CPLEX invokes an appropriate crossover step based on
the problem type and the number of threads available. Possible settings for the
parameter appear in Table 31.
Table 31. BarCrossAlg parameter settings
BarCrossAlg Values Meaning
-1 no crossover
0 automatic (default)
1 primal crossover
2 dual crossover
When you use the CPLEX barrier optimizer with no crossover, you can save the
primal and dual variable values and their associated reduced cost and dual values
in a SOL-format file (that is, a solution file with the extension .sol). You can then
read that solution file into CPLEX before you initiate a crossover at a later time.
After you read a solution file into CPLEX, all three optimizers (primal simplex,
dual simplex, and barrier simplex) automatically invoke crossover. See the CPLEX
File Formats Reference Manual, especially SOL file format: solution files, for more
about solution files.
Like the CPLEX simplex optimizers, the barrier optimizer records information
about its progress in a log file as it works. Some users find it helpful to keep a new
log file for each session. By default, CPLEX records information in a file named
cplex.log.
v In Concert Technology, use the method setOut of IloCplex to designate a log file
for a predefined channel.
v In the Callable Library, use the routine CPXsetlogfile with arguments to
indicate the log file.
v In the Interactive Optimizer, use the command set logfile filename to change
the name of the log file.
You can control the level of information that CPLEX records about barrier
optimization by setting the BarDisplay parameter. Those settings appear in
Table 32.
Table 32. BarDisplay parameter settings.
BarDisplay Values Meaning
0 no display
1 display normal information (default)
2 display detailed (diagnostic) output
Dual crossover.
The objective function of that model includes a quadratic term that is not positive
semi-definite. In such a case, CPLEX can apply an augmented system solver in the
barrier optimizer.
In particular, if you apply the optimality target parameter with a value of 2, then
CPLEX searches for a solution that satisfies first-order optimality conditions, but is
not necessarily globally optimal.
Here is an example of a log file from the augmented system solver on that model:
Number of nonzeros in lower triangle of Q = 1
Using Approximate Minimum Degree ordering
Total time for automatic ordering = 0.02 sec. (0.00 ticks)
Summary statistics for factor of Q:
Rows in Factor = 2
Integer space required = 2
Total non-zeros in factor = 3
Total FP ops to factor = 5
Note: Q in objective is not positive semi-definite.
Tried aggregator 1 time.
No QP presolve or aggregator reductions.
Presolve time = 0.02 sec. (0.00 ticks)
Summary statistics for factor of Augmented system:
Rows in Factor = 6
Integer space required = 0
Total non-zeros in factor = 21
Total FP ops to factor = 91
Itn Primal Obj Dual Obj Prim Inf Dual Inf Comple
0 1.2325952e-032 -1.2325952e-032 3.33e-016 4.00e+000 1.00e+000
1 -1.3681779e-033 -3.9837078e-014 2.22e-016 2.00e-003 5.00e-004
2 -8.6025148e-023 -4.0000199e-014 4.44e-016 1.00e-006 2.50e-007
3 -5.6343179e-022 -3.9999999e-014 1.11e-016 5.47e-010 1.25e-010
The opening lines of that log file record information about preprocessing by the
CPLEX presolver and aggregator. After those preprocessing statistics, the next line
records the number of nonzeros in the lower triangle of a particular matrix, AAT,
denoted A*A' in the log file.
The number of nonzeros in the lower triangle of AAT gives an early indication of
how long each barrier iteration will take in terms of a relative measure of time.
The larger this number, the more time each barrier iteration requires. If this
number is close to 50% of the square of the number of rows of the reduced LP,
then the problem may contain dense columns that are not being detected. In that
case, examine the histogram of column counts; then consider setting the barrier
column-nonzeros parameter to a value that enables CPLEX to treat more columns
as being dense.
In the Interactive Optimizer, you can examine the histogram of column counts with
the command display problem histogram. For example, that command executed
on the model logged in Sample log file from the barrier optimizer on page 161
produces these row and column reports:
Row counts (excluding fixed variables):
Nonzero Count: 1 2 3 4 5 6 7 9
Number of Rows: 2 16 1 1 4 1 1 1
Nonzero Count: 1 2 4
Number of Columns: 1 21 10
After the number of nonzeros in the lower triangle of AAT, CPLEX records the
time required by the ordering algorithm. (The CPLEX barrier optimizer offers you
a choice of several ordering algorithms, explained in Choosing an ordering
algorithm on page 169.) This section in the log file indicates which ordering
algorithm the default Automatic setting chose.
Information about the Cholesky factor continues with the number of nonzeros in
the factored matrix. The difference between this number and the number of
nonzeros in AAT indicates the fill-level of the Cholesky factor.
The final line of information indicates how many floating-point operations are
required to compute the Cholesky factor. This number is the best predictor of the
relative time that will be required to perform each iteration of the barrier
optimizer.
After the information about the Cholesky factor, the log file records progress at
each iteration. It records both primal and dual objectives (as Primal Obj and
Dual Obj) per iteration.
It also records absolute infeasibilities per iteration. Internally, the CPLEX barrier
optimizer treats inequality constraints as equality constraints with added slack and
surplus variables. Consequently, primal constraints in a problem are written as
Ax = b and x + s = u, and the dual constraints are written as ATy + z - w = c.
As a result, in the log file, the infeasibilities represent norms, as summarized in
Table 33.
Table 33. Infeasibilities and norms in the log file of a barrier optimization
Infeasibility In log file Norm
primal Prim Inf |b - Ax|
upper Upper Inf |u - (x + s)|
dual Dual Inf |c - yA - z + w|
If solution values are large in absolute value, then the infeasibilities may appear
inordinately large because they are recorded in the log file in absolute terms. The
optimizer uses relative infeasibilities as termination criteria.
If you are using one of the barrier infeasibility algorithms available in the CPLEX
barrier optimizer (that is, if you have set BarAlg to either 1 or 2, as discussed later
in this chapter), then CPLEX records a column of output titled Inf Ratio, the
infeasibility ratio. This ratio, always positive, is a measure of progress for that
particular algorithm. In a problem with an optimal solution, you will see this ratio
increase to a large number. In contrast, in a problem that is infeasible or
unbounded, this ratio will decrease to a very small number. (The infeasibility ratio
is not available in the standard barrier algorithm, that is, when the value of BarAlg
is set to 3.)
When CPLEX successfully solves a problem with the CPLEX barrier optimizer, it
reports the optimal objective value and solution time in a log file, as it does for
other LP optimizers.
Because barrier solutions (prior to crossover) are not basic solutions, certain
solution statistics associated with basic solutions are not available for a strictly
barrier solution. For example, reduced costs and dual values are available for
strictly barrier LP solutions, but range information about them is not.
To help you evaluate the quality of a barrier solution more readily, CPLEX offers a
special display of information about barrier solution quality. To display this
information in the Interactive Optimizer, use the command
display solution quality after optimization. When using the Component
Libraries, use the method cplex.getQuality or use the routines CPXgetintquality
for integer information and CPXgetdblquality for double-valued information.
Table 34 lists the items CPLEX displays and explains their meaning. In the solution
quality display, the term pi refers to dual solution values, that is, the y values in
the conventional barrier problem-formulation. The term rc refers to reduced cost,
that is, the difference z - w in the conventional barrier problem-formulation. Other
terms are best understood in the context of primal and dual LP formulations.
Normalized errors, for example, represent the accuracy of satisfying the constraints
while considering the quantities used to compute Ax on each row and yTA on each
column. In the primal case, for each row, consider the nonzero coefficients and the
xj values used to compute Ax. If these numbers are large in absolute value, then it
is acceptable to have a larger absolute error in the primal constraint.
If CPLEX returned an optimal solution, but the primal error seems high to you, the
primal normalized error should be low, since it takes into account the scaling of
the problem and solution.
Naturally, the default parameter settings for the CPLEX barrier optimizer work
best on most problems. However, you can tune several algorithmic parameters to
improve performance or to overcome numeric difficulties.
To help you decide whether default settings of parameters are best for your model,
or whether other parameter settings may improve performance, the tuning tool is
available. Chapter 9, Tuning tool, on page 121 explains more about this utility
and shows you examples of its use.
In addition, several parameters set termination criteria. With them, you control
when CPLEX stops optimization.
You can also control convergence tolerance (another factor that influences
performance). Convergence tolerance specifies how nearly optimal a solution
CPLEX must find: tight convergence tolerance means CPLEX must keep working
until it finds a solution very close to the optimal one; loose tolerance means
CPLEX can return a solution within a greater range of the optimal one and thus
stop calculating sooner.
At default settings, the CPLEX barrier optimizer will do all of its work in central
memory (also variously referred to as RAM, core, or physical memory). For models
too large to solve in the central memory on your computer, or in cases where you
simply do not want to use this much memory, it is possible to instruct the barrier
optimizer to use disk for part of the working storage it needs, specifically the
Cholesky factorization. Since access to disk is slower than access to central
memory, there may be some lost performance by this choice on models that could
be solved entirely in central memory, but the out-of-core feature in the barrier
To activate the out-of-core feature, set the memory emphasis parameter (memory
reduction switch) to 1 (one) instead of its default value of 0 (zero).
v MemoryEmphasis in Concert Technology
v CPX_PARAM_MEMORYEMPHASIS in the Callable Library
v emphasis memory in the Interactive Optimizer
This memory emphasis feature will also invoke other memory conservation tactics,
such as compression of the data within presolve.
Memory emphasis uses some working memory in RAM to store the portion of the
factor on which it is currently performing computation. You can improve
performance by allocating more working memory by means of the working
memory parameter (memory available for working storage).
v WorkMem in Concert Technology
v CPX_PARAM_WORKMEM in the Callable Library
v workmem in the Interactive Optimizer
More working memory allows the optimizer to transfer less data to and from disk.
In fact, the Cholesky factor matrix will not be written to disk at all if its size does
not exceed the value of the working memory parameter. The default for this
parameter is 128 megabytes.
When the barrier optimizer operates with memory emphasis, the location of disk
storage is controlled by the working directory parameter (directory for working
files).
v WorkDir in Concert Technology
v CPX_PARAM_WORKDIR in the Callable Library
v workdir in the Interactive Optimizer
For example, to use the directory /tmp/mywork, set the working directory parameter
to the string /tmp/mywork. The value of the working directory parameter should be
specified as the name of a directory that already exists, and CPLEX will create its
working directory as a subdirectory there. At the end of barrier optimization,
CPLEX will automatically delete any working directories it created, leaving the
directory specified by the working directory parameter intact.
Preprocessing
Describes the effect of preprocessing on the performance of the barrier optimizer.
For best performance of the CPLEX barrier optimizer, preprocessing should almost
always be on. That is, use the default setting where the presolver and aggregator
are active. While they may use more memory, they also reduce the problem, and
problem reduction is crucial to barrier optimizer performance. In fact, reduction is
so important that even when you turn off preprocessing, CPLEX still applies
minimal presolving before barrier optimization.
For problems that contain linearly dependent rows, it is a good idea to turn on the
preprocessing dependency parameter. (By default, it is off.) This dependency
checker may add some preprocessing time, but it can detect and remove linearly
These reductions can be applied to all types of problems: LP, QP, QCP, MIP,
including MIQP and MIQCP.
Dense columns can significantly degrade the performance of the barrier optimizer.
A dense column is one in which a given variable appears in many rows. So that
you can detect dense columns, the Interactive Optimizer contains a display feature
that shows a histogram of the number of nonzeros in the columns of your model,
display problem histogram c .
Nonzeros in lower triangle of A*A' in the log file on page 163 explains how to
examine a log file from the barrier optimizer in order to tell which columns CPLEX
detects as dense at its current settings.
In fact, when a few dense columns are present in a problem, it is often effective to
reformulate the problem to remove those dense columns from the model.
Otherwise, you can control whether CPLEX perceives columns as dense by setting
the column nonzeros parameter. At its default setting, CPLEX calculates an
appropriate value for this parameter automatically. However, if your problem
contains one (or a few) dense columns that remain undetected at the default
setting (according to the log file), you can adjust this parameter yourself to help
CPLEX detect it (or them). For example, in a large problem in which one column
contains forty entries while the other columns contain less than five entries, you
may benefit by setting the column nonzeros parameter to 30. This setting allows
CPLEX to recognize that column as dense and thus invoke techniques to handle it.
To set the dense column threshold, set the parameter BarColNz to a positive integer.
The default value of 0 (zero) means that CPLEX will set the threshold.
The log file, as explained in Ordering-algorithm time in the log file on page 163,
records the time spent by the ordering algorithm in a barrier optimization, so you
can experiment with different ordering algorithms and compare their performance
on your problem.
Automatic ordering, the default option, will usually be the best choice. This option
attempts to choose the most effective of the available ordering methods, and it
usually results in the best order. It may require more time than the other settings.
The ordering time is usually small relative to the total solution time, and a better
order can lead to a smaller total solution time. In other words, a change in this
parameter is unlikely to improve performance very much.
The AMD algorithm provides good quality order within moderate ordering time.
AMF usually provides better order than AMD (usually 5-10% smaller factors) but it
requires somewhat more time (10-20% more). ND often produces significantly
better order than AMD or AMF. Ten-fold reductions in runtimes of the CPLEX
barrier optimizer have been observed with it on some problems. However, ND
sometimes produces worse order, and it requires much more time.
CPLEX supports several different heuristics to compute the starting point for the
CPLEX barrier optimizer. The starting-point heuristic is specified by the
BarStartAlg parameter, and Table 36 summarizes the possible settings and their
meanings.
Table 36. BarStartAlg parameter settings for starting-point heuristics
Setting Heuristic
1 dual is 0 (default)
2 estimate dual
3 average primal estimate, dual 0
4 average primal estimate, estimate dual
For most problems the default works well. However, if you are using the dual
preprocessing option (setting the parameter PreDual to 1 ) then one of the other
heuristics for computing a starting point may perform better than the default.
v In the Interactive Optimizer, use the command set barrier startalg i,
substituting a value for i.
v When using the Component Libraries, set the barrier starting point algorithm
parameter IloCplex::BarStartAlg or CPX_PARAM_BARSTARTALG.
As noted in Differences between barrier and simplex optimizers on page 158, the
algorithms in the barrier optimizer have very different numeric properties from
those in the simplex optimizer. While the barrier optimizer is often extremely fast,
particularly on very large problems, numeric difficulties occasionally arise with it
in certain classes of problems. For that reason, it is a good idea to run simplex
optimizers in conjunction with the barrier optimizer to verify solutions. At its
default settings, the CPLEX barrier optimizer always crosses over after a barrier
solution to a simplex optimizer, so this verification occurs automatically.
Before you try tactics that apply to specific symptoms, as described in the
following sections, a useful CPLEX parameter to try is the numerical precision
emphasis parameter.
v NumericalEmphasis in Concert Technology
v CPX_PARAM_NUMERICALEMPHASIS in the Callable Library
v emphasis numerical in the Interactive Optimizer
Unlike the following suggestions, which deal with knowledge of the way the
barrier optimizer works or with details of your specific model, this parameter is
intended as a way to tell CPLEX to exercise more than the usual caution in its
computations. When you set it to its nondefault value specifying extreme
numerical caution, various tactics are invoked internally to try to avoid loss of
numerical accuracy in the steps of the barrier algorithm.
Be aware that the nondefault setting may result in slower solution times than
usual. The effect of this setting is to shift the emphasis away from fastest solution
time and toward numerical caution. On the other hand, if numerical difficulty is
causing the barrier algorithm to perform excessive numbers of iterations due to
loss of significant digits, it is possible that the setting of extreme numerical caution
could actually result in somewhat faster solution times. Overall, it is difficult to
project the impact on speed when using this setting.
The purpose of this parameter setting is not to generate "more accurate solutions"
particularly where the input data is in some sense unsatisfactory or inaccurate. The
numerical caution is applied during the steps taken by the barrier algorithm
during its convergence toward the optimum, to help it do its job better. On some
models, it may turn out that solution quality measures are improved (Ax-b
residuals, variable-bound violations, dual values, and so forth) when CPLEX
exercises numerical caution, but this would be a secondary outcome from better
convergence.
The CPLEX barrier optimizer implements the algorithms listed in Table 37. The
selection of barrier algorithm is controlled by the BarAlg parameter. The default
option invokes option 3 for LPs and QPs, option 1 for QCPs, and option 1 for
MIPs where the CPLEX barrier optimizer is used on the subproblems. Naturally,
the default is the fastest for most problems, but it may not work well on LP or QP
problems that are primal infeasible or dual infeasible. Options 1 and 2 in the
CPLEX barrier optimizer implement a barrier algorithm that also detects
infeasibility. (They differ from each other in how they compute a starting point.)
Though they are slower than the default option, in a problem demonstrating
numeric difficulties, they may eliminate the numeric difficulties and thus improve
the quality of the solution.
Table 37. BarAlg parameter settings for barrier optimizer
BarAlg Setting Meaning
0 default
1 algorithm starts with infeasibility estimate
2 algorithm starts with infeasibility constant
3 standard barrier algorithm
Detecting and eliminating dense columns on page 169 explains how to change
parameters to encourage CPLEX to detect and eliminate as many dense columns as
possible. However, in some problems, if CPLEX removes too many dense columns,
it may cause numeric instability.
You can check how many dense columns CPLEX removes by looking at the
preprocessing statistics at the beginning of the log file. For example, the following
log file shows that CPLEX removed 2249 columns, of which nine were dense.
Selected objective sense: MINIMIZE
Selected objective name: obj
Selected RHS name: rhs
Selected bound name: bnd
If you observe that the removal of too many dense columns results in numeric
instability in your problem, then increase the column nonzeros parameter,
BarColNz.
The default value of the column nonzeros parameter is 0 (zero); that value tells
CPLEX to calculate the parameter automatically.
If you decide that the current value of the column nonzeros parameter is
inappropriate for your problem and thus tells CPLEX to remove too many dense
columns, then you can increase the parameter BarColNz to keep the number of
dense columns removed low.
If your problem contains small numeric inconsistencies, it may be difficult for the
CPLEX barrier optimizer to achieve a satisfactory solution at the default setting of
the complementarity convergence tolerance. In such a case, you should increase the
convergence tolerance parameter (BarEpComp for LP or QP models, BarQCPEpComp
for QCP models).
Normally, the CPLEX barrier optimizer uses its barrier growth parameter,
BarGrowth , to detect such conditions. If this parameter is increased beyond its
default value, the CPLEX barrier optimizer will be less likely to detect that the
problem has an unbounded optimal face and more likely to encounter numeric
difficulties.
Consequently, you should change the barrier growth parameter only if you find
that the CPLEX barrier optimizer is terminating its work before it finds the true
optimum because it has falsely detected an unbounded face.
The CPLEX barrier optimizer stops when the absolute value of either the primal or
dual objective exceeds the objective range parameter, BarObjRng.
If you increase the value of BarObjRng, then the CPLEX barrier optimizer will
iterate more times before it decides that the current problem suffers from an
unbounded objective value.
If you know that your problem has large objective values, consider increasing
BarObjRng.
Also if you know that your problem has large objective values, consider changing
the barrier algorithm by resetting the BarAlg parameter.
When the CPLEX barrier optimizer terminates and reports an infeasible solution,
all the usual solution information is available. However, the solution values,
reduced costs, and dual variables reported then do not correspond to a basis;
hence, that information does not have the same meaning as the corresponding
output from the CPLEX simplex optimizers.
Actually, since the CPLEX barrier optimizer works in a single phase, all reduced
costs and dual variables are calculated in terms of the original objective function.
If the CPLEX barrier optimizer reports to you that a problem is infeasible, one
approach to overcoming the infeasibility is to invoke FeasOpt or the conflict
refiner. See Chapter 32, Repairing infeasibilities with FeasOpt, on page 443 and
Chapter 31, Diagnosing infeasibility by refining conflicts, on page 429 for an
explanation of these tools.
If the CPLEX barrier optimizer reports to you that a problem is infeasible, but you
still need a basic solution for the problem, use the primal simplex optimizer.
CPLEX will then use the solution provided by the barrier optimizer to find a
starting basis for the primal simplex optimizer. When the primal simplex optimizer
finishes its work, you will have an infeasible basic solution for further infeasibility
analysis.
If the default algorithm in the CPLEX barrier optimizer discovers that your
problem is primal infeasible or dual infeasible, then try the alternate algorithms in
the barrier optimizer. These algorithms, though slower than the default, are better
at detecting primal and dual infeasibility.
To select one of the barrier infeasibility algorithms, set the BarAlg parameter to
either 1 or 2.
If part of your problem is structured as a network, then you may want to consider
calling the CPLEX network optimizer. This optimizer may have a positive impact
on performance. There are two alternative ways of calling the network optimizer:
v If your problem is an LP where a large part is a network structure, you may call
the network optimizer for the populated LP object.
v If your entire problem consists of a network flow, you should consider creating a
network object instead of an LP object. Then populate it, and solve it with the
network optimizer. This alternative generally yields the best performance
because it does not incur the overhead of LP data structures. This option is
available only for the Callable library.
How much performance improvement you observe between using only a simplex
optimizer versus using the network optimizer followed by either of the simplex
optimizers depends on the number and nature of the other constraints in your
problem. On a pure network problem, performance has been measured as 10100
times faster with the network optimizer. However, if the network component of
your problem is small relative to its other parts, then using the solution of the
network part of the problem as a starting point for the remainder may or may not
improve performance, compared to running the primal or dual simplex optimizer.
Only experiments with your own problem can tell.
By convention, a node with strictly positive supply value (that is, s n > 0) is called
a supply node or a source, and a node with strictly negative supply value (that is, s
n < 0) is called a demand node or a sink. A node where s n = 0 is called a
transshipment node. The sum of all supplies must match the sum of all demands; if
not, then the network flow problem is infeasible.
T n is the set of arcs whose tails are node n; H n is the set of arcs whose heads are
node n. The usual form of a network problem looks like this:
subject to
That is, for each node, the net flow entering and leaving the node must equal its
supply value, and all flow values must be within their bounds. The solution of a
network-flow problem is an assignment of flow values to arcs (that is, the
modeling variables) to satisfy the problem formulation. A flow that satisfies the
constraints and bounds is feasible.
This example is based on a network where the aim is to minimize cost and where
the flow through the network has both cost and capacity. Figure 5 on page 179
shows you the nodes and arcs of this network. The nodes are labeled by their
identifying node number from 1 through 8. The number inside a node indicates its
supply value; 0 (zero) is assumed where no number is given. The arcs are labeled 1
through 14. The lower bound l , upper bound u , and objective value c of each arc
are displayed in parentheses (l, u, c) beside each arc. In this example, node 1
and node 5 are sources, representing a positive net flow, whereas node 4 and
The example in Figure 5 corresponds to the results of running the netex1.c. If you
run that application, it will produce a file named netex1.net which can be read
into the Interactive Optimizer with the command read netex1.net. After you read
the problem into the Interactive Optimizer, you can solve it with the command
netopt or the command optimize.
As CPLEX solves the problem, it produces a log like the following lines:
Iteration log . . .
Iteration: 0 Infeasibility = 48.000000 (150)
This network log file differs slightly from the log files produced by other CPLEX
optimizers: it contains values enclosed in parentheses that represent modified
objective function values.
As long as the network optimizer has not yet found a feasible solution, it is in
Phase I. In Phase I, the network optimizer uses modified objective coefficients that
penalize infeasibility. At its default settings, the CPLEX network optimizer displays
the value of the objective function calculated in terms of these modified objective
coefficients in parentheses in the network log file.
You can control the amount of information recorded in the network log file, just as
you control the amount of information in other CPLEX log files. To record no
information at all in the log file, use the command set network display 0. To
display the current objective value in parentheses relative to the actual unmodified
objective coefficients, use the command set network display 1. To see the display
mentioned earlier in this section, leave the network display parameter at its default
value, 2. (If you have changed the default value, you can reset it with the
command set network display 2.)
Chapter 12. Solving network-flow problems 179
Tuning performance of the network optimizer
Suggests strategies for improving performance of the network optimizer.
The default values of parameters controlling the network optimizer are generally
the best choices for effective performance. However, the following sections indicate
parameters that you may want to experiment with in your particular problem.
Controlling tolerance
You control the feasibility tolerance for the network optimizer through the
parameter NetEpRHS. Likewise, you control the optimality tolerance for the network
optimizer through the parameter NetEpOpt.
On the rare occasions when the network optimizer seems to take too long to find a
solution, you may want to change the pricing algorithm to try to speed up
computation. The pricing algorithm for the network optimizer is controlled by
parameter NetPPriInd. All the choices use variations of partial reduced-cost
pricing.
Use the parameter NetItLim if you want to limit the number of iterations that the
network optimizer performs.
You instruct CPLEX to apply the network optimizer for solving the LP at hand by
setting the algorithm for continuous problems parameter:
v setting CPX_PARAM_LPMETHOD to CPX_ALG_NET in the Callable Library
v or setting RootAlg to Network in Concert Technology
When you do so, CPLEX performs a sequence of steps. It first searches for a part
of the LP that conforms to network structure. Such a part is known as an
embedded network. It then uses the network optimizer to solve that embedded
network. Next, it uses the resulting basis to construct a starting basis for the full
LP problem. Finally, it solves the LP problem with a simplex optimizer.
You can also use the network optimizer when solving QPs (that is, problems with
a positive semi-definite quadratic term in the objective function), but not when
solving quadratically constrained problems. To do so using the Callable Library,
you set the algorithm for continuous quadratic optimization parameter
CPX_PARAM_QPMETHOD to CPX_ALG_NET. For Concert Technology, you set the RootAlg
parameter to Network. When CPLEX uses the network optimizer to solve a QP, it
first ignores the quadratic term and uses the network optimizer to solve the
resulting LP. CPLEX then uses the resulting basis to start a simplex algorithm on
the QP model with the original quadratic objective.
CPLEX can perform different levels of extraction. The level it performs depends on
the NetFind parameter.
v When the NetFind parameter is set to 1 (one), CPLEX extracts only the obvious
network; it uses no scaling; it scans rows in their natural order; it stops
extraction as soon as no more rows can be added to the network found so far.
v When the NetFind parameter is set to 2, the default setting, CPLEX also uses
reflection scaling (that is, it multiplies rows by -1) in an attempt to extract a
larger network.
v When the NetFind parameter is set to 3, CPLEX uses general scaling, rescaling
both rows and columns, in an attempt to extract a larger network.
Even if your problem does not conform precisely to network conventions, the
network optimizer may still be advantageous to use. When it is possible to
transform the original statement of a linear program into network conventions by
these algebraic operations:
v changing the signs of coefficients,
v multiplying constraints by constants,
v rescaling columns,
v adding or eliminating redundant relations,
then CPLEX will carry out such transformations automatically if you set the
NetFind parameter appropriately.
Briefly, the main function initializes the CPLEX environment and creates the
problem object; it also calls the optimizer to solve the problem and retrieves the
solution.
In detail, main first calls the Callable Library routine CPXopenCPLEX. As explained in
Initialize the CPLEX environment on page 60, CPXopenCPLEX must always be the
first CPLEX routine called in a Callable Library application. Those routines create
the CPLEX environment and return a pointer (called env) to it. This pointer will be
passed to every Callable Library routine. If this initialization routine fails, env will
be NULL and the error code indicating the reason for the failure will be written to
status. That error code can be transformed into a string by the Callable Library
routine CPXgeterrorstring.
After main initializes the CPLEX environment, it uses the Callable Library routine
CPXsetintparam to turn on the CPLEX messages to screen switch parameter
CPX_PARAM_SCRIND so that CPLEX output appears on screen. If this parameter is
turned off, CPLEX does not produce viewable output, neither on screen, nor in a
log file. It is a good idea to turn this parameter on when you are debugging your
application.
The function buildNetwork populates the problem object; that is, it loads the
problem data into the problem object. Pointer variables in the example are
initialized as NULL so that you can check whether they point to valid data (a good
programming practice). The most important calls in this function are to the
Callable Library routines, CPXNETaddnodes, which adds nodes with the specified
supply values to the network problem, and CPXNETaddarcs, which adds the arcs
connecting the nodes with the specified objective values and bounds. In this
example, both routines are called with their last argument NULL indicating that no
names are assigned to the network nodes and arcs. If you want to name arcs and
nodes in your problem, pass an array of strings instead.
The function buildNetwork also includes a few routines that are not strictly
necessary to this example, but illustrate concepts you may find useful in other
applications. To delete a node and all arcs dependent on that node, it uses the
Callable Library routine CPXNETdelnodes. To change the objective sense to
minimization, it uses the Callable Library routine CPXNETchgobjsen.
Look again at main , where it actually calls the network optimizer with the Callable
Library routine, CPXNETprimopt. If CPXNETprimopt returns a nonzero value, then
an error has occurred; otherwise, the optimization was successful. Before retrieving
that solution, it is necessary to allocate arrays to hold it. Then use CPXNETsolution
to copy the solution into those arrays. After displaying the solution on screen,
write the network problem into a file, netex1.net in the NET file format.
The TERMINATE: label is used as a place for the program to exit if any type of error
occurs. Therefore, code following this label cleans up: it frees the memory that has
been allocated for the solution data; it frees the network object by calling
CPXNETfreeprob; and it frees the CPLEX environment by calling CPXcloseCPLEX.
All freeing should be done only if the data is actually available. The Callable
The LP formulation of our example from Figure 5 on page 179 looks like this:
Minimize
3a1 + 3a2 + 4a3 + 3a4 + 5a5 + 6a6 + 7a7 + 4a8 + 2a9 + 6a10+ 5a11+ 4a12+ 3a13+ 6a14
subject to
a1 = 20
-a1 + a2 - a8 - a9 + a14 = 0
- a2 + a3 + a9 = 0
- a3 + a4 + a10 + a11 - a12 = -15
a7 + a8 - a10 - a13 = 5
- a5 + a6 - a11 + a12 + a13 - a14 = 0
- a4 + a5 = 0
- a6 - a7 = -10
with these bounds
18 a1 24 0 a2 25 a3 = 12
0 a4 10 0 a5 9 a6 free
0 a7 20 0 a8 10 0 a9 5
0 a10 15 0 a11 10 0 a12 11
0 a13 6 0 a14
In that formulation, in each column there is exactly one coefficient equal to 1 (one),
exactly one coefficient equal to -1, and all other coefficients are 0 (zero).
Generally, you can also use the same basis from a basis file for both the LP and the
network optimizers. However, there is one exception: in order to use an LP basis
with the network optimizer, at least one slack variable or one artificial variable
needs to be basic. Starting from an advanced basis on page 139 explains more
about this topic in the context of LP optimizers.
If you have already read the LP formulation of a problem into the Interactive
Optimizer, you can transform it into a network with the command
change problem network. Given any LP problem and this command, CPLEX will
try to find the largest network embedded in the LP problem and transform it into
a network-flow problem. However, as it does so, it discards all rows and columns
that are not part of the embedded network. At the same time, CPLEX passes along
as much basis information as possible to the network optimizer.
CPLEX solves quadratic programs; that is, a model in which the constraints are
linear, but the objective function can contain one or more quadratic terms. These
problems are also known as QP. When such problems are convex, CPLEX normally
solves them efficiently in polynomial time. Nonconvex QPs, however, are known to
be quite hard. In theoretical terms, they are characterized as NP-hard. CPLEX
applies various approaches to those problems, such approaches as barrier
algorithms or branch and bound algorithms. Notably, in the branch and bound
approach, there is no theoretical guarantee about the complexity of such a problem.
Consequently, solution of such a problem (that is, a nonconvex QP) can take many
orders of magnitude longer than the solution of a convex QP of comparable
dimensions. The following topics address the question of how to distinguish such
problems and describe the facilities that CPLEX offers to solve them.
subject to Ax ~ b
where the relation ~ may be any combination of equal to, less than or equal to,
greater than or equal to, or range constraints. As in other problem formulations, l
indicates lower and u upper bounds. Q is a matrix of objective function
coefficients. That is, the elements Qjj are the coefficients of the quadratic terms xj2,
and the elements Qij and Qji are summed together to be the coefficient of the term
xixj.
CPLEX can also compute points that satisfy first-order optimality conditions of
models with arbitrary quadratic objective functions. These models include
minimization problems with a concave objective function, maximization problems
with a convex objective function, and either minimization or maximization
Intuitively, recall that any point on the line between two arbitrary points of a
convex function will be above that function. In more formal terms, a continuous
segment (that is, a straight line) connecting two arbitrary points on the graph of
the objective function will not go below the objective of a minimization problem,
and equivalently, the straight line will not go above the objective of a
maximization problem. The image Figure 6 illustrates this intuitive idea for an
objective function in one variable. It is possible for a quadratic function in more
than one variable to be neither convex nor concave.
Matrix view
Describes the matrix view of a quadratic program.
In the matrix view, commonly found in textbook presentations of QP, the objective
function is defined as 1/2 xTQx + cTx, where Q must be symmetric. This view is
supported by the MPS file format and the Callable Library routines, where
information about the quadratic objective function is specified by providing the
matrix Q. Thus, by definition, the factor of 1/2 must be explicit when you enter a
model using the matrix view, as it will be implicitly assumed by the optimization
routines.
Similarly, symmetry of the Q matrix data is required; the MPS reader will return
an error status code if the file contains unequal off-diagonal components, such as a
nonzero value for one and zero (or omitted) for the other.
Algebraic view
Describes the algebraic view of a quadratic program.
This view is supported by the LP format, when you enter a quadratic objective
function in the Interactive Optimizer, and by Concert Technology. When you enter
a quadratic objective with the algebraic view, neither symmetry considerations nor
any implicit factors need to be considered, and indeed attempting to specify both
of the off-diagonal elements for one of the quadratic terms may result in double
the intended value of the coefficient.
CPLEX LP format requires the factor of 1/2 to be specified explicitly in the file.
Minimize
obj: [ 100 x1 ^2 - 200 x1 * x2 + 100 x2 ^2 ] / 2
MPS format for this same objective function contains the following block.
QMATRIX
x1 x1 100
x1 x2 -100
x2 x1 -100
x2 x2 100
Or since the algebraic view is supported, the factor of one-half could be simplified
as in the following equivalent expression:
model.add(IloMinimize(env, (50*x[0]*x[0] +
50*x[1]*x[1] -
100*x[0]*x[1])));
Again, the user may choose to simplify that expression algebraically if that suits
the purposes of the application better.
zqmatind[0] = 0; zqmatind[2] = 0;
zqmatval[0] = 100.0; zqmatval[2] = -100.0;
zqmatind[1] = 1; zqmatind[3] = 1;
zqmatval[1] =-100.0; zqmatval[3] = 100.0;
To re-emphasize the point about the factor of 1/2 in any of these methods: if that
objective function is evaluated with a solution of x1 = 1.000000 and x2 =
3.000000, the result is 200, not 400.
In the reformulation, y is a vector of free variables, one variable for each column of
F.
In general, while the number of rows in F must match the dimension of the square
matrix Q, the number of columns of F may be fewer. So, even if Q is dense and F
is also dense, you still may reduce the memory requirements to solve the model if
F has more rows than columns.
Saving QP problems
Recommends appropriate file formats to save a quadratic program.
Situations in which you must change the problem type of your model arise, for
example, when you remove quadratic terms from the objective function of a QP or
when you add a quadratic term to the objective function of a linear program (LP).
This topic highlights whether and how to change problem type according to the
CPLEX component that you use (Concert Technology, Python API, Callable Library,
Interactive Optimizer).
Concert Technology (that is, applications written in the C++, Java, or .NET API of
CPLEX) treats all models as capable of containing quadratic coefficients in the
objective function. These coefficients can therefore be added or deleted at will.
When extracting a model with a quadratic objective function, CPLEX will
automatically detect it as a QP and make the required adjustments to data
structures. Likewise, in the Python API, CPLEX treats all models as capable of
containing quadratic coefficients in the objective function.
However, the other ways of using CPLEX (the Callable Library and the Interactive
Optimizer) require an explicit problem type to distinguish a linear program (LP)
from a QP. The following sections discuss the topic for these users.
When you enter a problem, CPLEX discovers the problem type from the available
information. When read from a file (LP, MPS, or SAV format, for example), or
entered interactively, a continuous optimization problem is usually treated as being
of type QP if quadratic coefficients are present in the objective function and no
quadratic terms are present among the constraints. (Quadratic terms among the
constraints may make a problem of type QCP. For more about that type, see
Chapter 14, Solving problems with quadratic constraints (QCP), on page 197.)
Otherwise, the problem type is usually LP. The issue of problem types that support
If you enter a problem that lacks any quadratic coefficients, its problem type is
initially LP. If you then wish to modify the problem to contain quadratic
coefficients in the objective function, you do this by first changing the problem
type to QP. Conversely, if you have entered a QP model and wish to remove all the
quadratic coefficients from the objective function and thus convert the model to an
LP, you must also change the problem type to LP. Note that deleting each of the
quadratic coefficients individually still leaves the problem type as QP, although in
most instances the distinction between this problem and its LP or QP counterpart
is somewhat arbitrary in terms of the steps to solve it.
When using the Interactive Optimizer, you use the command change problem with
one of the following options:
v lp indicates that you want CPLEX to treat the problem as an LP. This change in
Problem Type removes from your problem all the quadratic information, if there
is any present.
v qp indicates that you want CPLEX to treat the problem as a QP. This change in
Problem Type creates in your problem an empty quadratic matrix, if there is not
one already present, for the objective function, ready for populating via the
change qpterm command.
From the Callable Library, use the routine CPXchgprobtype to change the problem
type to either CPXPROB_LP for the LP case or CPXPROB_QP for the QP case for the
same purposes.
subject to a + b 10
v In a QP, you can change the quadratic matrix coefficients in the Interactive
Optimizer by using the command change qpterm .
To change the off-diagonal quadratic term from 4 to 6, for example, use this
sequence of commands in the Interactive Optimizer:
CPLEX> change qpterm
Change which quadratic term [variable variable]: a b
Present quadratic term of variable a, variable b is 4.000000.
Change quadratic term of variable a, variable b to what: 6.0
Quadratic term of variable a, variable b changed to 6.000000.
From the Callable Library, the CPXchgqpcoef call to change the off-diagonal term
from 4 to 6 would change both of the off-diagonal matrix coefficients from 2 to 3.
Thus, the indices would be 0 and 1, and the new matrix coefficient value would be
3.
If you have entered a linear problem without any quadratic terms in the
Interactive Optimizer, and you want to create quadratic terms, you must first
change the problem type to QP. To do so, use the command change problem qp.
This command will create an empty quadratic matrix with Q = 0.
By changing quadratic terms, you may affect the convexity of the objective
function. If you change a quadratic term so that the resulting matrix is no longer
convex in the case of a minimization problem or concave in the case of a
maximization problem, you must set the optimality target parameter
(CPX_PARAM_OPTIMALITYTARGET, OptimalityTarget) to ask CPLEX to return either a
solution that satisfies first-order optimality conditions or a globally optimal
solution.
Optimizing QPs
Describes how to invoke an optimizer for a quadratic program and explains the
appropriate choice of optimizer.
CPLEX allows you to solve your QP models through a simple interface, by calling
the default optimizer.
v In the Interactive Optimizer, use the command optimize.
v From the Callable Library, use the routine CPXqpopt.
v In Concert Technology applications, use the method solve.
With default settings, this will result in CPLEX invoking the barrier optimizer to
solve a continuous QP.
Tip: For nonconvex QP problems, particularly for those that are non PSD, consider
changing the default value of optimality target to search for either a locally or
globally optimal solution. When instructed to look for a globally optimal solution,
CPLEX uses a branch-and-bound algorithm. In such cases, the algorithms and
For users who wish to tune the performance of their applications, CPLEX offers
two simplex optimizers to try for solving convex QPs: dual simplex and primal
simplex. (However, dual simplex and primal simplex cannot be used to solve
nonconvex QPs.) You can also use the network optimizer; this approach first solves
the model as an LP network (temporarily ignoring the quadratic term in the
objective function) and takes this solution as a starting point for the primal simplex
QP optimizer. This choice of QP optimizer is controlled by the root algorithm
parameter (QPMETHOD in the Interactive Optimizer and in the Callable Library).
The table Table 38 shows you the possible settings.
Table 38. RootAlg parameter settings for QPs.
Root algorithm value Optimizer
0 Automatic (default)
1 Primal Simplex
2 Dual Simplex
3 Network Simplex
4 Barrier
5 Sifting
6 Concurrent
Many of the optimizer tuning decisions for LP apply in the QP case; and
parameters that control barrier and simplex optimizers in the LP case can be set for
the QP case, although in some instances to differing effect. Most models are solved
fastest by default parameter settings. In case your model is not solved satisfactorily
by default settings, consider the advice offered in the topic Chapter 11, Solving
LPs: barrier optimizer, on page 157, especially Tuning barrier optimizer
performance on page 166 as well as in the topic Chapter 10, Solving LPs: simplex
optimizers, on page 133, especially Tuning LP performance on page 137.
The barrier optimizer for QP supports crossover for convex QPs, but unlike other
LP optimizers, its crossover step is off by default for QPs. The QP simplex
optimizers return basic solutions, and these bases can be used for purposes of
restarting sequences of optimizations, for example. As a result, application writers
who wish to allow end users control over the choice of QP optimizer need to be
aware of this fundamental difference and to program carefully. For most purposes,
the nonbasic barrier solution for convex QPs is entirely satisfactory, in that all such
solutions fully satisfy the standard optimality and feasibility conditions of
optimization theory.
Diagnosing QP infeasibility
Explains infeasibility in the context of a quadratic program.
This example shows you how to build and solve a QP. The problem being created
and solved is:
Maximize
x1 + 2x2 + 3x3 - +12
0.5(33x 22x22 + 11x32 - 12x1x2- 23x2x3)
subject to
-x1 + x2 + x3 20
x1 - 3x2 + x3 30
with these bounds
0 x1 40
0 x2 +
0 x3 +
The following sections solve this model in the various APIs available in CPLEX:
v Example: iloqpex1.cpp
v Example: QPex1.java on page 194
v Example: qpex1.c on page 194
Example: iloqpex1.cpp
Demonstrates the solution of a quadratic program in the C++ API.
Example: QPex1.java
Demonstrates the solution of a quadratic program in the Java API.
A quadratic objective may be built with square, prod, or sum methods. Inclusion of
IloPiecewiseLinear will change the model from a QP to a MIQP.
Example: qpex1.c
Demonstrates the solution of a quadratic program in the C API.
This example shows you how to optimize a QP with routines from the CPLEX
Callable Library when the problem data is stored in a file. The example derives
from lpex1.c discussed in Getting Started. The Concert forms of this example,
iloqpex1.cpp and QPex1.java , are included online in the standard distribution.
Like other applications based on the CPLEX Callable Library, this one begins with
calls to CPXopenCPLEX to initialize the CPLEX environment and to CPXcreateprob to
create the problem object. Before it ends, it frees the problem object with a call to
CPXfreeprob , and it frees the environment with a call to CPXcloseCPLEX.
In the routine setproblemdata , there are parameters for qmatbeg, qmatcnt, qmatind,
and qmatval to fill the quadratic coefficient matrix. The Callable Library routine
CPXcopyquad copies this data into the problem object created by the Callable
Library routine CPXcreateprob.
The off-diagonal terms in the matrix Q are one-half the value of the terms x1x2, and
x2x3 as they appear in the algebraic form of the example.
This example shows you how to optimize a QP with routines from the CPLEX
Callable Library when the problem data is stored in a file. The example derives
from lpex2.c discussed in Getting Started. The Concert forms of this example,
iloqpex2.cpp and QPex2.java , are included online in the standard distribution.
Like other applications based on the CPLEX Callable Library, this one begins with
calls to CPXopenCPLEX to initialize the CPLEX environment and to CPXcreateprob to
create the problem object. Before it ends, it frees the problem object with a call to
CPXfreeprob, and it frees the environment with a call to CPXcloseCPLEX.
The distinguishing characteristic of QCP is that quadratic terms may appear in one
or more constraints of the problem. The objective function of such a problem may
or may not contain quadratic terms as well. Thus, the most general formulation of
a QCP is:
1
Minimize / 2xTQx + cTx
subject to Ax ~ b
T
and a i x + xTQ i x r i for i=1,...,q
Convexity
Defines convexity in the context of a quadratically constrained program.
It might be less obvious at first glance that the equality x2 + y2 = 1 is not convex
either. As you see in the figure titled Figure 9 on page 199, there may be a
continuous line segment that joins two arbitrary points, such as e and f, in the
domain but the line segment may pass outside the domain. Another way to see
this idea is to note that an equality constraint is algebraically equivalent to the
intersection of two inequality constraints of opposite sense, and you have already
seen that at least one of those quadratic inequalities will not be convex. Thus, the
equality is not convex either.
Semi-definiteness
Defines semi-definiteness in the context of a quadratically constrained program.
When you call the barrier optimizer, your quadratic constraints will be checked for
the necessary PSD property, and an error status 5002 will be returned if any of
them violate it.
There is one exception to the PSD requirement; that is, there is an additional form
of quadratic constraint which is accepted but is not covered by the general
formulation in Identifying a quadratically constrained program (QCP) on page
197. Technically, the class of quadratically constrained problems that the barrier
optimizer solves is a Second-Order Cone Program (SOCP). CPLEX, through its
preprocessing feature, carries out the translation to SOCP for you, transparently,
returning the solution in terms of your original formulation. A constraint will be
accepted for solution by the barrier optimizer if it can be transformed to the
following convex second-order cone constraint:
When Q is the identity matrix, that formulation is known as a rotated cone. CPLEX
automatically translates those formulations as well and solves the rotated cone
program for you transparently.
For further background about second order cone programming, see also the topic
Examples: SOCP on page 213. For suggestions about accessing dual values and
reduced costs of SOCP models, see the topic Accessing dual values and reduced
costs of SOCP solutions on page 211. Those topics cite the Lagrangian
representation of second order cone programs and second order cone constraints.
Second order cone programs (SOCPs) are special in the sense that any
quadratically constrained program (QCP) can be transformed into an equivalent
second order cone program. CPLEX makes use of this fact to solve a wider range
of quadratically constrained models. Furthermore, CPLEX uses this equivalence to
return more information, such as dual values and reduced costs, about the model
and its solution, even about SOCPs.
To understand how best to use CPLEX routines and methods when you solve
SOCPs, recall how second order cone programs mathematically represent a model
that includes second order cone constraints. A second order cone program is an
optimization problem in this standard form, where the xi are pair-wise disjoint
vectors of variables such that xi = (xi,1 , . . . , xi,in):
Also recall that if ni = 1, then the following second order cone constraint:
(where the double bars specify a Euclidean norm) is equivalent to these two
constraints:
With those facts about the standard form of a second order cone constraint in
mind, you can specify the objective function and the linear constraints of a second
order cone program (SOCP) just as you would specify them in a similar linear
program (LP); then, in addition, you specify those two equivalent constraints for
each of the second order cone constraints in your model.
To clarify that procedure for specifying SOCPs, consider the following model:
min x1 + x2 + x3 + x4 + x5 + x6
x1 + x2 + x5 = 8
x3 + x5 + x6 = 10
(x1, x2, x3) 0 (x4, x5) 0 (x6) 0
At first glance, there appear to be three second order cones (SOC) to consider:
(x1, x2, x3) 0
(x4, x5) 0
(x6) 0
According to the procedure, you must specify two equivalent constraints for each
second order cone constraint in the model. For the first SOC, consider x1 greater
than or equal to the Euclidean norm of x2 and x3. This consideration leads to the
following pair of constraints.
-x12 + x22 + x33 0
x1 0
The third SOC consists of only one variable, so according to the procedure, it leads
only to the lower bound of 0 (zero) on x6.
For more detail about the syntax of LP format, see LP file format: algebraic
representation in the reference manual File formats supported by CPLEX.
Overview
Offers general considerations about QCP problem type in each API of CPLEX.
Tip:
When routines of the Callable Library read a problem from a file, they are capable
of detecting quadratic constraints. If they detect a quadratic constraint in the model
they read, Callable Library routines automatically set the problem type as QCP. If
there are no quadratic constraints, then Callable Library routines consider whether
there are any quadratic coefficients in the objective function. If there is a quadratic
CPLEX supports the definition of quadratic constraints in SAV files with the .sav
file extension, in LP files with the .lp file extension, and in MPS files with the .mps
file extension. In LP files, you state your quadratic constraints in the subject to
section of the file. For more detail about representing QCP models in MPS file
format, see the CPLEX File Formats Reference Manual, especially the topic
Quadratically constrained programs (QCP) in MPS files. Here is a sample of a file
including quadratic constraints in MPS format.
NAME p0033_qc1.lp.gz
ROWS
N R100
L R118
L R119
L R120
L R121
L R122
L R123
L R124
L R125
L R126
L R127
L R128
L ZBESTROW
L QC1
L QC2
L QC3
L QC4
COLUMNS
MARK0000 MARKER INTORG
C157 R100 171
C157 R122 -300
C157 R123 -300
C158 R100 171
C158 R126 -300
C158 R127 -300
C159 R100 171
C159 R119 300
C159 R120 -300
C159 R123 -300
C159 QC1 1
C160 R100 171
C160 R119 300
C160 R120 -300
C160 R121 -300
C161 R100 163
C161 R119 285
C161 R120 -285
C161 R124 -285
C161 R125 -285
By default, every model in Concert Technology is the most general problem type
possible. Consequently, it is not necessary to declare the problem type nor to
change the problem type, even if you add quadratic constraints to the model or
remove them from it.
In contrast, both the Callable Library and the Interactive Optimizer need for you
to specify a change in problem type explicitly if you remove the quadratic
constraints that make your model a QCP.
In both the Callable Library and Interactive Optimizer, if you want to remove the
quadratic constraints in order to solve the problem as an LP or a QP, then you
must change the problem type, just as you would, for example, if you removed the
quadratic coefficients from a quadratic objective function.
When using the Interactive Optimizer, you apply the command change problem
with one of the following options:
v lp specifies that you want CPLEX to treat the problem as an LP. This change in
the problem type removes all the quadratic information from your problem, if
there is any present.
v qp specifies that you want CPLEX to treat the problem as a QP (that is, a
problem with a quadratic objective). This choice removes the quadratic
constraints, if there were any in the model.
v qcp specifies that you want CPLEX to treat the problem as a QCP.
To modify a quadratic constraint in your model, you must first delete the old
quadratic constraint and then add the new one.
In Concert Technology, you add constraints (whether or not they are quadratic) by
means of the method add of the class IloModel , as explained about C++
applications in Adding constraints: IloConstraint and IloRange on page 7 and
about Java applications in The active model on page 33. To add constraints to a
model in the .NET framework, see Chapter 3, Concert Technology for
.NET users, on page 49.
Also in Concert Technology, you can remove constraints (again, whether or not
they are quadratic) by means of the method remove of the class IloModel , as
explained about C++ applications in Deleting and removing modeling objects on
page 18 and about Java applications in Modifying the model on page 47.
The Callable Library has a separate set of routines for creating and modifying
quadratic constraints; do not use the routines that create or modify linear
constraints.
In the Callable Library, you add a quadratic constraint by means of the routine
CPXaddqconstr. You remove and delete quadratic constraints by means of the
routine CPXdelqconstr. Dont forget to change the problem type, as explained in
Changing problem type in a QCP on page 206. If you want to change a
quadratic constraint, first delete it by calling CPXdelqconstrs and then add the new
constraint using CPXaddqconstr.
CPLEX allows you to solve your QCP models (that is, problems with quadratic
constraints) through a simple interface, by calling the default optimizer.
v In Concert Technology applications, use the solve method of IloCplex.
v From the Callable Library, use the routine CPXbaropt.
v In the Interactive Optimizer, use the command optimize.
With default settings, each of these approaches will result in the barrier optimizer
being called to solve a continuous QCP.
A word of warning: numeric difficulties are likely to be more acute for QCP than
for LP or QP. Symptoms include:
v lack of convergence to an optimal solution;
v violation of constraints.
Consequently, you will need to scale your variables carefully so that units of
measure are roughly comparable among them.
The CPLEX routines and methods that query dual values and reduced costs in
linear programs (LPs) also access dual values and reduced costs for quadratically
constrained programs, that is, QCP models. In order to use these routines and
methods effectively in your applications to solve SOCPs, recall these points about
Lagrangian functions and QCPs.
where:
Consequently, the Lagrangian dual of this conventional QCP model looks like this:
that is an optimal solution to those Lagrangian statements of the primal and dual
QCP model must also satisfy the Karush-Kuhn-Tucker (KKT) conditions:
primal feasibility
dual feasibility
complementary slackness
stationarity
can be undefined at x*. In such a case, KKT conditions cannot be formulated and
satisfied.
pair:
For example, in the Callable Library (C API) these routines are available for this
purpose:
v CPXXgetx queries the values x*.
v CPXXgetpi queries the dual values of a range of constraints in a (linear or)
quadratically constrained problem (QCP); that is:
v CPXXgetdj queries the reduced costs for a range of variables in a (linear or)
quadratically constrained problem (QCP); that is, it accesses the dual multipliers
for bound constraints on the specified variables.
v The values:
cannot be accessed directly. However, for a quadratic constraint k0 in the set K,
CPXXgetqconstrdslack returns the slack vector of the dual problem:
Tip:
For a demonstration of the procedure outlined here to access dual values and
reduced costs of a QCP model, see these examples distributed with the product:
v In the Callable Library (C API), see the examples xqcpdual.c and qcpdual.c.
v In the C++ API, see the example iloqcpdual.cpp.
v In the Java API, see the example QCPDual.java.
v In the C#.NET API, see the example QCPDual.cs.
v In the Python API, see the example qcpdual.py.
v In the MATLAB connector, see the example qcpdual.m.
For reference documentation of these routines and methods, see the reference
manual of your preferred application programming interface (API):
v Callable Library (C API)
CPXXgetqconstrdslack and CPXgetqconstrdslack
v C++ API
IloCplex::getQCDSlack
v Java API
IloCplex.getQCDSlack
v .NET API
Cplex.GetQCDSlack
v Python API
SolutionInterface.get_quadratic_dualslack
v MATLAB connector new fields in the Cplex.Solution structure
qcpdslack
The CPLEX routines and methods that query dual values and reduced costs in
linear programs (LPs) also access dual values and reduced costs for second order
cone programs, that is, SOCP models. In order to use these routines and methods
effectively in your applications to solve SOCPs, recall these points about
Lagrangian functions and SOCPs. First of all, the Lagrangian primal of the second
order cone program that you saw in Representing SOCP as Lagrangian on page
Similarly, the Lagrangian dual of the second order cone program of that same
holds:
With that equivalence in mind, you can rewrite the sample problem like this:
Given that restatement of the original SOCP in Lagrangian terms, you can set the
derivative (with respect to x) to zero, and define the vector zk like this:
After you solve a primal second order cone program in the form shown here, you
can query values of (lambda) of the corresponding dual of the SOCP model. To
query those values, use these routines and methods:
v In the Callable Library (C API), use CPXXgetpi.
v In the C++ API, use the methods IloCplex::getDuals.
v In the Java API, use the methods IloCplex.getDuals.
v In the .NET API, use the methods Cplex.GetDuals.
v In the Python API, use the methods SolutionInterface.get_dual_values.
To query the dual multipliers k for the second order cone constraints:
first query the z vector, that is, the dual slack vector. Then use the idea that k =
zk1.
v In the Callable Library (C API), you can obtain the z vector by summing
CPXXgetdj and CPXXgetqconstrdslack.
v In the C++ API, sum the results of both IloCplex::getReducedCosts and
IloCplex::getQCDSlack.
v In the Java API, sum the results of both IloCplex.getReducedCosts and
IloCplex.getQCDSlack.
v In the .NET API, sum the results of both Cplex.GetReducedCosts and
Cplex.GetQCDSlack.
v In the Python API, sum the results of both cplex.solution.get_reduced_costs and
cplex.solution.get_quadratic_dualslack.
Tip:
The following examples show you how to apply that procedure to query the dual
values and reduced costs of a problem stated as a second order cone program
(SOCP). Each of the examples defines a function checkkkt, which reads the dual
values , queries the values of z, constructs , then tests that these vectors satisfy
the Karush-Kuhn-Tucker conditions for the model in the example.
v Among the examples of the Callable Library (C API), see socpex1.c and
xsocpex1.c.
v Among the examples of the C++ API, see ilosocpex1.cpp.
v Among the examples of the Java API, see SocpEx1.java.
v Among the examples of the .NET API, see SocpEx1.cs and SocpEx1.vb.
v Among the examples of the Python API, see socpex1.py.
v Among the examples of the MATLAB connector, see socpex1.m.
Examples: SOCP
Lists examples of SOCP.
The following examples, distributed with the product, show how to create and
populate that SOCP model and then how to access dual values and reduced costs
for the SOCP model in each API:
v In the Callable Library (C API), see socpex1.c and xsocpex1.c.
v In the C++ API, see ilosocpex1.cpp.
v In the Java API, see SocpEx1.java.
v In the .NET API, see SocpEx1.cs and SocpEx1.vb.
v In the Python API, see socpex1.py.
v In the MATLAB connector, see socpex1.m.
Examples: QCP
Tells where to find sample applications solving a quadratically constrained
program.
For examples of QCPs, see these variations of the same problem in yourCPLEXhome
/examples/src:
v qcpex1.c
v iloqcpex1.cpp
v QCPex1.java
v QCPex1.cs
A mixed integer programming (MIP) problem may contain both integer and
continuous variables. If the problem contains an objective function with no
quadratic term, (a linear objective), then the problem is termed a Mixed Integer Linear
Program (MILP).
Tip:
The all zero objective function associated with a feasibility problem is also a linear
objective.
If there is a quadratic term in the objective function, the problem is termed a Mixed
Integer Quadratic Program (MIQP). If the model has any constraints containing a
quadratic term, regardless of the objective function, the problem is termed a Mixed
Integer Quadratically Constrained Program (MIQCP).
Integer variables may be restricted to the values 0 (zero) and 1 (one), in which case
they are referred to as binary variables. Or they may take on any integer values, in
which case they are referred to as general integer variables. A variable of any MIP
that may take either the value 0 (zero) or a value between a lower and an upper
bound is referred to as semi-continuous. A semi-continuous variable that is restricted
to integer values is referred to as semi-integer. Chapter 19, Using semi-continuous
variables: a rates example, on page 307 says a bit more about semi-continuous
variables later in this manual. Special Ordered Sets (SOS) are discussed in
Chapter 18, Using special ordered sets (SOS), on page 303. Continuous variables
in a MIP problem are those which are not restricted in any of these ways, and are
thus permitted to take any solution value within their (possibly infinite) lower and
upper bounds.
In CPLEX documentation, the comprehensive term integer variable means any of the
various types just mentioned except for continuous or SOS. The presence or
absence of a quadratic term in the objective function or among the constraints for a
given variable has no bearing on its being classified as continuous or integer.
Preliminary issues
When you are optimizing a MIP, there are a few preliminary issues that you need
to consider to get the most out of CPLEX. The following sections cover such topics
as entering variable types, displaying MIPs in the Interactive Optimizer, detecting
the problem type, and switching to the fixed form of your problem.
You enter MIPs into CPLEX as explained in each of the topics about the APIs of
CPLEX, with this additional consideration: you need to specify which variables are
binary, general integer, semi-continuous, and semi-integer, and which are contained
in special ordered sets (SOS).
Concert Technology users specify this information by passing the value of a type
to the appropriate constructor when creating the variable, as summarized in
Table 39.
Table 39. Specifying type of variable for a MIP in Concert Technology.
Type of Variable C++ API Java API .NET API
binary IloNumVar::Type::ILOBOOL IloNumVarType.Bool NumVarType.Bool
integer IloNumVar::Type::ILOINT IloNumVarType.Int NumVarType.Int
semi-continuous IloSemiContVar::Type::ILONUM IloNumVarType.Float NumVarType.Float
semi-integer IloSemiContVar::Type::ILOINT IloNumVarType.Int NumVarType.Int
Callable Library users specify this information through the routine CPXcopyctype.
In the Interactive Optimizer, to specify binary integers in the context of the enter
command, type binaries on a separate line, followed by the designated binary
variables. To specify general integers, type generals on a separate line, followed by
the designated general variables. To specify semi-continuous variables, type
semi-continuous on a separate line, followed by the designated variables.
Semi-integer variables are specified as both general integer and semi-continuous.
The order of these three sections (generals, semi-continuous, semi-integer as both)
does not matter. To enter the general integer variable of the Stating a MIP
problem on page 217, you type this:
generals
x4
Table 40 summarizes display options in the Interactive Optimizer that are specific
to MIP problems.
Table 40. Interactive Optimizer display options for MIP problems.
Interactive command Purpose
display problem binaries lists variables restricted to binary values
display problem generals lists variables restricted to integer values
display problem semi-continuous lists variables of type semi-continuous and
semi-integer
display problem integers lists all of the above
display problem sos lists the names of variables in one or more
Special Ordered Sets
display problem stats
lists LP statistics plus:
In Concert Technology, use one of the accessors supplied with the appropriate
object class, such as IloSOS2::getVariables .
From the Callable Library, use the routines CPXgetctype and CPXgetsos to access
this information.
However, the other ways of using CPLEX, the Callable Library and the Interactive
Optimizer, require an explicit declaration of a problem type to distinguish
When you enter a problem, CPLEX detects the problem type from the available
information. If the problem is read from a file (LP, MPS, or SAV format, for example),
or entered interactively, the problem type is discovered according to Table 41.
Table 41. Definitions of problem types
Problem No Integer Has Integer No Quadratic Has Has
Type Variables Variables Terms in the Quadratic Quadratic
Objective Terms in the Terms in
Function Objective Constraints
Function
lp X X
qp X X
qcp X possibly X
milp X X
miqp X X
miqcp X possibly X
However, if you enter a problem with no integer variables, so that its problem type
is initially lp, qp, or qcp, and you then wish to modify the problem to contain
integer variables, this modification is accomplished by first changing the problem
type to milp, miqp, or miqcp . Conversely, if you have entered an MILP, MIQP, or
MIQCP model and wish to remove all the integer declarations and thus convert
the model to a continuous formulation, you can change the problem type to lp, qp,
or qcp. Note that deleting each of the integer variable declarations individually still
leaves the problem type as milp, miqp, or miqcp, although in most instances the
distinction between this problem and its continuous counterpart is somewhat
arbitrary in terms of the steps that will be taken to solve it.
Thus, when using the Interactive Optimizer, you use the command change
problem with one of the following options:
v milp, miqp, or miqcp
specifying that you want CPLEX to treat the problem as an MILP, MIQP, or
MIQCP, respectively. This change in problem type makes the model ready for
declaration of the integer variables via subsequent change type commands. If
you change the problem to be an MIQP or MIQCP and there are not already
quadratic terms in the objective function or among the constraints, the
Interactive Optimizer creates an empty quadratic matrix, ready for populating
via the change qpterm command.
v lp, qcp, or qp
specifying that you want all integer declarations removed from the variables in
the problem. If you choose the qp or qcpproblem type and there are not already
quadratic terms in the objective function or among the constraints, the
Interactive Optimizer creates an empty quadratic matrix, ready for populating
via the change qpterm command.
From the Callable Library, use the routine CPXchgprobtype to change the problem
type to CPXPROB_MILP, CPXPROB_MIQP, or CPXPROB_MIQCP for the MILP, MIQP, and
MIQCP case respectively, and then assign integer declarations to the variables
At the end of a MIP optimization, the optimal values for the variables are directly
available. However, you may wish to obtain information about the LP, QP, or QCP
associated with this optimal solution (for example, to know the reduced costs for
the continuous variables of the problem at this solution). To do this, you must
change the problem to be of type Fixed, either fixed_milp for the MILP case or
fixed_miqp for the MIQP case. The fixed MIP is the continuous problem in which
the integer variables are fixed at the values they attained in the best integer
solution. After changing the problem type, you can then call any of the continuous
optimizers to re-optimize, and then display solution information for the continuous
form of the problem. If you then wish to change the problem type back to the
associated milp or miqp, you can do so without loss of information in the model.
In the Interactive Optimizer, the command change type adds (or removes) the
restriction on a variable that it must be an integer. When you enter the command
change type , the system prompts you to enter the variable that you want to
change, and then it prompts you to enter the type (c for continuous, b for binary, i
for general integer, s for semi-continuous, n for semi-integer).
You can change a variable to binary even if its bounds are not 0 (zero) and 1 (one).
However, in such a case, the optimizer will change the bounds to be 0 and 1.
A problem may be changed to a mixed integer problem, even if all its variables are
continuous.
Note:
The following topic, Tuning performance features of the mixed integer optimizer
on page 225, goes into great detail about the algorithmic features, controlled by
parameter settings, that are available in CPLEX to achieve performance tuning on
difficult MIP models. However, there is an important parameter, MIPEmphasis or
CPX_PARAM_MIPEMPHASIS, that is oriented less toward the user understanding the
algorithm being used to solve the model, and more toward the user telling the
algorithm something about the underlying aim of the optimization being run. That
parameter is discussed here.
For most models, a balance between these two sometimes-competing aims works
well, and this is another way of stating the philosophy behind the default
MIPEmphasis setting: it balances optimality and integer feasibility.
In many situations, the user may want a greater emphasis on feasibility and less
emphasis on analysis and proof of optimality. For example, a restrictive time limit
(set by the user with the TiLim parameter) may be in force due to a real-time
application deployment, where a model is of sufficient difficulty that a proof of
optimality is unlikely, and the user wants to have simply as good a solution as is
practicable when the time limit is reached. The MIPEmphasis setting of 1
(MIPEmphasisFeasibility in Concert Technology or CPX_MIPEMPHASIS_FEASIBILITY
To make clear a point that has been alluded to so far: every choice of MIPEmphasis
results in the search algorithm proceeding in a manner that eventually will find
and prove an optimal solution, or will prove that no integer feasible solution
exists. The choice of emphasis only guides CPLEX to produce feasible solutions in
a way that is in keeping with the user's particular purposes, but the accuracy and
completeness of the algorithm is not sacrificed in the process.
The MIPEmphasis parameter may be set in conjunction with any other CPLEX
parameters (discussed at length in the next section). For example, if you wish to
set an upward branching strategy via the BrDir parameter, this will be honored by
any setting of MIPEmphasis. Of course, certain combinations of MIPEmphasis with
other parameters may be counter-productive, such as turning off all cuts with
emphasis 3, but the user has the option if that is what is wanted.
An important termination criterion that the user can set explicitly is the MIP gap
tolerance. In fact, there are two such tolerances: a relative MIP gap tolerance that is
commonly used, and an absolute MIP gap tolerance that is appropriate in cases
where the expected optimal objective function is quite small in magnitude. The
default value of the relative MIP gap tolerance is 1e-4; the default value of the
absolute MIP gap tolerance is 1e-6. These default values indicate to CPLEX to stop
when an integer feasible solution has been proved to be within 0.01% of optimality.
On a difficult model with input data obtained with only approximate accuracy,
where a proved optimum is thought to be unlikely within a reasonable amount of
computation time, a user might choose a larger relative MIP gap to allow early
termination; for example, a relative MIP gap of 0.05 (corresponding to 5%).
Conversely, in a model where the objective function amounts to billions of dollars
and the data are accurate to a degree that further processing is worthwhile, a
tighter relative MIP Gap (even 0.0) may be advantageous to avoid any chance of
missing the best possible solution.
CPLEX also terminates optimization when it reaches any limit that you have set.
You can set limits on time, number of nodes, size of tree memory, and number of
integer solutions. Table 42 summarizes those parameters and their purpose.
Table 42. Parameters to limit MIP optimization
Concert Interactive
To set a limit on Use this parameter Technology Callable Library Optimizer
elapsed time optimizer time limit TiLim CPX_PARAM_TILIM timelimit
in seconds
elapsed deterministic time DetTiLim CPX_PARAM_DETTILIM dettimelimit
deterministic time limit
number of nodes MIP node limit NodeLim CPX_PARAM_NODELIM mip limits nodes
size of tree tree memory limit TreLim CPX_PARAM_TRELIM mip limits
treememory
number of integer MIP integer IntSolLim CPX_PARAM_INTSOLLIM mip limits
solutions solution-file switch solutions
and prefix
relative MIP gap relative MIP gap EpGap CPX_PARAM_EPGAP mip tolerances
tolerance tolerance mipgap
absolute MIP gap absolute MIP gap EpAGap CPX_PARAM_EPAGAP mip tolerances
tolerance tolerance absmipgap
CPLEX also terminates when an error occurs, such as when CPLEX runs out of
memory or when a subproblem cannot be solved. If an error is due to failure to
solve a subproblem, an additional line appears in the node log file to indicate the
reason for that failure. For suggestions about overcoming such errors, see
Troubleshooting MIP performance problems on page 260.
In addition to a robust branch & cut algorithm, CPLEX also offers a dynamic search
algorithm. The dynamic search algorithm consists of the same building blocks as
branch & cut: LP relaxation, branching, cuts, and heuristics. The following sections
of the manual describe performance and tuning in relation to branch and cut. The
parameters mentioned in this context have a similar effect in the dynamic search
algorithm as in conventional branch and cut. In fact, the generic description in
relation to branch & cut suffices conceptually for both branch and cut and
dynamic search, even though their implementations differ.
CPLEX offers the MIP search parameter (MIP dynamic search switch: MIPSearch,
CPX_PARAM_MIPSEARCH) for you to control whether it pursues dynamic search or
conventional branch & cut in solving your problem. At its default setting, this
parameter specifies that CPLEX should choose which algorithm to apply on the
basis of characteristics it finds in your model. Other settings allow you to specify
which search to pursue.
Because many parameter settings directly affect the branch & cut and dynamic
search algorithms, here is a general description of how branch & cut is
implemented within CPLEX.
In the branch & cut algorithm, CPLEX solves a series of continuous subproblems.
To manage those subproblems efficiently, CPLEX builds a tree in which each
subproblem is a node. The root of the tree is the continuous relaxation of the original
MIP problem.
If the solution to the relaxation has one or more fractional variables, CPLEX will
try to find cuts. Cuts are constraints that cut away areas of the feasible region of
the relaxation that contain fractional solutions. CPLEX can generate several types
of cuts. (Cuts on page 231 tells you more about that topic.)
If the solution to the relaxation still has one or more fractional-valued integer
variables after CPLEX tries to add cuts, then CPLEX branches on a fractional
variable to generate two new subproblems, each with more restrictive bounds on
the branching variable. For example, with binary variables, one node will fix the
variable at 0 (zero), the other, at 1 (one).
The CPLEX Mixed Integer Optimizer contains a wealth of features intended to aid
in the solution of challenging MIP models. While default strategies are provided
that solve the majority of models without user involvement, there exist difficult
models that benefit from attention to performance tuning.
This section discusses the CPLEX features and parameters that are the most likely
to offer help with difficult models.
CPLEX cuts off nodes when the value of the objective function associated with the
subproblem at that node is worse than the cutoff value.
You set the cutoff value by means of the CutUp parameter (for a minimization
problem) or the CutLo parameter (for a maximization problem), to indicate to
CPLEX that integer feasible solutions worse than this cutoff value should be
discarded. The default value of the lower cutoff is -1e+75; the default value of the
upper cutoff is 1e+75. The defaults, in effect, mean that no cutoff is to be supplied.
You can supply any number that you find appropriate for your problem. It is
never required that you supply a cutoff, and in fact for most applications is it not
done.
CPLEX will use the value of the best integer solution found so far, as modified by
the tolerance parameters ObjDif (absolute objective function difference) or
RelObjDif (relative objective function difference) as the cutoff. Again, it is not
typical that users set these parameters, but they are available if you find them
useful. Use care in changing these tolerances: if either of them is nonzero, you may
miss the optimal solution by as much as that amount. For example, in a model
where the true minimum is 100 and the absolute cutoff is set to 5, if a feasible
solution of say, 103 is found at some point, the cutoff will discard all nodes with a
solution worse than 98, and thus the solution of 100 would be overlooked.
Applying heuristics
Describes conditions in which CPLEX applies heuristics.
Periodically during the branch & cut algorithm, CPLEX may apply a heuristic that
attempts to compute an integer solution from available information, such as the
solution to the relaxation at the current node. This activity does not replace the
branching steps, but sometimes is able inexpensively to locate a new feasible
solution sooner than by branching, and a solution found in this way is treated in
the same way as any other feasible solution. At intervals in the tree, new cuts
beyond those computed at the root node may also be added to the problem.
You control the path that CPLEX traverses in the tree through several parameters,
as summarized in Table 43.
Table 43. Parameters for controlling branch & cut strategy.
Interactive Concert Technology Callable Library
Parameter Reference
Optimizer Command IloCPLEX Method Routine
set mip strategy setParam (BtTol , n) CPXsetdblparam (env, backtracking
backtrack CPX_PARAM_BTTOL , n) tolerance
set mip strategy setParam(NodeSel , CPXsetintparam (env, MIP node selection
nodeselect i) CPX_PARAM_NODESEL , strategy
i)
set mip strategy setParam(VarSel , i) CPXsetintparam(env, MIP variable
variableselect CPX_PARAM_VARSEL , selection strategy
i)
set mip strategy setParam(BBInterval CPXsetintparam(env, MIP strategy best
bbinterval , i) CPX_PARAM_BBINTERVAL bound interval
, i)
set mip strategy setParam(BrDir , i) CPXsetintparam(env, MIP branching
branch CPX_PARAM_BRDIR , i) direction
During the branch & cut algorithm, CPLEX may choose to continue from the
present node and dive deeper into the tree, or it may backtrack (that is, begin a
new dive from elsewhere in the tree). The value of the backtrack tolerance
parameter, BtTol or CPX_PARAM_BTTOL, influences this decision, in terms of the
relative degradation of the objective function caused by the branches taken so far
in this dive. Setting that parameter to a value near 0.0 increases the likelihood that
a backtrack will occur, while the default value near 1.0 makes it more likely that
the present dive will continue to a resolution (fathoming either via a cutoff or an
infeasible combination of branches or the discovery of a new incumbent integer
feasible solution). See the CPLEX Parameters Reference Manual for more details
about how this parameter influences the computation that makes the decision to
backtrack.
Selecting nodes
Describes options of the parameter to control selection of strategies applied at
nodes and their effect on the search.
When CPLEX backtracks, there usually remain large numbers of unexplored nodes
from which to begin a new dive. The node selection parameter sets this choice.
(For documentation of the node selection parameter, see MIP node selection
strategy in the CPLEX Parameters Reference Manual.)
Selecting variables
Describes options of the parameter to control selection of the next variable.
After a node has been selected, the variable selection parameter influences which
variable is chosen for branching at that node. (For documentation of that
parameter, see MIP variable selection strategy in the CPLEX Parameters Reference
Manual.)
Table 45. VarSel parameter settings for choice of branching variable
VarSel Setting Symbolic Value Branching Variable Choice
-1 CPX_VARSEL_MININFEAS Branch strictly at the nearest
integer value which is closest
to the fractional variable.
After a variable has been selected for branching, the branching direction parameter
influences the direction, up or down, of the branch on that variable to be explored
first. (For documentation of that parameter, see MIP branching direction in the
CPLEX Parameters Reference Manual.)
Table 46. BrDir parameter settings for choice of branching direction
BrDir Setting Symbolic Value Branching Direction Choice
-1 CPX_BRANCH_DOWN Branch downward
0 (Default) CPX_BRANCH_GLOBAL CPLEX automatically decides
each branch direction.
1 CPX_BRANCH_UP Branch upward
Priority orders complement the behavior of these parameters. They are introduced
in Issuing priority orders on page 251. They offer a mechanism by which you
supply problem-specific directives about the order in which to branch on variables.
In a priority order, you can also provide preferred branching directions for specific
variables.
Solving subproblems
Describes the parameters to control solution of subproblems.
CPLEX allows you to distinguish the algorithm applied to the initial relaxation of
your problem from the algorithm applied to other continuous subproblems of a
MIP. This distinction between initial relaxation and the other MIP subproblems
For more detail about situations in which you may find these parameters helpful,
see Unsatisfactory optimization of subproblems on page 270.
On difficult models that generate a great number of nodes in the tree, the amount
of available memory for node storage can become a limiting factor. Storage of node
files can be an effective technique which uses disk space to augment RAM, at little
or no penalty in terms of solution speed.
The node-file storage-feature enables you to store some parts of the branch & cut
tree in files while the branch & cut algorithm is being applied. If you use this
feature, CPLEX will be able to explore more nodes within a smaller amount of
computer memory. This feature includes several options to reduce the use of
physical memory, and it entails a very small increase in runtime. Node-file storage
as managed by CPLEX itself offers a much better option in terms of memory use
and performance time than relying on swap space as managed by your operating
system in this context.
For more about the parameters controlling node files, see Use node files for
storage on page 267.
Probing
Describes probing, a technique that fixes binary variables and analyzes the
implications.
The probing feature can help in many different ways on difficult models. Probing
is a technique that looks at the logical implications of fixing each binary variable to
0 (zero) or 1 (one). It is performed after preprocessing and before the solution of
the root relaxation. Probing can be expensive, so this parameter should be used
selectively. On models that are in some sense easy, the extra time spent probing
may not reduce the overall time enough to be worthwhile. On difficult models,
probing may incur very large runtime costs at the beginning and yet pay off with
shorter overall runtime. When you are tuning performance, it is usually because
the model is difficult, and then probing is worth trying.
To activate an increasing level of probing use the MIP probing level parameter:
Cuts
Describes types of cuts available in the MIP optimizer as performance features.
Cuts are constraints added to a model to restrict (cut away) noninteger solutions
that would otherwise be solutions of the continuous relaxation. The addition of
cuts usually reduces the number of branches needed to solve a MIP.
In the following descriptions of cuts, the term node includes the root node (that is,
the root relaxation). Cuts are most frequently seen at the root node, but they may
be added by CPLEX at other nodes as conditions warrant.
CPLEX can generate both global cuts and local cuts. A global cut is a cut that is
valid for all nodes of the branch-and-bound tree, even if that global cut was found
during the analysis of a particular node. In contrast, a local cut is a cut that is valid
only for a specific node and for all of its descendant nodes.
Here are descriptions of the cuts implemented in CPLEX. These types of cuts are
all separated as global cuts with the sole exception of implied bound cuts. Implied
bound cuts can be separated as global cuts or as local cuts as conditions warrant.
v Boolean Quadric Polytope (BQP) cuts
v Clique cuts on page 232
v Cover cuts on page 232
v Disjunctive cuts on page 232
v Flow cover cuts on page 232
v Flow path cuts on page 233
v Gomory fractional cuts on page 233
v Generalized upper bound (GUB) cover cuts on page 233
v Implied bound cuts: global and local on page 233
v Lift-and-project cuts on page 233
v Mixed integer rounding (MIR) cuts on page 234
v Multi-commodity flow (MCF) cuts on page 234
v Zero-half cuts on page 235
Boolean Quadric Polytope cuts, also known as BQP cuts, exploit a cutting plane
technique to solve continuous global quadratic programs (QP) or mixed integer
quadratic programs (MIQP) more efficiently. This technique is based on the
observation that every valid linear cut for the mixed integer set defined by:
A = { (x, X) : x in {0,1}, X_ij = x_i x_j if i is different from j, X_ii = 0 }
CPLEX makes use of such cuts to solve nonconvex quadratic programs (QP) and
nonconvex mixed integer quadratic programs (MIQP) to global optimality.
CPLEX does not apply BQP cuts to mixed integer linear programs (MILP).
Background
In his article The Boolean Quadric Polytope: Some characteristics, facets, and relatives
published in Mathematical Programming, volume 45, pages 139172, 1989,
Manfred Padberg defined a Boolean Quadric Polytope QPn in n(n+1)/2 dimensions
as resulting from the linearization of the given quadratic form Q.
Samuel Burer and Adam N. Letchford provided a theoretical basis for BQP cuts in
their article On non-convex quadratic programming with box constraints published in
2009 by the Society for Industrial and Applied Mathematics in the SIAM Journal
on Optimization, volume 20, issue 2, pages 1073-1089.
Clique cuts
Defines a clique cut.
A clique is a relationship among a group of binary variables such that at most one
variable in the group can be positive in any integer feasible solution. Before
optimization starts, CPLEX constructs a graph representing these relationships and
finds maximal cliques in the graph.
Cover cuts
Defines a cover cut.
If a constraint takes the form of a knapsack constraint (that is, a sum of binary
variables with nonnegative coefficients less than or equal to a nonnegative
righthand side), then there is a minimal cover associated with the constraint. A
minimal cover is a subset of the variables of the inequality such that if all the subset
variables were set to one, the knapsack constraint would be violated, but if any
one subset variable were excluded, the constraint would be satisfied. CPLEX can
generate a constraint corresponding to this condition, and this cut is called a cover
cut.
Disjunctive cuts
Defines disjunctive cuts.
A MIP problem can be divided into two subproblems with disjunctive feasible
regions of their LP relaxations by branching on an integer variable. Disjunctive cuts
are inequalities valid for the feasible regions of LP relaxations of the subproblems,
but not valid for the feasible region of LP relaxation of the MIP problem.
Flow covers are generated from constraints that contain continuous variables, where
the continuous variables have variable upper bounds that are zero or positive
depending on the setting of associated binary variables. The idea of a flow cover
comes from considering the constraint containing the continuous variables as
Flow path cuts are generated by considering a set of constraints containing the
continuous variables that define a path structure in a network, where the
constraints are nodes and the continuous variables are in-flows and out-flows. The
flows will be on or off depending on the settings of the associated binary variables.
Gomory fractional cuts are generated by applying integer rounding on a pivot row
in the optimal LP tableau for a (basic) integer variable with a fractional solution
value.
A GUB constraint for a set of binary variables is a sum of variables less than or
equal to one. If the variables in a GUB constraint are also members of a knapsack
constraint, then the minimal cover can be selected with the additional
consideration that at most one of the members of the GUB constraint can be one in
a solution. This additional restriction makes the GUB cover cuts stronger (that is,
more restrictive) than ordinary cover cuts.
In some models, binary variables imply bounds on nonbinary variables (that is,
general integer variables and continuous variables). CPLEX generates cuts to reflect
these relationships. In fact, CPLEX can generate two different types of implied
bound cuts:
v CPLEX generates global implied bound cuts by using globally valid bounds on
the continuous variables in the model. For a parameter to control this activity,
see the documentation of the MIP globally valid implied bound cuts switch in
the CPLEX Parameters Reference Manual.
v CPLEX generates local implied bound cuts by using locally valid bounds on the
continuous variables of the subproblem (that is, the branch-and-bound node)
under consideration. For a parameter to control this activity, see the
documentation of the MIP locally valid implied bound cuts switch in the CPLEX
Parameters Reference Manual.
Lift-and-project cuts
Defines lift-and-project cuts.
More formally, a valid split disjunction for a MIP problem looks like this:
Given the polyhedron P corresponding to the LP relaxation of the MIP, and given a
valid split disjunction for that MIP, all the MIP feasible solutions are contained in
one of the two disjoint polyhedra defined by P intersecting the split disjunction.
That is, the MIP feasible solutions are here:
OR
A lift-and-project cut is an inequality that is valid for the union of P0 and P1,
though not valid for P.
Lift and project cuts as implemented in CPLEX can also be helpful in solving
mixed integer quadratically constrained programs (MIQCP) under certain
conditions.
For a more thorough exploration of the theory supporting lift and project cuts, see
the reports of these researchers:
v Egon Balas, Sebastian Ceria, and Gerard Cornuejols: "A lift-and-project cutting
plane algorithm for mixed-integer programs" in Mathematical Programming
volume 58, pages 295-324, 1993
v Egon Balas, Sebastian Ceria, and Gerard Cornuejols: "Mixed 0-1 programming by
lift-and-project in a branch-and-cut framework" in Management Science volume
42, pages 1229-1246, 1996
v Pierre Bonami: "On optimizing over lift-and-project closures" in Mathematical
Programming Computation volume 4, pages 151179, 2012
MIR cuts are generated by applying integer rounding on the coefficients of integer
variables and the righthand side of a constraint.
When such a structure is present, CPLEX generates cuts that state that the
capacities installed on arcs pointing into a component of the network must be at
least as large as the total flow demand of the component that can not be satisfied
by flow sources within the component.
Zero-half cuts
Defines zero-half cuts and offers an example.
Zero-half cuts are based on the observation that when the lefthand side of an
inequality consists of integral variables and integral coefficients, then the righthand
side can be rounded down to produce a zero-half cut. Zero-half cuts are also
known as 0-1/2 cuts. To understand how zero-half cuts are generated, consider
these two constraints over five integer variables with integer coefficients:
x1 + 2x2 + x3 + 3x4 <= 8
x1 + 3x3 + x4 + 2x5 <= 5
Each time CPLEX adds a cut, the subproblem is re-optimized. CPLEX repeats the
process of adding cuts at a node until it finds no further effective cuts. It then
selects the branching variable for the subproblem.
Counting cuts
Describes methods and routines to calculate how many cuts have been added.
Cuts may be added to a problem during MIP optimization. After MIP optimization
terminates, to know the number of cuts that were added during optimization,
implement one of the following techniques in your application:
v For Concert Technology, use the method:
IloCplex::getNcuts in the C++ API;
IloCplex.getNcuts in the Java API;
Cplex.GetNcuts in the .NET API.
v For Callable Library, use the routine CPXgetnumcuts.
Tip:
Parameters control the way each class of cuts is used. Those parameters are listed
in the table Parameters for controlling cuts.
Table 47. Parameters for controlling cuts
Interactive Parameter
Cut Type Concert Technology Callable Library
Command Reference
Boolean Quadric set mip cuts bqp MIP.Cuts.BQP CPXPARAM_MIP_Cuts_BQP Boolean Quadric
Polytope (BQP) Polytope cuts
Clique set mip cuts MIP.Cuts.Cliques CPXPARAM_MIP_Cuts_Cliques MIP cliques switch
cliques
Cover set mip cuts MIP.Cuts.Covers CPXPARAM_MIP_Cuts_Covers MIP covers switch
covers
Disjunctive set mip cuts MIP.Cuts.DisjunctiveCPXPARAM_MIP_Cuts_DisjunctiveMIP disjunctive
disjunctive cuts switch
Flow Cover set mip cuts MIP.Cuts.FlowCovers CPXPARAM_MIP_Cuts_FlowCovers MIP flow cover cuts
flowcovers switch
Flow Path set mip cuts MIP.Cuts.PathCut CPXPARAM_MIP_Cuts_PathCut MIP flow path cut
pathcut switch
Gomory set mip cuts MIP.Cuts.Gomory CPXPARAM_MIP_Cuts_Gomory MIP Gomory
gomory fractional cuts
switch
GUB Cover set mip cuts MIP.Cuts.GUBCovers CPXPARAM_MIP_Cuts_GUBCovers MIP GUB cuts
gubcovers switch
Globally Valid set mip cuts MIP.Cuts.Implied CPXPARAM_MIP_Cuts_Implied MIP globally valid
Implied Bound implied implied bound cuts
switch
Locally Valid set mip cuts MIP.Cuts.LocalImplied MIP locally valid
CPXPARAM_MIP_Cuts_LocalImplied
Implied Bound localimplied implied bound cuts
switch
Lift and Project set mip cuts MIP.Cuts.LiftProj CPXPARAM_MIP_Cuts_LiftProj Lift-and-project cuts
liftproj switch for MIP and
MIQCP
Mixed Integer set mip cuts MIP.Cuts.MIRCut CPXPARAM_MIP_Cuts_MIRCut MIP MIR (mixed
Rounding (MIR) mircut integer rounding)
cut switch
Multi-commodity set mip cuts MIP.Cuts.MCFCut CPXPARAM_MIP_Cut_MCFCut MCF cut switch
Flow mcfcut
Zero-Half set mip cuts MIP.Cuts.ZeroHalfCutCPXPARAM_MIP_Cuts_ZeroHalfCutMIP zero-half cuts
zerohalfcut switch
In the Interactive Optimizer, the command set mip cuts all i applies the value i
to all types of cut parameters. That is, you can set them all simultaneously.
Heuristics
Introduces heuristics in performance features.
Being integrated into branch & cut, these heuristic solutions gain the same
advantages toward a proof of optimality as any solution produced by branching,
and in many instances, they can speed the final proof of optimality, or they can
provide a suboptimal but high-quality solution in a shorter amount of time than by
branching alone. With default parameter settings, CPLEX automatically invokes the
heuristics when they seem likely to be beneficial.
Node heuristic
Describes the heuristic applied at nodes by the MIP optimizer.
The node heuristic employs techniques to try to construct a feasible solution from
the current (fractional) branch-and-cut node. This feature is controlled by the
parameter HeurFreq . At its default value of 0 (zero), CPLEX dynamically sets the
frequency with which the heuristic is invoked. The setting -1 turns the feature off.
A positive value specifies the frequency (in node count) with which the heuristic
will be called. For example, if the HeurFreq parameter is set to 20, then the node
heuristic will be applied at node 0, node 20, node 40, and so on.
RINSHeur controls how often RINS is invoked, in a manner analogous to the way
that HeurFreq works. A setting of 100, for example, means that RINS is invoked
every 100th node in the tree, while -1 turns it off. The default setting is 0 (zero),
which means that CPLEX decides when to apply it; with this automatic setting,
RINS is applied very much less frequently than the node heuristic is applied
because RINS typically consumes more time. Also, with the default setting, RINS is
turned entirely off if the node heuristic has been turned off via a HeurFreq setting
of -1; with any other RINSHeur setting than 0 (zero), the HeurFreq setting does not
affect RINS frequency.
For more about this heuristic, see the article published by Emilie Danna, Edward
Rothberg, Claude Le Pape in 2005, titled Exploring relaxation induced neighborhoods to
improve MIP solutions in the journal Mathematical Programming in volume 102,
issue 1, pages 7190.
Solution polishing
Describes solution polishing as a heuristic of the MIP optimizer.
Solution polishing can yield better solutions in situations where good solutions
are otherwise hard to find. More time-intensive than other heuristics, solution
polishing is actually a variety of branch and cut that works after an initial solution
Because of the high cost entailed by solution polishing, it is not called throughout
branch and cut like other heuristics. Instead, solution polishing works in a second
phase after a first phase of conventional branch and cut. As an additional step after
branch and cut, solution polishing can improve the best known solution.
Solution polishing obeys the same stopping criteria as branch and cut.
v The absolute gap tolerance is a stopping criterion for polishing. For more
general information about it, see absolute MIP gap tolerance in the CPLEX
Parameters Reference Manual.
EpAGap in Concert Technology
CPX_PARAM_EPAGAP in the Callable Library
mip tolerances absmipgap in the Interactive Optimizer
v The relative gap tolerance is a stopping criterion for polishing. For more general
information about it, see relative MIP gap tolerance in the CPLEX Parameters
Reference Manual.
EpGap in Concert Technology
CPX_PARAM_EPGAP in the Callable Library
mip tolerances mipgap in the Interactive Optimizer
v The optimizer time limit is a stopping criterion for polishing. For more general
information about it, see optimizer time limit in seconds in the CPLEX
Parameters Reference Manual.
TiLim in Concert Technology
CPX_PARAM_TILIM in the Callable Library
timelimit in the Interactive Optimizer
v The node limit is a stopping criterion for polishing. For more general
information about it, see MIP node limit in the CPLEX Parameters Reference
Manual.
NodeLim in Concert Technology
CPX_PARAM_NODELIM in the Callable Library
mip limits nodes in the Interactive Optimizer
v The integer solution limit is a stopping criterion for polishing. For more general
information about it, see MIP integer solution-file switch and prefix in the
CPLEX Parameters Reference Manual.
IntSolLim in Concert Technology
CPX_PARAM_INTSOLLIM in the Callable Library
mip limits solutions in the Interactive Optimizer
Those criteria apply to the overall optimization, that is, branch and cut plus
solution polishing. For example, if you set the optimizer time limit in seconds
(TiLim, CPX_PARAM_TILIM) to 100 seconds, then CPLEX spends at most 100 seconds
in total for branch and cut plus solution polishing.
You control when CPLEX switches from branch and cut to solution polishing with
these parameters:
v The absolute MIP gap before starting to polish a feasible solution
PolishAfterEpAGap in Concert Technology
CPX_PARAM_POLISHAFTEREPAGAP in the Callable Library
mip polishafter absmipgap in the Interactive Optimizer
v The relative MIP gap before starting to polish a feasible solution
PolishAfterEpGap in Concert Technology
CPX_PARAM_POLISHAFTEREPGAP in the Callable Library
mip polishafter mipgap in the Interactive Optimizer
v The number of MIP integer solutions to find before starting to polish a feasible
solution
PolishAfterIntSol in Concert Technology
CPX_PARAM_POLISHAFTERINTSOL in the Callable Library
mip polishafter solutions in the Interactive Optimizer
v The number of nodes to process before starting to polish a feasible solution
PolishAfterNode in Concert Technology
CPX_PARAM_POLISHAFTERNODE in the Callable Library
mip polishafter nodes in the Interactive Optimizer
v The amount (in seconds) of time before starting to polish a feasible solution
PolishAfterTime in Concert Technology
CPX_PARAM_POLISHAFTERTIME in the Callable Library
mip polishafter time in the Interactive Optimizer
With each of those parameters, a user tells CPLEX when to switch from branch
and cut to solution polishing. CPLEX is able to switch after it has found a feasible
solution and put into place the MIP structures needed for solution polishing.
When these two conditions are met (feasible solution and structures in place),
CPLEX stops branch and cut then switches to solution polishing whenever the first
of these starting conditions is met:
v when CPLEX achieves a specified absolute MIP gap;
v when CPLEX achieves a specified relative MIP gap;
v when CPLEX finds a specified number of integer feasible solutions;
v when CPLEX processes a specified number of nodes;
v when CPLEX has spent a specified amount of time in optimization.
Callbacks are valid and work during solution polishing. However, nodes are
processed much more slowly during solution polishing because of the more
expensive work carried out at each node. Consequently, callbacks may be called
less often during solution polishing.
To create conditions where you can find a first solution and then improve it by
means of polishing, follow these steps:
1. Set to 1 (one) the number of MIP integer solutions to find before starting to
polish a feasible solution.
v PolishAfterIntSol in Concert Technology
v CPX_PARAM_POLISHAFTERINTSOL in the Callable Library
v mip polishafter solutions in the Interactive Optimizer
2. Set the optimizer time limit in seconds to a positive value, such as 200 seconds,
to specify the total time for CPLEX to spend in branch and cut plus polishing.
3. Call the optimizer.
To create conditions to improve a MIP start with polishing, follow these steps:
1. Set to 0 (zero) the time before starting to polish a feasible solution.
2. Set the optimizer time limit in seconds to a positive number of seconds.
v TiLim in Concert Technology
v CPX_PARAM_TILIM in the Callable Library
With those settings, CPLEX switches to polishing as soon as all MIP structures
needed for polishing are in place. It does not completely solve the root node with
those settings. In particular, it does not generate cuts under these conditions.
If CPLEX is unable to process the MIP start into a solution, then solution polishing
does not begin until after branch and cut finds a solution.
In contrast, if you want to solve the root node, and if the MIP start has been
processed into a solution, you must change that step 1 before applying the other
steps:
1. Set the starting condition for polishing by means of this parameter, the number
of nodes to process before starting to polish a feasible solution, set to 1 (one).
v PolishAfterNode in Concert Technology
v CPX_PARAM_POLISHAFTERNODE in the Callable Library
v mip polishafter nodes in the Interactive Optimizer
2. Set the optimizer time limit in seconds to a positive number of seconds.
3. Verify that the advanced start switch remains at its default value of 1 (one).
4. Specify the MIP start, for example, by reading it from a file or adding it to the
model, as explained in Establishing starting values in a MIP start on page
247.
5. Call the optimizer.
If your model and application are such that processing the root node takes too
much time, try the recommendations in these other topics:
v Large number of unhelpful cuts on page 261
v Too much time at node 0 on page 260
In the rare event that solution polishing is unable to improve a MIP start that you
provide, polishing may be more successful if you disable some combination of
dual reductions, nonlinear reductions, or symmetry reductions during
preprocessing.
For details about the parameters to disable those features, see the CPLEX Parameter
Reference Manual, especially these topics:
v the presolve switch: PreInd, CPX_PARAM_PREIND
v the linear reduction switch: PreLinear, CPX_PARAM_PRELINEAR
v the symmetry breaking parameter: Symmetry, CPX_PARAM_SYMMETRY
Under those conditions, if CPLEX finds a feasible solution within the first 100
seconds of branch and cut, then it switches to solution polishing after exactly 100
seconds. However, if CPLEX does not find a feasible solution within the first 100
seconds, then it continues branch and cut until it finds a first feasible solution and
switches to solution polishing afterward.
Those settings guarantee that CPLEX spends at most 300 seconds on the model
and that CPLEX applies polishing only if it finds a feasible solution within that
time.
CPLEX also allows you to control when polishing starts and when polishing ends
with a gap as a criterion. The gap may be relative or absolute. For example,
suppose you want to apply branch and cut until achieving a 10% relative gap and
then you want to switch to solution polishing until achieving a 2% relative gap.
To apply branch and cut until achieving a 10% gap and then to switch to solution
polishing until achieving a 2% gap, follow these steps:
1. Set the relative MIP gap tolerance to 2%. This parameter determines when
overall optimization stops.
v EpGap in Concert Technology
v CPX_PARAM_EPGAP in the Callable Library
v mip tolerances mipgap in the Interactive Optimizer
2. Set to 10% the relative MIP gap before starting to polish a feasible solution.
This parameter sets the starting condition for polishing. It controls when
CPLEX switches from branch and cut to solution polishing.
3. Set the optimizer time limit in seconds to a positive value (for example, 200
seconds) to specify the total time spent in branch and cut plus polishing. For
difficult problems, this step is a precaution to guarantee that CPLEX terminates
even if the targeted gap cannot be achieved.
v TiLim in Concert Technology
v CPX_PARAM_TILIM in the Callable Library
v timelimit 200.0 in the Interactive Optimizer
For technical detail about how solution polishing works, see the article by Edward
Rothberg, An evolutionary algorithm for polishing mixed integer programming solutions,
published in INFORMS Journal on Computing, volume 19, issue 4, pages 534541
(2007).
Feasibility pump
Describes the feasibility pump as a heuristic of the MIP optimizer.
The feasibility pump is a heuristic that finds an initial feasible solution even in
certain very hard mixed integer programming problems (MIPs). In CPLEX, you
control this heuristic by means of a parameter, feasibility pump switch, that
specifies how the feasibility pump looks for a feasible solution.
v FBHeur in Concert Technology
v CPX_PARAM_FPHEUR in the Callable Library
v mip strategy fpheur in the Interactive Optimizer
Various settings of this parameter turn off the feasibility pump entirely, allow
CPLEX to determine whether to apply the feasibility pump, emphasize finding any
feasible solution, or emphasize finding a feasible solution with a good objective
value. For more detail about this parameter and its settings, see feasibility pump
switch in the Parameters Reference Manual.
For a theoretical and practical explanation of the feasibility pump heuristic, see
research reported by Fischetti, Glover, and Lodi (2003, 2005), by Bertacco, Fischetti,
and Lodi (2005), and by Achterberg and Berthold (2005, 2007).
When you invoke the MIP optimizer, whether through the Interactive Optimizer
command mipopt, through a call to the Concert Technology IloCplex method
solve , or through the Callable Library routine CPXmipopt , CPLEX by default
automatically preprocesses your problem. Table 48 summarizes the preprocessing
parameters. In preprocessing, CPLEX applies its presolver and aggregator one or
more times to reduce the size of the integer program in order to strengthen the
initial linear relaxation and to decrease the overall size of the mixed integer
program.
Table 48. Parameters to control MIP preprocessing.
Interactive Concert Callable Library Parameter Comment Parameter
Command Technology Reference
Parameter
set preprocessing AggInd CPX_PARAM_AGGIND on by default preprocessing
aggregator aggregator
application limit
set preprocessing PreInd CPX_PARAM_PREIND on by default presolve switch
presolve
These and other parameters also control the behavior of preprocessing of the
continuous subproblem (LP, QP, or QCP) solved during a MIP optimization. See
Preprocessing on page 137 for further details about these parameters in that
context. The following discussion pertains to these parameters specifically in MIP
preprocessing.
It is possible to apply preprocessing a second time, after cuts and other analyses
have been performed and before branching begins. If your models tend to require
a lot of branching, this technique is sometimes useful in further tightening the
formulation. Use the MIP repeat presolve switch (RepeatPresolve,
CPX_PARAM_REPEATPRESOLVE) parameter to invoke this additional step. Its default
When you are solving a mixed integer programming problem (MIP), you can
supply hints to help CPLEX find an initial solution. These hints consist of pairs of
variables and values, known as a MIP start, an advanced start, or a warm start. A
MIP start might come from a different problem you have previously solved or
from your knowledge of the problem, for example. You can also provide CPLEX
with one or more MIP starts, that is, multiple MIP starts.
A MIP start may be a feasible solution of the model, but it need not be; it may
even be infeasible or incomplete. If you are interested in debugging an infeasible
MIP start, that is, if you want to discover why CPLEX regards the model inferred
from the pairs of variables and values in a MIP start as infeasible, consider using
the conflict refiner on that model inferred from that MIP start, as explained in
Refining a conflict in a MIP start on page 439.
A MIP start may include continuous variables and discrete variables of various
types, such as integer variables, binary variables, semi-continuous variables,
semi-integer variables, or variables appearing in special ordered sets. For more
information about each of those types, see these topics in this manual:
v Chapter 19, Using semi-continuous variables: a rates example, on page 307
v Chapter 33, User-cut and lazy-constraint pools, on page 453
v Chapter 21, Indicator constraints in optimization, on page 321 or Chapter 22,
Logical constraints in optimization, on page 325
v Chapter 18, Using special ordered sets (SOS), on page 303
After a MIP start has been established for your model, you control its use by the
advanced start switch (AdvInd in Concert Technology; CPX_PARAM_ADVIND in the
Callable Library). At the default setting of 1 (one) , the MIP start values that you
specify are used. If you set AdvInd to the value 0 (zero), then the MIP start will not
be used. If you set this parameter to 2, CPLEX retains the current incumbent (if
there is one), re-applies presolve, and starts a new search from a new root. Setting
2 can be particularly useful for solving fixed MIP models, where a start vector but
no corresponding basis is available. For more about a fixed MIP, see Working with
the fixed MIP problem.
You may invoke relaxation induced neighborhood search on a starting solution. See
Relaxation induced neighborhood search (RINS) heuristic on page 238 in this
manual for more about that topic.
You can establish MIP starting values by using the method addMIPStart in a
Concert Technology application, or by using CPXaddmipstarts in a Callable
Library application.
You can establish MIP starting values from a file in either MST or SOL format.
MST and SOL share the same underlying XML format. MST format is documented
in MST file format: MIP starts in the CPLEX File Formats Reference Manual. SOL
Tip:
Make sure that the name of each variable is consistent between the original model
and your target model when you use this approach.
When you tell CPLEX to write a MIP start to a formatted file, you can also specify
a degree of detail to record there, such as only values of discrete variables, values
of all variables, and so forth. The write level for MST, SOL files is a parameter
(WriteLevel, CPX_PARAM_WRITELEVEL), documented in the CPLEX Parameter Reference
Manual, to manage the level of detail.
When CPLEX reads from such a file, it processes all the MIP starts. They will be
processed at the next MIP optimization. Immediately after an optimization, the first
MIP start is the MIP start corresponding to the incumbent solution, so if you write
a file with multiple MIP starts, the first MIP start will be that corresponding to the
incumbent.
Because processing a large number of MIP starts may be costly, CPLEX allows you
to associate an individual effort level with each MIP start. The effort level tells
CPLEX how to expend its effort in processing that MIP start. For more detail about
effort level, see MIP starts and effort level.
You may want CPLEX to process multiple MIP starts differently, expending more
effort on some than on others. Moreover, you may want to limit the effort CPLEX
applies to MIP starts when it transforms each MIP start into a feasible solution,
especially if there are many of them. In that context, you can specify a level of
effort that CPLEX should expend for each MIP start to transform it into a feasible
solution.
You specify the level of effort as an argument to the method or routine that adds a
MIP start to a model or that modifies a MIP start. When CPLEX writes MIP starts
to a file, such as a file in MST format, CPLEX records the level of effort the user
specified for each MIP start. If you have not specified an effort level, CPLEX
assigns a default effort level.
You may specify a different level of effort for each MIP start, for example, differing
levels of effort for the incumbent, for a MIP start corresponding to a solution in the
solution pool, for a MIP start supplied by the user. By default, CPLEX expends
effort at level 4 for the first MIP start and at level 1 (one) for other MIP starts. You
may change that level of effort; you do so by means of an argument to the method
or routine when you add a MIP start to a model or when you modify the MIP
start.
If the values specified in your MIP start do not lead directly to an integer-feasible
solution, CPLEX applies a heuristic to try to repair the MIP start. The number of
times that CPLEX attempts to repair a MIP start is controlled by a parameter, the
number of attempts to repair infeasible MIP start (RepairTries in Concert
Technology, CPX_PARAM_REPAIRTRIES in the Callable Library). If this process
succeeds, the solution will be treated as an integer solution of the current problem.
To write a particular MIP start to a file from the Interactive Optimizer, use the
write command supplying the file name, the file extension for MST formatted files,
and the identifier of the MIP start, like this:
write filename.mst id
The identifier of the MIP start may be its name or its index number. In the
Interactive Optimizer, MIP starts are named by default like this: m1, m2, m3, and so
forth (that is, m followed by a number). The index number of a MIP start ranges
from 1 (one) through the number of existing MIP starts for the current problem.
To write all existing MIP starts from the current session of the Interactive
Optimizer to a formatted file, use this command:
write filename.mst all
To delete a MIP start from the current session of the Interactive Optimizer, use this
command, where id is the name or index of the MIP start to delete:
To delete a range of MIP starts, supply one the conventional options for a range in
the Interactive Optimizer, using hyphen or wild card star, like these examples:
change delete mipstart 5-7
change delete mipstart *
Use the method IloCplex::addMIPStart to add a MIP start to your model. This
method is not incremental. In other words, successive calls of this method do not
add more values to an existing MIP start. Instead, successive calls of the method
override any existing MIP start. That is, each call of this method creates a new MIP
start.
Use the method IloCplex.addMIPStart to add a MIP start to your model. This
method is not incremental. In other words, successive calls of this method do not
add more values to an existing MIP start. Instead, successive calls of the method
override any existing MIP start. That is, each call of this method creates a new MIP
start.
Problems that use integer variables to represent different types of decisions should
assign higher priority to those that must be decided first. For example, if some
variables in a model activate processes, and others use those activated processes,
then the first group of variables should be assigned higher priority than the second
group. In that way, you can use priority to achieve better solutions.
You can specify priority for any variable, though the priority is used only if the
variable is a general integer variable, a binary integer variable, a semi-continuous
variable, a semi-integer variable, or a member of a special ordered set. To specify
priority, use one of the following routines or methods:
v From the Callable Library, use CPXcopyorder to copy a priority order and apply
it, or CPXreadcopyorder to read the copy order from a file in ORD format. That
format is documented in the CPLEX File Formats Reference Manual.
v From Concert Technology, use the method setPriority to set the priority of a
given variable or setPriorities to set priorities for an array of variables. Use
the method readOrder to read priorities from a file in ORD format and apply
them.
The parameter MIPOrdInd, when set to 0 (zero), allows you to direct CPLEX to
ignore a priority order that was previously read from a file. The default setting for
this parameter means that CPLEX applies a priority order, if one has been read in.
The following topics discuss the optimal solution or the best feasible solution, if
no optimum has been proved. For information about managing the entire pool of
feasible solutions, see Chapter 17, Solution pool: generating and keeping multiple
solutions, on page 279.
After you have solved a MIP, you will usually want to make use of the solution in
some way. If you are interested only in the values of the variables at the optimum,
then you can perform some simple steps to get that information:
v In Concert Technology, the method getValues accesses this information.
v In the Callable Library, use the routine CPXgetx.
After your program has placed the solution values into arrays in this way, it can
print the values to the screen, write the values to a file, perform computations
using the values, and so forth.
You can also tell CPLEX the name of a file where you want CPLEX to write integer
solutions to your MIP. To do so, use the parameter MIP integer solution-file switch
and prefix documented in the CPLEX Parameters Reference Manual.
Such a file can be useful, for example, to collect incumbents in your CPLEX
application.
In the Interactive Optimizer, you can print the nonzero solution values to the
screen with the command display solution variables. A copy of this information
goes to the log file, named cplex.log by default. Thus one way to print your
solution to a file is to rename the log file temporarily. For example, the following
series of commands in the Interactive Optimizer will place the solution values of
all variables whose values are not zero into a file named solution.asc:
set logfile solution.asc
display solution variables
set logfile cplex.log
For any of the MIP problem types, the following additional solution information is
available in the Interactive Optimizer through options of the display command
after optimization has produced a solution:
v objective function value for the best integer solution, if one exists;
v best bound, that is, best objective function value among remaining subproblems;
v solution quality;
v primal values for the best integer solution, if one has been found;
v slack values for best integer solution, if one has been found.
If you request other solution information than these items for a MIP, an error
status will be issued. For example, in the Interactive Optimizer, you would get the
following message:
Not available for mixed integer problems
use CHANGE PROBLEM to change the problem type
Such post-solution information does not have the same meaning in a mixed integer
program (MIP) as in a linear program (LP) because of the special nature of the
integer variables in the MIP. The reduced costs, dual values, and sensitivity ranges
give you information about the effect of making small changes in problem data so
long as feasibility is maintained. Integer variables, however, lose feasibility if a
small change is made in their value, so this post-solution information cannot be
used to evaluate changes in problem data in the usual way of continuous models.
CPLEX can also display information about the quality of a MIP solution. Here is a
sample, typical of such quality information.
MILP objective -2.1989035553e+06
MILP solution norm |x| (Total, Max) 1.95445e+10 1.68134e+08
MILP solution error (Ax=b) (Total, Max) 3.90105e+05 8.14760e+03
MILP x bound error (Total, Max) 0.00000e+00 0.00000e+00
MILP x integrality error (Total, Max) 1.96296e-06 9.81482e-07
MILP slack bound error (Total, Max) 8.27493e-08 2.95847e-09
Furthermore, the integrality error declares the maximum amount by which the
solution value of a variable restricted to be integral has violated its integrality
restriction. By default, CPLEX uses a default value of integrality tolerance of 1e-05.
(You can adjust this value by means of the integrality tolerance parameter.)
The MILP slack bound error recomputes the slack or artificial variable values
based on the structural variable values in the solution. The MILP slack bound error
corresponds to the slack values that would be obtained from all the slack values in
The slack bound error differs from the primal residual solution error Ax - b.
Specifically, the slack bound error involves recomputing the slacks; in contrast, the
primal residual solution error computes residuals based on the structural and
slack values obtained from the node relaxation or heuristic from which the final
integer solution was computed. These two metrics (slack bound error and primal
residual solution error), along with the integrality error, are the most important
measures of MIP solution quality.
For the solution x = 1 - 1e-10, y = 0, the integrality error is 1e-10. The MIP slack
bound violation is obtained by comparing the lefthand side of the solution
value, 10000*(1-1e-10), to the righthand side of 10000. The difference implies a
value of -1e-6 for the surplus variable on the constraint, a slack bound violation of
1e-6. In other words, constraint coefficients larger than one can magnify a bound
violation for a structural variable into a larger slack bound violation.
Integer variables often represent major structural decisions in a model, and many
continuous variables of the model may be related to these major decisions. With
that observation in mind, if you take the integer variable solution values as given,
then you can obtain useful post-solution information that applies only to the
continuous variables, in the usual way. This observation about the information
available only for continuous variables in a MIP is the idea behind the so-called
"fixed MIP" problem. The fixed MIP is a form of the MIP problem where all of the
discrete variables are placed at values corresponding to the MIP solution; that is,
the discrete variables are fixed in the sense of set at a given value. Thus a fixed
MIP is a continuous problem though not strictly a relaxation of the MIP.
If you wish to access dual information in such a problem, first optimize your MILP
problem to create the fixed MILP problem; then re-optimize it, like this:
v In Concert Technology, call the method solveFixed . (There is no explicit
problem type in Concert Technology, so there is no need to change the problem
type as in other components.)
v In the Callable Library, call the routine CPXchgprobtype with the argument
CPXPROB_FIXEDMILP as the problem type and then call CPXlpopt .
v In the Interactive Optimizer, use these commands to change the problem type
and re-optimize:
change problem fixed_milp
optimize
As explained in Managing a MIP start with the advanced start switch on page
247, setting 2 of the advanced start switch (AdvInd in Concert Technology;
You control how information in the log file is recorded and displayed, through two
CPLEX parameters. The MIPDisplay parameter controls the general nature of the
output that goes to the node log. The table Table 51 summarizes its possible values
and their effects.
Table 51. Values of the MIP display parameter
Value Effect
0 No display until optimal solution has been found
1 Display integer feasible solutions
2 Display integer feasible solutions plus an entry for every n-th node;
default
3 Display integer feasible solutions, every n-th node entry, number of cuts
added, and information about the processing of each successful MIP start
4 Display integer feasible solutions, every n-th node entry, number of cuts
added, information about the processing of each successful MIP start, and
information about the LP subproblem at root
5 Display integer feasible solutions, every n-th node entry, number of cuts
added, information about the processing of each successful MIP start, and
information about the LP subproblem at root and at nodes
Here is an example of a log file from the Interactive Optimizer, where the
MIPInterval parameter has been set to 10:
Tried aggregator 1 time.
Presolve time = 0.00 sec. (0.00 ticks)
MIP emphasis: balance optimality and feasibility.
MIP search method: dynamic search.
Parallel mode: none, using 1 thread.
Root relaxation solution time = 0.00 sec (0.00 ticks)
Nodes Cuts/
Node Left Objective IInf Best Integer Best Bound ItCnt Gap
* 0+ 0 0.0000 3261.8212 8 ---
* 0+ 0 3148.0000 3261.8212 8 3.62%
As you can see in that example, CPLEX logs an asterisk (* ) in the left-most
column for any node where it finds an integer-feasible solution (that is, a new
incumbent). In the next column, it logs the node number. It next logs the number
of nodes left to explore.
In the next column, Objective, CPLEX either records the objective value at the
node or a reason to fathom the node. (A node is fathomed if the solution of a
subproblem at the node is infeasible; or if the value of the objective function at the
node is worse than the cutoff value for branch & cut; or if the linear programming
relaxation at the node supplies an integer solution.) This column is left blank for
lines that report that CPLEX found a new incumbent by primal heuristics. A plus
(+) after the node number distinguishes such lines.
The column labeled Cuts/Best Bound records the best objective function value
achievable. If the word Cuts appears in this column, it means various cuts were
generated; if a particular name of a cut appears, then only that kind of cut was
generated.
The column labeled ItCnt records the cumulative iteration count of the algorithm
solving the subproblems. Until a solution has been found, the column labeled Gap
is blank. If a solution has been found, the relative gap value is printed: when it is
less than 999.99 , the value is printed; otherwise, hyphens are printed. The gap is
computed as:
Consequently, the printed gap value may not always move smoothly. In particular,
there may be sharp improvements whenever a new best integer solution is found.
Moreover, if the populate procedure of the solution pool is invoked, the printed
gap value may become negative after the optimal solution has been found and
proven optimal.
CPLEX also logs its addition of cuts to a model. In the previous sample node log
file, CPLEX reports that it added a variety of cuts (cover cuts, zero-half cuts,
Gomory fractional cuts).
CPLEX also logs the number of clique inequalities in the clique table at the
beginning of optimization. Cuts generated at intermediate nodes are not logged
individually unless they happen to be generated at a node logged for other
reasons. CPLEX logs the number of applied cuts of all classes at the end.
CPLEX also shows, in the node log file, each instance of a successful application of
the node heuristic. The previous sample node log file shows that a heuristic found
a solution at node 20. The + denotes an incumbent generated by the heuristic.
Stating a MIP problem on page 217 presents a typical MIP problem. Here is the
node log file for that problem with the default setting of the MIP display
parameter and two threads:
Selected objective sense: MINIMIZE
Selected objective name: obj
Selected RHS name: rhs
Selected bound name: bnd
Nodes Cuts/
Node Left Objective IInf Best Integer Best Bound ItCnt Gap
These additional items appear only in the node log file (not on screen) in
conventional branch & cut:
v Variable records the name of the variable where CPLEX branched to create this
node. If the branch was due to a special ordered set, the name listed here will be
the right-most variable in the left subset.
v B specifies the branching direction:
D means the variables was restricted to a lower value;
U means the variable was restricted to a higher value;
L means the left subset of the special ordered set was restricted to 0 (zero);
R means the right subset of the special ordered set was restricted to 0 (zero);
A means that constraints were added or more than one variable was
restricted;
N means that cuts added to the node LP resulted in an integer feasible
solution; no branching was required;
NodeID specifies the node identifier.
v Parent specifies the NodeID of the parent.
v Depth tells the depth of this node in the branch & cut tree.
Those additional items are not applicable in dynamic search. Here is a sample
session in the Interactive Optimizer showing the log file typical in dynamic search.
In this log file, you see activity at the root node, a variety of cuts added to the
model, and accumulation of solutions in the solution pool.
Selected objective sense: MINIMIZE
Selected objective name: total_costs
Selected RHS name: rhs
Selected bound name: bnd
Nodes Cuts/
Node Left Objective IInf Best Integer Best Bound ItCnt Gap
Even the most sophisticated methods currently available to solve pure integer and
mixed integer programming problems require noticeably more computation than
the methods for similarly sized continuous problems. Many relatively small integer
programming models still take enormous amounts of computing time to solve.
Indeed, some such models have never yet been solved. In the face of these
practical obstacles to a solution, proper formulation of the model is crucial to
successful solution of pure integer or mixed integer programs.
For help in formulating a model of your own integer or mixed integer problem,
you may want to consult H.P. Williamss textbook about practical model building
(referenced in Further reading on page xx in the preface of this manual).
Also you may want to develop a better understanding of the branch & cut
algorithm. For that purpose, Williamss book offers a good introduction, and
Nemhauser and Wolseys book (also referenced in Further reading on page xx in
the preface of this manual) goes into greater depth about branch & cut as well as
other techniques implemented in the CPLEX MIP optimizer.
Tuning performance features of the mixed integer optimizer on page 225 in this
chapter has already discussed several specific features that are important for
performance tuning of difficult models. Here are more specific performance
symptoms and the remedies that can be tried.
If you observe that a very long time passes before the search begins processing
nodes, it may be that the root relaxation problem itself is taking a long time. The
standard screen display will print a line saying "Root relaxation solution time =
" after this root solve is complete, and a large solution time would be an indicator
of an opportunity for tuning. If you set the MIPDisplay parameter to 4 , you may
get a further indication of the difficulties this root solve has run into. Tuning
techniques found in Chapter 10, Solving LPs: simplex optimizers, on page 133,
Chapter 13, Solving problems with a quadratic objective (QP), on page 185, and
Chapter 14, Solving problems with quadratic constraints (QCP), on page 197 are
For some problems, CPLEX will spend a significant amount of time performing
computation at node 0, apart from solving the continuous LP, QP, or QCP root
relaxation. While this investment of time normally saves in the overall
branch & cut, it does not always do so. Time spent at node 0 can be reduced by
two parameters.
First, you can try turning off the node heuristic by setting the parameter HeurFreq
to -1 . Second, try a less expensive variable selection strategy by setting the
parameter VarSel to 4, pseudo reduced costs.
For some models, CPLEX finds an integer feasible solution early in the process and
then does not find a better one for quite a while. One possibility, of course, is that
the first feasible solution is optimal and will eventually be proven optimal. In that
case, there are no better solutions.
Solution polishing also helps you find additional solutions. A good strategy in this
respect is to let branch and cut find the first feasible solution and then let solution
polishing improve it. For instructions to apply this strategy, see Finding a first
solution to improve by polishing.
While the cuts added by CPLEX reduce the time required to solve most problems,
on occasion they can have the opposite effect. If you notice, for example, that
To turn off cuts selectively, use the cut parameters summarized in Table 47 on page
236, with a value of -1 (minus one). For example, in the Interactive Optimizer, the
following command turns off only cover cuts:
set mip cuts covers -1
To limit types of cuts generated, consider the type of cut limit parameter
(EachCutLim, CPX_PARAM_EACHCUTLIM).
To limit the number of passes that CPLEX executes to generate cuts, consider the
number of cutting plane passes parameter (CutPass, CPX_PARAM_CUTPASS).
To turn off all cuts, set the cut pass parameter to -1 (minus one). For example, in
the Interactive Optimizer, use the following command to turn off all generation of
all cuts:
set mip cuts all -1
For some models, the Best Node value in the node log changes very slowly or not
at all. The time required to solve such models can sometimes be reduced by the
variable selection strategy known as strong branching. Strong branching explores a
set of candidate branching-variables in-depth, performing a limited number of
simplex iterations to estimate the effect of branching up or down on each.
Important:
Strong branching consumes significantly more computation time per node than the
default variable selection strategy.
Other parameters to consider trying, in the case of slow movement of the Best
Node value, are nondefault levels for Probe (try the aggressive setting of 3 first,
and then reduce it if the probing step itself takes excessive time for your
purposes), and MIPEmphasis set to a value of 3.
The default relative optimality tolerance is 0.0001. At this tolerance, the final
integer solution is guaranteed to be within 0.01% of the optimal value. Of course,
many formulations of integer or mixed integer programs do not require such tight
tolerance, so requiring CPLEX to seek integer solutions that meet this tolerance in
those cases is wasted computation. If you can accept greater optimality tolerance in
your model, then you should change the parameter EpGap.
If, however, you know that the objective values of your problem are near zero,
then you should change the absolute gap because percentages of very small
numbers are less useful as optimality tolerance. Change the parameter EpAGap in
this case.
To speed up the proof of optimality, you can set objective difference parameters,
both relative and absolute. Setting these parameters helps when there are many
integer solutions with similar objective values. For example, setting the ObjDif
parameter to 100.0 makes CPLEX skip any potential solution with its objective
value within 100.0 units of the best integer solution so far. Or, setting the
RelObjDif to 0.01 would mean that CPLEX would skip any potential new solution
that is not at least 1% better than the incumbent solution. Naturally, since this
objective difference setting may make CPLEX skip an interval where the true
integer optimum may be found, the objective difference setting weakens the
guarantee of optimality.
Cutoff parameters can also be helpful in restricting the search for optimality, for
example, when you already know solutions that have an objective value worse
than a given value, or when you know that there are solutions within a certain
distance of the initial relaxation of your problem. In such a case, you can readily
set the upper cutoff parameter for minimization problems and the lower cutoff
parameter for maximization problems. Set the parameters CutUp and CutLo ,
respectively, to establish a cutoff value.
When you set a MIP cutoff value, CPLEX searches with the same solution strategy
as though it had already found an integer solution, using a node selection strategy
that differs from the one it uses before a first solution has been found.
When you set that parameter to report kappa statistics about your model, CPLEX
tells you whether it encountered suspicious, unstable, or ill-posed bases while
solving your model. For a definition of the range of condition numbers for
suspicious, unstable, and ill-posed bases, see the documentation of these symbols
in the reference manuals of the APIs:
v CPX_KAPPA_SUSPICIOUS or KappaSuspicious reports the percentage of numerically
suspicious simplex bases (condition number kappa between 1e+7 and 1e+10)
among simplex bases encountered during a MIP solve.
v CPX_KAPPA_UNSTABLE or KappaUnstable reports the percentage of numerically
unstable simplex bases (condition number kappa between 1e+10 and 1e+14)
among simplex bases encountered during a MIP solve.
v CPX_KAPPA_ILLPOSED or KappaIllposed reports the percentage of numerically
ill-posed simplex bases (condition number greater than 1e+14) among simplex
bases encountered during a MIP solve.
Here is a sample from a log file showing results when the parameter
CPX_PARAM_MIPKAPPASTATS=2 tells CPLEX to compute MIP kappa for all LP
subproblems:
MILP objective -2.1989035553e+06
MILP solution norm |x| (Total, Max) 1.95445e+10 1.68134e+08
MILP solution error (Ax=b) (Total, Max) 3.90105e+05 8.14760e+03
MILP x bound error (Total, Max) 0.00000e+00 0.00000e+00
MILP x integrality error (Total, Max) 1.96296e-06 9.81482e-07
MILP slack bound error (Total, Max) 8.27493e-08 2.95847e-09
You should reconsider your model if CPLEX reports any ill-posed bases or more
than 5% unstable bases.
In a report like that sample, consider the solution norm values. If the report shows
large solution norm values, check the lower order decimal places for round off
error. If the round off error exceeds the tolerances set for this optimization, you can
encounter inconsistent results and other numerical difficulties.
For example, that sample report of solution quality shows that the maximum
individual x value is on the order of 1e+8. Conventional machine precision is
1e-16; that is, there are 16 digits of accuracy; that is, values beyond the eighth
decimal place are likely to arise from round-off error. CPLEX uses default
tolerances for feasibility and optimality of 1e-6 implying that the solution in the
Also consider the solution error in the report. The solution error is a vector of
residuals on the constraints; that is:
b - Ax
The Max solution error is the maximum individual absolute residual element in
that vector. The Total solution error is the sum of the absolute values of all
elements in that vector. In that sample report, both the maximum and total
solution error are quite significant.
To detect the source of the large residuals and to assess their importance, start by
looking for constraints with large right hand side values where a modest relative
violation in the constraint results in a large absolute violation. Then, examine the
nonzero matrix and associated variable values for such constraints. This procedure
suggests the cause of the large residuals. Possible remedies include rescaling
constraints with extremely large coefficients or reformulating parts of the model to
reduce large solution values.
Even a small percentage of ill-posed bases in the MIP kappa statistics is cause for
concern about the model. Likewise, a significant percentage of unstable bases raises
concern about the model. For practical suggestions about how to address such
concerns, see the popular CPLEX Tech Note Diagnosing ill conditioning at your IBM
Support Portal: http://www-304.ibm.com/support/docview.wss?uid=swg21399993
On some models, the integer solution returned by CPLEX at default settings may
contain solution values for the discrete variables that violate integrality by a small
amount. The integrality tolerance parameter (EpInt, CPX_PARAM_EPINT) has a default
value of 1e-5, which means that any discrete variable that violates integrality by no
more than this amount will not be branched upon for resolution. For most model
formulations, this situation is satisfactory and avoids branching that may be
essentially meaningless, only consuming additional computing time.
Tip:
If this adjustment is insufficient to give satisfactory results, you can also try setting
EpInt all the way to zero, preferably in conjunction with a tightened EpRHS setting.
This very tight integrality tolerance directs CPLEX to attempt to branch on any
integer infeasibility, no matter how small. Numeric round-off due to floating-point
arithmetic on any computer may make it impossible to achieve this tolerance, but
in practice, the setting achieves its aim in many models and reduces the integrality
violations in many others. In cases where the integrality violation even after
branching remains above EpInt or is above 1e-10 when EpInt has been set to a
value smaller than that, a solution status returned will be
CPX_STAT_OPTIMAL_INFEAS instead of the usual CPX_STAT_OPTIMAL. In most cases a
solution with status CPX_STAT_OPTIMAL_INFEAS will be satisfactory, and reflects only
round-off error after presolve uncrush, but extra care in using the solution may be
advisable in numerically sensitive formulations.
If these suggestions are not appropriate for your problem, another alternative to
consider is reformulation of your model with indicator constraints. Chapter 21,
Indicator constraints in optimization, on page 321 offers more information about
that alternative.
A very common difficulty with MIPs is running out of memory. This problem
almost always occurs when the branch & cut tree becomes so large that
insufficient memory remains to solve a continuous LP, QP, or QCP subproblem. (In
the rare case that the dimensions of a very large model are themselves the main
contributor to memory consumption, you can try adjusting the memory emphasis
parameter, as described in Lack of memory on page 144.) As memory gets tight,
you may observe warning messages from CPLEX as it attempts various operations
in spite of limited memory. In such a situation, if CPLEX does not find a solution
shortly, it terminates the process with an error message.
Certainly, if you increase the amount of available memory, you extend the
problem-solving capability of CPLEX. Unfortunately, when a problem fails because
of insufficient memory, it is difficult to project how much further the process
needed to go and how much more memory is needed to solve the problem. For
these reasons, the following suggestions aim at avoiding memory failure whenever
possible and recovering gracefully otherwise.
To avoid a failure due to running out of memory, set the working memory
parameter, WorkMem, to a value significantly lower than the available memory on
your computer (in megabytes), to instruct CPLEX to begin compressing the storage
of nodes before it consumes all of available memory. See the related topic Use
node files for storage, for other choices of what should happen when WorkMem is
exceeded. That topic explains how to tell CPLEX that it should use disk for
working storage.
Because the storage of nodes can require a lot of space, it may also be advisable to
set a tree limit on the size of the entire tree being stored so that not all of your disk
will be filled up with working storage. The call to the MIP optimizer will be
stopped after the size of the tree exceeds the value of TreLim, the tree limit
parameter. At default settings, the limit is infinity (1e+75), but you can set it to a
lower value (in megabytes).
This feature is especially helpful when you are using steepest-edge pricing as the
subproblem simplex pricing strategy because pricing information itself consumes a
great deal of memory.
Note:
Try node files whenever the MIP optimizer terminates with the condition "out of
memory" and the node count is greater than zero. The message in such a situation
looks like the following sample output.
Clique cuts applied: 30
CPLEX Error 1001: Out of memory.
Consider using CPLEX node files to reduce memory usage.
MIP-Error termination, integer feasible: Objective = 5.6297000000e+04
Current MIP best bound = 5.5731783224e+04 (gap = 565.217, 1.00%)
CPLEX uses node file storage most effectively when the amount of working
memory is reasonably large so that it does not have to create node files too
frequently. The default value of the WorkMem parameter is 2048 megabytes (that is, 2
gigabytes). Setting it to higher values, even on a machine with very large memory,
can be expected to result in only marginally improved efficiency. However, if your
machine has less than 2 gigabytes of RAM, it is advisable to reduce this setting by
at least 128 MB to avoid defeating the purpose of node files, a situation that would
occur if your application inadvertently triggers the swap space of your operating
system.
When tree storage size exceeds the limit defined by WorkMem, and if the
tree-memory limit has not been exceeded, then what happens next is decided by
the setting of the node storage file switch (NodeFileInd in Concert Technology or
CPX_PARAM_NODEFILEIND in the Callable Library). If that parameter is set to zero,
then optimization proceeds with the tree stored in memory until CPLEX reaches
the tree memory limit (TreLim in Concert Technology or CPX_PARAM_TRELIM in the
Callable Library). If the node file indicator is set to 1 (the default), then a fast
compression algorithm is used on the nodes to try to conserve memory, without
resorting to writing the node files to disk. If the parameter is set to 2, then node
files are written to disk. If the parameter is set to 3, then nodes are both
compressed (as in option 1) and written to disk (as in option 2). Table 52
summarizes these different options.
Table 52. Parameter values for node file storage
Value Meaning Comments
0 no node files optimization continues
1 node file in memory and optimization continues
compressed (default)
2 node file on disk files created in temporary
directory
3 node file on disk and files created in temporary
compressed directory
Among the memory conservation tactics employed by CPLEX when the memory
emphasis parameter has been set, the maximum setting for the node file indicator
is automatically chosen, so that node-file storage will go to disk. You may still
wish to adjust the working memory or tree limit parameters to fit the capabilities
of your computer.
In cases where node files are written to disk, CPLEX will create a temporary
subdirectory under the directory specified by the directory for working files
parameter (WorkDir in Concert Technology or CPX_PARAM_WORKDIR in the Callable
CPLEX creates the temporary directory by means of system calls. If the system
environment variable is set (on Windows platforms, the environment variable TMP;
on UNIX platforms, the environment variable TMPDIR), then the system ignores the
CPLEX node-file directory parameter and creates the temporary node-file directory
in the location indicated by its system environment variable. Furthermore, if the
directory specified in the CPLEX node-file directory parameter is invalid (for
example, if it contains illegal characters, or if the directory does not allow write
access), then the system chooses a location according to its own logic.
The temporary directory created for node file storage will have a name prefixed by
cpx. The files within it will also have names prefixed by cpx.
CPLEX automatically removes the files and their temporary directory when it frees
the branch & cut tree:
v in the Interactive Optimizer,
at problem modification;
at normal termination;
v from Concert Technology,
when you call env.end
when you modify the extracted model
v from the Callable Library,
when you call a problem modification routine;
when you call CPXfreeprob .
Node files could grow very large. Use the tree memory limit parameter (TreLim,
CPX_PARAM_TRELIM) to limit the size of the tree so that it does not exceed available
disk space, when you choose settings 2 or 3 in the node storage file switch
(NodeFileInd, CPX_PARAM_NODEFILEIND). It is usually better to let CPLEX terminate
the run gracefully, with whatever current feasible solution has been found, than to
trigger an error message or even a program abort.
When CPLEX uses node-file storage, the sequence of nodes processed may differ
from the sequence in which nodes are processed without node-file storage. Nodes
in node-file storage are not accessible to user-written callback routines.
Change algorithms
The best approach to reduce memory use is to modify the solution process. Here
are some ways to do so:
v Switch the node selection strategy to best estimate, or more drastically to
depth-first, as explained in Table 44 on page 228. Depth-first search rarely
generates a long, memory-consuming list of unexplored nodes since CPLEX
dives deeply into the tree instead of jumping around. A narrowly focused
search, like depth-first, also often results in faster processing times for individual
nodes. However, overall solution time is generally much worse than with
best-bound node selection because each branch is searched exhaustively to its
deepest level before it is fathomed in favor of better branches.
There are classes of MIPs that produce very difficult subproblems, for example, if
the subproblems are dual degenerate. In such a case, a different optimizer, such as
the primal simplex or the barrier optimizer, may be better suited to your problem
than the default dual simplex optimizer for subproblems. These alternatives are
discussed in Unsatisfactory optimization of subproblems. You may also consider
a stronger dual simplex pricing algorithm, such as dual steepest-edge pricing (that
is, the parameter DPriInd or CPX_PARAM_DPRIIND set to the value 2).
If the subproblems are dual degenerate, then consider using the primal simplex
optimizer for the subproblems. You make this change by setting the MIP
subproblem algorithm parameter (NodeAlg, CPX_PARAM_SUBALG) to 1 (one).
QCP subproblems are solved only by the barrier optimizer. However, MIQCP
models are not always solved by a sequence of QCP subproblems. The MIQCP
strategy switch (MIQCPStrat, CPX_PARAM_MIQCPSTRAT) allows you to control what
kinds of subproblems are solved in a mixed integer quadratically constrained
programming model. Consequently, the following suggestions may also help that
class of problem as well.
You can control which algorithm CPLEX applies to the root relaxation of your
problem separately from your control of which algorithm CPLEX applies to other
subproblems. The following sections explain those parameters more fully.
The RootAlg algorithm parameter indicates the algorithm for CPLEX to use on the
initial subproblem. In a typical MIP, that initial subproblem is usually the linear
relaxation of the original MIP. By default, CPLEX starts the initial subproblem with
the dual simplex optimizer. You may have information about your problem that
suggests that another optimizer could be more efficient. Table 53 on page 271
summarizes the values available for the RootAlg parameter.
The NodeAlg parameter indicates the algorithm for CPLEX to use on node
relaxations other than the root node. By default, CPLEX applies the dual simplex
optimizer to subproblems, and unlike the RootAlg parameter it is extremely
unusual for this to not be the most desirable choice, but again, you may have
information about your problem that tells you another optimizer could be more
efficient. The values and symbolic constants are the same for the NodeAlg
parameter as for the RootAlg parameter in Table 53.
Note:
Only simplex and barrier optimizers can solve problems of type QP (quadratic
term in the objective function).
Only the barrier optimizer can solve problems of type QCP (quadratic terms
among the constraints).
When subproblems are not solved satisfactorily, another option is to solve as few
subproblems of the original model as possible by switching to solution polishing as
soon as a first feasible solution is found. This strategy is helpful when finding a
good integer solution is more important than proving optimality. For more
information about this strategy, see Solution polishing on page 238 in this
manual.
ilomipex1.cpp
Shows how to optimize a MIP model in the C++ API.
The example derives from ilolpex8.cpp. Here are the differences between that
linear program and this mixed integer program:
v The problem to solve is slightly different. It appears in Stating a MIP problem
on page 217.
v The routine populatebyrow added the variables, objective, and constraints to the
model created by the method IloModel model(env).
MIPex1.java
Shows how to optimize a MIP model in the Java API.
Also available among the examples distributed with the product is a Java
implementation of the same MIP.
Also available among the examples distributed with the product are a C#.NET and
a Visual Basic.NET implementation of the same MIP.
mipex1.c
Shows how to optimize a MIP model in the C API.
The example derives from lpex8.c. Here are the differences between that linear
program and this mixed integer program:
v The problem to solve is slightly different. It appears in Stating a MIP problem
on page 217.
v The routine setproblemdata has an argument, ctype, to set the types of the
variables to indicate which ones must assume integer values. The routine
CPXcopyctype associates this data with the problem that CPXcreateprob creates.
v The example calls CPXmipopt to optimize the problem, rather than CPXlpopt.
v The example calls the routines CPXgetstat, CPXgetobjval, CPXgetx, and
CPXgetslack (instead of CPXsolution ) to get a solution.
You do not get dual variables this way. If you want dual variables, you must do
the following:
ilomipex2.cpp
Shows how to read data from a file and solve a MIP model in the C++ API.
Like other applications based on CPLEX Concert Technology, this one uses env, an
instance of IloEnv, to initialize the Concert Technology environment and
IloModel model(env) to create a problem object. Before the application ends, it calls
env.end to free the environment.
mipex2.c
Shows how to read data from a file and solve a MIP model in the C API.
The example derives from lpex2.c, an LP example explained in the manual Getting
Started. That LP example differs from this MIP example in these ways:
v This example solves only MIPs, so it calls only CPXmipopt, and its command line
does not require the user to specify an optimizer.
v This example does not generate or print a basis.
Like other applications based on the Callable Library, this one calls CPXopenCPLEX
to initialize the CPLEX environment; it sets the screen-indicator parameter to direct
output to the screen and calls CPXcreateprob to create a problem object. Before it
ends, it calls CPXfreeprob to free the space allocated to the problem object and
CPXcloseCPLEX to free the environment.
As introduced in the topic Stating a MIP problem on page 217, a mixed integer
programming (MIP) problem can contain both integer and continuous variables. If
the problem contains an objective function with no quadratic term, (a linear
objective), then the problem is termed a Mixed Integer Linear Program (MILP).
If the model has any constraints containing a quadratic term, regardless of the
objective function, the problem is termed a Mixed Integer Quadratically Constrained
Program (MIQCP). For more information about solving a MIQCP, see the topic
MIQCP: mixed integer programs with quadratic terms in the constraints on page
276.
This topic explores MIQP further and documents the features of CPLEX that solve
MIQP problems.
The topic Distinguishing between convex and nonconvex QPs on page 185
clarified the difference between convex and nonconvex quadratic programs (QP).
That same distinction is relevant to MIQP problems as well. By default, CPLEX can
solve MIQPs where the restriction of the problem to its continuous and general
integer variables is a convex quadratic program (QP). If this assumption is not
satisfied, CPLEX will return the error CPXERR_Q_NOT_POS_DEF.
To change this default behavior and thus possibly solve a nonconvex MIQP,
CPLEX offers the parameter optimality target. This parameter can instruct CPLEX
to search for a globally or locally optimal solution.
For an example of setting this parameter and optimizing to find a globally optimal
solution, consider one of these samples delivered with the product:
v xglobalmiqpex1.c in the Callable Library (C API) 64-bit
v globalmiqpex1.c in the Callable Library (C API) 32-bit
v iloglobalqpex1.cpp in the C++ API
v GlobalQPex1.java in the Java API
v GlobalQPex1.cs in the C#.NET API
v globalqpex1.py in the Python API
v GlobalQPex1.vb in the Visual Basic.NET API
v globalqpex1.m in the MATLAB connector
When your application is solving a nonconvex MIQP, you can also control how
CPLEX applies BQP cuts, based on the Boolean Quadric Polytope of your model.
For more detail about BQP cuts, see Boolean Quadric Polytope cuts in the CPLEX
Parameters Reference Manual. The topic Boolean Quadric Polytope (BQP) cuts on
page 231 in the CPLEX User's Manual offers a brief definition of BQP cuts and a
bibliographic reference about Boolean Quadric Polytopes for further reading.
Similarly, you can instruct CPLEX to search for a locally optimal solution,
satisfying first-order optimality conditions, by setting the solution target type to
the value 2 (CPX_OPTIMALITYTARGET_FIRSTORDER). Then optimize as usual.
Perhaps you are familiar with the idea that to solve a MILP, CPLEX relaxes the
original problem within bounds and solves the relaxation. Similarly, when you
instruct CPLEX to solve a nonconvex MIQP to global optimality, CPLEX uses a
variety of techniques (as discussed in the theoretical literature) to relax and solve
the original problem. However, in the case of a nonconvex MIQP, it is possible that
a given relaxation of the original problem is not bounded. In such a case, CPLEX
terminates optimization and returns the status
CPXMIP_ABORT_RELAXATION_UNBOUNDED.
As introduced in the topic Stating a MIP problem on page 217, a mixed integer
programming (MIP) problem can contain both integer and continuous variables. If
the problem contains an objective function with no quadratic term, (a linear
objective), then the problem is termed a Mixed Integer Linear Program (MILP).
If there is a quadratic term in the objective function and all the constraints in the
model are linear, the problem is termed a Mixed Integer Quadratic Program (MIQP).
(For more information about solving a MIQP, see the topic MIQP: mixed integer
programs with quadratic terms in the objective function on page 275.) If the
This topic explores MIQCP further and specifies the features of MIQCP problems
that CPLEX solves.
If these assumptions about the objective and about the constraints are not satisfied,
CPLEX will return the error CPXERR_Q_NOT_POS_DEF.
CPLEX solves most MIQCP models roughly in the same way as it solves other
mixed integer programs (MIP). However, CPLEX offers algorithmic options
adapted to the nonlinearity of the quadratic constraints in a MIQCP model. Indeed,
CPLEX offers two main search strategies to solve a MIQCP model.
v One strategy exploits a branch and bound search tree based on the continuous
QCP solver. For more information about this strategy, see Chapter 14, Solving
problems with quadratic constraints (QCP), on page 197.
Chapter 16. Solving mixed integer programming problems with quadratic terms 277
v Another strategy exploits outer approximation relying on the continuous LP
solver to build a search tree.
To choose between these two strategies, the user sets the MIQCP strategy switch,
as documented in the reference manual Parameters of CPLEX.
In the branch and bound strategy based on the QCP solver, CPLEX develops a
search tree and solves the continuous relaxation of the model by the SOCP solver
at each node of the tree. That is, at each node of the tree, all quadratic constraints
are satisfied.
By default, CPLEX tries to choose the best of the two strategies. However, you may
have knowledge of your model that implies that one of the two strategies is more
appropriate. In that case, you can choose the strategy manually to yield better
results.
Usually, the branch and bound strategy based on the QCP solver has the
advantage of obtaining tighter bounds at the nodes; these tighter bounds at the
nodes can cost much more time to process each node.
In contrast, the outer approximation strategy based on the LP solver has the
advantage of processing nodes faster. To do so, it uses more of the underlying MIP
infrastructure, but as a consequence, the relaxations in this strategy can be weaker.
All the cuts available in CPLEX to solve a mixed integer program (MIP) are also
available to solve a MIQCP. For more about MIP cuts, see Cuts on page 231 in
this manual.
Lift-and-project cuts on page 233 can be useful in solving some MIQCP models.
See documentation of the parameter Lift-and-project cuts switch for MIP and
MIQCP in the CPLEX Parameters Reference Manual.
The solution pool allows you to generate and store multiple solutions to a mixed
integer programming (MIP) model. This feature uses an extension of the IBM ILOG
CPLEX branch-and-cut algorithm to generate multiple solutions in addition to the
optimal solution.
Furthermore, you can use the solution pool and tools associated with it to explore
and evaluate alternative solutions in a variety of ways:
v You can collect solutions within a given percentage of the optimal solution. To
do so, apply the solution pool gap parameters (relative or absolute), as explained
in Parameters of the solution pool on page 295.
v You can collect a set of diverse solutions. To do so, use the solution pool
replacement strategy parameter (SolnPoolReplace, CPX_PARAM_SOLNPOOLREPLACE)
to set the solution pool replacement strategy to CPX_SOLNPOOL_DIV , as explained
in the documentation of that parameter in the CPLEX Parameters Reference
Manual. In order to control the diversity of solutions even more finely, apply a
diversity filter, as explained in Diversity filters on page 298.
v In an advanced application of this feature, you can collect solutions with specific
properties. To do so, see Filtering the solution pool on page 297
v You can collect all solutions or all optimal solutions to a MIP model. To do so,
see Enumerating all solutions on page 288.
Tip:
The solution pool distinguishes solutions by the values of their discrete variables
only. For more explanation of this point, see Limitations due to continuous
variables and finite precision on page 289
Each region needs a specified amount of goods, and each warehouse can store only
a limited quantity of goods (constraints c4 to c7). In addition, each region must be
served by exactly one warehouse (constraints c8 to c16). Constraints c17 to c52
complete the model by stating that warehouse i must be open in order for goods
to be shipped from warehouse i to any region j.
The model for this simple facility location problem is available online in the
formatted LP file yourCPLEXhome/examples/data/location.lp . In standard form,
a model for the simple facility location problem looks like this:
Minimize
obj: cost
Subject To
c1: - cost + fixed + transport = 0
c2: - fixed + 130 x1 + 150 x2 + 170 x3 + 180 x4 = 0
c3: - transport
+ 10 y11 + 30 y12 + 25 y13 + 55 y14
+ 10 y21 + 25 y22 + 25 y23 + 45 y24
+ 20 y31 + 23 y32 + 30 y33 + 40 y34
+ 25 y41 + 10 y42 + 26 y43 + 40 y44
+ 28 y51 + 12 y52 + 20 y53 + 29 y54
+ 36 y61 + 19 y62 + 16 y63 + 22 y64
+ 40 y71 + 39 y72 + 22 y73 + 27 y74
+ 75 y81 + 65 y82 + 55 y83 + 35 y84
+ 34 y91 + 43 y92 + 41 y93 + 62 y94 = 0
c4: 10 y11 + 10 y21 + 12 y31 + 15 y41 + 15 y51 + 15 y61 + 20 y71 + 25 y81 + 30 y91 - 90 x1 <= 0
c5: 10 y12 + 10 y22 + 12 y32 + 15 y42 + 15 y52 + 15 y62 + 20 y72 + 25 y82 + 30 y92 - 110 x2 <= 0
c6: 10 y13 + 10 y23 + 12 y33 + 15 y43 + 15 y53 + 15 y63 + 20 y73 + 25 y83 + 30 y93 - 130 x3 <= 0
c7: 10 y14 + 10 y24 + 12 y34 + 15 y44 + 15 y54 + 15 y64 + 20 y74 + 25 y84 + 30 y94 - 150 x4 <= 0
c8: y11 + y12 + y13 + y14 = 1
c9: y21 + y22 + y23 + y24 = 1
c10: y31 + y32 + y33 + y34 = 1
c11: y41 + y42 + y43 + y44 = 1
c12: y51 + y52 + y53 + y54 = 1
c13: y61 + y62 + y63 + y64 = 1
c14: y71 + y72 + y73 + y74 = 1
c15: y81 + y82 + y83 + y84 = 1
c16: y91 + y92 + y93 + y94 = 1
c17: x1 - y11 >= 0
c18: x1 - y21 >= 0
c19: x1 - y31 >= 0
c20: x1 - y41 >= 0
c21: x1 - y51 >= 0
c22: x1 - y61 >= 0
c23: x1 - y71 >= 0
c24: x1 - y81 >= 0
c25: x1 - y91 >= 0
c26: x2 - y12 >= 0
c27: x2 - y22 >= 0
c28: x2 - y32 >= 0
c29: x2 - y42 >= 0
c30: x2 - y52 >= 0
There are two ways to fill the solution pool associated with a model:
v You can accumulate successive incumbents, as explained in Accumulating
incumbents in the solution pool.
v You can generate alternative solutions, as explained in Populating the solution
pool on page 282.
Other details about filling and managing the solution pool are explained in
Deleting solutions from the solution pool on page 294 and Model changes and
the solution pool on page 290.
MIP optimization automatically adds incumbents to the solution pool as they are
discovered when you call it in one of these conventional ways:
v In Concert Technology, you invoke MIP optimization by means of one of these
methods:
IloCplex::solve in the C++ API.
IloCplex.solve in the Java API.
Cplex.Solve in the .NET API.
v In the Callable Library (C API), you invoke the MIP optimizer by means of the
routine CPXmipopt .
Chapter 17. Solution pool: generating and keeping multiple solutions 281
v In the Interactive Optimizer, you invoke the command mipopt.
For example, if you read the model of Example: simple facility location problem
on page 279 into the Interactive Optimizer and invoke the usual command mipopt ,
MIP optimization finds solutions that it stores in the solution pool, and the log
looks something like this (allowing for variation in the MIP path):
Solution pool: 4 solutions saved.
The following topics tell you more about the populate procedure.
Briefly, the algorithm that populates the solution pool works in two phases.
In the first phase, it solves the model to optimality (or some stopping criterion set
by the user), but it retains nodes that might contain useful, even if not necessarily
optimal integer feasible solutionsnodes that would be pruned by the optimality
The amount of preparation in the first phase and the intensity of exploration in the
second phase are controlled by the solution pool intensity parameter:
v SolnPoolIntensity in Concert Technology;
v CPX_PARAM_SOLNPOOLINTENSITY in the Callable Library;
v mip pool intensity in the Interactive Optimizer.
After a model has been read (or created), the first call to populate will carry out
both the first and second phase. In the general case, subsequent calls to populate
will re-use stored information and proceed with the continuation of the second
phase. The first phase will be re-computed if:
v the value of the pool intensity parameter has increased between successive calls
of populate;
v any filters have been deleted.
The details of the algorithm that populates the solution pool are published in the
paper titled "Generating Multiple Solutions for Mixed Integer Programming
Problems," by Emilie Danna, Mary Fenelon, Zonghao Gu, and Roland Wunderling,
in the Proceedings of the Twelfth Conference on Integer Programming and
Combinatorial Optimization (IPCO 2007), LNCS 4513, pages 280 - 294.
You can generate multiple solutions with populate. To see this effect in the
Interactive Optimizer, first read the example cited in Example: simple facility
location problem on page 279, like this:
read location.lp
populate
At default settings in the Interactive Optimizer, you will see results such as these:
Populate: phase I
Tried aggregator 1 time.
MIP Presolve eliminated 3 rows and 3 columns.
MIP Presolve modified 47 coefficients.
Reduced MIP has 49 rows, 40 columns, and 148 nonzeros.
Presolve time = 0.01 sec.
Clique table members: 45.
MIP emphasis: balance optimality and feasibility.
Root relaxation solution time = 0.04 sec.
Nodes Cuts/
Node Left Objective IInf Best Integer Best Node ItCnt Gap
0 0 452.1107 27 452.1107 51
* 0+ 0 549.0000 452.1107 51 17.65%
0 0 468.2224 23 549.0000 Cuts: 17 64 14.71%
* 0+ 0 512.0000 468.2224 64 8.55%
Chapter 17. Solution pool: generating and keeping multiple solutions 283
0 0 470.5942 23 512.0000 Cuts: 2 68 8.09%
0 0 470.6800 20 512.0000 Cuts: 3 70 8.07%
0 2 470.6800 20 512.0000 470.6800 70 8.07%
* 10 6 integral 0 499.0000 479.9271 129 3.82%
Cover cuts applied: 2
Zero-half cuts applied: 2
Gomory fractional cuts applied: 1
Populate: phase II
MIP emphasis: balance optimality and feasibility.
100 26 infeasible 499.0000 500.0000 234 -0.20%
In that log, you see that the procedure executed its first and second phases. It
reports parameter settings, such as MIP emphasis, like other optimization logs. It
also reports how many solutions it found. It stops when it reaches the populate
limit. (In this example, the populate limit rests at its default, 20 solutions.)
Interestingly, the gap printed in that log becomes negative in the second phase of
populate. At the end of the first phase of populate, the model was solved to
optimality; the best node value and the best integer value coincided and were
equal to the optimal objective value; the gap was zero. Early in the second phase,
the best integer value remained equal to the optimal objective value, but as
populate progressed, nodes were explored and fathomed. At some point, all nodes
with a relaxation value equal to the optimal objective value were fathomed. This
fathoming explains why the best node value increased above the optimal objective
value (for a minimization problem, such as this example) as the search space was
explored in the second phase. Recall that the gap value is computed as:
(bestInteger-bestBound)*objSense/(abs(bestInteger)+1e-10)
Consequently, the gap can become negative. A negative gap value ( -g% ) indicates
that the search space explored by populate does not contain any more solutions
that are less than g% worse than the optimal objective value.
You can invoke the populate procedure multiple times. In successive invocations, it
will re-use information it has accumulated in previous invocations. For example, if
you then immediately invoke populate a second time on this model, it re-uses the
information it gathered in the previous invocation to resume its second phase, like
this:
CPLEX> populate
Populate: phase II
MIP emphasis: balance optimality and feasibility.
200 32 infeasible 499.0000 512.0000 268 -2.61%
300 38 infeasible 499.0000 514.0000 282 -3.01%
400 44 516.0000 1 499.0000 516.0000 295 -3.41%
500 48 518.0000 1 499.0000 518.0000 312 -3.81%
In this second invocation, populate does not disturb the twenty solutions already
accumulated in the solution pool, and it continues to search for another twenty
solutions before stopping at its default limit again.
The status line of both invocations of populate indicates that the optimal solution
of the model has been found. Nevertheless, populate continues to produce
solutions: optimality is not the stopping criterion for populating the solution pool.
For more detail about stopping criteria, see Stopping criteria for the populate
procedure.
Optimality is not a stopping criterion for the populate procedure. Even if the
optimality gap is zero, this procedure will still try to find alternative solutions. The
stopping criteria for populating the solution pool are these:
v Populate limit. This parameter controls how many solutions are generated before
the populate procedure stops. Its default value is 20. Consequently, the
procedure stopped after generating 20 solutions in the example with model
location.lp in Example: calling populate on page 283.
maximum number of solutions generated for solution pool by populate
PopulateLim in Concert Technology
CPX_PARAM_POPULATELIM in the Callable Library (C API)
mip limits populate in the Interactive Optimizer
Note:
The parameter to limit the number of integer solutions in a MIP (MIP integer
solution-file switch and prefix: IntSolLim in Concert Technology,
CPX_PARAM_INTSOLLIM in the Callable Library, or mip limits solutions in the
Interactive Optimizer) does not apply to the populate procedure; if you want
to limit the populate procedure, apply the populate limit parameter
(PopulateLim, CPX_PARAM_POPULATELIM) instead.
v optimizer time limit in seconds, as in a standard MIP optimization.
TiLim in Concert Technology
CPX_PARAM_TILIM in the Callable Library (C API)
timelimit in the Interactive Optimizer
v MIP node limit, as in a standard MIP optimization.
NodeLim in Concert Technology
CPX_PARAM_NODELIM in the Callable Library (C API)
mip limit nodes in the Interactive Optimizer
v In the absence of other stopping criteria, the populate procedure stops when it
cannot enumerate any more solutions.
In particular, if you specify an objective tolerance with the relative or absolute
solution pool gap parameters, populate stops if it cannot enumerate any more
solutions within the specified objective tolerance.
However, there may exist additional solutions that are feasible, and if you have
specified an objective tolerance, those feasible solutions may also satisfy this
Chapter 17. Solution pool: generating and keeping multiple solutions 285
additional criterion. Depending on the solution pool intensity parameter,
populate may or may not enumerate all possible solutions. Consequently,
populate may stop when it has enumerated only a subset of the solutions
satisfying your criteria.
By default, CPLEX stores all solutions generated by populate in the solution pool.
However, the user may not be interested in examining every generated solution.
For that reason, the user may set a maximum number of solutions kept in solution
pool using the solution pool capacity parameter: SolnPoolCapacity,
CPX_PARAM_SOLNPOOLCAPACITY to specify a finite maximum number of solutions to
store in the solution pool. Only solutions stored in the pool are available for
examination when populate terminates.
As explained in the topic Stopping criteria for the populate procedure on page
285, the populate limit provides a stopping criterion for populate. In other words,
it is a criterion that governs the algorithm. In contrast, the solution pool capacity
(SolnPoolCapacity, CPX_PARAM_SOLNPOOLCAPACITY) does not control the algorithm
associated with populate at all. Instead, the solution pool capacity, along with the
solution pool replacement strategy parameter (SolnPoolReplace,
CPX_PARAM_SOLNPOOLREPLACE), controls how CPLEX manages the solutions
generated by populate.
Both MIP optimization and populate generate a series of solutions, but the two
procedures differ in their aims. The aim of MIP optimization is optimality: after a
solution has been found, MIP optimization prunes nodes unless they yield
solutions of improving objective value, and the procedure will stop when
optimality has been proven. In contrast, the aim of populate is to generate as many
solutions as possible: after a solution has been found, populate may generate
solutions of both improving and degrading objective value because it has retained
nodes that may contain such solutions. It will stop only when it cannot generate
any additional solutions or because other stopping criteria intervene.
In order to decide which procedure is better for your application, you should first
try the MIP optimizer. If the solutions produced are sufficient for your application,
then the MIP optimizer is the appropriate choice. If not, then you should try
populate to generate more solutions and to have more control over the properties
of the generated solutions.
Should you call MIP optimization and then populate, or should you call populate
alone?
You can call populate after you call the MIP optimizer, or you can call populate on
its own after you read or create a model. In order to decide which to do, you need
to know more about the two procedures.
Recall that the algorithm underlying populate works in two phases. If you call the
MIP optimizer after the model is read, it will gather and store information about
the search as it solves the model. In practice, its activity constitutes the first phase
of the populate algorithm. In the general case, if you then call populate, populate
will re-use the information stored by the MIP optimizer and carry out only the
second phase.
In contrast, if you call populate immediately after the model is read, populate will
perform both the first phase and the second phase.
If you specify a nondefault setting of the pool intensity parameter, then calling the
MIP optimizer and afterwards calling populate will give the same results in terms
of performance and solutions generated as calling populate alone. (The exception
to this generalization occurs when the pool intensity parameter is set at its default
value, 0 (zero) that is, automatic. For details about that case, see the documentation
of the solution pool intensity parameter.)
Calling populate alone is simpler than calling populate after MIP optimization.
However, if you want more control over the details of the two phases (for
example, if you want to specify different stopping criteria for each phase), then
you need to call MIP optimization followed by populate, instead of calling
populate alone. The risk associated with this approach is that populate might not
be able to reuse the information about the tree from the previous MIP
optimization; in that case, populate will start from scratch; that is, it again
performs the first phase, followed by the second phase. In particular, this repetition
of the first phase will happen if you increase the pool intensity parameter between
the call to MIP optimization and the call to populate.
More information about this topic can be found in the documentation about the
solution pool intensity parameter (SolnPoolIntensity,
CPX_PARAM_SOLNPOOLINTENSITY) in the CPLEX Parameters Reference Manual.
In short, if you want the simplicity of a black box, call populate alone; if you need
more control, call MIP optimization, then populate.
After invoking MIP Optimization, you can generate additional solutions with
populate. You can use this possibility to get a few additional solutions quickly if
the solutions obtained during MIP Optimization are not satisfactory. However, as
explained in Advanced use: interaction of MIP optimization and populate, the
sequence MIP Optimization followed by populate is especially useful to control the
parameters and the stopping criteria of each phase of populate.
Chapter 17. Solution pool: generating and keeping multiple solutions 287
Consider again the model in Example: simple facility location problem on page
279. Suppose that the transportations costs are subject to fluctuations, and
consequently it does not make sense to spend time optimizing the model exactly to
optimality. You can set the MIP gap tolerance (absolute MIP gap tolerance: EpAGap,
CPX_PARAM_EPAGAP; relative MIP gap tolerance EpGap, CPX_PARAM_EPGAP) to a value
higher than the default (in this example: 5%) so that the MIP optimization, which
constitutes the first phase of populate, stops earlier. Then, populate will go
immediately into the second phase, so it can start producing solutions sooner.
The commands to reproduce this situation looks like this in the Interactive
Optimizer:
read location.lp
set mip pool intensity 2
set mip tolerances mipgap 0.05
mipopt
populate
MIP optimization (as executed by mipopt in the Interactive Optimizer) shows these
results:
Solution pool: 3 solutions saved.
Populate (following mipopt in the Interactive Optimizer) shows results like these:
Solution pool: 23 solutions saved.
You can also enumerate all solutions that are valid for a specific criterion. For
example, if you want to enumerate all alternative optimal solutions, do the
following steps:
Procedure
1. Set the absolute gap for solution pool parameter (SolnPoolAGap,
CPX_PARAM_SOLNPOOLAGAP) to 0.0 (zero).
Results
Beware, however, that, even for small models, the number of possible solutions is
likely to be huge. Consequently, enumerating all of them will take time and
consume a large quantity of memory.
In addition, when you attempt to enumerate all solutions, some restrictions apply,
as explained in the following sections.
v Limitations due to continuous variables and finite precision
v Limitations due to unbounded MIP models
v Limitations due to numeric difficulties
There may be an infinite number of possible values for a continuous variable, and
it is not practical to enumerate all of them on a finite-precision computer.
Therefore, populate gives only one solution for each set of discrete variables, even
though there may exist several solutions that have the same values for all discrete
variables but different values for continuous variables.
Likewise, for the same reason, populate does not generate all possible solutions for
unbounded MIP models. As soon as the proof of unboundedness is obtained,
populate stops.
A solution may be considered feasible for one pair of values for these two
parameters, and infeasible for a different pair. This phenomenon is especially
noticeable in models with numeric difficulties, for example, in models with Big M
coefficients.
Since the definition of a feasible MIP solution is subject to tolerances, the total
number of solutions to a MIP model may vary, depending on the approach used to
enumerate solutions, and on precisely which tolerances are used. In most models,
this tolerance issue is not problematic for CPLEX. But, in the presence of numeric
Chapter 17. Solution pool: generating and keeping multiple solutions 289
difficulties, CPLEX may create solutions that are slightly infeasible or integer
infeasible, and therefore create more solutions than expected.
You can find more details about the topic of numeric difficulties in the CPLEX
Users Manual in Numeric difficulties on page 146 and Slightly infeasible integer
variables on page 265.
What might users do between a call of MIP optimization and a call of populate or
between successive calls of populate?
Users can continue to call populate until they have a pool of solutions they are
satisfied with. Between calls, users may examine solutions. If the solutions are
satisfactory, users can stop calling populate. If the solutions are not satisfactory,
then users can make changes to improve the solution pool. Changes that have an
impact on the solutions that populate generates and stores include these:
v changing parameter settings;
v adding filters;
v removing filters;
v changing characteristics of filters.
Changes of the model itself, such as altering the objective function, are treated
differently. For details, see Model changes and the solution pool.
Successive calls to MIP optimization or populate create solutions that are stored in
the solution pool. Each call to MIP optimization or populate applies to all solutions
in the pool. In particular, CPLEX may replace solutions in the pool obtained during
previous invocations of MIP optimization or populate if the pool is at its capacity
and CPLEX finds new solutions satisfying the replacement criteria.
If you want to keep all solutions produced through all calls to MIP optimization or
populate, then you must query the solution pool before calling MIP optimization
or populate again and store the solutions in user-defined arrays.
When a user modifies a model, for example, by adding constraints or changing the
coefficients of the objective function, the existing solutions already in the solution
pool may or may not be feasible in terms of the changed model. Therefore,
immediately after changing a model, the user cannot access the solution pool. In
However, the MIP starts constructed from the solutions in the solution pool before
the changes to the model may still exist if those MIP starts have not been modified
or deleted. If those unmodified MIP starts still exist, they are accessible through
these methods and routines:
v writeMIPStart in the C++ API;
v writeMIPStart in the Java API;
v Cplex.WriteMIPStart in the .NET API;
v CPXgetmipstarts and CPXwritemipstarts in the Callable Library (C API).
Also in the Interactive Optimizer, the command display solution list shows the
objective value of solutions in the pool, along with the percentage of discrete
variables that take a value different from the incumbent. To display all solutions in
the pool, use display solution list * . Alternatively, you can specify the indices
of the solutions to display, for example: display solution list 2-4 .
Table 54 on page 292 summarizes methods, routines, and commands that access
aggregated information about the solution pool itself, such information as the
number of solutions in the pool, the number of solutions that have been replaced,
and the arithmetic mean of the objective value of solutions in the pool.
Solutions are replaced, according to the replacement policy, when the pool reaches
its maximum capacity. Its maximum capacity is specified by the solution pool
capacity parameter (maximum number of solutions kept in solution pool:
SolnPoolCapacity, CPX_PARAM_SOLNPOOLCAPACITY) not by the parameter that limits
populate (maximum number of solutions generated for solution pool by populate:
PopulateLimit, CPX_PARAM_POPULATELIM).
Chapter 17. Solution pool: generating and keeping multiple solutions 291
Table 54. Accessing information about the solution pool
Purpose Concert Technology Callable Library Interactive Optimizer
Number of solutions getSolnPoolNsolns CPXgetsolnpoolnumsolns display solution
pool
Number replaced getSolnPoolNreplaced CPXgetsolnpoolnumreplaced display solution
pool
Arithmetic mean of getSolnPoolMeanObjValue CPXgetsolnpoolmeanobjval display solution
objective values pool
If you want to examine all the solutions available in the solution pool, your
application should loop from 0 (zero) to N-1 (that is, one less than the number of
solutions in the pool).
To learn the number of solutions in the pool for such a loop, use one of the
following methods or routines.
v In Concert Technology,
In the C++ API, use the method IloCplex::getSolnPoolNsolns.
In the Java API, use the method IloCplex.getSolnPoolNsolns.
In the .NET API, use the method Cplex.GetSolnPoolNsolns.
v In the Callable Library, use the routine CPXgetsolnpoolnumsolns.
CPXgetsolnpooldblquality
Difference between (see Notes) (see Notes) display solution
solutions difference i j
Note:
The solutions in the solution pool are available for use in applications or further
optimizations. For example, you can write a particular solution from the solution
pool to a solution file in SOL format.
v In Concert Technology
In the C++ API, use IloCplex::writeSolution.
In the Java API, use IloCplex.writeSolution.
In the .NET API, use Cplex.WriteSolution.
v In the Callable library, use the routine CPXsolwritesolnpool .
v In the Interactive Optimizer, use this command, where i represents the index of
the solution in the solution pool: write filename .sol i
You can also write all the solutions from the solution pool into a single SOL file.
v In Concert Technology
In the C++ API, use IloCplex::writeSolutions.
In the Java API, use IloCplex.writeSolutions.
In the .NET API, use Cplex.WriteSolutions.
v In the Callable library, use the routine CPXsolwritesolnpoolall.
v In the Interactive Optimizer, use this command: write filename .sol all
Similarly, you can use a solution from the solution pool to change the fixed
problem of your MIP model. Only these two types are supported for this change:
v CPXPROB_FIXEDMILP
v CPXPROB_FIXEDMIQP
v In Concert Technology
In the C++ API, use IloCplex::solveFixed.
In the Java API, use IloCplex.solveFixed.
In the .NET API, use Cplex.SolveFixed.
v In the Callable Library (C API), use the routine CPXchgprobtypesolnpool.
Chapter 17. Solution pool: generating and keeping multiple solutions 293
v In the Interactive Optimizer, the following command changes the fixed problem
to that of the solution at index i in the pool: change problem fixed i
The parameter write level for MST, SOL files (WriteLevel, CPX_PARAM_WRITELEVEL),
documented in the CPLEX Parameters Reference Manual, enables you to specify
various levels of information, such as values for only discrete variables, values for
all variables, and so forth, for CPLEX to record about a solution when it writes the
solution to a formatted file.
When the advanced start switch (AdvInd, CPX_PARAM_ADVIND) is set to 0 (zero) and a
new optimization is started, either by MIP optimization or by populate, all
solutions in the solution pool are deleted automatically.
A copy of the incumbent solution (that is, the best integer solution found relative
to the objective function) is always added to the pool, as long as the pool capacity
is at least one, regardless of its evaluation with respect to any filters and regardless
of the replacement criterion governing the solution pool. This copy of the
incumbent solution will be the first member of the solution pool, that is, the
solution with index 0 (zero). The incumbent is accessible through queries that use
the symbolic value identifying the incumbent.
v In the C++ API, use the value IloCplex::IncumbentId as an argument to such
methods as IloCplex::getValues, getSlack, getSlacks, getQuality, getObjValue.
v In the Java API, use the value IloCplex.IncumbentId as an argument to such
methods as IloCplex.getValues, getSlack, getSlacks, getQuality, getObjValue.
v In the .NET API, use the value Cplex.IncumbentId as an argument to such
methods as Cplex.GetValues, GetSlack, GetSlacks, GetQuality, GetObjValue.
v In the Callable Library (C API), use the symbolic value CPX_INCUMBENT_ID as an
argument to such routines as,CPXgetsolnpoolx, CPXgetsolnpoolobjval,
CPXgetsolnpoolslack, CPXgetsolnpoolqconstrslack, CPXgetsolnpooldblquality,
CPXgetsolnpoolintquality, CPXgetsolnpoolsolnname, CPXsolwritesolnpool,
CPXchgprobtypesolnpool.
v In the Interactive Optimizer use the command display solution member 0 or
display solution member incumbent. That is, you can display the incumbent
solution by index number or by name.
CPLEX provides parameters to control the solution pool. The table titled Table 56
summarizes these parameters. CPLEX Parameters Reference Manual documents each
of these parameters in greater detail.
Table 56. Parameters for the solution pool
Interactive Parameter
Purpose Concert parameter Callable Library parameter
Optimizer Reference
Intensity SolnPoolIntensity CPX_PARAM_SOLNPOOLINTENSITY mip pool intensity solution pool
intensity
Limit on populate PopulateLim CPX_PARAM_POPULATELIM mip limits maximum number
(number of populate of solutions
solutions generated) generated for
solution pool by
populate
Maximum capacity SolnPoolCapacity CPX_PARAM_SOLNPOOLCAPACITY mip pool capacity maximum number
(number of of solutions kept in
solutions stored) solution pool
Replacement SolnPoolReplace CPX_PARAM_SOLNPOOLREPLACE mip pool replace solution pool
strategy replacement
strategy
Relative objective SolnPoolGap CPX_PARAM_SOLNPOOLGAP mip pool relgap relative gap for
gap solution pool
Absolute objective SolnPoolAGap CPX_PARAM_SOLNPOOLAGAP mip pool absgap absolute gap for
gap solution pool
The MIP node limit parameter (NodeLim, CPX_PARAM_NODELIM) and optimizer time
limit in seconds parameter (TiLim, CPX_PARAM_TILIM) also have an effect on the
solution pool, just as they influence MIP optimization generally. For more detail
about these parameters, see their entries in the CPLEX Parameters Reference Manual.
In many cases, solutions are interesting only if their objective value is close to the
optimal objective value of the model. In that context, you can control the quality of
solutions generated and saved in the solution pool with the absolute gap for
solution pool parameter (SolnPoolAGap, CPX_PARAM_SOLNPOOLAGAP) and the relative
gap for solution pool parameter (SolnPoolGap, CPX_PARAM_SOLNPOOLGAP).
To demonstrate this idea, consider again the example cited in Example: simple
facility location problem on page 279. In order to obtain solutions that are less
than 10% worse than the optimal objective value, specify the solution pool relative
gap parameter in the Interactive Optimizer like this:
Chapter 17. Solution pool: generating and keeping multiple solutions 295
read location.lp
set mip pool relgap 0.1
populate
Then display the objective value of each solution in the Interactive Optimizer with
this command:
Afterwards, you see that all solutions in the pool are of a cost less than or equal to
548; that is, within 10% of the optimal objective value of 499.
If you need many solutions, but do not want to impair performance too greatly,
the value 2 (moderate) is a good choice for most models.
For example, the following session in the Interactive Optimizer reads a model in
LP format of the Example: simple facility location problem on page 279.The
session then effectively removes the stopping criterion of the populate limit
parameter by setting it very high.
read location.lp
set mip limits populate 10000
set mip pool intensity 2
set mip pool relgap 0.1
populate
You can see from the log that setting the pool intensity to 2 yields results faster
than when populate is called after MIP optimization at the default value of
solution pool intensity.
At solution pool intensity 2, a large number of solutions are produced (in this case,
196 solutions, though the precise number of solutions may vary on your platform).
If you set solution pool intensity at 3 instead, populate will generate a greater
number of solutions (in this case, 208 solutions).
Likewise, if you set solution pool intensity at 4 instead, a very great number of
solutions will be produced and stored in the solution pool, as this setting
exhaustively enumerates solutions.
In this small example, the settings 3 and 4 happen to produce the same number of
solutions (208), but in general it will not be the case that the two settings have the
same effect.
These settings make sure that pool capacity will not increase as solutions are
added. Instead, solutions will be replaced in the pool according to their diversity if
the number of solutions generated exceeds the limited capacity of the pool.
Logically, the pool contains only ten solutions now (its capacity) even though more
solutions have been generated. The number of solutions that have been generated
but are not retained in the pool is reported in the log by the number of solutions
replaced.
If you apply a time limit of 10 seconds instead of 1 (one), many more solutions
will be generated. That greater number of solutions also leads to greater diversity
among the solutions retained in the pool.
Filtering allows you to control properties of the solutions generated and stored in
the solution pool. CPLEX provides two predefined ways to filter solutions.
v If you want to filter solutions based on their difference as compared to a
reference solution, use a diversity filter, as explained in Diversity filters on
page 298.
v If you want to filter solutions based on their validity in an additional linear
constraint, use a range filter, as explained in Range filters on page 299.
Those two ways are practical for most purposes. However, if you require finer
control of which solutions to keep and which to eliminate, use an incumbent
callback, as explained in Incumbent callback as a filter on page 301.
Chapter 17. Solution pool: generating and keeping multiple solutions 297
Adding or deleting filters does not affect the solutions already in the pool; new
filters are applied only at the next call of MIP optimization or the populate
procedure.
Filters are assigned an index number when they are added to the solution pool,
and they may be accessed through this index number. Diversity filters and range
filters share the same sequence of indices.
To count the number of filters associated with the solution pool, use one of these
methods, routines, or commands:
v In Concert Technology
getNfilters in the C++ API;
IloCplex.getNFilters in the Java API;
Cplex.GetNFilters in the .NET API.
v CPXgetsolnpoolnumfilters in the Callable Library (C API );
Diversity filters
Describes diversity filters of the solution pool.
A diversity filter allows you generate solutions that are similar to (or different from)
a set of reference values that you specify for a set of binary variables. In particular,
you can use a diversity filter to generate more solutions that are similar to an
existing solution or to an existing partial solution. Several diversity filters can be
used simultaneously, for example, to generate solutions that share the
characteristics of several different solutions.
For greater detail about the characteristics of diversity filters and reference sets, see
the documentation of those methods and routine in the Reference Manuals of the
APIs. For an example of a filter in use, see Example: controlling properties of
solutions with filters on page 300.
Range filters
Describes range filters of the solution pool.
A range filter allows you to generate solutions that obey a new constraint, specified
as a linear expression within a range.
The difference between adding a range filter and adding a linear constraint directly
to the model is that you can add range filters without losing information
computed in previous invocations of MIP optimization or populate and stored in
the search space. In contrast, if you change the model directly by adding
constraints, the next call of optimize or populate will discard previous information
and restart from scratch on the changed model.
Range filters can be used to express diversity constraints that are more complex
than the standard form implemented by diversity filters. In particular, range filters
also apply to general integer variables, semi-integer variables, continuous variables,
and semi-continuous variables, not just to binary variables.
To create a range filter, use one of the following methods, routines, or commands.
v In Concert Technology
In the C++ API, use the method IloCplex::addRangeFilter to add the range
filter to the invoking instance of IloCplex.
In the Java API, use the method IloCplex.addRangeFilter to add the range
filter to the invoking instance of IloCplex.
In the .NET API, use the method Cplex.AddRangeFilter to add the range filter
to the invoking instance of Cplex.
v In the Callable Library (C API), use the routine CPXaddsolnpoolrngfilter,
passing a linear constraint and range as arguments; or, use your favorite text
editor to create a file formatted according to the specifications in FLT file format:
filter files for the solution pool, and then install the contents of that file with the
routine CPXreadcopysolnpoolfilters.
v In the Interactive Optimizer, use your favorite text editor to create a file
formatted according to the specifications in FLT file format: filter files for the
solution pool; then install the contents of that formatted file with the command:
read filename.flt
For more detail about the characteristics of range filters, see the documentation of
those methods and routine in the Reference Manuals of the APIs. For an example of
a filter in use, see Example: controlling properties of solutions with filters on
page 300.
Chapter 17. Solution pool: generating and keeping multiple solutions 299
Filter files
Describes filter files for the solution pool.
You can store filters in a file, known as a filter file, distinguished by the file
extension .flt . The same filter file can contain several filters, including both
diversity filters and range filters. For documentation of the format of a filter file,
see FLT file format: filter files for the solution pool in the CPLEX File Formats
Reference Manual.
To create filters, use your favorite text editor to create a file formatted according to
the specifications in FLT file format: filter files for the solution pool.
To install filters declared in a filter file, use one of these methods, routines, or
commands:
v In Concert Technology, use the methods:
IloCplex::readFilters
IloCplex.readFilters
Cplex.ReadFilters
v In the Callable Library (C API), use the routine CPXreadcopysolnpoolfilters to
add diversity or range filters.
v In the Interactive Optimizer, use the read command to import a filter file.
To write existing filters to a formatted file (for example, for re-use later), use these
methods, routines, or commands:
v In Concert Technology, use the methods:
writeFilters in the C++ API;
writeFilters in the Java API;
Cplex.WriteFilters in the .NET API.
v In the Callable Library (C API), use the routine CPXfltwrite.
v In the Interactive Optimizer, use the write command with the file type option
flt to create a filter file of the filters currently associated with the solution pool.
For example, the following command creates a file named filename.flt
containing the filters associated with the solution pool: write filename flt
The model in Example: simple facility location problem on page 279 has two
categories of variables. The x variables specifying the facilities to open are of a
higher decision level than the y variables deciding how the goods are shipped
from facilities to regions. Suppose, for example, that you want to populate the
solution pool with solutions that differ by which facilities are opened, without
specifying any specific criteria for the shipping decisions. The replacement strategy
(shown in Example: diverse solutions through replacement parameter on page
296) does not allow you to specify a customized diversity measure that takes into
account only a subset of the variables. However, this diversity measure expressed
only over the x variables can be enforced through a diversity filter.
Suppose further that facilities 1 and 2 are open. Let a solution keeping those two
facilities open be the reference; that is, the reference value for x1 is 1 (one), for x2
is 1 (one), for x3 is 0 (zero), for x4 is 0 (zero). Then use a diversity filter to
stipulate that any solution added to the solution pool must differ from the
The y variables are not specified in the filter; hence, they are not taken into account
in the diversification.
NAME location
DIVFILTER f1 2 inf
x1 1.0 1
x2 1.0 1
x3 1.0 0
x4 1.0 0
ENDATA
Range filters also enforce additional constraints. Suppose, for example, that you
want to limit transportation costs to less than fixed costs. The following range filter
enforces this restriction by expressing the linear constraint:
If you need to enforce more complex constraints on solutions (if you need to
enforce nonlinear constraints, for example), you can use the incumbent callback in
Concert Technology or the Callable Library. During the populate procedure, the
incumbent callback is called each time a new solution is found, even if the new
solution does not improve the objective value of the incumbent. The incumbent
callback allows your application to accept or reject the new solution based on your
own criteria.
Bear in mind that the incumbent callback disables dynamic search. At default
parameter settings, the incumbent callback, as a control callback, also disables
deterministic parallel MIP optimization though you can override this default
behavior by setting the parallel mode switch (ParallelMode,
CPX_PARAM_PARALLELMODE) yourself. For more about dynamic search, see
Branch & cut or dynamic search? on page 225. For more about parallel MIP
optimization, see Parallel MIP optimizer on page 360.
Chapter 17. Solution pool: generating and keeping multiple solutions 301
In the .NET API, implement a subclass of Cplex.IncumbentCallback. Use it as
documented in the parent class Cplex.Callback.
v In the Callable Library (C API), use the routine CPXXsetincumbentcallbackfunc.
IBM ILOG CPLEX uses special branching strategies to take advantage of SOSs. For
many classes of problems, these branching strategies can significantly improve
performance. These special branching strategies depend upon the order among the
variables in the set. The order is specified by assigning weights to each variable.
The order of the variables in the model (such as in the MPS or LP format data file,
or the column index in a Callable Library application) is not used in SOS
branching. If there is no ordered relationship among the variables (such that
weights cannot be specified or would not be meaningful), other formulations
should be used instead of a special ordered set.
To give you a feel for how SOSs can be useful, heres an example of an SOS
Type 1 used to choose the size of a warehouse. Assume for this example that a
warehouse of 10000, 20000, 40000, or 50000 square feet can be built. Define binary
variables for the four sizes, say, x1, x2, x4, and x5. Connect these variables by a
constraint defining another variable to denote available square feet, like this:
z - 10000x1 - 20000x2 - 40000x4 - 50000x5 = 0.
Those four variables are members of a special ordered set. Only one size can be
chosen for the warehouse; that is, at most one of the x variables can be nonzero in
the solution. And, there is an order relationship among the x variables (namely, the
sizes) that can be used as weights. Then the weights of the set members are 10000,
20000, 40000, and 50000.
Assume furthermore that there is a known fractional (that is, noninteger) solution
of x1 = 0.1, x5 = 0.9. These values indicate that other parts of the model have
Split the set before the variable with weight exceeding the weighted average. In
this case, split the set like this: x1, x2, and x4 will be in one subset; x5 in the other.
Now branch. One branch restricts x1, x2, x4 to 0 (zero). This branch results in x5
being set to 1 (one).
Members of an SOS should be given unique weights that in turn define the order
of the variables in the set. (These unique weights are also called reference row
values.) Each of those ways of declaring SOS members allows you to specify
weights.
The SOS example, Example: SOS Type 1 for sizing a warehouse on page 303,
used the coefficients of the warehouse capacity constraint to assign weights.
ilomipex3.cpp
Illustrates priority orders and a special ordered set in the C++ API.
304 CPLEX Users Manual
This example derives from ilomipex1.cpp . The differences between that simpler
MIP example and this one are:
v The problem solved is slightly different so the output is interesting. The actual
SOS and priority order that the example implements are arbitrary; they do not
necessarily represent good data for this problem.
v The routine setPriorities sets the priority order.
mipex3.c
Illustrates priority orders and a special ordered set in the C API.
This example derives from mipex1.c. The differences between that simpler MIP
example and this one are:
v The problem solved is slightly different so the output is interesting. The actual
SOS and priority order that the example implements are arbitrary; they do not
necessarily represent good data for this problem.
v The CPLEX preprocessing parameters for the presolver and aggregator are
turned off to make the output interesting. Generally, this is not required nor
recommended.
v The routine setsosandorder sets the SOS and priority order:
It calls CPXcopysos to copy the SOS into the problem object.
It calls CPXcopyorder to copy the priority order into the problem object.
It writes the priority order to files by calling CPXordwrite.
v The routine CPXwriteprob writes the problem with the constraints and SOSs to
disk before the example copies the SOS and priority order to verify that the base
problem was copied correctly.
A semi-continuous variable is a variable that by default can take the value 0 (zero)
or any value between its semi-continuous lower bound (sclb) and its upper bound
(ub). The semi-continuous lower bound (sclb) must be finite. The upper bound (ub)
need not be finite. The semi-continuous lower bound (sclb) must be greater than or
equal to 0 (zero). An attempt to use a negative value for the semi-continuous lower
bound (sclb) will result in that bound being treated as 0 (zero).
In that manual, you will see that IloSemiContVar derives from IloNumVar, the
Concert Technology class for numeric variables. Like other numeric variables,
semi-continuous variables assume floating-point values by default (type ILOFLOAT).
However, you can designate a semi-continuous variable as integer (type ILOINT). In
that case, it is a semi-integer variable.
As input for this example, you need such data as the minimum and maximum
output level for each generator. The application will use Concert Technology arrays
minArray and maxArray for that data. It will read data from a file into these arrays,
and then learn their length (that is, the number of generators available) by calling
the method getSize.
The application also needs to know the cost per unit of output for each generator.
Again, a Concert Technology array, cost, serves that purpose as the application
reads data in from a file with the operator >>.
The application also needs to know the demand for power, represented as a
numeric variable, demand.
Building a model
Describes the application to solve a model with semi-continuous variables.
With that model, now the application is ready to create an algorithm (in this case,
an instance of IloCplex ) and extract the model.
To solve the problem, create the algorithm, extract the model, and solve.
if (cplex.solve()) {
As in all C++ CPLEX applications, this program ends with a call to IloEnv::end to
de-allocate the model and algorithm after they are no longer in use.
env.end();
Complete program
Identifies location of the sample application in the C++ API and other APIs as
well.
You can see the entire program online in the standard distribution of CPLEX at
yourCPLEXinstallation/examples/src/rates.cpp.
Some problems are most naturally represented by constraints over functions that
are not purely linear but consist of linear segments. Such functions are also known
as piecewise linear. In this topic, a transportation example shows you various ways
of stating and solving problems that lend themselves to a piecewise linear model.
Before plunging into the problem itself, this section defines a few terms appearing
in this discussion.
In other words, for a piecewise linear function of n breakpoints, you need to know
n+1 slopes.
The slopes of its segments are indicated as an array of numeric values as well. For
example, the slopes of f(x) are specified in this way, where the first argument
again specifies the environment, the second argument specifies the number of
slopes given, and the remaining arguments specify the slope of the segments:
IloNumArray (env, 4, -0.5, 1., -1., 2.)
The geometric coordinates of at least one point of the function, (x, f(x)) must
also be specified; for example, (4, 2) . Then in Concert Technology, those
elements are brought together in an instance of the class IloPiecewiseLinear in
this way:
IloPiecewiseLinear(x,
IloNumArray(env, 3, 4., 5., 7.),
IloNumArray(env, 4, -0.5, 1., -1., 2.),
4, 2)
Another way to specify a piecewise linear function is to give the slope of the first
segment, two arrays for the coordinates of the breakpoints, and the slope of the
last segment. In this approach, the example f(x) from Figure 10 on page 311 looks
like this:
IloPiecewiseLinear(x, -0.5, IloNumArray(env, 3, 4., 5., 7.),
IloNumArray(env, 3, 2., 3., 1.), 2);
Note:
It may help you understand the signatures of these functions to recall the familiar
Cartesian representation of a line or segment in two dimensions, x and y:
y = ax + b
where a represents the slope of the line or segment and b represents the height at
which the line theoretically crosses the y-axis at the point (0, b).
Thus far, you have seen a piecewise linear function where the segments are
continuous. Intuitively, in a continuous piecewise linear function, the endpoint of
one segment has the same coordinates as the initial point of the next segment, as
in Figure 10 on page 311. One way of defining a continuous piecewise linear
function is by specifying its breakpoints and the slope of each segment. Those
specification breakpoints are also known as the anchors of their segments.
There are piecewise linear functions, however, where the endpoint of one segment
and the initial point of the next segment may have the same x coordinate but differ
in the value of f(x). Such a difference is known as a step in the piecewise linear
function, and such a function is known as discontinuous. Figure 11 on page 314
shows a discontinuous piecewise linear function with two steps.
Tip:
When the anchor of a segment resides at a step, then the discontinuous piecewise
linear function is not uniquely defined by specifying its breakpoints and the slopes
of its segments. In contrast, as long as the anchor point resides elsewhere, rather
than at a step, one can specify a discontinuous piecewise linear function by its
breakpoints and slopes as one specifies a continuous piecewise linear function.
By convention, a breakpoint belongs in both segments associated with the step. For
example, in Figure 11 on page 314, at the breakpoint x=3 , the points (3,1) and
(3,3) are both admissible. Similarly, when x = 5 , the points (5,4) and (5,5) are
both admissible.
Chapter 20. Using piecewise linear functions in optimization: a transport example 313
In Concert Technology, a discontinuous piecewise linear function is represented by
the overloaded functionIloPiecewiseLinear. For example, the function in Figure 11
is declared in this way, with the discontinuous step values (2 and 1) highlighted:
IloPiecewiseLinear(x,
IloNumArray(env, 4, 3. ,3. ,5. ,5.),
IloNumArray(env, 5, 0., 2., 0.5, 1., -1.),
0, 1);
The anchor point of (0,1) specified as the last two arguments does not reside at a
discontinuity in that example.
Similarly, one can specify the same discontinuous piecewise linear function using
breakpoints and values by providing the slope of the segment preceding the first
break point, arrays of the breakpoints and associated function values, and the slope
of the segment following the last breakpoint, like this:
IloPiecewiseLinear(x, 0.,
IloNumArray(env, 4, 3. ,3. ,5. ,5.),
IloNumArray(env, 4, 1., 3., 4., 5.),
-1.);
Note:
It may help to understand the signature of the function in this example to recall
that in Cartesian coordinates, the slope of a horizontal line (that is, a line or
segment parallel to the x-axis) is 0 (zero), and the slope of a vertical line (that is, a
line or segment parallel to the y-axis) is undefined in the conventional
representation:
y = ax + b
In the signature of the function, the undefined slope of the vertical segment at the
point of discontinuity is represented as the height of the step.
When you specify the same point more than twice as you declare a piecewise
linear function, you inadvertently create an isolated point. IBM ILOG CPLEX does
not support isolated points. When it encounters an isolated point in the declaration
of a piecewise linear function, CPLEX issues a warning and ignores the isolated
point. An isolated point may appear as a visible point in the graph of a
discontinuous piecewise linear function. For example, the point (3, 2) would be
an isolated point in Figure 11 on page 314 and consequently ignored by CPLEX.
Isolated points may also be less conspicuously visible; for example, if the height of
a step in a discontinuous piecewise linear function is 0 (zero), the isolated point
overlaps with an endpoint of two other segments, and consequently, the isolated
point will be ignored by CPLEX.
Problem statement
Describes a model using piecewise linear functions.
Assume that a company must ship cars from factories to showrooms. Each factory
can supply a fixed number of cars, and each showroom needs a fixed number of
cars. There is a cost for shipping a car from a given factory to a given showroom.
The objective is to minimize the total shipping cost while satisfying the demands
and respecting supply.
In concrete terms, assume there are three factories and four showrooms. Here is
the quantity that each factory can supply:
supply0 = 1000
supply1 = 850
supply2 = 1250
Chapter 20. Using piecewise linear functions in optimization: a transport example 315
Minimize
subject to
Now consider the costs of shipping from a given factory to a given showroom.
Assume that for every pair (factory, showroom), there are different rates, varying
according to the quantity shipped. To illustrate the difference between convex and
concave piecewise linear functions, in fact, this example assumes that there are two
different tables of rates for shipping cars from factories to showrooms. The first
table of rates looks like this:
v a rate of 120 per car for quantities between 0 and 200;
v a rate of 80 per car for quantities between 200 and 400;
v a rate of 50 per car for quantities higher than 400.
These costs that vary according to quantity define the piecewise linear function
represented in Figure 12. As you see, the slopes of the segments of that function
are decreasing, so that function is concave.
Also assume that there is a second table of rates for shipping cars from factories to
showrooms. The second table of rates looks like this:
v a rate of 30 per car for quantities between 0 and 200;
v a rate of 80 per car for quantities between 200 and 400;
v a rate of 130 per car for quantities higher than 400.
With this additional consideration about costs varying according to quantity, our
model now looks like this:
Minimize
subject to
With this problem in mind, consider how to represent the data and model in
Concert Technology.
Developing a model
Describes an application using piecewise linear functions.
Chapter 20. Using piecewise linear functions in optimization: a transport example 317
As in other examples in this manual, this application begins by creating an
environment, an instance of IloEnv.
IloEnv env;
Then constraints and an objective are added to the model. The following sections
sketch these steps.
Those two-dimensional arrays (that is, arrays of arrays) are now available in the
application to represent the demands from the showrooms and the supplies
available from the factories.
IloInt nbDemand = 4;
IloInt nbSupply = 3;
IloNumArray supply(env, nbSupply, 1000., 850., 1250.);
IloNumArray demand(env, nbDemand, 900., 1200., 600., 400.);
Adding constraints
Describes adding constraints in the application.
According to the description of the problem, the supply of cars from the factories
must meet the demand of the showrooms. At the same time, it is important not to
ship cars that are not in demand; in terms of this model, the demand should meet
the supply as well. Those ideas are represented as constraints added to the model,
like this:
for(i = 0; i < nbSupply; i++) { // supply must meet demand
model.add(IloSum(x[i]) == supply[i]);
}
for(j = 0; j < nbDemand; j++) { // demand must meet supply
IloExpr v(env);
for(i = 0; i < nbSupply; i++)
v += x[i][j];
model.add(v == demand[j]);
v.end();
}
Adding an objective
Describes adding an objective function to the application.
model.add(IloMinimize(env, obj));
obj.end();
Displaying a solution
Describes display of the solution from the application.
Chapter 20. Using piecewise linear functions in optimization: a transport example 319
env.out() << endl;
}
env.out() << Cost = << cplex.getObjValue() << endl;
As in other C++ examples in this manual, the application ends with a call to the
method IloEnv::end to clean up the memory allocated for the environment and
algorithm.
env.end();
You can see the complete program online in the standard distribution of CPLEX at
youCPLEXinstallation/examples/src/transport.cpp.
You may recognize those expressions as logical constraints. For more information
about logical constraints in the object-oriented application programming interfaces,
see the topic Chapter 22, Logical constraints in optimization, on page 325 in this
manual.
In the Python API, you can introduce an indicator constraint in your model by
means of the method indicator_constraints.add.
Further reading
IBM Tech Note: Difference between using indicator constraints and a big-M
formulation
Paul Rubin's comprehensive blog about the perils of Big M
For an example of indicator constraints in use, see fixnet.c among the examples
distributed with the product. This example shows a model of a fixed-charge
problem using indicator constraints.
In the Interactive Optimizer, you can include indicator constraints among the
usual linear constraints in LP-file format. You can also use the commands enter
and add with indicator constraints. For example, you could declare y as a binary
variable and enter the following:
constr01: y = 0 -> x1 + x2 + x3 = 0
That Big M formulation relies on the x values summing to less than the Big M
value (in this case, one billion). Such an assumption may cause numeric instability
or undesirable solutions in certain circumstances, whereas a model with the
indicator constraint, by contrast, introduces no new assumptions about upper
bounds. In that respect, the use of indicator constraints instead of a Big M
formulation offers a more numerically stable model, closer to the mathematical
programming issues of the problem, and thus more likely to produce useful
solutions of the problem.
CPLEX does not impose any arbitrary limit on the number of indicator constraints
or indicator variables that you introduce, but there may be practical limits due to
resources available on your platform.
For IBM ILOG CPLEX, a logical constraint combines linear constraints by means of
logical operators, such as logical-and, logical-or, negation (that is, not), conditional
statements (that is, if ... then ...) to express complex relations between linear
constraints. CPLEX can also handle certain logical expressions appearing within a
linear constraint. One such logical expression is the minimum of a set of variables.
Another such logical expression is the absolute value of a variable. Theres more
about logical expressions in Which nonlinear expressions can be extracted? on
page 327.
In the Callable Library, indicator constraints provide a similar facility. For more
about that idea, see Chapter 21, Indicator constraints in optimization, on page
321 in this manual.
Overview
Introduces logical constraints in the context of extraction.
Concert Technology offers classes for you to design a model of your problem, of
course. You can then invoke an algorithm to extract information from your model
to solve the problem. In this context, an algorithm is an instance of a class such as
IloCplex, documented in the CPLEX Reference Manuals of the C++ and Java APIs,
or of the class CPLEX, documented in the CPLEX Reference Manual of the .NET API.
For more about this idea of extraction generally, see topics in Part 1, Languages
and APIs, on page 1, or see the concept of Extraction in the CPLEX Reference
Manual of the C++ API.
For similar facilities in the Callable Library, see Chapter 21, Indicator constraints
in optimization, on page 321.
In C++ applications, the class IloCplex can extract modeling objects to solve a
wide variety of MIPs, as you see in Solving the model on page 9, summarized in
the table in Overview on page 9. In fact, the C++ class IloCplex can extract
logical constraints as well as some logical expressions. The logical constraints that
IloCplex can extract are these:
v IloAnd
v IloOr
v IloNot
v IloIfThen
v IloDiff
v == that is, the equivalence relation
Among those extractable objects, IloAnd IloOr , IloNot , and IloDiff can also be
represented in your application by means of the overloaded C++ operators:
v || (for IloOr )
v && (for IloAnd )
v ! (for IloNot )
v != that is, the exclusive-or relation (for IloDiff )
All those extractable objects accept as their arguments other linear constraints or
logical constraints, so you can combine linear constraints with logical constraints in
complicated expressions in your application.
For example, to express the idea that two jobs with starting times x1 and x2 and
with duration d1 and d2 must not overlap, you can either use overloaded C++
operators, like this:
model.add((x1 >= x2 + d2) || (x2 >= x1 + d1));
Since IloCplex can also extract logical constraints embedded in other logical
constraints, you can also write logical constraints like this:
IloIfThen(env, (x >= y && x >= z), IloNot(x <= 300 || y >= 700))
Of course, because the Java programming language does not support the
overloading of operators as C++ does, overloaded logical operators are not
supported in the Java API of Concert Technology. However, the Java class
IloCplexModeler offers logical modeling facilities through methods, such as:
v IloCplexModeler.and
v IloCplexModeler.or
v IloCplexModeler.not
v IloCplexModeler.ifThen
Moreover, like their C++ counterparts, those extractable Java objects accept as their
arguments other linear constraints or logical constraints, so you can combine linear
constraints with logical constraints in complicated expressions in your Java
application.
Similarly, the .NET API of Concert Technology supports logical constraints, though
not operator overloading. The .NET class Cplex offers these overloaded logical
methods:
v Cplex.And
v Cplex.Or
v Cplex.Not
v Cplex.IfThen
Again, those extractable .NET objects accept other linear constraints or logical
constraints as their arguments, thus making it possible for you to combine linear
constraints with logical constraints in expressions in your .NET applications.
Some expressions are easily recognized as nonlinear, for example, a function such
as x2 + y2 1. However, other nonlinearities are less obvious, such as absolute
value as a function. In a very real sense, MIP is a class of nonlinearly constrained
problems because the integrality restriction destroys the property of convexity
which any linear constraints otherwise might possess. Because of that
characteristic, certain (although not all) nonlinearities are capable of being
converted to a MIP formulation, and thus can be solved by CPLEX. In fact,
IloCplex can extract the following nonlinear expressions in a C++ application:
v IloMin the minimum of an array of numeric expressions or over a numeric
expression and a constant in C++
v IloMax the maximum of an array of numeric expressions or over a numeric
expression and a constant in C++
v IloAbs the absolute value of a numeric expression
v IloPiecewiseLinear the piecewise linear combination of a numeric expression,
v A linear constraint can appear as a term in a logical constraint.
It is important to note here that only linear constraints can appear as arguments of
logical constraints extracted by CPLEX. That is, quadratic constraints are not
handled in logical constraints. Similarly, quadratic terms can not appear as
arguments of logical expressions such as IloMin, IloMax, IloAbs, and
IloPiecewiseLinear.
It is important to note here that only linear constraints can appear as arguments of
logical constraints extracted by IloCplex. That is, quadratic constraints are not
handled in logical constraints. Similarly, quadratic terms cannot appear as
arguments of logical expressions such as IloMin, IloMax, IloAbs, and
IloPiecewiseLinear.
The problem is to plan the blending of five kinds of oil, organized in two
categories (two kinds of vegetable oils and three kinds of non vegetable oils) into
batches of blended products over six months.
Some of the oil is already available in storage. There is an initial stock of oil of 500
tons of each raw type when planning begins. An equal stock should exist in
storage at the end of the plan. Up to 1000 tons of each type of raw oil can be
stored each month for later use. The price for storage of raw oils is 5 monetary
units per ton. Refined oil cannot be stored. The blended product cannot be stored
either.
The rest of the oil (that is, any not available in storage) must be bought in
quantities to meet the blending requirements. The price of each kind of oil varies
over the six-month period.
The two categories of oil cannot be refined on the same production line. There is a
limit on how much oil of each category (vegetable or non vegetable) can be refined
in a given month:
v Not more than 200 tons of vegetable oil can be refined per month.
v Not more than 250 tons of non vegetable oil can be refined per month.
The aim of the six-month plan is to maximize profit by lowering production and
storage costs.
What is known?
In this particular example, the planning period is six months, and there are five
kinds of oil to be blended. Those details are represented as constants, like this:
const IloInt nbMonths = 6;
const IloInt nbProducts = 5;
The five kinds of oil (vegetable and non vegetable) are represented by an
enumeration, like this:
typedef enum { v1, v2, o1, o2, o3 } Product;
The varying price of the five kinds of oil over the six-month planning period is
represented in a numeric matrix, like this:
NumMatrix cost(env, nbMonths);
cost[0]=IloNumArray(env, nbProducts, 110.0, 120.0, 130.0, 110.0, 115.0);
cost[1]=IloNumArray(env, nbProducts, 130.0, 130.0, 110.0, 90.0, 115.0);
cost[2]=IloNumArray(env, nbProducts, 110.0, 140.0, 130.0, 100.0, 95.0);
cost[3]=IloNumArray(env, nbProducts, 120.0, 110.0, 120.0, 120.0, 125.0);
cost[4]=IloNumArray(env, nbProducts, 100.0, 120.0, 150.0, 110.0, 105.0);
cost[5]=IloNumArray(env, nbProducts, 90.0, 100.0, 140.0, 80.0, 135.0);
That matrix could equally well be filled by data read from a file in a large-scale
application.
What is unknown?
like this:
IloNumVarArray produce(env, nbMonths, 0, IloInfinity);
NumVarMatrix use(env, nbMonths);
NumVarMatrix buy(env, nbMonths);
NumVarMatrix store(env, nbMonths);
IloInt i, p;
for (i = 0; i < nbMonths; i++) {
Notice that how much to use and buy is initially unknown, and thus has an
infinite upper bound, whereas the amount of oil that can be stored is limited, as
you know from the description of the problem. Consequently, one of the
constraints is expressed here as the upper bound of 1000 on the amount of oil by
type that can be stored per month.
As you know from Describing the problem on page 331, there are various
constraints in this problem.
For each type of oil, there must be 500 tons in storage at the end of the plan. That
idea can be expressed like this:
for (p = 0; p < nbProducts; p++) {
store[nbMonths-1][p].setBounds(500, 500);
}
Note:
On a monthly basis, the profit can be represented as the sale price per ton (150)
multiplied by the amount produced minus the cost of production and storage, like
this, where profit is defined as IloExpr profit(env);:
profit += 150 * produce[i] - IloScalProd(cost[i],
buy[i]) - 5 * IloSum(store[i]);
Then use a for-loop to add the constraints for each month (from Representing the
data on page 332: What are the constraints?), like this:
IloExpr profit(env);
for (i = 0; i < nbMonths; i++) {
model.add(use[i][v1] + use[i][v2] <= 200);
model.add(use[i][o1] + use[i][o2] + use[i][o3] <= 250);
model.add(3 * produce[i] <=
8.8 * use[i][v1] + 6.1 * use[i][v2] +
2 * use[i][o1] + 4.2 * use[i][o2] + 5 * use[i][o3]);
model.add(8.8 * use[i][v1] + 6.1 * use[i][v2] +
2 * use[i][o1] + 4.2 * use[i][o2] + 5 * use[i][o3]
<= 6 * produce[i]);
model.add(produce[i] == IloSum(use[i]));
if (i == 0) {
for (IloInt p = 0; p < nbProducts; p++)
model.add(500 + buy[i][p] == use[i][p] + store[i][p]);
}
else {
for (IloInt p = 0; p < nbProducts; p++)
model.add(store[i-1][p] + buy[i][p] == use[i][p] + store[i][p]);
}
profit += 150 * produce[i]
- IloScalProd(cost[i], buy[i])
- 5 * IloSum(store[i]);
To consolidate the monthly objectives, add the overall objective to the model, like
this:
model.add(IloMaximize(env, profit));
You have already seen how to represent the logical constraints of this problem in
Representing the data on page 332: What are the constraints? However, they
deserve a second glance because they illustrate an important point about logical
constraints and their automatic transformation in CPLEX.
// Logical constraints
// The food cannot use more than 3 oils
// (or at least two oils must not be used)
model.add((use[i][v1] == 0) + (use[i][v2] == 0) + (use[i][o1] == 0) +
(use[i][o2] == 0) + (use[i][o3] == 0) >= 2);
// When an oil is used, the quantity must be at least 20 tons
for (p = 0; p < nbProducts; p++)
model.add((use[i][p] == 0) || (use[i][p] >= 20));
// If products v1 or v2 are used, then product o3 is also used
model.add(IloIfThen(env, (use[i][v1] >= 20) || (use[i][v2] >= 20),
use[i][o3] >= 20));
Consider, for example, the constraint that the blended product cannot use more
than three oils in a batch. Given that constraint, many programmers might
naturally write the following statement (or something similar) in C++:
model.add ( (use[i][v1] != 0)
+ (use[i][v2] != 0)
+ (use[i][o1] != 0)
+ (use[i][o2] != 0)
+ (use[i][o3] != 0)
<= 3);
That statement expresses the same constraint without changing the set of solutions
to the problem. However, the formulations are different and can lead to different
running times and different amounts of memory used for the search tree. In other
words, given a logical English expression, there may be more than one logical
constraint for expressing it, and the different logical constraints may perform
differently in terms of computing time and memory.
Like other C++ applications using CPLEX with Concert Technology, this one ends
with a call to free the memory used by the environment.
env.end();
In that context, many researchers have observed that column generation is a very
powerful technique for solving a wide range of industrial problems to optimality
or to near optimality. Ford and Fulkerson, for example, suggested column
generation in the context of a multi-commodity network flow problem as early as
1958 in the journal of Management Science. By 1960, Dantzig and Wolfe had
adapted it to linear programming problems with a decomposable structure.
Gilmore and Gomory then demonstrated its effectiveness in a cutting stock
problem. More recently, vehicle routing, crew scheduling, and other
integer-constrained problems have motivated further research into column
generation.
Column generation rests on the fact that in the simplex method, the solver does
not need access to all the variables of the problem simultaneously. In fact, a solver
can begin work with only the basis (a particular subset of the constrained
variables) and then use reduced cost to decide which other variables to access as
needed.
In the CPLEX C++ API Reference Manual, the concept Column-Wise Modeling
provides more detail about this topic and offers simple examples of its use.
Generally, a cutting stock problem begins with a supply of rolls of material of fixed
length (the stock). Strips are cut from these rolls. All the strips cut from one roll are
known together as a pattern. The point of this example is to use as few rolls of
stock as possible to satisfy some specified demand of strips. By convention, it is
assumed that only one pattern is laid out across the stock; consequently, only one
dimensionthe widthof each roll of stock is important.
Even with that simplifying assumption, the fact that there can be so many different
patterns makes a naive model of this problem (where a user declares one variable
for every possible pattern) impractical. Such a model introduces too many
symmetries. Fortunately, for any given customer order, a limited number of
patterns will suffice, so many of the possible patterns can be disregarded, and the
application can focus on finding the relevant ones.
Minimize:
subject to:
Solving this model with all columns present from the beginning is practically
impossible. In fact, even with only 10 types of items with a size roughly 1/10 of
the width of the roll, there would exist roughly 10^10 kinds of patterns, and hence
that many decision variables. Such a formulation might not even fit in memory on
a reasonably large computer. Moreover, most of those patterns would obviously
not be interesting in a solution. These considerations make column generation an
interesting approach for this problem.
To move closer to a satisfactory solution, the application can then generate other
columns. That is, other decision variables (other Xj) will be chosen to add to the
model. Those decision variables are chosen on the basis of their favorable reduced
cost with the help of a subproblem. This subproblem is defined to identify the
coefficients of a new column of the master problem with minimal reduced cost.
With as the vector of the dual variables of the current solution of the master
problem, the subproblem is defined like this:
Minimize:
subject to:
That is, the subproblem data consists of W as the width of a roll of stock, Wi as the
width of strip i that can be used in a pattern cut from the roll. The entries Ai are
the modeling variables of the subproblem. They specify the number of times the
i-th strip is used in the pattern. Their solution values will be the coefficients of the
new column to be added to the master model if a solution with a negative
objective function is found for the subproblem. Consequently, the variables Ai must
be nonnegative integers.
The data defining this problem includes the width of a roll of stock. This value is
read from a file and represented by a numeric value, rollWidth. The widths of the
ordered strips are also read from a file and put into an array of numeric values,
size. Finally, the number of rolls ordered of each width is read from a file and put
into an array of numeric values, amount.
In this problem, an initial model cutOpt is built first to represent the master model.
Later, through its modifications, another model patGen is built to generate the new
columns. That is, patGen represents the subproblem.
When an objective is added to the model, the application needs to keep a handle
to the objective RollsUsed because it is needed when the application generates
columns. For that purpose, the application relies on the template function IloAdd,
like this:
IloObjective RollsUsed = IloAdd(cutOpt, IloMinimize(env));
Apart from the fact that it preserves type information, that single line is equivalent
to these lines:
340 CPLEX Users Manual
IloObjective RollsUsed = IloMinimize(env);
cutOpt.add(RollsUsed);
Procedure
1. Create a column expression defining the new column.
2. Create a variable using that column expression and add the variable to the
model.
Results
The terms of a column expression are connected to one another by the overloaded
operator + .
The master model is initialized with one variable for each size. Each such variable
represents the pattern of cutting a roll into as many strips of that size as possible.
These variables are stored as they are created in the array Cut by the following
loop:
IloInt nWdth = size.getSize();
for (j = 0; j < nWdth; j++)
Cut.add(IloNumVar(RollsUsed(1) + Fill(1)(int(rollWidth / size[j]))));
Consequently, the variable Cut[j] will have an objective coefficient of 1 (one) and
only one other nonzero coefficient (rollWidth/size[j]) for constraint Fill[j].
Later, in the column generation loop, new variables will be added. Those variables
will have coefficients defined by the solution vectors of the subproblem stored in
the array newPatt.
(However, those lines do not appear in the example at hand.) Concert Technology
offers a shortcut in the operator() for an array of range constraints. Those lines of
code can be condensed into the following line:
IloNumVar var(RollsUsed(1) + Fill(newPatt), 0, MAXCUT);
In other words, Fill(newPatt) returns the column expression that the loop would
create. You see a similar shortcut in the example.
With Concert Technology, in order to change the type of a variable in a model, you
actually create an extractable object (an instance of IloConversion) and add that
object to the model.
In the example, when the application needs to change the elements of Cut (an
array of numeric variables) from their default type of ILOFLOAT to integer (type
ILOINT), it creates an instance of IloConversion for the array Cut, and adds the
conversion to the model, cutOpt, like this:
cutOpt.add(IloConversion(env, Cut, ILOINT));
The submodel of the cutting stock problem is represented by the model patGen in
this example. This pattern generator patGen (in contrast to cutOpt) is defined by
the integer variables in the array Use. That array appears in the only constraint
added to patGen: a scalar product making sure that the patterns used do not
exceed the width of rolls. The application also adds a rudimentary objective
function to patGen. This objective initially consists of only the constant 1 (one). The
After the dual solution vector of the master model is available, the objective
function of the subproblem is adjusted by a call to the method
IloObjective::setLinearCoefs, like this:
ReducedCost.setLinearCoefs(Use, price);
This example does not solve the problem to optimality. It only generates a good
feasible solution. It does so by first solving a continuous relaxation of the
column-generation problem. In other words, the application drops the requirement
for integrality of the variables while the columns are generated. After all columns
have been generated for the continuous relaxation, the application keeps the
variables generated so far, changes their type to integer, and solves the resulting
integer problem.
As youve seen, this example manages two models of the problem, cutOpt and
patGen. Likewise, it uses two algorithms (that is, two instances of IloCplex) to
solve them.
Heres how to create the first algorithm cutSolver and extract the initial model
cutOpt:
IloCplex cutSolver(cutOpt);
And here is how to create the second algorithm and extract the model patGen:
IloCplex patSolver(patGen);
The heart of the example is here, in the column generation and optimization over
current patterns:
IloNumArray price(env, nWdth);
IloNumArray newPatt(env, nWdth);
for (;;) {
/// OPTIMIZE OVER CURRENT PATTERNS ///
cutSolver.solve();
report1 (cutSolver, Cut, Fill);
patSolver.getValues(newPatt, Use);
Cut.add( IloNumVar(RollsUsed(1) + Fill(newPatt)) );
}
cutOpt.add(IloConversion(env, Cut, ILOINT));
cutSolver.solve();
Those lines solve the current subproblem cutOpt by calling cutSolver.solve. Then
they copy the values of the negative dual solution into the array price. They use
that array to set objective coefficients in the model patGen. Then they solve the
right pattern generation problem.
If the objective value of the subproblem is nonnegative within the tolerance RC_EPS,
then the application has proved that the current solution of the model cutOpt is
optimal within the given optimality tolerance (RC_EPS). Otherwise, the application
copies the solution of the current pattern generation problem into the array
newPatt and uses that new pattern to build the next column to add to the model
cutOpt. Then it repeats the procedure.
As in other C++ Concert Technology applications, this program ends with a call of
IloEnv::end to de-allocate the models and algorithms after they are no longer in
use.
env.end();
Complete program
Tells where to find the sample application in the C++ API and other variations in
other APIs.
You can see the entire program online in the standard distribution of IBM ILOG
CPLEX at yourCPLEXinstallation/examples/src/cutstock.cpp.
For each job, there is a due date, that is, the ideal date to finish this job by
finishing the last activity of this job. If the job is finished earlier than the due date,
there will be a cost proportional to the earliness. Symmetrically, if the job is
finished later than the due date, there will be a cost proportional to the tardiness.
The data for this problem are available online with your installation of the product
in the file yourCPLEXhome /examples/data/etsp.dat.
The data of this example consists of arrays and arrays of arrays (that is, matrices).
One array of arrays represents the resources required for each activity of a job. For
example, job0 entails eight activities, and those eight activities require the
following ordered list of resources:
1, 3, 4, 1, 2, 4, 2, 4
A second array of arrays represents the duration required for each activity of a job.
For job0, the following ordered list represents the duration of each activity:
41, 32, 72, 65, 53, 35, 53, 2
In other words, job0 requires resource1 for a duration of 41 time units; then job0
requires resource3 for 32 time units, and so forth.
There is also an array representing the due date of each job. That is, array[i]
specifies the due date of jobi.
To represent the penalty for the early completion of each job, there is an array of
penalties.
Likewise, to represent the penalty for late completion of each job, there is an array
of penalties for tardiness.
The first part of this application reads data from a file and fills matrices:
IloEnv env;
IntMatrix activityOnAResource(env);
NumMatrix duration(env);
IloNumArray jobDueDate(env);
IloNumArray jobEarlinessCost(env);
IloNumArray jobTardinessCost(env);
f >> activityOnAResource;
f >> duration;
f >> jobDueDate;
f >> jobEarlinessCost;
f >> jobTardinessCost;
Each line in the data file corresponds to an array in the matrix and thus represents
all the information about activities for a given job.
For each job, other arrays contain further information from the data file:
v jobDueDate contains the due date for each job;
v jobEarlinessCost contains the penalty for being too early for each job;
v jobTardinessCost contains the penalty for being too late for each job.
Creating variables
Identifies unknowns of the problem.
The unknowns of the problem are the starting dates of the various activities. To
represent these dates with Concert Technology modeling objects, the application
creates a matrix of numeric variables (that is, instances of IloNumVar) with bounds
between 0 and Horizon , where Horizon is the maximum starting date for an
activity that does not exclude interesting solutions of the problem. In this example,
it is set arbitrarily at 10000. The type NumVarMatrix is defined as
typedef IloArray<IloNumVarArray> NumVarMatrix;
NumVarMatrix s(env, nbJob);
for(j = 0; j < nbJob; j++){
s[j] = IloNumVarArray(env, nbResource, 0.0, Horizon);
}
In each job, activities must be processed one after the other. This order is enforced
by the precedence constraints, which look like this:
Each resource can process one activity at a time. To avoid having two (or more)
activities that share the same resource overlap with each other, disjunctive
constraints are added to the model. Disjunctive constraints look like this:
s1 >= s2 + d2 or s2 >= s1 + d1
When IBM ILOG CPLEX extracts disjunctive constraints and piecewise linear
functions, it transforms them to produce a MIP with linear constraints and possibly
SOS constraints over integer or continuous variables. The tightness of the
transformation depends on the bounds set on the variables.
In this example, the Horizon is set to 10000, but if you have information about
your problem that indicates that a good or even optimal solution exists with a
tighter horizon (say, 2000 instead) then the linear formulation of disjunctions will
be tighter with that tighter horizon.
That kind of tightening often leads to a better lower bound at the root node and to
a reduction of the solving time.
You can see the entire example online in the standard distribution of CPLEX at
yourCPLEXinstallation/examples/src/etsp.cpp. Implementations of the same model,
using the same features of CPLEX, are available as Etsp.java, Etsp.cs, and
Etsp.vb as well.
IBM ILOG CPLEX offers multithreaded parallel barrier, parallel MIP, and
concurrent optimizers. These parallel optimizers are implemented to run on
hardware platforms with multiple cores. These multithreaded parallel optimizers
can be called from the Interactive Optimizer and the Component Libraries.
Threads
Documents threads in the context of parallel optimizers.
Thread safety
Summarizes special considerations about thread safety in parallel applications.
When you use callbacks with the CPLEX parallel optimizers, the nature of the
callback can affect the thread safety of the application. You should consider the
performance trade-offs that may occur in such a situation. For example, if the
application invokes a callback that provides significant performance improvements,
but cannot be written in a thread-safe way, you should assess whether running
CPLEX in sequential mode in the presence of the callback yields better
performance than running CPLEX in parallel mode without the callback.
Also, in order to make sure of thread safety, in certain cases CPLEX restricts the
way in which it calls a callback. In particular, when it calls an informational
callback in either deterministic or opportunistic mode, CPLEX makes sure of
thread safety by calling the callback only from the main thread that contains the
relevant global information required by the informational callback.
When CPLEX solves MIPs, each deterministic thread runs independently and
solves a separate subtree, so you may see a significant number of nodes processed
between calls to the callback.
For this reason, if you need to terminate an optimization running in parallel, you
need to assess whether stopping the optimization deterministically or stopping it
as quickly as possible is more important.
Threads parameter
Describes the effect of the threads parameter in parallel.
You manage the number of threads that CPLEX uses with the global thread count
parameter (Threads, CPX_PARAM_THREADS) documented in the CPLEX Parameters
Reference Manual. At its default setting 0 (zero), the number of threads that CPLEX
actually uses during a parallel optimization is no more than 32 or the number of
CPU cores available on the computer where CPLEX is running (whichever is
smaller).
If you set the threads parameter to a value greater than its default of 0 (zero), then
the number of threads that CPLEX uses equals that value. When you set the
threads parameter to 1 (one), you enforce sequential operation, that is, processing
on one thread only.
The number of threads used by a parallel CPLEX optimizer is separate from and
independent of the number of users. A typical CPLEX installation permits one use,
that is, a single concurrent execution on one computer. By default, CPLEX uses a
number of threads equal to the number of cores or 32 threads (whichever number
When the wait time is particularly high, as in that example, consider changing the
parallel mode from deterministic to opportunistic or changing from parallel to
sequential processing. Determinism of results explains more about these choices.
Another key consideration in setting optimizer and global thread limits is your
management of overall system load.
Determinism of results
Defines determinism and describes its effect in parallel optimization.
Tip:
CPLEX real time limits (also known as wall clock time) depend on system time.
System time is not deterministic. In other words, system time may vary from one
run to another. Consequently, two consecutive runs even with the same time limit
may yield results that are not deterministic. In contrast, CPLEX deterministic time
limits do not depend on system time. Consequently, two consecutive runs with the
same deterministic time limit will follow the same solution path.
In addition to the threads parameter, you can use the parallel mode switch
(ParallelMode, CPX_PARAM_PARALLELMODE) to control the invocation of opportunistic
algorithms. With its default setting of 0 (zero), only deterministic algorithms are
used, unless the threads parameter is changed to a value strictly greater than one.
To force CPLEX to use deterministic algorithms in all cases, set the parallel mode
parameter to 1 (one).
To allow CPLEX to use opportunistic algorithms in all situations, set the parallel
mode parameter to -1 (minus one).
The presence of a time limit, set by the optimizer time limit in seconds parameter
(TiLim, CPX_PARAM_TILIM) for example, poses problems for reproducibility in any
algorithm that is otherwise deterministic, even in sequential rather than parallel
mode. Subtle variations in runtime on computer architectures can lead to variation
in the measurement of time. (Other limits, such as node limits, are not subject to
this variability.)
Because time limits are so important in many applications, CPLEX will still
attempt to use deterministic mode without regard to whether a time limit is in
effect. While variability will still be much lower than with the opportunistic
setting, the user is advised that complete determinism within time limits can not
be assured.
There are other features of CPLEX that also depend implicitly on computer run
time. Because of this implicit dependence, they too can pose problems for
reproducibility in otherwise deterministic algorithms. Examples include
terminating the optimization, using a termination signal in a callback, or any other
use of a callback where behavior varies according to the order in which the
callbacks are invoked on multiple threads. In fact, the subtle variations in
computer run time can alter the order in which the threads invoke a callback on
consecutive runs.
For the mixed integer programming (MIP) optimizers of discrete models, the
barrier optimizer for continuous models, and the concurrent optimizers for
continuous models, the following sequence of commands invokes parallel
deterministic optimization at default settings.
1. Start the parallel CPLEX Interactive Optimizer with the command cplex at the
operating system prompt.
2. Enter your problem object, and populate it with data as usual.
Linear programming models (LPs) are solved by default with the dual simplex
algorithm. The dual simplex algorithm does not use multiple threads. In order to
benefit from parallel execution for LP, you need to invoke the barrier or concurrent
optimizers explicitly.
When you use one of the Component Libraries, such as the Callable Library, follow
these steps to invoke parallel optimization.
1. Create your CPLEX environment and initialize a problem object in the usual
way. See Initialize the CPLEX environment on page 60 and Instantiate the
problem as an object on page 61 for details.
2. Enter and populate your problem object in the usual way, as in Put data in the
problem object on page 61.
3. If you want to use an opportunistic parallel algorithm, set the parallel mode
switch (ParallelMode, CPX_PARAM_PARALLELMODE) in your application. If you
want to use deterministic mode with fewer than the maximum number of
available threads, set both the parallel mode and the threads parameter in your
application. See Determinism of results on page 355 for background about
this step.
4. Call the parallel optimizer with the appropriate method or routine (identified
from the table).
Table 59. Parallel optimizer methods and routines of the Component Libraries
Optimizer Concert IloCplex Method Callable Library
parallel MIP optimizer solve CPXmipopt
parallel barrier optimizer setParam(RootAlg, Barrier) CPXbaropt or CPXhybbaropt
and then solve
concurrent optimizer setParam(RootAlg,
Concurrent) and then solve CPXsetintparam(env,
CPX_PARAM_LPMETHOD,
CPX_ALG_CONCURRENT)
The CPLEX parallel barrier optimizer achieves significant speedups over its serial
counterpart on a wide variety of classes of problems. (The serial barrier optimizer
is introduced in Chapter 11, Solving LPs: barrier optimizer, on page 157, and
explored further in Chapter 13, Solving problems with a quadratic objective
(QP), on page 185 and in Chapter 14, Solving problems with quadratic
constraints (QCP), on page 197.) Consequently, the parallel barrier optimizer will
be the best continuous choice on a parallel computer more frequently than on a
single-processor. For that reason, you should be careful not to apply performance
data or experience based on serial optimizers when you are choosing which
optimizer to use on a parallel platform.
In the presence of more than three threads (for example, on hardware with four or
more processors), the behavior of the concurrent optimizer depends on whether
the parallel mode is opportunistic or deterministic. If the parallel mode is
opportunistic and a third processor is available, the concurrent optimizer runs all
three optimizers simultaneously: dual simplex, primal simplex, and barrier. All
further available threads are devoted to making the barrier optimization parallel.
In contrast, if the parallel mode is deterministic (the default mode) and more than
two threads are available, then the concurrent optimizer runs the dual simplex and
barrier optimizers simultaneously and applies all additional threads to making the
barrier optimizer parallel.
When the concurrent optimizer is invoked on a linear program (LP), it checks the
aspect ratio of the model. If the aspect ratio is large, and if more than 10 (ten)
threads are available to CPLEX, then concurrent optimization also invokes sifting
on the LP. For more information about sifting, see the topic Sifting optimizer on
page 136.
Barrier optimization is not considered complete until the crossover step has been
performed and simplex re-optimization has converged; in other words, regardless
of which optimizer turns out to be the fastest, the concurrent optimizer always
returns a basis solution at optimality.
The concurrent optimizer requires more memory than any individual optimizer,
and of course it adds system load by consuming more aggregate CPU time than
the fastest individual optimizer would alone. However, the advantages offered in
terms of robust solution of models, and assurance in most cases of the minimum
solution time, will make it attractive in many situations.
When you invoke concurrent optimization in your application, you may observe a
point in the log when CPLEX reports a solution of the model by an optimizer, but
no other report of activity appears in the log for a considerable time afterward.
During this apparently inactive period, concurrent optimization deterministically
manages potential race conditions among competing optimizers. Among other
decisions during this period, the concurrent optimizer decides deterministically
from which of those competing optimizers to accept the final solution.
If this apparent wait during the race between concurrent optimizers poses a
problem for your application, there are two alternative remedies available to you:
v Use opportunistic mode, rather than the default deterministic mode. To do so,
change the setting of the parallel mode switch (CPX_PARAM_PARALLELMODE,
ParallelMode) documented in the CPLEX Parameters Reference Manual.
v Set the optimizer that consistently solves your model first as the starting
algorithm in your application.
To do so, set the parameter appropriate for your type of model:
algorithm for continuous problems (CPX_PARAM_LPMETHOD, RootAlg)
documented in the CPLEX Parameters Reference Manual.
algorithm for continuous quadratic optimization (CPX_PARAM_QPMETHOD,
RootAlg) documented in the CPLEX Parameters Reference Manual.
algorithm for initial MIP relaxation (CPX_PARAM_STARTALG, RootAlg)
documented in the CPLEX Parameters Reference Manual.
CPLEX respects limits that the user sets, such limits as the number of iterations,
the amount of time spent in optimization, and so forth. However, to achieve
deterministic results, CPLEX must impose strict requirements with respect to
synchronization of parallel threads or processes. The stricter synchronization
requirements of deterministic parallel algorithms may cause CPLEX to deviate
slightly from any non default limits on the optimization. For example, with a node
limit of 2000 on four threads, the deterministic parallel MIP algorithm achieves the
two-thousandth node on one thread but must wait for the other three threads to
finish processing their nodes. In consequence, a final node count of up to 2003 is
possible. Similarly, with a finite time limit on the parallel concurrent optimizer, if
the thread for the dual simplex optimizer reaches the time limit first, it must wait
for the other optimizers to finish their next iteration before that thread terminates.
By default, CPLEX uses the deterministic parallel MIP optimizer to solve a mixed
integer programming problem. In doing so, it exploits parallel computations while
it solves nodes of the MIP branch & cut tree. It also executes strong branching
computations in parallel. Furthermore, CPLEX also exploits parallelism while
solving the root node.
In some models, the continuous root relaxation takes significant solution time
before parallel branching begins. These models have potential for additional speed
increases by running the root relaxation step in parallel. If the root problem is an
LP or QP, CPLEX invokes the concurrent optimizer by default. This practice
provides a form of parallelism that assigns the available threads to multiple
optimizers. If the root problem is a QCP, the barrier optimizer alone is used.
You can try a different form of parallelism at the root by selecting the barrier
optimizer specifically with the algorithm for initial MIP relaxation parameter:
v RootAlg in Concert Technology;
v CPX_PARAM_STARTALG in the Callable Library;
v set mip strategy startalgorithm in the Interactive Optimizer.
In such a case, the parallel threads will all be applied to the barrier algorithm.
To help you evaluate how much time is spent at the root node before parallel
processing begins, the log of parallel MIP processing distinguishes time spent
processing the root node before parallel branch and cut begins, like this typical log:
The log of sequential MIP optimization also distinguishes time spent processing
the root node before sequential branch and cut begins. If you contrast the
following sample log file of sequential MIP optimization with the previous sample
log file of parallel MIP optimization, you get an idea of the time spent in
synchronization and waiting. This contrast in your own model may help you
estimate how much speed up in performance to anticipate from parallel
optimization.
Root node processing (before b&c):
Real time = 4.25 sec. (971.17 ticks)
Sequential b&c:
Real time = 19.12 sec. (6441.66 ticks)
-------
Total (root+branch&cut) = 23.37 sec. (7412.83 ticks)
Before the parallel MIP optimizer invokes parallel processing, it makes separate,
internal copies of the initial problem. The individual processors use these copies
during computation, so each of them requires an amount of memory roughly equal
to the size of the presolved model.
The parallel MIP optimizer generates slightly different output in the node log (see
Progress reports: interpreting the node log on page 255) from the sequential MIP
optimizer. The following paragraphs explain those differences.
CPLEX prints a summary of timing statistics specific to the parallel MIP optimizer
at the end of optimization. You can see typical timing statistics in the following
sample run.
Selected objective sense: MINIMIZE
Selected objective name: COST
Selected RHS name: RHS
Selected bound name: BOUND
Nodes Cuts/
Node Left Objective IInf Best Integer Best Bound ItCnt Gap
The sample reports that the processors spent an average of 0.00 of a second of real
time waiting for other processors to provide nodes to be processed. The Sync time
is the average time a processor spends trying to synchronize by accessing globally
shared data. Because only one processor at a time can access such data, the amount
of time spent in synchronization is really crucial: any other processor that tries to
access this region must wait, thus sitting idle, and this idle time is counted
separately from the Wait time.
There is another difference in the way logging occurs in the parallel MIP optimizer.
When this optimizer is called, it makes a number of copies of the problem. These
If a log file is active when the clones are created, then CPLEX creates a clone log
file for each clone. The clone log files are named cloneK.log, where K is the index
of the clone, ranging from 0 (zero) to the number of threads minus one. Since the
clones are created at each call to the parallel MIP optimizer and discarded when it
exits, the clone logs are opened at each call and closed at each exit. (The clone log
files are not removed when the clones themselves are discarded.)
The clone logs contain information normally recorded in the ordinary log file (by
default, cplex.log) but inconvenient to send through the normal log channel. The
information likely to be of most interest to you are special messages, such as error
messages, that result from calls to the LP optimizers called for the subproblems.
On most platforms, CPLEX offers two ways of measuring time: CPU time and
wall-clock time. Wall-clock time is the default on all platforms.
You can choose the type of clock by setting the clock type for computation time
parameter to a value different from the default 2.
v In Concert Technology, use the method:
IloCplex::setParam(ClockType , i ) in the C++ API;
IloCplex.setParam(ClockType, i ) in the Java API;
Cplex.SetParam(ClockType, i ) in the .NET API.
v In the Callable Library, use the routine
CPXsetintparam(env, CPX_PARAM_CLOCKTYPE , i ) .
v In the Interactive Optimizer, use the command set clocktype i.
Replace the i with the value 0 (zero, the automatic setting) to use CPU time for
sequential optimizers and wall-clock time for parallel optimizers; 1 (one) to specify
CPU time; or 2 to specify wall-clock time.
CPLEX offers a programming interface to support remote objects that greatly ease
development of distributed parallel applications of CPLEX optimizers. The
following topics document the CPLEX remote object primarily in terms of the
CPLEX Callable Library (C API), but several of these topics emphasize special
considerations for the C++ and Java application programming interfaces (APIs).
IBM ILOG CPLEX offers features that greatly simplify your development of
applications implementing parallel algorithms that use CPLEX on distributed
hardware. Though CPLEX itself does not implement a distributed parallel
algorithm as such, it does provide the CPLEX remote object to facilitate your own
implementation.
According to that layout, any application using CPLEX eventually invokes routines
of the CPLEX Callable Library (C API). These routines are invoked either directly
(by applications using the C API) or through dedicated application programming
The introduction of the CPLEX remote object changes the situation slightly to a
layout that looks like the next figure.
According to that figure, on your local machine, your application calls a function
in a remote CPLEX API, instead of calling the CPLEX Callable Library directly.
This remote API implements stubs that forward function calls to an instance of the
CPLEX Callable Library that is actually located on a remote machine. This instance
of the Callable Library on the remote machine is also known as the CPLEX remote
object.
As you can see in that figure, nothing fundamental changes for the user-written
applications based on CPLEX. Such applications simply have to call the
corresponding functions of the remote API (instead of calling the same familiar
routines of the Callable Library locally). The remote API was carefully designed so
that only minimal changes are required when a user switches from a strictly local
application to a distributed application.
All three of these components are conventionally compiled into different executable
files.
A worker process runs on a remote machine. (In fact, workers can run in separate
processes on the local machine as well; more about that idea in other topics.) One
instance of a worker process represents one instance of a remote Callable Library
object. A worker process starts when the cplex binary file is invoked with the
argument -worker=process, -worker=mpi, or -worker=tcpip depending on which
data transport type is needed. The topic Transport types for the remote object on
page 371 explains data transport types and their options.
By default, a worker process does not run any user-written code. However, it is
possible to augment or extend a worker process by dynamically loading
user-defined, user-written code into this worker process. To augment or extend a
worker process, a user function instructs the worker process to load a dynamically
loaded object and to execute a predefined function in this object. The idea of a user
function is explained in the topic User functions to run user-defined code on the
remote machine on page 383.
That skeleton code solves the model declared in the file filename on the local
machine. What if you want to run the code on the local machine but perform the
actual solve on a different (remote) machine? In that situation, you can use the
CPLEX remote object. To solve the problem on a remote machine change this line:
env = CPXXopenCPLEX(&status);
In that code, the arguments transport, argc, argv define the machine on which
the remote solve takes place. In contrast to the original routine CPXXopenCPLEX,
the remote function CPXXopenCPLEXremote does not open the Callable Library
on the local machine, but rather on the remote machine specified by the
arguments. Like CPXXopenCPLEX, CPXXopenCPLEXremote returns a pointer to
the CPLEX environment, CPXENVptr that acts as a handle to the CPLEX Callable
Library; however, this time, it acts as the handle to a library instantiated on a
remote machine.
As you can see in the modified code snippet, the rest of the code does not need to
know nor care whether the CPXENVptr passed to it references a local or a remote
instance of the library. The implementation of the functions simply takes care to do
the right thing: if the CPXENVptr references a remote library, then the function call
is forwarded to this remote library instance; if the CPXENVptr argument references
a local library, then the call is executed locally.
This simple declaration assumes that the cplex binary file is available to your
application, for example, in your $PATH environment variable. This simple
declaration also assumes that it must start the remote environment in a different
process on the same machine.
For that more ambitious declaration to work successfully (invoking a secure shell
in a different process on a remote machine), you need to satisfy these
prerequisites:
v The cplex binary file must be installed on the remote machine, as cited in the
declaration; in this example, the remote machine is cited as remote.com, but
clearly your application must supply a valid name of a remote machine, where a
user account to which your application has login-access has already been set up
for remote access (such as automatic login, secure shell, or other facilities
appropriate to your environment).
v The location of the cplex binary file must be available in the remote
environment; for example, through the $PATH environment variable on
remote.com.
CPXXopenCPLEXremote
Upon success the routine stores 0 (zero) in status_p and returns a handle to the
remote library instance. In case of error, NULL is returned, and an error code is
stored in status_p.
The transport argument selects the transport type used to communicate with the
remote machine. CPLEX supports various transport types that allow you to use
different communication protocols with remote machines. The arguments argc and
argv are transport-specific arguments. They specify parameters and options that
the selected transport needs to establish the connection to a remote machine. For
more about these arguments, see the topic Transport types for the remote object
on page 371.
The CPLEX remote object is also available in the Concert C++ API. The examples
iloparbenders.cpp and iloparmipopt.cpp illustrate how to use the remote object
through the Concert C++ API. As you see in those examples, you use one of these
two constructors in the class IloCplex to instantiate a CPLEX remote object.
IloCplex(IloEnv env, char const *transport, int argc, char const *const *argv);
IloCplex(IloModel model, char const *transport, int argc, char const *const *argv);
The arguments transport, argc, argv are exactly the same arguments as for the
Callable Library (C API) routine CPXXopenCPLEXremote. Those arguments specify
how to connect to the remote object worker.
Internally, those constructors create a remote object master that off-loads all work
to the remote object worker specified by those connection arguments. Function
calls to the remote object worker are transparent; that is, you can not distinguish
between instances of the class IloCplex that were created with a remote object and
those that were not created with a remote object. In fact, you do not need to
distinguish whether an instance of IloCplex was created with a remote object. All
work the same.
The CPLEX remote object is also available in the Concert Java API. The example
RemoteParBenders.java illustrates how to use the remote object through the
Concert Java API. As you see in that example, you use this constructor in the class
IloCplex to instantiate a CPLEX remote object.
IloCplex(String transport, String[] args);
The arguments transport and args are exactly the same arguments as for the
Callable Library (C API) routine CPXXopenCPLEXremote. Those arguments specify
how to connect to the remote object worker.
Internally, that constructor creates a remote object master that off-loads all work to
the remote object worker specified by those connection arguments. Function calls
to the remote object worker are transparent; that is, you can not distinguish
between instances of the class IloCplex that were created with a remote object and
those that were not created with a remote object. In fact, you do not need to
distinguish whether an instance of IloCplex was created with a remote object. All
work the same.
Other arguments are passed without change, exactly as they are, as command-line
arguments to the new process that is started by the transport layer. These
remaining arguments define the command line of the cplex process to be started.
The following examples illustrate that point.
Example 1: Start a process on the local machine using named pipes in the current
directory.
Example 2: Start a process on the remote machine (remote.com for example) using
named pipes of that process for communication.
char const *args[] = { "-namedpipes=.",
"/usr/bin/ssh", "remote.com", "cplex",
"-worker=process" };
int status;
CPXENVptr env;
env = CPXXopenCPLEXremote("processtransport", sizeof(args) / sizeof(args[0]),
args, &status);
Both those examples assume that the cplex binary file is installed and accessible
according to the environment variable $PATH. If such is not the case, then you need
to specify a full path to the cplex binary file.
In Example 2, the cplex binary file must be installed on the remote machine
(identified in the example as remote.com); and the binary file must be accessible in
the remote $PATH environment variable there.
Example 2 also assumes that a remote login, not requiring a password, has already
been set up and is available to the application.
To use the Message Passing Interface (MPI), you must configure MPI to start the
CPLEX executable with the argument -worker=mpi on all machines being used.
Optionally, you can also pass arguments to specify a log file and to extend the
paths searched during dynamic loading of libraries (that is, the libpath
environment variable).
With that configuration, you must then create two environments on master, like
this:
CPXENVptr env[2];
int i;
for (i = 0; i < 2; ++i) {
int status;
char rank[32];
char const *argv[1] = { rank };
Note:
The Message Passing Interface (MPI) transport protocol is available for most (not
all) combinations of machine, operating system, compiler. On combinations for
which the MPI transport is provided, it was tested with OpenMPI V1.6. The
OpenMPI library for testing was configured with the following command-line
argument:
--enable-mpi-thread-multiple
where address is an internet (IP) address followed by a port number; for example:
127.0.0.1:1234. The worker listens to this address; the master connects to this
address, so the IP address and the port number must be valid in your
environment.
Like the process and MPI transport protocols, the TCP/IP transport protocol also
supports an optional argument to specify directories to search when dynamic
libraries are loaded.
-libpath=%s
This optional argument adds the specified directory to the list of directories
searched when dynamic libraries are loaded. This argument is optional. This
argument can be repeated.
The worker also supports an option to redirect output from the worker to a log
file:
-logfile=file
where file specifies the name of the file for logging output.
In certain situations, you need to call the same function on all remote objects, or
possibly on a group of remote objects. Of course, one option is to loop over all the
remote objects in the group, invoking the function for each object individually.
However, if great amounts of data (such as a large matrix) must be transferred as
the arguments of the function, then calling the function repeatedly in a loop
quickly introduces too much overhead. Some transport protocols offer improved
data handling in situations where one knows that the same data must be sent to
multiple machines. In that context, the following paragraphs explain how to send
data to multiple remote objects at the same time, a practice known as multicasting.
Tip:
This function creates a group from the number of environments nenvs specified in
envs. It stores a pointer to the newly created group in group_p. Each environment
can appear only once in the group. That is, it is an error to include duplicates in
the array of environments. After the group is created, you can invoke other
functions on the group, rather than on individual environments.
The reference manual documents the following functions, which are available for
operations on a group of environments.
v void CPXXfreeenvgroup (CPXENVGROUPptr *group_p)
v int CPXXgetenvgroupsize (CPXCENVGROUPptr group)
v CPXENVptr CPXXgetenvgroupenv (CPXCENVGROUPptr group, int idx)
For every CPLEX Callable Library routine that accepts an environment env and a
pointer to a linear programming problem lp, (such as CPXXfoo(env, lp, ...)),
there is CPXXfoo_multicast(group, ...) available for use with the remote object.
Likewise, for every CPLEX Callable Library routine that accepts only an
environment env (such as, for example, CPXbar(env, ...)), there is a
corresponding multicast routine CPXbar_multicast(group, ...). In both cases,
(whether the routine accepts only an environment, or whether the routine accepts
both an environment and a linear programming problem) the rest of the argument
list of the multicast routine replicates the argument list of the original routine.
In short, not all CPLEX Callable Library routines have a corresponding multicast
function. To support a multicast implementation, a CPLEX routine must satisfy
these criteria:
v No output arguments except a status return value
v At most, one problem object as an input argument
v An environment as an input argument
After you have created a group of environments, it is easy to call the same
function on each environment in the group simultaneously. As an example,
Instead, you can invoke a multicast function on the group of environments, like
this:
CPXENVGROUPptr group;
int status;
That routine deletes the rows with indices specified by the arguments [begin,
end] from the problem object referenced by lp.
The signature of the multicast version of that routine looks like this:
int CPXdelrows_multicast (CPXENVGROUPptr group, CPXDIM begin, CPXDIM end)
Notice that there is no problem object in the signature of the multicast function. In
fact, the problem object reference would be different for different remote objects
and a meaningful multicast would no longer be possible. To allow multicasts with
functions that have a problem object argument (either a pointer to a CPLEX linear
program CPXLPptr or to a CPLEX network program CPXNETptr) the problem
object argument is implied for multicast functions. In other words, each remote
object holds at most one problem object. When it performs a multicast, CPLEX
assumes that this unique problem object is the argument for the function (if a
problem object is required at all).
There are situations in which the function that is executed on the remote worker
may consume significant time, but the master can do other things while that
function runs on the remote worker. Consider, for example, an invocation of
CPXXmipopt on the remote worker. CPXXmipopt may take a long time to
complete. Instead of waiting for completion of CPXXmipopt, the master can update
a graphic user interface (GUI), start solutions on other machines, or run any other
application locally. Only when it has nothing else to do should it wait for the
remote call of CPXXmipopt to complete.
To support asynchronous execution with the remote object, the CPLEX Callable
Library (C API) offers specialized routines.
Consider, for example, the familiar CPLEX Callable Library routine, CPXXgetx.
int CPXXgetx (CPXCENVptr env, CPXCLPptr lp, double *x, CPXDIM begin, CPXDIM end);
The input arguments to this routine are env, lp, begin, end, so the routine that
initiates an asynchronous call to CPXXgetx has the following signature:
int CPXXgetx_async (CPXCENVptr env, CPXCLPptr lp, CPXDIM begin, CPXDIM end, CPXASYNCptr *handle_p);
Tip: A successful return of the routine CPXXgetx_async does not mean that
CPXXgetx on the remote worker completed and returned successfully. That
preliminary successful return of the asynchronous routine means only that
CPXXgetx started successfully.
Consider a specific example. For the conventional routine CPXXgetx, the signature
of the corresponding join routine looks like this:
int CPXXgetx_join (CPXHANDLEptr *handle_p, double *x);
CPXXgetx_join waits for the completion of the call of CPXXgetx on the remote
worker. Then the join routine fetches the result of that call (in this example, the
solution vector) and stores the result in x. The return value of CPXXgetx_join is
usually the return value of CPXXgetx on the remote worker. If a transport or
protocol error occurred while the join routine was waiting for the completion of
CPXXgetx, or if an invalid asynchronous pointer CPXASYNCptr was used, then the
join routine can also return an error code specifying this condition.
Tip: The asynchronous join routine destroys the handle pointed to by handle_p
and also sets *handle_p to NULL. It is an error to use the handle pointed to by
handle_p after the asynchronous join routine returns.
In the signature of an asynchronous test routine, the handle argument is the handle
returned by the corresponding asynchronous routine (such as the imaginary
CPXXfoo_async). This handle defines the asynchronous routine. The asynchronous
test will store a true value in the argument running_p if the routine defined by the
handle is still running; otherwise, it stores a value of false.
It is possible to terminate (that is, to kill) an asynchronous routine that has not yet
been joined. To do so, call the asynchronous kill routine.
int CPXXasynckill (CPXASYNCptr handle)
Calling that routine kills the function defined by the argument handle.
Use
After you call CPXXasynckill, you still need to call the corresponding join routine
(for example, the imaginary CPXXfoo_join) to wait for completion of the killed
routine; that is, to wait for the termination to take effect and to clean up resources.
Semantics
Not all conventional routines of the CPLEX Callable Library support asynchronous
execution. For example, routines such as CPXXgetnumrows do not support
asynchronous execution because they do not return a status. If a routine (for
example, the imaginary CPXXfoo) supports asynchronous execution, then the
CPLEX header files define a corresponding pair of routines, CPXXfoo_async and
CPXXfoo_join.
In the class IloCplex, several methods support asynchronous execution with the
remote object. These methods typically solve a model. For example, consider the
methods IloCplex::solve, IloCplex::feasOpt, IloCplex::refineConflict and others. For
When your application invokes that method with the value true for the argument
async, that method starts asynchronous execution of the method solve on the
remote object worker associated with the invoking instance of IloCplex.
Furthermore, the instance of IloCplex::SolveHandle returned by this method
represents the asynchronous solve just started.
The class IloCplex::AsyncHandle offers two important methods: test and kill.
IloCplex::AsyncHandle::test returns a Boolean value specifying whether the
asynchronous solve is still running. IloCplex::AsyncHandle::kill kills the
asynchronous solve.
Also CPLEX offers a static method (join) with several overloaded variations. Use
these methods in your application to join the asynchronous solve. In this context,
to join an asynchronous solve means for your application to wait for its completion
and to query the result. Specifically, AsyncHandle::join discards the Boolean return
value, which specifies the presence of a feasible solution, and SolveHandle::join
returns the Boolean value specifying the presence of a feasible solution.
To execute the same pseudo-code asynchronously with a CPLEX remote object, your
application includes lines like these:
IloCplex::SolveHandle handle = cplex.solve(true);
while ( handle.test() ) {
// sleep or do something else,
// maybe check for user abort and invoke handle->kill() if solve
// should be aborted.
}
bool feasible = handle.joinSolve();
if ( feasible ) { ... }
else { ... }
Tip: If you fail to join an asynchronous handle, then your application will leak
resources.
If you invoke the method (such as solve, feasOpt, refineConflict or other method)
with the value false for the argument async, then the method is executed
synchronously. That is, the method returns only after the activity has finished. The
To support asynchronous execution with the remote object, the Concert Java API
offers several methods in the class IloCplex. These methods typically solve a
model. For example, consider the methods IloCplex.solve, IloCplex.feasOpt,
IloCplex.refineConflict and others. For each of those methods, CPLEX provides
another overloaded method that accepts an additional argument (async) of type
boolean. Furthermore, each of those overloaded methods returns an instance of
IloCplex.AsyncHandle, or more precisely, an instance of one of its derived
subclasses.
For example, the class IloCplex.SolveHandle derives from the generic class
IloCplex.AsyncHandle. Consequently, for the method IloCplex.solve, the additional
overloaded method to support asynchronous execution with the remote object
offers this signature:
IloCplex.SolveHandle IloCplex.solve(boolean async);
When your application invokes that method with the value true for the argument
async, that method starts asynchronous execution of the method solve on the
remote object worker associated with the invoking instance of IloCplex.
Furthermore, the instance of IloCplex.SolveHandle returned by this method
represents the asynchronous solve just started.
The class IloCplex.AsyncHandle offers two important methods: test and kill.
IloCplex.AsyncHandle.test returns a Boolean value specifying whether the
asynchronous solve is still running. IloCplex.AsyncHandle.kill kills the
asynchronous solve.
Also CPLEX offers a static method (join) with several overloaded variations. Use
these methods in your application to join the asynchronous solve. In this context,
to join an asynchronous solve means for your application to wait for its completion
and to query the result. Specifically, AsyncHandle.join discards the Boolean return
value, which specifies the presence of a feasible solution, and SolveHandle.join
returns the Boolean value specifying the presence of a feasible solution.
Tip: If you fail to join an asynchronous handle, then your application will leak
resources.
If you invoke the method (such as solve, feasOpt, refineConflict or other method)
with the value false for the argument async, then the method is executed
synchronously. That is, the method returns only after the activity has finished. The
method still returns an instance of IloCplex.AsyncHandle. The method
IloCplex.AsyncHandle.test for this handle always returns false; that is, the activity
has already finished. Likewise, the method IloCplex.AsyncHandle.kill for this
handle does nothing because there is no activity to interrupt. However, you still
need to invoke the method join in your application in order to obtain a result and
to free resources.
In the framework of the CPLEX remote object, the worker that runs on the remote
machine is always a predefined binary file (that is, the cplex binary file) started by
the special argument -worker= . . . on the command line.
You can dynamically extend this binary file by means of a user functions. A user
function is a piece of executable code that is compiled into a dynamically loadable
object (that is, a shared library or DLL) and loaded by the worker binary file at
runtime.
Tip: User functions to extend the worker binary file are available from the Callable
Library (C API), the Concert C++ API, and the Concert Java API. However, to
implement a user function on the CPLEX remote object worker, you must use the
Callable Library (C API). In fact, the user function cannot "see" any of the Concert
C++ classes nor any of the Concert Java classes that are used on the master side of
the CPLEX remote object. For examples of how to implement a user function for
the the CPLEX remote object, see the sample iloparmipopt.cpp.
In order to load such an object, use the following command line argument:
where object is the name of the object file that contains the code to be loaded, and
function is the name of a function called by the worker binary file as soon as object
is loaded. The signature of this function must conform to this pattern:
CPXEXPORT void CPXPUBLIC function (CPXMESSAGEHANDLERptr handler)
Furthermore, the function must be visible to the worker binary file; that is, the
function can not be a static function.
The purpose of function is to register a handler function for user messages with the
message handler that is passed in to function. This user message handler has the
following signature:
int userfunction (CPXENVptr env, int id, CPXLONG inlen,
void const *indata, CPXLONG maxout,
CPXLONG *outlen_p, void *outdata, void *handle)
To register the handler function, use the following routine in the Callable Library
(C API):
int CPXXsetuserfunction (CPXMESSAGEHANDLERptr handler,
CPXUSERFUNCTION *function, void *handle)
When you serialize input and output data in an application of the Callable Library
(C API), the structures CPXSERIALIZERptr and CPXDESERIALIZERptr may be helpful.
You can create those structures by means of the routines CPXXserializercreate and
CPXXdeserializercreate to serialize data independantly of machine specifics, such
as byte order. See the reference manual of the CPLEX Callable Library (C API) for
documentation of these structures and routines for more detail, and see also the
topic Serializing for the remote object in this manual.
In the Concert C++ API, use the class IloCplex::Serializer to serialize arguments for
user functions. Likewise, use the class IloCplex::Deserializer to de-serialize results
of user functions.
In the Concert Java API, use the class IloCplex.Serializer to serialize arguments for
user functions. Likewise, use the class IloCplex.Deserializer to de-serialize results
of user functions.
Because pointers in a local process (such as master) are not meaningful to remote
processes (such as the workers, running in different processes, possibly on different
machines), you cannot simply serialize the pointer values of handles to the familiar
CPLEX LP object or network object. Instead, in order to serialize or de-serialize
handles to problem objects, use these special CPLEX routines:
v CPXXstoreNEThandle
v CPXXloadNEThandle
v CPXXstoreLPhandle
v CPXXloadLPhandle
In order to send an info message in the Callable Library (C API), the worker
invokes the appropriate routine, according to type.
v CPXXsendinfobyte
v CPXXsendinfoshort
v CPXXsendinfoint
v CPXXsendinfolong
v CPXXsendinfodouble
Tip: When you want to send data more complex than an info message from a
worker to master, first serialize the data of the message into memory by means of
the structure CPXSERIALIZER; then send the memory block by means of
CPXXsendinfobyte.
In addition to its data payload, each info message carries a user-defined tag. These
user-defined tags make it possible for your application to distinguish various
messages, particularly messages from different workers.
See the topic Processing status updates and receiving informational messages on
page 395 for sample code using info messages in an example of parallel concurrent
MIP optimization distributed among multiple workers.
In the Concert C++ API, your application can also capture info messages sent from
a remote worker. To do so, use the class IloCplex::RemoteInfoHandler in the
following procedure.
1. Subclass IloCplex::RemoteInfoHandler.
2. Implement the pure virtual function IloCplex::RemoteInfoHandler::main
3. Register an instance of the subclass with this instance of IloCplex by calling the
method IloCplex::setRemoteInfoHandler
In the Concert Java API, your application can also capture info messages sent from
a remote worker. To do so, use the class IloCplex.RemoteInfoHandler in the
following procedure.
1. Subclass IloCplex.RemoteInfoHandler.
2. Implement the pure virtual function IloCplex.RemoteInfoHandler.main
3. Register an instance of the subclass with this instance of IloCplex by calling the
method IloCplex.setRemoteInfoHandler
CPLEX offers a completely coded example parmipopt.c showing you how to use
the remote object in an application to solve a distributed concurrent mixed integer
program (MIP). The idea of the example is to solve a given MIP on two machines
known as the workers, using a third machine, known as the master, to track
progress and coordinate results.
One machine works mainly on the primal solution of the MIP. The other machine
works mainly on the dual solution of the MIP. The machine working on the primal
solution of the problem exploits all heuristic parameters, thus trying hard to find
A third machine, known as the master, keeps track of the best primal and dual
bounds found by either of the two worker machines. If the two bounds coincide,
the problem is solved, and the application fetches the optimal solution from the
machine that has the best primal bound.
You can find the entire code (parmipopt.c) among the examples provided with
CPLEX in the subdirectory identifying remote examples. In order to execute that
example, go to the directory corresponding to system and library format of your
installation, and execute the following command:
make -f Makefile.remote remote-run-parmipopt
To run all available examples of the remote object, use the following command:
make -f Makefile.remote remote-run
The code in parmipopt.c that runs on the master machine is compiled only if the
preprocessor macro CPX_REMOTE_CLIENT is defined.
To run concurrent MIP optimization, you must create a remote object on two
remote machines. In this example, localhost serves as the remote machine, but in
general any machine does the job.
Those lines create a remote object with an empty problem on a remote machine.
Choices such as which machine to use and how to connect to that machine are
specified by arguments of the function CPXXopenCPLEXremote.
v transport names the data transport protocol used to communicate with the
remote machine. Possible values are mpitransport, processtransport, or
tcpiptransport. (The topic Transport types for the remote object on page 371
explains more about those values.)
v nextarg specifies the number of strings in args.
v args is an array of strings expressing transport-specific configuration options.
The argument group contains a reference to the newly created group, which
contains all the jobs of all the environments of all the remote objects stored in the
argument env. After that group is created, it is possible to read the data file and
multicast it to all remote objects in the group, as in the following lines of code.
Those lines of code read the file locally into memory and then send this memory
block to all remote objects in the group. The remote objects then parse the memory
block as if it were a file, and thereby each remote object loads the respective
model. After CPXXreadcopyprob_multicast returns, all remote objects in the group
will have loaded the exact same model.
This example also sets the number of threads in each remote object to 1 (one). This
setting reduces machine load in case all "remote" machines are in fact the same
local host.
The following line deletes the group after the group is no longer needed.
CPXXfreeenvgroup (&group);
After the problem is solved, the application must gracefully shut down the remote
objects on the remote machines. This operation occurs in a simple loop, like this:
Tip:
You must destroy the objects in reverse order. For internal technical reasons, you
must respect this reversal. Destroying the objects in any other order can result in
unpredictable behavior.
In the example, a call of the user-written function applySetting applies these sets of
parameters. This function invokes the Callable Library routines CPXXsetintparam,
CPXXsetlongparam, CPXXsetdblparam on the CPLEX environment pointers
CPXENVptr that reference instances of remote objects. The code in the example is
familiar to users of conventional CPLEX, exactly the same as without the remote
object interface.
From the perspective of the master, the algorithm that the example parmipopt.c
implements looks like this:
Start the solve on all remote machines
while ( all machines are solving )
display progress message
if ( best known lower and upper bounds are close enough )
stop all machines
stop all machines that are still running
report the results
To start the solves on all remote objects, the example invokes the asynchronous
version of MIP optimization, like this:
After that call, each machine will have started solving the problem with the
defined parameter settings. The argument handle[i] is a handle to the remote
function call being executed on machine i. To check whether this function is still
running (that is, to verify that the machine is still solving and has not yet found
the optimal solution), the example uses the following line, an asynchronous test:
CPXXasynctest (handle[i], &running);
If the function is still running, that test puts a true value in the argument running;
otherwise, the function puts a false value there. If the value of running is false,
then the function call being tested has solved the problem to optimality. In that
case, the example stops all other machines and reads the optimal solution from the
machine that found it.
In order to stop an ongoing solve on a remote object that has not yet finished, the
example uses an asynchronous kill, like this:
CPXXasynckill (handle[j]);
In the first part of the loop, this example checks whether the best known lower
and upper bounds are close enough. Interestingly, the bounds may come from
different machines. If the bounds are close enough, then the optimal solution has
been found and optimality has been proven. In that case, the example can abort
the solve on all remote objects.
In the second part of the loop, the example checks whether any remote solve has
already finished. If so, then the example stops the solve on all other machines and
exits from the loop.
Since the loop implements a polling mechanism, the example implements a small
delay (usleep(200)) so as to not poll frantically.
In order to access the x vector of the optimal solution, the example (parmipopt.c)
simply keeps track of the remote object that reported the best feasible solution. In
fact, the argument bestidx is the index of the remote object that reported the best
In the example parmipopt.c, the remote worker machines regularly send status
updates to the master machine. To handle status messages (also known as
informational messages or informally, known as info messages) from a remote
object, the example needs to install an information handler (also known informally
as an info handler) in the environment that references the respective remote object.
The following lines of code install such an information handler to manage status
messages.
if ( (status = CPXXsetinfohandler (env[i], infohandler, &remotestats[i])) != 0 ) {
fprintf (stderr, "CPXXsetinfohandler: %d\n", status);
abort ();
}
In the example, these types of messages are managed in a simple switch statement
in the function infohandler by means of updates of global data structures.
The example parmipopt.c hooks into a MIP callback on the remote machine in
order to send status updates frequently from the remote machine to the master.
However, the remote object application programming interface (API) does not
explicitly support callbacks, so the example must accomplish the same end by
defining a custom user function for the remote object.
A custom user function is a piece of code that is loaded by the remote object at
runtime. The CPLEX remote API supports this customizing convention by means
of general user functions. On the client side, there is the generic function
CPXXuserfunction.
The argument id is an input argument and specifies which user function to invoke.
The arguments insize and in specify the input arguments for the user function.
When the function is called the first insize bytes of in are copied literally to the
remote object. Upon return of the function, the output provided by the user
function is copied literally from the remote object and stored in outsize_p and out.
The argument outspace gives the size of out, that is, the maximum amount of data
that can be stored there.
The means of getting that piece of code (that is, the custom user function) into the
remote object that actually executes the user-defined function is explained in the
topic Code running on the remote worker machines.
The command-line of the invocation of the cplex binary file in that example also
includes another option to specify and load a user-defined function. In the example
parmipopt.c, the user function code is compiled whenever the macro
COMPILE_USERFUNCTION is defined. It entails the following functions:
v REGISTER_USERFUNCTION is invoked when the shared object containing the
userfunction code is loaded. It simply registers the function named
userfunction as a CPLEX remote object user function; it does so by means of
CPXXsetuserfunction. (The topic User functions to run user-defined code on the
remote machine on page 383 introduces the idea of user functions in general
and outlines their registration.)
v infocallback is a MIP informational callback. In the example, it is installed by
the function named userfunction. The callback keeps track of the currently best
known primal and dual bounds as well as the current deterministic time.
Whenever a change in a primal or dual bound is observed, an info message (that
is, a status message) with the new bound is sent to the master. Every time the
callback is invoked, it sends an info message with the current deterministic time
to the master. (The topic Sending status messages to the master on page 386
introduces the idea of info messages and explains their use in general terms.)
v userfunction is invoked whenever the master issues a call to CPXXuserfunction.
Depending on the identifier, it performs setup operations (such as registering the
MIP informational callback), or it carries out cleanup operations.
To execute an application of the CPLEX remote object, you must perform these
steps:
1. Build the master process.
2. Build the object that implements the dynamically loadable user function.
3. Deploy both the user-function object and the cplex binary file to the remote
machine.
4. Execute that code.
For simplicity, the following amplification of those steps executes everything on the
same local machine, rather than on a real remote machine. However, later topics
highlight the differences between local and remote execution. Furthermore, the
make files provided with the product explicitly address those differences with
machine-specific settings.
To use another transport protocol, apply one of the following commands to specify
another transport instead:
make -f Makefile.remote TRANSPORT=mpi remote-run
or
make -f Makefile.remote TRANSPORT=tcpip remote-run
The simplified example of executing all code on the same, local machine glosses
over a few important points. When you deploy a distributed application using the
CPLEX remote object on multiple machines, you need to keep in mind the
following issues:
v You need to choose the correct cplex binary file to run on the remote machine.
The binary file to be executed remotely must be compiled for the architecture
and operating system of the remote machine.
v The same caveat applies to dynamically loadable objects that implement user
functions. The dynamically loadable objects must be built for the architecture
and operating system of the remote machine. If the remote platform (either
architecture or operating system) is different from the development platform,
this discrepancy requires either compiling directly on the remote machine or
cross compiling.
v Routines such as CPXXreadcopyprob read files on the file system of the master
process and transfer the contents of such files to remote machines. These
routines do not read files from the remote file system.
v Routines such as CPXXwriteprob write to the file system where the master
process is running. They do not write to the file system where the workers are
running.
To execute the example from the directory corresponding to system and library
format of your installation on GNU/Linux, UNIX, or MacOS operating systems,
use the following command:
make -f Makefile.remote remote-run-parbenders
The topic Chapter 27, Remote object for distributed parallel optimization, on
page 365 introduced the idea of a remote object to support distributed parallel
applications of CPLEX optimizers.
This topic goes further in that direction, introducing the CPLEX algorithm to
support a distributed application designed to solve a mixed integer program (MIP)
in parallel. Specifically, this topic outlines an algorithm that implements a variation
of branch and bound suitable for application across multiple machines (or multiple
nodes of a single machine) to solve a difficult mixed integer program (MIP) in
parallel.
This distributed parallel MIP algorithm runs on a single master associated with
multiple workers. The master and the workers can be physical or virtual machines.
Indeed, in this context, a virtual machine may simply be a process in the operating
system of a machine. Throughout the runtime of this algorithm, the master
coordinates the workers, and the workers perform the "heavy lifting" (that is, the
actual solving of the MIP).
The algorithm begins by presolving the MIP on the master. After presolving, the
algorithm sends the reduced model to each of the workers.
Each of the workers then starts to solve the reduced model. Each worker has its
own parameter settings, possibly different from the parameter settings of other
workers. Each worker solves the reduced model with its own parameter settings
for a limited period of time. This phase is known as ramp up. During ramp up,
each worker conducts its own search, according to its own parameter settings.
Ramp up stops when the master concludes that at least one of the workers has
created a sufficiently large search tree.
At that point, when ramp up stops, the master decides which of the workers
performed best. In other words, the master selects a winner. The parameter settings
The search tree on each of the non-winning workers is deleted. The search tree of
the winning worker is distributed over all workers, so that authentic distributed
parallel branch and bound starts from this point. In other words, all workers now
work on the same search tree, with the master coordinating the search in the
distributed tree.
The topic Distributed optimization of MIPs: the algorithm on page 401 outlined
the algorithm underlying distributed parallel MIP in CPLEX.
Search tree
In the CPLEX implementation of distributed parallel branch and bound, the master
keeps a number of nodes of the global search tree. If a worker becomes idle, the
master sends some of those nodes to that worker. The worker then starts branch
and bound on those nodes. However, the worker does not simply solve a node,
create some new nodes in doing so, and send them all back to the master. Instead,
the worker considers the search tree node received from the master as a new MIP.
The worker presolves that MIP and finds an optimal solution for that node using
branch and bound. In other words, a worker not only solves a single node; in fact,
the worker solves an entire subtree rooted at that node.
While this distributed parallel branch and bound algorithm is running, the master
can ask a worker to provide some open nodes (that is, unsolved nodes). The
master can then use these nodes to assign work to idle workers. To satisfy such a
request from the master, a worker picks a few open nodes from its current tree.
Because the current tree in a worker is a subtree of the global tree (indeed, it is the
subtree rooted at the node sent to the worker), every node in that subtree is also a
node in the global tree.
Stopping criterion
The distributed parallel branch and bound algorithm stops if all the nodes of the
global search tree have been processed or if it reaches a limit set by the user. Such
limits include a time limit, a limit on the number of nodes processed, a limit on the
number of solutions found, or other similar criteria.
The name of the parallel mode switch varies according to the application
programming interface (API) that you choose to use:
v In the C++ API, use the parameter IloCplex::Param::Parallel
v In the Java API, use the parameter IloCplex.Param.Parallel
v In the .NET API, use the parameter Cplex.Param.Parallel
v In the Callable Library (C API) API, use the parameter CPXPARAM_Parallel
v In the Python API, use the parameter parameters.parallel
v In the MATLAB API, use the parameter Cplex.Param.parallel
v In the Interactive Optimizer, use the command set with the option parallel and
the value -1.
If you are already familiar with solving a mixed integer program (MIP) by means
of conventional CPLEX MIP optimization, there are a few technical differences in
distributed parallel MIP optimization that are worth noting.
Distributed parallel MIP does not support control callbacks. Distributed parallel
MIP does not support query callbacks. However, you can use informational
callbacks with distributed parallel MIP. An informational callback pertains to the
master (not the workers). Furthermore, during rampup, there is no uniquely
defined tree. Consequently, callback information that depends on specifics of a
branch and bound tree does not exist. For more detail about the situations in
which informational callbacks work with distributed parallel MIP, see the
announcement Informational callbacks and distributed MIP. For definitions of
informational, query, and control callbacks, see the topic Chapter 35, Using
optimization callbacks, on page 477 in the CPLEX User's Manual. For examples of
informational callbacks, see these samples provided with the product:
v For users of the Callable Library (C API), see xdistmipex2.c.
v For users of the C++ API, see ilodistmipex2.cpp.
v For users of the Java API, see DistMIPEx2.java.
v For users of the Python API, see distmipex2.py.
v For users of MATLAB (Cplex class API), see distmipex2.m.
v For users of the C#.NET API, see DistMIPEx2.cs.
Distributed parallel MIP always starts from scratch. In contrast, with the
conventional MIP optimizer, you can specify limits, reach them, and restart. For
example, you can specify a limit on the number of nodes to solve and solve your
MIP conventionally with shared memory, stopping when the node limit occurs;
you can then extend the limit on nodes to a greater number and invoke solve
again. With conventional MIP with shared memory, the second solve takes up
where the first solve stopped.
This pattern of stopping and restarting from a previous point is not possible with
distributed parallel MIP. If you attempt that scenario of solving to a node limit,
stopping at the limit, extending the limit to a greater number, and restarting,
distributed parallel MIP simply starts over from scratch (not from the state when it
reached the previous limit).
Likewise, if you mix calls to conventional MIP with shared memory with calls to
distributed parallel MIP, the call to distributed parallel MIP will always start from
scratch. In fact, if you begin with distributed parallel MIP optimization, let it run
to a limit (such as a time limit, node limit, solutions limit), and then invoke
conventional MIP optimization, even the conventional MIP starts over from
scratch. It does not use any of the information accumulated in the previous
optimization.
A virtual machine configuration (VMC) file is an XML file that specifies the
communication protocol between a master and the workers, as well as the
configuration of each worker (one entry per worker) in a distributed parallel
optimization application. CPLEX accepts VMC files to specify a virtual machine
configuration for distributed parallel optimization.
To assist you in creating such a file yourself, your installation of the product
includes a document type definition (DTD) vmc.dtd and an XML schema
declaration (XSD) vmc.xsd.
CPLEX also offers a way of specifying parameters that apply only during ramp up
for distributed parallel optimization. To specify that a given parameter applies only
during ramp up, prepend
rampup:
to the name of the parameter in the virtual machine configuration (VMC) file.
For example, the following line in a VMC file specifies that CPLEX performs
depth-first search in both the ramp up phase and the distributed MIP optimization.
< param name="CPX_PARAM_NODESEL" value="0"/ >
In contrast, the following line using the new feature in a VMC file specifies that
CPLEX performs depth-first search only in the ramp up phase and applies the
default setting for node selection otherwise.
< param name="rampup:CPX_PARAM_NODESEL" value="0"/ >>
CPLEX expands an environment variable cited in a VMC file at the time that
CPLEX reads the VMC file. Reference to an environment variable must be of the
form:
$(name)
In particular, CPLEX does not recognize $name nor ${name}. CPLEX interprets the
character sequence $$ as a single $. This convention means that you can produce
verbatim $( by writing $$(.
If you are not using Linux, you may need to adapt the following points in the
instructional topics:
v LD_LIBRARY_PATH: The distributed parallel MIP algorithm implemented in
CPLEX uses dynamically loadable libraries. You have to prepare the runtime
linker so that it can find those libraries when CPLEX requests them. On Linux
this is done by pointing the LD_LIBRARY_PATH environment variable to the
directory that contains the CPLEX dynamically loadable libraries. On other
operating systems this variable may have different names. For example, on
MacOS it is called DYLD_LIBRARY_PATH, on AIX it is called LIBPATH and on
Windows it is PATH.
v Shared libraries: On Linux, the names of shared libraries files conventionally
have the suffix .so. On other operating systems, the suffix is different. For
example, on MacOS, the suffix is .dyld. On AIX, the suffix is .a. On Windows,
the suffix is .dll.
For simplicity, the instructions in the following topics assume that you have
deployed CPLEX in a directory that is shared over the network by all the machines
involved in your distributed application. This practice (an installation directory
shared across a network) is the conceptually simplest way to deploy CPLEX for
distributed parallel MIP. However, by no means is a "network share" a requirement
to run the CPLEX distributed MIP algorithm. Indeed, it is perfectly fine to deploy
CPLEX to a local directory on each machine involved.
The visible difference in that session from a conventional session with shared
memory MIP optimization in the Interactive Optimizer is the command to read a
configuration file:
read configuration.vmc
That command reads a file containing the configuration values for virtual
machines. That configuration tells CPLEX which machines are available for the
distributed parallel algorithm.
To see the current virtual machine configuration in your Interactive session, use
this command:
display vmc
This topic shows how to run distributed parallel MIP optimization with the Open
Message Passing Interface, also known as Open MPI, as the transport protocol to
manage communication between the master and workers.
Prerequisites
v Your CPLEX installation
v A network share or other network file-sharing system, such as Network File
System (NFS), accessible for all the machines, both master and workers, that you
plan to use; for clarification about this prerequisite, see Before you begin on
page 405, especially the point about a "network share" not being a hard
requirement.
v Your Open Message Passing Interface (Open MPI) installation in $MPIDIR
Procedure
1. Find the bin folder or directory of your CPLEX installation.
2. Put the cplex binary file and object files (that is, all files in bin) on the
accessible network share. On many platforms, the object files are designated by
the .so file extension. On Windows platforms, these equivalent files are
designated by the .dll file extension. On MacOS, the files are designated by
the .dyld file extension.
For the purpose of this example, all the files from the bin directory of your
CPLEX installation are now in /nfs/CPLEX.
3. Edit the following script to specify your master and workers as well as the
paths to libraries in your installation.
$MPIDIR/bin/mpirun \
-x "LD_LIBRARY_PATH=/nfs/CPLEX:$MPIDIR/lib" \
-tag-output \
-host host1 -host host2 -host host3 \
/nfs/CPLEX/cplex \
-mpi \
-libpath="$MPIDIR/lib" \
-mpilib="$MPIDIR/lib/libmpi.so" \
-mpiapi="openmpi"
<machine name="host3">
<transport type="MPI">
<rank value="2"/>
</transport>
</machine>
</vmc>
5. On the master (host1 in this example), start a session in the CPLEX Interactive
Optimizer.
6. In your interactive session, execute the command to read the configuration file
that you created.
CPLEX> read configuration.vmc
After that read command, CPLEX is set up for solving distributed parallel MIP
models.
7. Still in your interactive session, enter a model or read it from a file, and then
solve it, like this:
CPLEX> read model.lp
CPLEX> mipopt
8. All the usual commands of the Interactive Optimizer are available to access and
analyze your results.
This topic shows how to run distributed parallel MIP optimization with MPICH, a
portable implementation of the message passing interface, as the transport protocol
to manage communication between the master and workers.
Prerequisites
v Your CPLEX installation
v A network share or other network file-sharing system, such as Network File
System (NFS), accessible for all the machines, both master and workers, that you
plan to use; for clarification about this prerequisite, see Before you begin on
page 405, especially the point about a "network share" not being a hard
requirement.
v Your MPICH installation in $MPIDIR
Procedure
1. Find the bin folder or directory of your CPLEX installation.
2. Put the cplex binary file and object files (that is, all files in bin) on the
accessible network share. On many platforms, the object files are designated by
the .so file extension. On MacOS, the suffix is .dyld. On AIX, the suffix is .a.
On Windows platforms, these equivalent files are designated by the .dll file
extension.
For the purpose of this example, all the files from the bin folder or directory of
your CPLEX installation are now in /nfs/CPLEX/.
3. Edit the following script to specify your master and workers as well as the
paths to libraries in your installation. (The syntax is only slightly different from
the sample script in Using Open MPI with distributed parallel MIP on page
407.)
$MPIDIR/bin/mpirun \
-genv LD_LIBRARY_PATH /nfs/CPLEX:$MPIDIR/lib \
-hosts host1,host2,host3 \
--prepend-rank \
/nfs/CPLEX/cplex \
-mpi \
-libpath="$MPIDIR/lib" \
-mpilib="$MPIDIR/lib/libmpi.so" \
-mpiapi="mpich"
In that script, the argument -genv LD_LIBRARY_PATH /nfs/CPLEX:$MPIDIR/lib"
sets the environment variable so that the runtime linker can easily find the
CPLEX remote object and the MPICH libraries. For more information about the
CPLEX remote object, see the topic Chapter 27, Remote object for distributed
parallel optimization, on page 365 in the CPLEX User's Manual.
The arguments following -mpi are required only if you have installed more
than one MPI library or if your MPI library is not installed in the standard
location.
That sample script creates an MPI cluster of three machines, with host1 as
master and host2 and host3 as workers.
4. Create a Virtual Machine Configuration file, configuration.vmc, to define the
available workers. Here is a sample of such a file for this example.
<machine name="host3">
<transport type="MPI">
<rank value="2"/>
</transport>
</machine>
</vmc>
5. On the master (host1 in this example), start a session in the CPLEX Interactive
Optimizer.
6. In your interactive session, execute the command to read the configuration file
that you created.
CPLEX> read configuration.vmc
After that read command, CPLEX is set up for solving distributed parallel MIP
models.
7. Still in your interactive session, enter a model or read it from a file, and then
solve it, like this:
CPLEX> read model.lp
CPLEX> mipopt
8. All the usual commands of the Interactive Optimizer are available to access and
analyze your results.
This topic shows how to run distributed parallel MIP optimization with a process
as the transport protocol to manage communication between the master and
workers. Specifically, this example uses a secure shell (ssh) for the master to
connect to the workers. For simplicity, the example assumes that this secure shell
has been set up to use no password on the remote machines. For more information
about a process transport protocol, compare the topic Transport types for the
remote object: process on page 372 about the CPLEX remote object.
Procedure
The procedure for using a process transport protocol for communication is slightly
simpler than for using a message passing transport protocol since you do not need
to declare a message passing cluster. However, the declarations in the Virtual
Machine Configuration file configuration.vmc are more detailed.
1. Find the bin folder or directory of your CPLEX installation.
2. Put the cplex binary file and object files (that is, all files in the bin folder or
directory) on the accessible network share. On many platforms, the object files
are designated by the .so file extension. On Windows platforms, these
equivalent files are designated by the .dll file extension. On MacOS, the suffix
is .dyld. On AIX, the suffix is .a.
For the purpose of this example, all the files from the bin directory of your
CPLEX installation are now in /nfs/CPLEX.
3. Create a Virtual Machine Configuration file, configuration.vmc, to define the
available workers. Here is a sample of such a file for a secure shell as the
process.
<?xml version="1.0"?>
<vmc>
<machine name="host2">
<transport type="process">
<cmdline>
<item value="ssh"/>
<item value="host2"/>
<item value="/nfs/CPLEX/cplex"/>
<item value="-worker=process"/>
<item value="-stdio"/>
<item value="-libpath=/nfs/CPLEX"/>
</cmdline>
</transport>
</machine>
<machine name="host3">
<transport type="process">
<cmdline>
<item value="ssh"/>
<item value="host2"/>
<item value="/nfs/CPLEX/cplex"/>
<item value="-worker=process"/>
<item value="-stdio"/>
<item value="-libpath=/nfs/CPLEX"/>
</cmdline>
</transport>
</machine>
</vmc>
4. On the master (host1 in this example), start a session in the CPLEX Interactive
Optimizer.
5. In your interactive session, execute the command to read the configuration file
that you created.
This topic shows how to run distributed parallel MIP optimization with TCP/IP,
Transmission Control Protocol/Internet Protocol, as the transport protocol to
manage communication between the master and workers. This approach is slightly
more complicated than a process transport or a message passing interface (MPI)
transport because you must start a CPLEX remote object worker, whereas these
remote objects as workers start automatically for the other types of transport. For
more information about using TCP/IP with CPLEX, compare the topic Transport
types for the remote object: TCP/IP on page 374.
Prerequisites
v Your CPLEX installation
v A network share or other network file-sharing system, such as Network File
System (NFS), accessible for all the machines, both master and workers, that you
plan to use; for clarification about this prerequisite, see Before you begin on
page 405, especially the point about a "network share" not being a hard
requirement.
Procedure
1. Find the bin folder or directory of your CPLEX installation.
2. Put the cplex binary file and object files (that is, all files in bin) on the
accessible network share. On many platforms, the object files are designated by
the .so file extension. On Windows platforms, these equivalent files are
designated by the .dll file extension. On MacOS, the suffix is .dyld. On AIX,
the suffix is .a.
For the purpose of this example, all the files from the bin folder or directory of
your CPLEX installation are now in /nfs/CPLEX.
3. For each host that will serve as a worker, execute the following command:
/nfs/CPLEX/cplex -worker=tcpip -libpath=/nfs/cplex -address=ip:12345
where ip is the name of the host or its IP address and 12345 is the number of
the port where the worker will listen for incoming connections. (You are free to
choose a different port number here.)
That command starts a TCP/IP server to wait for connections from the master.
The TCP/IP server also spawns worker processes as requested. The server does
not terminate itself, however. You must explicitly terminate it; for example, by
pressing CTRL-C when your optimization completes.
4. Create a Virtual Machine Configuration file, configuration.vmc, to define the
available workers. Here is a sample of such a file for TCP/IP.
<machine name="host3">
<transport type="TCP/IP">
<address host="ip-of-host3" port="12345"/>
</transport>
</machine>
</vmc>
That sample file defines two workers, one worker on host2 and one worker on
host3. The sample value ip-of-hostN is either the IP address or the name of the
host. The host names and the port numbers must be the same in the
configuration file as those used to start the TCP/IP worker on the
corresponding host.
5. On the master (host1 in this example), start a session in the CPLEX Interactive
Optimizer.
6. In your interactive session, execute the command to read the configuration file
that you created for this TCP/IP example.
CPLEX> read configuration.vmc
After that read command, CPLEX is set up for solving distributed parallel MIP
models.
7. Still in your interactive session, enter a model or read it from a file, and then
solve it, like this:
CPLEX> read model.lp
CPLEX> mipopt
8. All the usual commands of the Interactive Optimizer are available to access and
analyze your results.
Compilation of applications that use distributed parallel MIP conforms to the same
conventions as any other Callable Library (C API) application of CPLEX.
You write a distributed parallel MIP application in C++ in much the same way that
you write a conventional (shared memory) application of CPLEX in C++. In that
application, you include an additional step: before you invoke the method
IloCplex::solve, you must load a Virtual Machine Configuration (a
configuration.vmc file, as explained in Distributed parallel MIP in the Interactive
Optimizer on page 406.) The presence of the configuration file tells IloCplex::solve
to run distributed parallel MIP instead of conventional (shared memory) MIP
optimization.
Source code
CPLEX offers sample code ilodistmipex1.cpp that shows how to use distributed
parallel MIP. The important statements in that code are these statements:
IloEnv env;
IloModel model(env);
IloCplex cplex(model);
cplex.importModel(model, argv[i]);
cplex.readCopyVMConfig(vmconfig);
cplex.solve();
Those first four statements create an instance of the CPLEX solver and load the
model of a problem into it.
Finally, the call to IloCplex::solve checks whether the problem is a MIP and
whether a Virtual Machine Configuration has been loaded. If so, CPLEX runs
distributed parallel MIP optimization. Otherwise, it uses one of the conventional
algorithms to solve the loaded model.
On platforms other than Windows, however, the linking is slightly different for an
application of distributed parallel MIP optimization. When your application uses
distributed parallel MIP, you must link with libcplexdistmip.a, a part of the
CPLEX distribution, located in the same place as libcplex.a. You must also link
with libdl.so to enable your application to load shared objects. To link with
libdl.so, you can add the option -ldl to your linker command.
You write a distributed parallel MIP application in Java in much the same way that
you write a conventional (shared memory) application of CPLEX in Java. In that
application, you include an additional step: before you invoke the method
IloCplex.solve, you must load a Virtual Machine Configuration (a
Source code
CPLEX offers sample code DistMIPex1.java that shows how to use distributed
parallel MIP. The important statements in that code are these statements:
cplex = new IloCplex();
cplex.importModel(arg);
cplex.readCopyVMConfig(vmconfig);
cplex.solve();
Those first two statements create an instance of the CPLEX solver and load the
model of a problem into it.
Finally, the call to IloCplex.solve checks whether the problem is a MIP and
whether a Virtual Machine Configuration has been loaded. If so, CPLEX runs
distributed parallel MIP optimization. Otherwise, it uses one of the conventional
algorithms to solve the loaded model.
Java applications that use distributed parallel MIP build in the same way as
conventional Java applications of CPLEX.
You write a distributed parallel MIP application in Python in much the same way
that you write a conventional (shared memory) application of CPLEX in Python. In
that application, you include an additional step: before you invoke the method
solve of the class Cplex, you must load a Virtual Machine Configuration (a
configuration.vmc file, as explained in Distributed parallel MIP in the Interactive
Optimizer on page 406.) The presence of the configuration file tells solve to run
distributed parallel MIP instead of conventional (shared memory) MIP
optimization.
Source code
CPLEX offers sample code distmipex1.py that shows how to use distributed
parallel MIP. The important statements in that code are these statements:
cpx = cplex.Cplex()
cpx.read(arg)
cplex.read_copy_vmconfig(vmconfig)
cpx.solve()
Those first two statements create an instance of the CPLEX solver and load the
model of a problem into it.
Finally, the call to solve checks whether the problem is a MIP and whether a
Virtual Machine Configuration has been loaded. If so, CPLEX runs distributed
parallel MIP optimization. Otherwise, it uses one of the conventional algorithms to
solve the loaded model.
There are situations in which you want to run all code on a single machine. For
example, when you are debugging a complex application, running all code locally
can be beneficial. For the purpose of this example, assume that the local
environment offers 12 cores. In such a case, it can be useful to view that computer
as 12 virtual machines with one core each.
To configure such an environment, you simply add a threads parameter for each
virtual machine in your Virtual Machine Configuration, like this:
<machine>
<transport type=". . .">
. . .
</transport>
<param name="threads" value="1" />
</machine>
That configuration tells CPLEX to use one thread on this particular virtual
machine. The deployment in this case is almost the same as in other cases, with
these differences:
v If you choose message passing interface (MPI) as the transport protocol, use
"localhost" as the host name for each host.
v If you choose the process transport, omit the following arguments from the
command line:
<item value="-stdio" />
<item value="ssh" />
<item value="hostname" />
v If you choose TCP/IP transport protocol, you need to start only one worker on
the local host. All virtual machines declared in the Virtual Machine
Configuration (that is, the configuration.vmc file) should connect to the same
address, such as localhost:12345, for example.
Such a configuration provides interesting opportunities. For example, you can use
a single machine to ramp up an infinite horizon. That practice of ramping up with
an infinite horizon is also known as concurrent MIP optimization. For certain hard
models, concurrent MIP optimization or ramping up with an infinite horizon has
been shown to improve performance, even on a single computer with multiple
cores.
The topics discussed in Part 3, Continuous optimization, on page 131 and Part 4,
Discrete optimization, on page 215 often contained the implicit assumption that a
bounded feasible solution to your model actually exists. Such may not always be
the case.
The following topics discuss steps to try when the outcome of an optimization is a
declaration that your model is either:
v infeasible; that is, no solution exists that satisfies all the constraints, bounds,
and integrality restrictions; or
v unbounded; that is, the objective function can be made arbitrarily large.
(A more careful definition of unbounded is provided in What is
unboundedness? on page 425.)
Infeasibility can arise from various causes, and it is not possible to automate
procedures to deal with those causes entirely without input or intervention from
the user. For example, in a shipment model, infeasibility could be caused by
insufficient supply, or by an error in demand, and it is likely that the optimizer
will tell the user only that the mismatch exists. The formulator of the model has to
make the ultimate judgment of what the actual error is.
However, there are ways for you to try to narrow down the investigation with
help from CPLEX, for example, through tools such as the conflict refiner,
documented in Chapter 31, Diagnosing infeasibility by refining conflicts, on page
429. In certain situations, CPLEX even provides some degree of automatic repair,
for example, with tools such as FeasOpt, documented in Chapter 32, Repairing
infeasibilities with FeasOpt, on page 443.
The following topic explains how to interpret reports of infeasibility that occur
before optimization begins, that is, reports that arise from preprocessing.
Reductions that are independent of the objective function are called primal
reductions; those that are independent of the righthand side (RHS) of the
constraints are called dual reductions.
Preprocessing operates on the assumption that the model being solved is expected
by the user to be feasible and that a finite optimal solution exists. If this
assumption is false, then the model is either infeasible or no bounded optimal
solutions exist; that is, it is unbounded.
Since primal reductions are independent of the objective function, they cannot
detect unboundedness; they can detect only infeasibility.
If the preprocessor still finds inconsistency in the model, the preprocessor will
declare the model infeasible, instead of infeasible or unbounded.
To control the types of reductions performed by the presolver, set the parameter to
one of the following values:
v 0 = no primal and dual reductions
v 1 = only primal reductions
v 2 = only dual reductions
v 3 = both primal and dual reductions (default)
These settings of the parameter are intended for diagnostic use, as turning off
reductions will usually have a negative impact on performance of the optimization
algorithms in the normal (feasible and bounded) case.
What is unboundedness?
Defines unboundedness and tells how to access further information about the
problem.
Any class of model, continuous or discrete, linear or quadratic, has the potential to
result in a solution status of unbounded. An unbounded discrete model must have a
continuous relaxation that is also unbounded. Therefore, the discussion here
assumes that you first relax any discrete elements, and thus you deal with an
unbounded continuous optimization problem, when you try to diagnose the cause.
Note:
The reverse of that observation (that an unbounded discrete model necessarily has
an unbounded continuous relaxation) is not necessarily the case: a discrete
optimization model may have an unbounded continuous relaxation and yet have a
bounded optimum.
A declaration of unboundedness means that IBM ILOG CPLEX has detected that
the model has an unbounded ray. That is, for any feasible solution x with objective
z, a multiple of the unbounded ray can be added to x to give a feasible solution
with objective z-1 (or z+1 for maximization models). Thus, if a feasible solution
exists, then the optimal objective is unbounded.
When a model is declared unbounded, CPLEX has not necessarily concluded that a
feasible solution exists. Users can call methods or routines to discover whether
CPLEX has also concluded that the model has a feasible solution.
v In Concert Technology, call one of these methods:
isDualFeasible
isPrimalFeasible
try/catch the exception
v In the Callable Library, call the routine CPXsolninfo.
The default variable type in CPLEX has a lower bound of 0 (zero) and an upper
bound of infinity. If you declare a variable to be of type Free, its lower bound is
negative infinity instead of 0 (zero). A model can not be unbounded unless one or
more of the variables has either of these infinite bounds. Therefore, one
If an unbounded solution is not possible in the physical system you are modeling,
then adding finite lower and upper bounds or adding other constraints may
represent something realistic about the system that is worth expressing in the
model anyway. However, great care should be taken to assign meaningful bounds,
in cases where it is not possible to be certain what the actual bounds should be. If
you happen to select bounds that are tighter than an optimal solution would
obtain, then you can get a solution with a worse value of the objective function
than you want. On the other hand, picking extremely large numbers for bounds
(just to be safe) carries some risk, too: on a finite-precision computer, even a bound
of one billion may introduce numeric instability and cause the optimizer to solve
less rapidly or not to converge to a solution at all, or may result in solutions that
satisfy tolerances but contain small infeasibilities.
Diagnosing unboundedness
Explains typical messages related to unboundedness and suggests ways to access
further information.
You may be able to diagnose the cause of unboundedness by examining the output
from the optimizer that detected the unboundedness. For example, if the
preprocessing at the beginning of optimization made a series of reductions and
then stopped with a message like this:
Similarly, the primal simplex optimizer may terminate with a message like this:
Diverging variable = x2
In such a case, you should focus attention on x2. (The dual simplex and barrier
optimizers work differently than primal; they do not detect unboundedness in this
way.) Unfortunately, the variable which is reported in one of these ways may or
may not be a direct cause of the unboundedness, because of the many algebraic
manipulations performed by the optimizer along the way.
Since an unbounded outcome means that an unbounded ray exists, one approach
to diagnosis is to display this ray. In Concert Technology, use the method getRay;
If you are familiar with LP theory, then you might consider transforming your
model to the associated dual formulation. This transformation can be
accomplished, for example, by writing out the model in DUA format and then
reading it back in. (See the CPLEX File Formats Reference Manual for a brief
description of DUA as a file format.)
Another way to transform your model to its associated dual formulation is first to
enter your model in the Interactive Optimizer and then to use the following series
of commands to write out the dual formulation of your model to a file in PRE
format.
set preprocessing dual 1 \\ presolve the primal and pass the dual to the optimizer
set preprocessing presolve 0 \\ turn off presolve
set preprocessing reduce 0 \\no primal or dual reductions
write dual.pre
The dual model of an unbounded model will be infeasible. And that means that
you can use the conflict refiner to reduce the infeasible model to a minimal
conflict. (See Chapter 31, Diagnosing infeasibility by refining conflicts, on page
429 for more about the conflict refiner.) It is possible that the smaller model will
allow you to identify the source of the (dual) infeasibility more easily than the full
model allows.
What is a conflict?
Defines a conflict as a set of mutually contradictory constraints in a model.
CPLEX refines conflicts only among the constraints and bounds in your model. It
disregards the objective function while it is refining a conflict. In particular, if you
have set a MIP cutoff value with the idea that the cutoff value will render your
model infeasible, and then you apply the conflict refiner, you will not achieve the
effect you expect. In such a case, you should add one or more explicit constraints
to enforce the restriction you have in mind. In other words, add constraints rather
than attempt to enforce a restriction through the lower cutoff and upper cutoff
parameters:
v CutLo or CutUp in Concert Technology (not recommended to enforce infeasibility)
v CPX_PARAM_CUTLO or CPX_PARAM_CUTUP in the Callable Library (not recommended
to enforce infeasibility)
v mip tolerance lowercutoff or uppercutoff in the Interactive Optimizer (not
recommended to enforce infeasibility)
CPXrefineconflictext CPXgetconflictext
Interactive Optimizer conflict display conflict all write file.clp
The following sections explain more about these methods and routines.
However, a conflict is more general than an IIS. The IIS finder is applicable only to
continuous LP models, (including those obtained from a continuous QP by
removing the quadratic objective terms). In contrast, the conflict refiner is capable
of doing its work on all continuous and discrete problem types that CPLEX
supports, except for those with explicit second order cone constraints.
Also, you can specify one or more groups of constraints for a conflict; a group will
either be present together in the conflict, or else will not be part of it at all.
While the conflict refiner usually will deliver a smaller set of constraints to
consider than the IIS finder will, the methods are different enough that the reverse
can sometimes be true. The fact that the IIS finder implements a standard
You can get acquainted with the conflict refiner in the Interactive Optimizer.
Certain features of the conflict refiner, namely, preferences and groups, are
available only through an application of the Callable Library or Concert
Technology. Those additional features are introduced in Using the conflict refiner
in an application on page 440.
read filename
or you can use the enter command, followed by a name for the problem, followed
by these lines:
Minimize
obj: cost
Subject To
c1: - cost + 80 x1 + 60 x2 + 55 x3 + 30 x4 + 25 x5 + 80 x6 + 60 x7 + 35 x8
+ 80 x9 + 55 x10 = 0
c2: x1 + x2 + 0.8 x3 + 0.6 x4 + 0.4 x5 >= 2.1
c3: x6 + 0.9 x7 + 0.5 x8 >= 1.2
c4: x9 + 0.9 x10 >= 0.8
c5: 0.2 x2 + x3 + 0.5 x4 + 0.5 x5 + 0.2 x7 + 0.5 x8 + x10 - service = 0
c6: x1 + x6 + x9 >= 1
c7: x1 + x2 + x3 + x6 + x7 + x9 >= 2
c8: x2 + x3 + x4 + x5 <= 0
c9: x4 + x5 + x8 <= 1
c10: x1 + x10 <= 1
Bounds
service >= 3.2
Binaries
x1 x2 x3 x4 x5 x6 x7 x8 x9 x10
End
This simple model, for example, might represent a project-staffing problem. In that
case, the ten binary variables could represent employees who could be assigned to
duty.
The first constraint defines the cost function. In this example, the objective is to
minimize the cost of salaries. The next three constraints (c2, c3, c4) represent three
nonoverlapping skills that the employees must cover to varying degrees of ability.
The fifth constraint represents some additional quality metric (perhaps hard to
The remaining constraints represent various work rules that reflect either policy
that must be followed or practical guidance based on experience with this work
force. Constraint c6, for example, dictates that at least one person with managerial
authority be present. Constraint c7 requires at least two senior personnel be
present. Constraint c8 indicates that several people are scheduled for off-site
training during this period. Constraint c9 recognizes that three individuals are not
productive together. Constraint c10 prevents two employees who are married to
each other from working in this group in the same period, since one is a manager.
If you apply the optimize command to this example, you will see these results:
Row c8 infeasible, all entries at implied bounds.
Presolve time = 0.00 sec.
MIP - Integer infeasible.
Current MIP best bound is infinite.
Solution time = 0.00 sec. Iterations = 0 Nodes = 0
The declaration of infeasibility comes from presolve. In fact, presolve has already
performed various reductions by the time it detects the unresolvable infeasibility in
constraint c8. This information by itself is unlikely to provide any useful insights
about the source of the infeasibility, so try the conflict refiner, by entering this
command:
conflict
The first line of output mentions 14 members; this total represents constraints,
lower bounds, and upper bounds that may be part of the conflict. There are ten
constraints in this model; there are two continuous variables with lower and upper
bounds that represent the other four members to be considered. Because binary
variables are not reasonable candidates for bound analysis, the Interactive
Optimizer treats the bounds of only the variables cost and service as potential
members of the conflict. If you want all bounds to be candidates, you could
instead declare the binary variables to be general integer variables with bounds of
[0,1]. (Making that change in this model would likely result in the conflict refiner
As you can see in the log displayed on the screen, the conflict refiner works to
narrow the choices until it arrives at a conflict containing only two members. Since
the conflict is small in this simplified example, you can see it in its entirety by
entering this command:
You can also write the entire conflict to a file in LP-format to browse later by using
the command (where modelname is the name you gave the problem):
Interpreting conflict
Interprets the conflict detected by the conflict refiner in the Interactive Optimizer.
In those results in Interpreting the results and detecting conflict on page 432, you
see that c8, the constraint mentioned by presolve, is indeed a fundamental part of
the infeasibility, as it directly conflicts with one of the skill constraints. In this
example, with so many people away at training, the skill set in c2 cannot be
covered. Perhaps it would be up to the judgment of the modeler or management to
decide whether to relax the skill constraint or to reduce the number of people who
will be away at training during this period, but something must be done for this
model to have a feasible solution.
For the sake of explanation, assume that a managerial decision is made to cancel
the training in this period. To implement that decision, try entering this command:
Perhaps presolve has identified a source of infeasibility, but if you run the
conflict command again, you see these results:
Refine conflict on 13 members...
Iteration Max Members Min Members
1 12 0
2 9 0
3 6 0
4 4 0
5 3 0
6 3 1
7 3 2
8 3 3
Minimal conflict: 2 linear constraint(s)
1 lower bound(s)
0 upper bound(s)
Conflict computation time = 0.00 sec. Iterations = 8
The constraints mentioned by presolve are part of the minimal conflict detected by
the conflict refiner. The additional information provided by this conflict is that the
lower bound on service quality could also be considered for modification to
The other information this Conflict provides is that no change of the upper bound
on service, currently infinity, could aid toward feasibility; perhaps that is already
obvious, but even a finite upper bound would not be part of this conflict (as long
as it is larger than the lower bound of 3.2).
Note the additional constraint provided in this conflict: sum_eq. It is a sum of all
the equality constraints in the conflict. In this case, there is only one such
constraint; sometimes when there are more, an imbalance will become quickly
apparent when positive and negative terms cancel.
Changing a bound
Explains the effect of changing a bound reported in a conflict by the conflict
refiner.
Again, for the sake of the example, assume that it is decided after consultation
with management to repair the infeasibility by reducing the minimum on the
service metric, on the grounds that it is a somewhat arbitrary metric anyway. A
minimal conflict does not directly tell you the magnitude of change needed, but in
this case it can be quickly detected by examination of the minimal conflict that a
new lower bound of 2.9 could be achievable; select 2.8, to be safe. Modify the
model by entering this command:
Displaying the solution indicates that employees {2,3,5,6,7,10} are used in the
optimal solution.
Adding a constraint
Explains the effect of adding a constraint to an infeasible model.
A natural question is why so many employees are needed. Look for an answer by
adding a constraint limiting employees to five or fewer, like this:
add
x1+x2+x3+x4+x5+x6+x7+x8+x9+x10 <= 5
end
optimize
Constraint c11, flagged by presolve, is the newly added constraint, not revealing
very much. To learn more about why c11 causes trouble, run conflict again, and
view the minimal conflict with the following command again:
The constraints in conflict with this new limitation are all of the skill requirements.
When viewed in this light, the inconsistency is easy to spot: one employee is
obviously needed for constraint c4, two are needed for c3, and a simple calculation
reveals that three are needed for c2. Since there is no overlap in the skill sets, five
employees are too few.
This change results in a model with an optimal cost of 335, using six employees.
No better cost is possible in this formulation. Still, you may wonder, "Why not?" To
try yet another scenario, instead of limiting the number of employees, try focusing
on the cost by changing the upper bound of the cost to 330, like this:
change bound cost upper 330
optimize
conflict
display conflict all
This series of commands again renders the model infeasible and shows a minimal
conflict:
Subject To
c1: - cost + 80 x1 + 60 x2 + 55 x3 + 30 x4 + 25 x5 + 80 x6 + 60 x7
+ 35 x8 + 80 x9 + 55 x10 = 0
The upper bound on cost is, of course, expected to be in the conflict, so relaxing it
would merely put the scenario back the way it was. The constraint c1 defines cost,
so unless there is some unexpected latitude in setting salaries, no relief will be
found there. Constraints c2 and c3 represent two skill requirements, previously
judged beyond negotiation, and constraint c5 represents service quality, already
compromised a bit. That rough analysis leaves c9, the requirement not to use three
particular employees together.
Relaxing a constraint
Explains the effect of relaxing a constraint in the model.
How much is it costing to maintain this rule? Consider asking them to work
productively pairwise, if not all three, and relax the upper limit of this constraint,
like this:
change rhs c9 2
optimize
The model is now restored to feasibility, and the new optimum has an overall cost
of 310, a tangible improvement of 25 over the previous optimum, using employees
{2,3,5,6,8,10}; employee 7 has been removed in favor of employee 8. Is that enough
monetary benefit to offset whatever reasons there were for separating employees 4
and 8? That is not a decision that can be made here; but at least this model
provides some quantitative basis toward making that decision. Additionally, a
check of the service variable shows that its solution value is back up to 3.2, a
further benefit from relaxing constraint c9. Perhaps this decision should have been
made sooner, the first time constraint c9 appeared in a conflict.
The solution of 310 could be investigated further by changing the upper bound of
cost to be 305, for example. The conflict resulting from this change consists of the
skills constraint plus the constraint requiring at least one manager on duty. At this
point, the analysis has reached a conclusion, unless management or the model
formulator decides to challenge the policy.
Presolve proved the infeasibility of that simplified example in A model for the
conflict refiner on page 431. However, a minimal conflict can be refined from an
infeasible model regardless of how the infeasibility was found. The infeasibility
may have been proven by presolve, by the continuous optimizers, or by the mixed
integer optimizer.
A minimal conflict on a nontrivial model can take longer to refine than the
associated optimization algorithm would have taken either to prove the
infeasibility or to solve a similar model instance that was feasible. One reason that
refining a minimal conflict may take longer is that multiple passes (that is,
iterations) are performed; each iteration solves a submodel to decide its feasibility
If the user sets a resource limit, such as a time limit, an iteration limit, or node
limit, for example, or if a user interrupts the process interactively, the conflict that
is available at that termination will be the best (that is, the most refined) that was
achievable at that point. Even a nonminimal conflict may be more useful than the
full model for discovering the cause of infeasibility. The status of a bound or
constraint in such a nonminimal conflict may be proved, meaning that the conflict
refiner had sufficient resources to prove participation of bound or constraint in the
conflict, or the status may be possible, meaning that the conflict refiner has not yet
proven whether the bound or constraint is necessarily part of a minimal conflict.
If a model contains more than one cause of infeasibility, then the conflict that is
delivered may not be unique. As you saw in the example, you may repair one
infeasibility only to find that there is another arising. An iterative approach may be
necessary.
Removing any one of the constraints in conflict (1) results in feasibility. Likewise,
removing either of the constraints in conflict (2) also results in feasibility. Either
representation may guide you toward a correct analysis of the infeasibilities in the
model.
Keep in mind also that a conflict may guide you toward multiple ways to repair a
model, some more reasonable than others. For example, if the conflict in a model
using continuous variables to represent percentages looked like this:
x1 + x2 + x3 >= 400
Bounds
0 <= x1 <= 100
0 <= x2 <= 100
0 <= x3 <= 100
When the model passed to the conflict refiner is actually feasible, the conflict
refiner will return this message:
Problem is feasible; no conflict available
An attempt to display or access a conflict when none exists, whether because the
conflict refiner has not yet been invoked or because an error occurred, results in
this error message:
No conflict exists.
The cause of those messages will usually be apparent to a user. However, numeric
instability may cause genuine uncertainty for a user. In an unstable model, one of
the optimizers may return a valid conclusion of infeasibility, based on the numeric
precision allowed by the model, and yet when a trivial modification is made, the
model status changes, and a feasible solution now seems attainable. Because one of
the conventional signs of instability can be this switching back and forth from
feasibility to infeasibility, the user should be alert to this possibility. The conflict
refiner halts and returns an error code if an infeasible model suddenly appears
feasible during its analysis, due to this presumption of numeric instability. The
user should turn attention away from infeasibility analysis at that point, and
toward the sections in this manual such as Numeric difficulties on page 146.
The conflict refiner can accept a MIP start. This feature may aid you in debugging
a MIP start that CPLEX rejects. That is, you can use the conflict refiner to analyze a
rejected MIP start and may then be able to repair the MIP start appropriately.
To use the conflict refiner to debug a rejected MIP start, follow these steps:
1. Add the MIP start to the CPLEX object.
2. Call a special version of the conflict refiner, specifying which MIP start to
consider:
v In Concert Technology
In the C++ API, use IloCplex::refineMIPStartConflict.
In the Java API, use IloCplex.refineMIPStartConflict.
In the .NET API, use Cplex.RefineMIPStartConflict.
v In the Callable Library, use CPXrefinemipstartconflict or
CPXrefinemipstartconflictext.
v In the Interactive Optimizer, use the command conflict i where i specifies
the index of the MIP start.
When a MIP start is added to the current model, you may specify an effort level to
tell CPLEX how much effort to expend in transforming the MIP start into a feasible
solution. The conflict refiner respects effort levels except level 1 (one): check
feasibility. It does not check feasibility of a MIP start.
Here is an example using the conflict refiner in the C++ API of Concert
Technology. You will modify one of the standard examples ilomipex2.cpp
distributed with the product. Starting from that example, locate this statement in it:
cplex.solve();
Immediately after that statement, insert the following lines to prepare for and
invoke the conflict refiner:
if ( ( cplex.getStatus() == IloAlgorithm::Infeasible ) ||
( cplex.getStatus() == IloAlgorithm::InfeasibleOrUnbounded ) ) {
cout << endl << "No solution - starting Conflict refinement" << endl;
IloConstraintArray infeas(env);
IloNumArray preferences(env);
infeas.add(rng);
infeas.add(sos1); infeas.add(sos2);
if ( lazy.getSize() || cuts.getSize() ) {
cout << "Lazy Constraints and User Cuts ignored" << endl;
}
for (IloInt i = 0; i<var.getSize(); i++) {
if ( var[i].getType() != IloNumVar::Bool ) {
infeas.add(IloBound(var[i], IloBound::Lower));
infeas.add(IloBound(var[i], IloBound::Upper));
}
}
if ( cplex.refineConflict(infeas, preferences) ) {
IloCplex::ConflictStatusArray conflict = cplex.getConflict(infeas);
env.getImpl()->useDetailedDisplay(IloTrue);
cout << "Conflict :" << endl;
for (IloInt i = 0; i<infeas.getSize(); i++) {
if ( conflict[i] == IloCplex::ConflictMember)
cout << "Proved : " << infeas[i] << endl;
if ( conflict[i] == IloCplex::ConflictPossibleMember)
cout << "Possible: " << infeas[i] << endl;
}
}
else
cout << "Conflict could not be refined" << endl;
cout << endl;
}
Now run this modified version with the model you have seen in A model for the
conflict refiner on page 431. You will see results like these:
No solution - starting Conflict refinement
For example, in the resource allocation problem from A model for the conflict
refiner on page 431, management might consider the three skill requirements (c2,
c3, c4) as inseparable. Adjusting the data in any one of them should require a
careful re-evaluation of all three. To achieve that effect in the modified version of
ilomipex2.cpp, replace this line:
infeas.add(rng);
After that modification, the cost constraint and the constraints indexed 4 through
10 are treated individually (that is, normally) as before. The three constraints
indexed 1 through three are combined into a skills constraint through the IloAnd
operator, and added to the infeasible set.
Individual preferences are not assigned to any of these members in this example,
but you could assign preferences if they express your knowledge of the problem.
After this modification to group the skill constraints, a minimal conflict is reported
like this, with the skill constraints grouped inseparably:
Conflict :
Proved : IloAnd and36 = {
c2( 2.1 <= ( x1 + x2 + 0.8 * x3 + 0.6 * x4 + 0.4 * x5 ) )
c3( 1.2 <= ( x6 + 0.9 * x7 + 0.5 * x8 ) )
c4( 0.8 <= ( x9 + 0.9 * x10 ) ) }
What is FeasOpt?
Defines FeasOpt, defines preferences, and describes facilities in FeasOpt.
The infeasibility on which FeasOpt works must be present explicitly in your model
among its constraints and bounds. In particular, if you have set a MIP cutoff value
with the idea that the cutoff value will render your model infeasible, and then you
apply FeasOpt, you will not achieve the effect you expect. In such a case, you
should add one or more explicit constraints to enforce the restriction you have in
mind. In other words, add constraints rather than attempt to enforce a restriction
through the lower cutoff and upper cutoff parameters:
v CutLo or CutUp in Concert Technology (not recommended to enforce infeasibility)
v CPX_PARAM_CUTLO or CPX_PARAM_CUTUP in the Callable Library (not recommended
to enforce infeasibility)
v mip tolerance lowercutoff or uppercutoff in the Interactive Optimizer (not
recommended to enforce infeasibility)
Invoking FeasOpt
Describes routines and methods to invoke FeasOpt.
Depending on the interface you are using, you invoke FeasOpt in one of the ways
listed in the table Table 61.
Table 61. FeasOpt
API or Component FeasOpt
Concert Technology for C++ users IloCplex::feasOpt
Concert Technology for Java users IloCplex.feasOpt
Concert Technology for .NET users Cplex.FeasOpt
Callable Library CPXfeasopt and CPXfeasoptext
In the Callable Library, you can allow changes without distinguishing bounds on
variables from ranges over constraints.
In each of the APIs, there is an additional argument where you specify whether
you want merely a feasible solution suggested by the bounds and ranges that
FeasOpt identifies, or an optimized solution that uses these bounds and ranges.
Tip:
FeasOpt does NOT apply to models with explicit second order cone constraints,
unless the preferences indicate that FeasOpt cannot modify those constraints.
FeasOpt does accept convex quadratic constraints.
Specifying preferences
Describes preferences and their effect on bounds and ranges in FeasOpt.
You specify the bounds or ranges that FeasOpt may consider for modification by
assigning positive preferences for each. A negative or zero preference means that the
associated bound or range is not to be modified. One way to construct a weighted
penalty function from these preferences is like this:
Thus, the larger the preference, the more likely it will be that a given bound or
range will be modified. However, it is not necessary to specify a unique preference
for each bound or range. In fact, it is conventional to use only the values 0 (zero)
and 1 (one) except when your knowledge of the problem suggests assigning
explicit preferences.
The output from FeasOpt consists of two vectors: a conventional solution vector
and another vector of values for infeasible constraints and variables.
FeasOpt produces a vector of solution values, like the solution vector produced by
the Callable Library routine CPXlpopt or the Concert Technology method solve.
Your application accesses this vector by means of the usual queries to access a
solution vector.
v In Concert Technology, use one of these methods to access the solution vector
produced by FeasOpt:
If the model that your application passed to FeasOpt is infeasible, these solution
values violate at least one constraint or variable bound.
Similarly, these amounts in the infeasibility vector are significant for ranges, such
as ranged rows in the Callable Library (C API) or for instances of the class
IloRange in the C++ API and Java API, or Range in the .NET API. Specifically, a
negative infeasibility value specifies the amount by which the lower bound of the
range must be changed; a positive value specifies the amount by which the upper
bound of the range must be changed.
The following examples show you how to use FeasOpt. These fragments of code
are written in Concert Technology for C++ users, but the same principles apply to
the other APIs as well. The examples begin with a model similar to one that you
have seen repeatedly in this manual.
IloEnv env;
try {
IloModel model(env);
IloNumVarArray x(env);
IloRangeArray con(env);
IloNumArray vals(env);
IloNumArray infeas(env);
cplex.setOut(env.getNullStream());
IloNumArray lb(env);
IloNumArray ub(env);
env.out() << endl << "*** First feasOpt call ***" << endl;
env.out() << "*** Consider all constraints ***" << endl;
int rows = con.getSize();
lb.add(rows, 1.0);
ub.add(rows, 1.0);
The code first turns off logging to the screen by the optimizers, simply to avoid
unnecessary output. It then allocates arrays lb and ub, to contain the preferences as
input. The preference is set to 1.0 for all three constraints in both directions to
indicate that any change to a constraint range is permitted.
Then the code calls FeasOpt. If the FeasOpt call succeeds, then several lines of
output show the results. Here is the output:
*** First feasOpt call ***
*** Consider all constraints ***
There are several items of note in this output. First, you see that FeasOpt
recommends only the first constraint to be modified, namely, by increasing its
lower bound by 50 units.
To get a more concrete idea, assume that this constraint represents a limit on a
supply, and assume further that increasing the supply to 70 is not practical. Now
rerun FeasOpt, not allowing this constraint to be modified, like this:
// second feasOpt call
env.out() << endl << "*** Second feasOpt call ***" << endl;
env.out() << "*** Consider all but first constraint ***" << endl;
lb[0]=ub[0]=0.0;
Those lines disallow any changes to the first constraint by setting lb[0]=ub[0]=0.0.
FeasOpt runs again, and here are the results of this second run:
*** Second feasOpt call ***
*** Consider all but first constraint ***
*** Suggested bound changes = [-0, -0, -50]
*** Feasible objective value would be = 50
Solution status = Infeasible
Solution obj value = 50
Values = [40, 17.5, 42.5]
Notice that the projected maximal objective value is quite different from the first
time, as are the optimal values of the three variables. This solution was completely
unaffected by the previous call to FeasOpt. This solution also is infeasible with
respect to the original model, as you would expect. (If it had been feasible, you
would not have needed FeasOpt in the first place.) The negative suggested bound
change of the third constraint means that FeasOpt suggests decreasing the upper
bound of the third constraint by 50 units, transforming this constraint:
x[0] + x[1] + x[2] >= 150
into
x[0] + x[1] + x[2] >= 100
That second call changed the range of a constraint. Now consider changes to the
bounds.
env.out() << endl << "*** Third feasOpt call ***" << endl;
env.out() << "*** Consider all bounds ***" << endl;
In those lines, all six bounds (lower and upper bounds of three variables) are
considered for possible modification because a preference of 1.0 is set for each of
them. Here is the result:
*** Third feasOpt call ***
*** Consider all bounds ***
Those results suggest modifying only one bound, the upper bound on the first
variable. And just as you might expect, the solution value for that first variable is
exactly at its upper bound; there is no incentive in the weighted penalty function
to set the bound any higher than it has to be to achieve feasibility.
Now assume for some reason it is undesirable to let this variable have its bound
modified. The final call to FeasOpt changes the preference to achieve this effect,
like this:
// fourth feasOpt call
env.out() << endl << "*** Fourth feasOpt call ***" << endl;
env.out() << "*** Consider all bounds except first ***" << endl;
lb[0]=ub[0]=0.0;
Then after the fourth call of FeasOpt, the output to the screen looks like this:
*** Fourth feasOpt call ***
*** Consider all bounds except first ***
*** Could not repair the infeasibility
Unknown exception caught
This is a correct outcome, and a more nearly complete application should catch
this exception and handle it appropriately. FeasOpt is telling the user here that no
modification to the model is possible within this set of preferences: only the
bounds on the last two variables are permitted to change according to the
preferences expressed by the user, and they are already at [0,+inf], so the upper
bound can not increase, and no negative value for the lower bounds would ever
improve the feasibility of this model.
Not every infeasibility can be repaired, and an application calling FeasOpt will
usually need to take this practicality into account.
In contrast to the cuts that IBM ILOG CPLEX may automatically add while solving
a problem, user cuts are those cuts that a user defines based on information
already implied about the problem by the constraints; user cuts may not be strictly
necessary to the problem, but they tighten the model. Lazy constraints are
constraints that the user knows are unlikely to be violated, and in consequence, the
user wants them applied lazily, that is, only as necessary or not before needed.
User cuts can be grouped together in a pool of user cuts. Likewise, lazy constraints
can also be grouped into a pool of lazy constraints.
Important:
Only linear constraints may be included in a pool of user cuts or lazy constraints.
Neither user cuts nor lazy constraints may contain quadratic terms.
Sometimes, for a MIP formulation, a user may already know a large set of helpful
cutting planes (user cuts), or can identify a group of constraints that are unlikely to
be violated (lazy constraints). Simply including these cuts or constraints in the
original formulation could make the LP subproblem of a MIP optimization very
large or too expensive to solve. Instead, these situations can be handled in one of
these ways:
The principle in common between these two pools allows the optimization
algorithm to perform its computations on a smaller model than it otherwise might,
in the hope of delivering faster run times. In either case (whether in the case of
pools of user cuts or pools of lazy constraints), the model starts out small, and
then potentially grows as members of the pools are added to the model. Both
kinds of pool may be used together in solving a MIP model, although that would
be an unusual circumstance.
Cuts may resemble ordinary constraints, but are conventionally defined to mean
those which can change the feasible space of the continuous relaxation but do not
rule out any feasible integer solution that the rest of the model permits. A
collection of cuts, therefore, involves an element of freedom: whether or not to
apply them, individually or collectively, during the optimization of a MIP model;
the formulation of the model remains correct whether or not the cuts are included.
This degree of freedom means that if valid and necessary constraints are
mis-identified by the user and passed to CPLEX as user cuts, unpredictable and
possibly incorrect results could occur.
By contrast, lazy constraints represent simply one portion of the constraint set, and
the model would be incomplete (and possibly would deliver incorrect answers) in
their absence. CPLEX always makes sure that lazy constraints are satisfied before
producing any solution to a MIP model. Needed lazy constraints are also kept in
effect after the MIP optimization terminates, for example, when you change the
problem type to fixed-integer and re-optimize with a continuous optimizer.
Another important difference between pools of user cuts and pools of lazy
constraints lies in the timing by which these pools are applied. CPLEX may check
user cuts for violation and apply them at any stage of the optimization.
Conversely, it does not guarantee to check them at the time an integer-feasible
solution candidate has been identified. Lazy constraints are only (and always)
checked when an integer-feasible solution candidate has been identified, and of
course, any of these constraints that turn out to be violated will then be applied to
the full model.
Cuts that are based on optimality and that remove integer feasible solutions
without removing all optimal solutions are known as optimality-based cuts.
Optimality-based cuts do not fit the definition of either a user cut nor a lazy
constraint. For example, symmetry-breaking constraints are sometimes known as
optimality-based cuts because symmetry-breaking constraints can remove integer
feasible solutions without removing all optimal solutions. Symmetry-breaking
constraints are not user cuts in the sense addressed here. Symmetry-breaking
constraints are not necessarily lazy constraints either. However, CPLEX can support
optimality-based cuts as lazy constraints. If you add an optimality-based cut as a
lazy constraint in your model, you can also add it to the user cut pool. This
Another way of comparing these two types of pools is to note that the user
designates constraints as lazy in the strong hope and expectation that they will not
need to be applied, thus saving computation time by their absence from the
working problem. In practice, it is relatively costly (for a variety of reasons) to
apply a lazy constraint after a violation is identified, and so the user should err on
the side of caution when deciding whether a constraint should be marked as lazy.
In contrast, user cuts may be more liberally added to a model because CPLEX is
not obligated to use any of them and can apply its own rules to govern their
efficient use.
CPLEX offers an example that highlights the difference between pools of user cuts
and lazy constraints. The example demonstrates lazy constraint callbacks to
separate integer feasible LP solutions and user cut callbacks to separate fractional
infeasible LP solutions in a Benders decomposition of an asymmetric travelling
salesperson problem.
v In the Callable Library, see xbendersatsp.c (64-bit) and bendersatsp.c (32-bit).
v In the C++ API, see ilobendersatsp.cpp.
v In the Java API, see BendersATSP.java.
v In the C#.NET API, see BendersATSP.cs.
v In the VB.NET API, see BendersATSP.vb.
v In the Python API, see bendersatsp.py.
Here is a procedure for identifying which constraints in your model are good
candidates to be lazy constraints. The procedure entails changing the problem type
more than once.
1. Begin with your mixed integer programming model (MIP).
2. Change its problem type to a linear program (LP).
3. Solve that LP.
4. Identify constraints for which the dual value is 0 (zero). These are the
constraints to make lazy after another change of problem type.
5. Change the LP problem type to a mixed integer linear program problem type
(MILP) so that the identified constraints can be added as lazy constraints.
6. Solve the problem again, this time as a MILP.
Tip: Lazy constraints can be added only to problems of the type MILP; lazy
constraints cannot be added to a problem of the type LP.
For an introduction to changing a problem type, see the topic Changing problem
type in MIPs on page 219.
Certain considerations apply to user cuts, user-cut pools, and user-cut callbacks.
v If the linear reduction switch (CPX_PARAM_PRELINEAR, PreLinear) is set to 1 (one),
its default, then nonlinear presolve reductions are allowed. If there are user cuts
in the model as well, then it can happen that a user cut cannot be crushed to the
presolved model, and CPLEX therefore disregards the uncrushed user cut. In
that case, CPLEX displays a log message on the results channel: "Could not
crush NNN user cuts." where NNN is the number of user cuts that CPLEX
could not crush and that CPLEX consequently disregarded.
v Furthermore, the linear reduction switch (CPX_PARAM_PRELINEAR, PreLinear) is set
to 1 (one), its default, and there is a user cut callback, then it can happen that
the user cut generated in the user cut callback cannot be crushed to the
presolved model. This situation means that a call to CPXcutcallbackadd or
CPXcutcallbackaddlocal returns the status CPXERR_PRESLV_CRUSHFORM. This error
is not critical: the message simply tells the user that the cut was discarded
because it could not be crushed to the presolved model; in other words, the cut
could not be represented in terms of presolved problem variables. Since user
cuts are optional (they do not affect the integer-feasible region of the problem),
discarding a user cut does not affect the correctness of the algorithm.
Lazy constraints, lazy constraint pools, and lazy constraint callbacks differ from
their user-cut counterparts in this respect: CPLEX automatically adjusts these
parameters in all APIs to turn off dual reductions when CPLEX detects the
presence of lazy constraints in a model:
v the linear reduction switch (CPX_PARAM_PRELINEAR, PreLinear)
v the primal and dual reduction type (CPX_PARAM_REDUCE, Reduce)
The following facilities add user defined cuts to a user cut pool.
The following facilities will add lazy constraints to a lazy constraint pool.
v The Callable Library routine is CPXaddlazyconstraints.
v The Concert Technology methods
addLazyConstraints in the C++ API
IloCplex.addLazyConstraints in the Java API
Cplex.AddLazyConstraints in the .NET API
v The Python API method
cplex.linear_constraints.advanced.add_lazy_constraints
User cuts and lazy constraints appear when you enter the command display
problem all in the Interactive Optimizer. You can also add user cuts and lazy
constraints to an existing problem with the add command of the Interactive
Optimizer.
User cuts and lazy constraints may also be specified in LP-format files, and so may
be read:
v With the Interactive Optimizer read command
v Through the routine CPXreadcopyprob of the Callable Library
v Through the methods of Concert Technology:
importModel of the C++ API
IloCplex.importModel of the Java API
Cplex.ImportModel of the .NET API
When CPLEX writes LP-format files, user cuts and lazy constraints added through
their respective add routines or read from LP format files are included in the
output files along with their names (if any).
General syntax
The general syntax rules for LP format documented in the CPLEX File Formats
Reference Manual apply to user cuts and lazy constraints.
v The user cuts section or sections must be preceded by the keywords USER CUTS.
v The lazy constraints section or sections must be preceded by the keywords
LAZY CONSTRAINTS.
Example
CPLEX stores user cuts and lazy constraints in memory separately from ordinary
constraints.
User cuts and lazy constraints may also be specified SAV-format files, and so may
be read:
v With the Interactive Optimizer read command
v Through the routine CPXreadcopyprob of the Callable Library
v Through the methods of Concert Technology:
importModel of the C++ API
IloCplex.importModel of the Java API
Cplex.ImportModel of the .NET API
When CPLEX writes SAV format files, user cuts and lazy constraints added
through their respective add routines or read from SAV format files are included in
the output files along with their names (if any).
CPLEX extends the MPS file format with additional optional sections to
accommodate user defined cuts and lazy constraints. The usual routines of the
Callable Library and methods of Concert Technology to read and write MPS files
also read and write these optional sections. These additional sections follow the
ROWS section of an MPS file in this order:
v ROWS
v USERCUTS
The syntax of these additional sections conforms to the syntax of the ROWS section
with this exception: the type R cannot appear in USERCUTS nor in LAZYCONS . For
details about the format of the ROWS section in the MPS file format, see the
CPLEX File Format Reference Manual, especially these sections:
v The ROWS section
v User-defined cuts in MPS files
v Lazy constraints in MPS files
In the Callable Library, the pools of user cut and lazy constraint are cleared by the
routines CPXfreeusercuts and CPXfreelazyconstraints. Clearing the pools does
not change the MIP solution.
Clearing a pool means that the user cuts and lazy constraints in the pool are
removed and are not applied the next time MIP optimization is called, and that the
What is a goal?
Defines a goal in CPLEX.
Goals allow you to take control of the branch & cut search procedure used by IBM
ILOG CPLEX to solve MIP problems. To help you understand how to use goals
with CPLEX, these sections review how this procedure works.
v Overview of goals in the search
v How goals are implemented in branch & cut on page 462
v About the method execute in a goal on page 462
Note:
The search procedure manages a search tree consisting of nodes. Every node
represents a subproblem to be solved, and the root node of the tree represents the
entire problem. Nodes are called active if they have not yet been processed.
The tree is first initialized to contain the root node as the only active node. CPLEX
processes active nodes from the tree until either no more active nodes are available
or some limit has been reached. After a node has been processed, it is no longer
active.
When processing a node, CPLEX starts by solving the continuous relaxation of its
subproblem (that is, the subproblem without integrality constraints). If the solution
violates any cuts, CPLEX adds them to the node problem and re-solves. This
procedure is iterated until no more violated cuts are found by CPLEX. If at any
point the relaxation becomes infeasible, the node is pruned, that is, it is removed
from the tree.
To solve the node problem, CPLEX checks whether the solution satisfies the
integrality constraints. If so, and if its objective value is better than that of the
current incumbent, the solution of the node problem is used as the new incumbent.
Otherwise, CPLEX splits the node problem into one or two smaller subproblems,
typically by branching on a variable that violates its integrality constraint. These
subproblems are added to the tree as active nodes and the current node is
deactivated.
Note:
The discussion of the details of using goals will be presented mainly in terms of
the C++ API. The Java and .NET APIs follow the same design and are thus
equivalent at this level of discussion. In cases where a difference between these
APIs needs to be observed, the point will be raised. Where the difference is only in
syntax, the other syntax will be mentioned in parentheses following the C++
syntax.
Typically, the implementation of the method execute will perform the following
steps:
1. Check feasibility. An interesting possibility here is that the feasibility check may
include more than verifying integrality of the solution. This allows you to
enforce constraints that could not reasonably be expressed using linear
constraints through cuts or branching. In other words, this allows you to use
goals in a way that makes them part of the model to be solved. Such a use is
common in Constraint Programming, but it is less frequently used in
Mathematical Programming. Note, however, that this CP-style application of
goals prevents you from making use of MP-style advanced starting
information, either from a MIP Start file or by restarting a previous
optimization with one that uses goals.
2. Optionally find local or global cuts to be added. Local cuts will be respected
only for the subtree below the current node, whereas global cuts will be
enforced for all nodes from then on.
3. Optionally construct a solution and pass it to CPLEX.
4. Instruct CPLEX how to proceed. Instruct CPLEX how to proceed through the
return value of the method execute; the return value of execute is another goal.
CPLEX simply continues by executing this goal.
An Or goal is a goal that creates subnodes of the current node. This function takes
at least 2 and up to 6 goals as arguments. For each of its arguments, the Or goal
will create a subnode in such a way that when that subnode is processed, the
corresponding goal will be executed. After the goal has been executed, the current
node is immediately deactivated.
And goal
Describes the And goal to combine other goals in a fixed order of execution.
An And goal also takes goals as arguments. It returns a goal that will cause CPLEX
to execute all the goals passed as arguments in the order of the arguments.
Fail goal
Describes the Fail goal for pruning the search tree.
A Fail goal creates a goal that causes CPLEX to prune the current node. In other
words, it discontinues the search at the node where the goal is executed. CPLEX
will continue with another active node from the tree, if one is available.
A local cut goal adds a local cut to the node where the goal is executed.
In the C++ API, the class IloCplex::Goal has constructors that take an instance of
IloRange or an instance of IloRangeArray (IloRange[]) as an argument. When one
of these constructors is used, a local cut goal is created.
To create local cut goals with the Java API, use the method
IloCplex.constraintGoal or if more convenient, one of the methods
IloCplex.leGoal, IloCplex.geGoal, or IloCplex.eqGoal.
Null goal
Describes the null goal to stop further branching.
In the C++ API, a null goal is an IloCplex::Goal handle object with a null (0)
implementation pointer.
Use a null goal when you want to instruct CPLEX not to branch any further. For
example, when CPLEX finds a feasible solution, and you want to accept it without
further branching, a null goal is appropriate.
For example, the following sample from the C++ API accepts an integer feasible
solution and stops branching.
if ( isIntegerFeasible() )
return 0;
Instead of instructing CPLEX not to branch any further (as you can do with a null
goal), it is also possible to tell CPLEX to branch as it normally would; in other
words, to branch as CPLEX. You give CPLEX this instruction by means of a
branching goal. A branching goal tells CPLEX to take over control of the branch &
cut search with its built-in strategies.
The following lines in the C++ API tell CPLEX to continue branching as it would
normally do:
if ( !isIntegerFeasible() )
AndGoal(BranchAsCplexGoal(getEnv()), this);
Solution goal
Describes a goal to inject a solution at a node.
A solution goal is a goal that injects a solution, such as a solution supplied by you
or a solution found by CPLEX during the search.
For example, here is a sample in the C++ API which injects a solution at the
beginning of branch & bound and then uses a branching goal:
if (getNnodes() == 0)
goal = AndGoal(OrGoal(SolutionGoal(goalvar,startVals),
AndGoal(MyBranchGoal(getEnv(), var), goal)),
goal);
else
goal = AndGoal(MyBranchGoal(getEnv(), var), goal);
return goal;
For more about this kind of special goal, see Injecting heuristic solutions on page
471.
Since And goals and Or goals take other goals as arguments, goals can be
combined into aggregate goals. In fact, this is how goals are typically used for
specifying a branching strategy. A typical return goal of a user-written execute
method for C++ looks like this:
return AndGoal(OrGoal(var <= IloFloor(val), var >= IloFloor(val)+1), this);
For the C++ case, note that since this statement would be called from the execute
method of a subclass of IloCplex::GoalI, the full name IloCplex::GoalI::OrGoal
can be abbreviated to OrGoal. Likewise, the full name IloCplex::GoalI::AndGoal
can be abbreviated to AndGoal.
This return statement returns an And goal that first executes the Or goal and then
the current goal itself specified by the this argument. When the Or goal is
executed next, it will create two subnodes. In the first subnode, the first local cut
goal representing
(where
denotes the floor of val) will be executed, thus adding the constraint
for the subtree of this node. Similarly, the second subnode will be created, and
when executing its constraint goal the constraint
var
will be added for the subtree. this is then executed on each of the nodes that have
just been created; the same goal is used for both subtrees. Further details about
how goals are processed are available in The goal stack on page 467,
Controlling goal-defined search on page 472, and Search limits on page 475.
This example shows how to implement and use a goal for controlling the branch
strategy used by CPLEX. As discussed, goals are implemented as subclasses of the
class IloCplex::GoalI (IloCplex.Goal or Cplex.Goal). The C++ implementation of
that example uses the macro
ILOCPLEXGOAL1(MyBranchGoal, IloNumVarArray, vars)
instead. This macro defines two things, class MyBranchGoalI and the function
IloCplex::Goal MyBranchGoal(IloEnv env, IloNumVarArray vars);
The use of the macro is very convenient as the amount of user code is equivalent
to the amount for defining a function, but with a slightly unusual syntax. IloCplex
provides seven such macros that can be used for defining goals with 0 to 6 private
members. If more than 6 members are needed, IloCplex::GoalI (IloCplex.Goal or
Cplex.Goal) must be subclassed by hand.
Since the Java programming language does not provide macros, a subclass of
IloCplex.Goal must always be implemented by hand. In this example, this class is
called MyBranchGoal and there is no helper function for creating an instance of that
class (as the macro does in the case of C++).
The goal is then used for solving the extracted node by calling:
cplex.solve(MyBranchGoal(env, var));
instead of the usual cplex.solve. The rest of the main function contains nothing
new and will not be discussed any further.
In the implementation of the goal, or more precisely its method execute, starts by
declaring and initializing some arrays. These arrays are then used by methods of
class IloCplex::GoalI (IloCplex.Goal or Cplex.Goal) to query information about
the node subproblem and the solution of its relaxation. The method getValues is
used to query the solution values for the variables in vars, the method
getObjCoefs is used to query the linear objective function coefficients for these
variables, and method getFeasibilities is used to query feasibility statuses for
them. The feasibility status of a variable indicates whether IloCplex considers the
current solution value of the variable to be integer feasible or not. IloCplex::GoalI
(IloCplex.Goal or Cplex.Goal) provides a wealth of other query methods. For
details, see the CPLEX Reference Manuals.
Then create a new goal handle object res. By default, this is initialized to an empty
goal. However, if an integer infeasible variable was found among those in vars,
then variable bestj will be 0 and a nonempty goal will be assigned to res:
res = AndGoal(OrGoal(vars[bestj] >= IloFloor(x[bestj])+1,
vars[bestj] <= IloFloor(x[bestj])),
this);
and continues branching in both subtrees with the same goal this. Finally, call
method end for all temporary arrays and return goal res.
Since Java objects are garbage collected, there is no need for the variable res.
Instead, depending on the availability of an integer infeasible variable, the null
goal is returned or the returned goal is created in the return statement itself:
return cplex.and(cplex.or(cplex.geGoal(_vars[bestj],
Math.floor(x[bestj]))+1,
cplex.leGoal(_vars[bestj],
Math.floor(x[bestj]))),
this);
To understand how goals are executed, consider the concept of the goal stack. Every
node has its own goal stack. When cplex.solve(goal) is called, the goal stack of
the root node is simply initialized with goal and then the regular cplex.solve
method is called.
When CPLEX processes a node, it pops the first goal from the node's goal stack
and calls method execute. If a nonempty goal is returned, it is simply pushed back
on the stack. CPLEX keeps doing this until the node becomes inactive or the node's
goal stack becomes empty. When the node stack is empty, CPLEX continues with
its built-in search strategy for the subtree rooted at this node.
In light of the goal stack, here are the different types of goals:
v As explained in Or goal on page 463, the Or goal creates child nodes. CPLEX
first initializes the goal stack of every child node with a copy of the remaining
goal stack of the current node. Then it pushes the goal passed as the argument
to the Or goal on the goal stack of the corresponding node. Finally, the current
node is deactivated, and CPLEX continues search by picking a new active node
from the tree to process.
With this understanding, consider further what really goes on when a goal returns:
return AndGoal(OrGoal(var <= IloFloor(val), var >= IloFloor(val)+1), this);
The And goal is pushed onto the current node's goal stack, only to be immediately
popped back off of it. When it is executed, it will push this on the goal stack and
then push the Or goal. Thus, the Or goal is the next goal that CPLEX pops and
executes. The Or goal creates two subnodes, and initializes their goal stacks with
copies of the goal stack of the current node. At this point both subnodes will have
this on top of their goal stacks. Next, the Or goal will push a local cut goal for
(where
denotes the floor of val) on the goal stack of the first subnode. Similarly, it pushes
a local cut goal for
var
on the goal stack of the second subnode. Finally, the current node is deactivated
and CPLEX continues its search with a new active node from the tree.
When CPLEX processes one of the subnodes that have been created by the Or goal,
it will pop and execute the first goal from the node's goal stack. As you just saw,
this will be a local cut goal. Thus CPLEX adds the constraint to the node problem
and re-solves the relaxation. Next, this will be popped from the goal stack and
executed. That fact means that the same search strategy as implemented in the
original goal is applied at that node.
Java and .NET use garbage collection to handle all memory management issues.
Thus the following applies only to the C++ library. Java or .NET users may safely
skip ahead to Cuts and goals on page 469.
To conserve memory, in the C++ API, CPLEX only stores active nodes of the tree
and deletes nodes as soon as they become inactive. When deleting nodes, CPLEX
It does so by keeping track of how many references to every goal are in use. As
soon as this number drops to zero (0), the goal is automatically deleted. This
technique is known as reference counting.
Other than that, nothing special needs to be observed when dealing with goals. In
particular, goals don't have end methods like other handle classes in the C++ API
of Concert Technology. Instead, CPLEX goal objects are automatically deleted when
no more references to them exist.
Local cut goals contain IloRange objects. Since the IloRange object is only applied
when the goal is executed, the method end must not be called for a range
constraint from which a local cut goal is built. The goal will take over memory
management for the constraints and call the method end when the goal itself is
destroyed. Also, an IloRange object can only be used in exactly one local cut goal.
Similarly, method end must not be called for IloRangeArray objects that are passed
to local cut goals. Also such arrays must not contain duplicate elements.
Going back to the example ilogoalex1.cpp, you see that the method end is called
for the temporary arrays x, obj, and feas at the end of the execute method.
Though a bit hidden, two IloRange constraints are constructed for the goal,
corresponding to the arguments of the Or goal. CPLEX takes over memory
management for these two constraints as soon as they are enclosed in a goal. This
takeover happens via the implicit constructor IloCplex::Goal::Goal(IloRange rng)
that is called when the range constraints are passed as arguments to the Or goal.
In summary, the user is responsible for calling end on all Concert Technology
objects created in a goal, except when they have been passed as arguments to a
new goal.
Also, user code in the execute method is not allowed to modify existing Concert
Technology objects in any way. CPLEX uses an optimized memory management
system within goals for dealing with temporary objects. However, this memory
management system cannot be mixed with the default memory management
system used by Concert Technology. Thus, for example, it is illegal to add an
element to array vars in the example, since this array has been created outside of
the goal.
Goals can also be used to add global cuts. Whereas local cuts are respected only in
a subtree, global cuts are added to the entire problem and are therefore respected
at every node after they have been added.
Although it is now solvable directly in less than two minutes, the number of nodes
examined is still on the order of 2.5 billion, even for only 128 original variables.
However, cuts can be derived, and the addition of these cuts makes the problem
solvable in under a second with roughly 600 nodes. These cuts are:
x21 - x22 <= 0
x22 - x23 <= 0
x23 - x24 <= 0
2.08*x11 + 2.98*x21 + 3.47*x31 + 2.24*x41 + 2.08*x51 +
0.25*w11 + 0.25*w21 + 0.25*w31 + 0.25*w41 + 0.25*w51 <= 20.25
2.08*x12 + 2.98*x22 + 3.47*x32 + 2.24*x42 + 2.08*x52 +
0.25*w12 + 0.25*w22 + 0.25*w32 + 0.25*w42 + 0.25*w52 <= 20.25
2.08*x13 + 2.98*x23 + 3.47*x33 + 2.24*x43 + 2.08*x53 +
0.25*w13 + 0.25*w23 + 0.25*w33 + 0.25*w43 + 0.25*w53 <= 20.25
2.08*x14 + 2.98*x24 + 3.47*x34 + 2.24*x44 + 2.08*x54 +
0.25*w14 + 0.25*w24 + 0.25*w34 + 0.25*w44 + 0.25*w54 <= 20.25
2.08*x15 + 2.98*x25 + 3.47*x35 + 2.24*x45 + 2.08*x55 +
0.25*w15 + 0.25*w25 + 0.25*w35 + 0.25*w45 + 0.25*w55 <= 16.25
These cuts derive from interpreting the problem as a resource allocation model on
five machines with scheduling horizon constraints and transaction times. The first
three cuts break symmetries among the machines, while the others capture
minimum bounds on transaction costs.
Of course, the best way to solve the noswot model with these cuts is simply to add
them to the model before calling the optimizer. However, for demonstration
purposes here, the cuts are added by means of a goal. The source code of this
example can be found in the examples/src directory of the CPLEX distribution.
The equivalent Java implementation appears as GoalEx2.java in the same location.
Likewise, there is also the C#.NET version in Goalex2.cs and the VB.NET version
in Goalex2.vb .
The goal CutGoal in that example receives a list of "less than" constraints to use as
global cuts and a tolerance value eps. The constraints are passed to the goal as an
array of lhs expressions and an array of corresponding rhs values. Both are
initialized in function makeCuts.
The goal CutGoal checks whether any of the constraints passed to it are violated by
more than the tolerance value. It adds violated constraints as global cuts. Other
than that, it follows the branching strategy CPLEX would use on its own.
The goal starts out by checking whether the solution of the continuous relaxation
of the current node subproblem is integer feasible. This check is done by the
method isIntegerFeasible. If the current solution is integer feasible, a candidate
for a new incumbent has been found, and the goal returns the empty goal to
instruct CPLEX to continue on its own.
The global cut goal for lhs[i] rhs[i] is created by the method GlobalCutGoal. It
is then combined with the goal named goal by the method AndGoal, so that the
new global cut goal will be executed first. The resulting goal is stored again in
goal. Before any global cut goals are added, the goal is initialized as:
IloCplex::Goal goal = AndGoal(BranchAsCplexGoal(getEnv()), this);
Thus the goal returned by CutGoal will add all currently violated constraints as
global cuts one by one. Then it will branch in the way CPLEX would branch
without any goals and execute the CutGoal again in the child nodes.
At any time in the execution of a goal, you may find that, for example, by slightly
manipulating the current node subproblem solution, you may construct a solution
to your model. Such solutions are called heuristic solutions, and a procedure that
generates them is called a heuristic.
Heuristic solutions can be injected into the branch & cut search by creating a
solution goal with the method IloCplex::GoalI::SolutionGoal
(IloCplex.solutionGoal or Cplex.SolutionGoal). Such a goal can be returned
typically as a subgoal of an And goal much like global cut goals.
When CPLEX executes a solution goal, it does not immediately use the specified
solution as a potential new incumbent. The reason is that with goals, part of the
model may be specified via global cuts or through specialized branching strategies.
Thus the solution needs first to be tested for feasibility with respect to the entire
model, including any part of the model specified through goals.
By processing this subnode as the next node, CPLEX makes sure that either the
solution is feasible with respect to all goals or otherwise it is discarded. Goals that
have been executed so far are either reflected as global cuts or by the local cuts
that are active at the current node. Thus, if the relaxation remains feasible after the
variable fixings have been added, the feasibility of these goals is certain.
Chapter 34. Using goals 471
If at that point the goal stack is not empty, the goals on the goal stack need to be
checked for feasibility as well. Thus by continuing to execute the goals from the
goal stack, CPLEX will either prove feasibility of the solution with respect to the
remaining goals or, in case the relaxation becomes infeasible, decide it really is
infeasible and discard the solution. The rest of the branch & cut search remains
unaffected by all of this.
The benefit of this approach is that your heuristic need not be aware of the entire
model including all its parts that might be implemented via goals. Your heuristic
can still safely be used, as CPLEX will make sure of feasibility for the entire model.
However, there are some performance considerations to observe. If parts of the
model specified with goals are dominant, heuristic solutions you generate might
need to be rejected so frequently that you do not get enough payoff for the work
of running the heuristic. Also, your heuristic should account for the global and
local cuts that have been added at the node where you run your heuristic so that a
solution candidate is not rejected right away and the work wasted.
So far, you have seen how to control the branching and cut generation of CPLEX
branch & cut search. The remaining missing piece is the node selection strategy.
The node selection strategy sets which of the active nodes in the tree CPLEX
chooses when it selects the next node for processing. CPLEX has several built-in
node selection strategies, selected through the node selection parameter (NodeSel,
CPX_PARAM_NODESEL).
When you use goal-controlled search, you use node evaluators to override the
built-in node selection strategy. You combine a goal with a node evaluator by
calling the method IloCplex::Goal::Apply (IloCplex.apply or Cplex.Apply). This
method returns a new goal that implements the same search strategy as the goal
passed as the argument, but adds the node evaluator to every node in the subtree
defined by the goal. Consequently, nodes may have a list of evaluators attached to
them.
When node evaluators are used, nodes are selected like this:
1. CPLEX starts to choose the node with the built-in strategy as a first candidate.
2. Then CPLEX loops over all remaining active nodes and considers choosing
them instead.
3. If a node has the same evaluator attached to it as the current candidate, the
evaluator is asked whether this node should take precedence over the current
candidate. If the response is positive, the node under investigation becomes the
new candidate, and the test against other nodes continues.
If a node has multiple evaluators attached, they are consulted in the order the
evaluators have been applied. Here is the application order:
v If the first evaluator prefers one node over the other, the preferred node is used
as candidate and the next node is considered.
v If the first evaluator does not give preference to one node over the other, the
second evaluator is considered, and so on.
Thus, by adding multiple evaluators, you can build composite node selection
strategies where later evaluators are used for breaking ties in previous evaluations.
Like goals, node evaluators use reference counting for memory management. As a
result, you should always use the handle objects when dealing with node
evaluators, and there is no method end to be called.
Node evaluators use a two-step process to decide whether one node should take
precedence over another. First, the evaluator computes a value for every node to
which it is attached. This is done by the method evaluate in C++:
IloNum IloCplex::NodeEvaluatorI::evaluate();
and in C#.NET:
double Cplex.NodeEvaluator.Evaluate();
This method must be implemented by users who write their own node evaluators.
In the method evaluate, the protected methods of the class
IloCplex::NodeEvaluatorI (IloCplex.NodeEvaluator or Cplex.NodeEvaluator) can
be called to query information about the node being evaluated. The method
evaluate must compute and return an evaluation (that is, a value) that is used
later on, in the second step, to compare two nodes and select one of them. The
evaluate method is called only once for every node, and the result is cached and
reused whenever the node is compared against another node with the evaluator.
The second step consists of comparing the current candidate to another node. This
comparison happens only for evaluators that are shared by the current candidate
and the other node. By default, the candidate is replaced by the other node if its
evaluation value is smaller than that of the candidate. You can alter this behavior
by overwriting the method:
IloBool IloCplex::NodeEvaluatorI::subsume(IloNum candVal, IloNum nodeVal);
CPLEX calls this method of an evaluator attached to the current candidate if the
node being compared also has the same evaluator attached. The first argument
candVal is the evaluation value the evaluator has previously computed for the
current candidate, and nodeVal is the evaluation value the evaluator has previously
computed for the node being tested. If this method returns IloTrue (true), the
candidate is replaced. Otherwise, the method is called again with reversed
arguments. If it still returns IloFalse (false), both nodes are tied with respect to
that evaluator, and the next evaluator they share is consulted. Otherwise, the
current candidate is kept and tested against the next node.
must be implemented by the user to return a copy of the invoking node evaluator
object. This method is called by IloCplex to create copies of the evaluator for
parallel branch & cut search.
Similarly, the class IISumEvaluatorI and function IISumEvaluator are also defined.
The evaluate method returns the negation of the sum of integer infeasibilities of
the node being evaluated. This number is obtained by calling method
getInfeasibilitySum. Thus, this evaluator favors nodes with larger sums of integer
infeasibilities.
Node evaluators are only active while the search is controlled by goals. That is, if
the goal stack becomes empty at a node and CPLEX continues searching with its
built-in search strategy, that search is no longer controlled by any node evaluator.
In order to maintain control over the node selection strategy while using the
CPLEX branch strategy, you can use the goal returned by the method
IloCplex::GoalI::BranchAsCplexGoal (IloCplex.branchAsCplex or
CplexBranchAsCplex). A goal that follows the branching performed by the built-in
strategy of IloCplex can be easily implemented as:
ILOCPLEXGOAL0(DefaultSearchGoal) {
if ( !isIntegerFeasible() )
return AndGoal(BranchAsCplexGoal(getEnv()), this);
return 0;
}
Notice the test for integer feasibility. Without that test, the application would create
an endless loop because when an integer feasible solution has been found, the goal
BranchAsCplex does not change the node at all, and this would continue to be
executed indefinitely.
Search limits
Describes search limits induced by a goal.
As with node evaluators, it is possible to apply search limits to the branch & cut
search controlled by goals. Search limits allow you to limit the search in certain
subtrees; that is, they allow you to discontinue processing nodes when a specified
condition applies. Search limits are implemented in subclasses of the class
IloCplex::SearchLimitI (IloCplex.SearchLimit or Cplex.SearchLimit), and the
procedure for implementing and using them is very similar to that for node
evaluators. See the reference manuals for more details about implementing and
using search limits.
Callbacks allow you to monitor closely and to guide the behavior of CPLEX
optimizers. In particular, callbacks allow user code to be executed regularly during
an optimization or during a tuning session. To use callbacks (either optimization or
tuning callbacks) with CPLEX, you must first write the callback function, and then
pass it to CPLEX. The topic Callbacks for tuning on page 129 offers information
about callbacks specific to a tuning session. This part of the manual concentrates
on optimization callbacks, and the topic Chapter 38, Advanced MIP control
interface, on page 511 offers further information beyond this introduction to
optimization callbacks. There are three types of optimization callbacks:
informational callbacks, query callbacks, and control callbacks.
The callback class hierarchy for Java and .NET is exactly the same as the hierarchy
for C++, but the class names differ, in that there is no I at the end. For example,
the Java implementation class corresponding to the C++ class
IloCplex::OptimizationCallbackI is IloCplex.OptimizationCallback.
The names of callback classes in .NET correspond very closely to those in the C++
and Java APIs. However, the name of a .NET class does not begin with Ilo.
Furthermore, the names of .NET methods are capitalized (that is, they begin with
an uppercase character) according to .NET conventions. For example, the
corresponding callback class in .NET is Cplex.OptimizationCallback.
Similarly, callbacks in Python follow the usual Python naming conventions for
classes, modules, and packages. Thus, the Python name of the callback class
corresponding to the C++ class IloCplex::MIPInfoCallback is
callbacks.MIPInfoCallback.
Depending on the programming language you choose, you may need to pay more
or less attention to the CPLEX environment when you write and use callbacks in
your application.
In Java, .NET, and Python applications, the CPLEX environment is not exposed to
your application, so you need not worry about using the correct environment.
Thus your only concern with respect to the CPLEX environment is that your
callback must not modify the model currently being optimized.
Informational callbacks
Documents informational callbacks.
An informational callback can also enable your application to abort (that is, to
terminate) optimization.
Informational callbacks are compatible with MIP dynamic search. For many
models, MIP dynamic search finds feasible and optimal solutions more quickly
than conventional MIP branch & cut.
Informational callbacks are also compatible with all modes of parallel optimization.
Furthermore, CPLEX makes sure that informational callbacks are called in a
thread-safe manner and in a deterministic order. For more information about
deterministic and opportunistic modes of parallel optimization, see the topics
Determinism of results on page 355 and Parallel MIP optimizer on page 360 in
this manual.
In the Callable Library (C API), use the following routines to install and access an
informational callback:
v CPXsetinfocallbackfunc
v CPXgetinfocallbackfunc
For examples of how to create and use an informational callback, see the following
samples of code in yourCPLEXhome/examples/src.
v xmipex4.c and mipex4.c
v ilomipex4.cpp
Query or diagnostic callbacks are distinguished by the place where they are called
during an optimization. There are nine such places where CPLEX calls a query or
diagnostic callback:
v The presolve query callback is called regularly during presolve.
IloCplex::PresolveCallbackI in the C++ API
IloCplex.PresolveCallback in the Java API
Cplex.PresolveCallback in the .NET API
CPXsetlpcallbackfunc in the Callable Library (C API)
callbacks.PresolveCallback in the Python API
v The crossover query callback is called regularly during crossover from a barrier
solution to a simplex basis.
IloCplex::CrossoverCallbackI in the C++ API
IloCplex.CrossoverCallback in the Java API
Cplex.CrossoverCallback in the .NET API
CPXsetlpcallbackfunc in the Callable Library (C API)
callbacks.CrossoverCallback in the Python API
v The network query callback is called regularly during the network simplex
algorithm.
IloCplex::NetworkCallbackI in the C++ API
IloCplex.NetworkCallback in the Java API
Cplex.NetworkCallback in the .NET API
CPXsetnetcallbackfunc in the Callable Library (C API)
network simplex is not supported in the Python API
v The barrier query callback is called at each iteration during the barrier
algorithm.
IloCplex::BarrierCallbackI or IloCplex::ContinuousCallbackI in the C++
API
IloCplex.BarrierCallback or IloCplex.ContinuousCallback in the Java API
Query or diagnostic callbacks are not compatible with dynamic search, a feature
which explores a solution space in a way that departs from a conventional
branch & cut tree.
In order to make sure of thread safety, CPLEX locks the query callbacks when
they are used with either mode of parallel search.
Control callbacks
Documents control callbacks.
Control callbacks allow you to control the branch & cut search during the
optimization of MIP problems. Because control callbacks intervene in the search,
the presence of a control callback in an application will cause CPLEX to turn off
By default, parallel MIP solving is also turned off. That is, if control callbacks are
enabled, and if the global thread count (Threads, CPX_PARAM_THREADS) parameter is
at its default value of 0 (zero), then CPLEX uses only a single thread and executes
sequentially; that is, CPLEX does not execute in parallel mode by default under
those conditions. The reason for this default behavior is that CPLEX cannot
guarantee the order of control callback invocation in the presence of user-written
control callbacks that intervene in the search. That is, their invocation is
nondeterministic, even in deterministic parallel mode. Consequently, by default,
CPLEX protects a user from this nondeterministic behavior by turning off parallel
optimization and solving sequentially in the presence of control callbacks.
However, you can enable parallel solving even in the presence of control callbacks
by setting the threads parameter to a nonzero positive value. There are distinct
situations in which you might want to enable parallel solving even in the presence
of control callbacks:
v You are using only locally available CPLEX data for the decisions in your control
callbacks.
v You are using opportunistic parallel mode. (That is, you do not require
deterministic behavior.)
The topic Control callbacks and dynamic search on page 486 offers more
information about this point.
If you want to take advantage of dynamic search in your application, and you see
a need for a callback to report progress, consider an informational callback instead
of a control callback. The topic Informational callbacks on page 478 explains
more about this alternative, which is compatible with dynamic search.
If you determine that your application needs to seize control, intervene in the
search, and redirect the optimizer, then the following control callbacks are available
to do so.
v The node callback allows you to query and optionally overwrite the next node
CPLEX will process during a branch & cut search.
IloCplex::NodeCallbackI in the C++ API
IloCplex.NodeCallback in the Java API
Cplex.NodeCallback in the .NET API
CPXsetnodecallbackfunc in the Callable Library (C API)
callbacks.NodeCallback in the Python API
v The solve callback allows you to specify and configure the optimizer option to
be used for solving the LP at each individual node.
IloCplex::SolveCallbackI in the C++ API
IloCplex.SolveCallback in the Java API
Cplex.SolveCallback in the .NET API
CPXsetsolvecallbackfunc in the Callable Library (C API)
callbacks.SolveCallback in the Python API
Control callbacks are not compatible with dynamic search, a feature which explores
a solution space in a way that departs from a conventional branch & cut tree.
If you want to enforce parallel behavior in the presence of control callbacks, you
can override the default behavior of CPLEX by means of the global thread count
(CPX_PARAM_THREADS, Threads); set its value to a nonzero positive number greater
than 1 (one).
However, even if you turn on parallel search in this way, the order in which your
callbacks are executed from different threads can be nondeterministic even in
deterministic parallel mode. Your application can safely interact with the CPLEX
environment and with the problem object passed to the callback, but side effects
that your callback produces on other data is nondeterministic. Thus, if you base
the algorithmic decisions inside your control callback on such data, the resulting
algorithm can be nondeterministic, even if you have selected deterministic parallel
mode.
Callbacks are accessed via the IloCplex::Callback handle class in the C++
implementation of IloCplex. An instance of that handle class points to an
implementation object of a subclass of IloCplex::CallbackI. In Java and .NET,
there is no handle class, and a programmer deals only with implementation classes
which are subclasses of IloCplex.Callback or Cplex.Callback. One such
implementation class is provided for each type of callback. The implementation
class provides the functions that can be used for the particular callback as
protected methods.
To reflect the fact that some callbacks share part of their protected API, the callback
classes are organized in a class hierarchy, as documented in the reference manuals
of the APIs. For example, the class hierarchy of C++ callbacks is visible when you
select Tree or Graph in the reference manual of that API. Likewise, the class and
interface hierarchy of Java callbacks is visible when you select Tree in the reference
manual of the Java API. Similarly, you can see the class and interface hierarchy of
.NET callbacks in that reference manual. The reference manual of the Python API
also documents the class hierarchy of callbacks.
There are two ways of implementing callbacks for CPLEX: a more complex way
that exposes all the C++ implementation details, and a simplified way that uses
macros to handle the C++ technicalities. Since Java and .NET do not provide
macros, only the more complex way is available for Java or .NET users. This
section first explains the more complex way and discusses the underlying design.
To implement your C or C++ callback quickly without details about the internal
design, proceed directly to Writing callbacks with macros in C++ on page 489.
To implement your own callback for IloCplex, first select the callback class
corresponding to the callback you want implemented. From it, derive your own
implementation class, and overwrite the virtual method main . This is where you
implement the callback actions, using the protected methods of the callback class
from which you derived your callback or one of its base classes.
Next write a function that creates a new object of your implementation class using
the environment operator new and returning it as an IloCplex::Callback handle
object. Here is an example implementation of such a function:
IloCplex::Callback MyCallback(IloEnv env, IloInt num) {
return (new (env) MyCallbackI(num));
}
It is not customary to write such a function for Java nor for .NET applications;
instead, new is called explicitly for creating a callback object when needed.
One object of a callback implementation class can be used with only one IloCplex
object at a time. Thus, when you use a callback with more than one cplex object, a
copy of the implementation object is created every time cplex.use is called except
for the first time. In C++, the method IloCplex::use returns a handle to the
callback object that has actually been installed to enable calling end on it.
In most cases you can avoid writing callback classes by hand, using supplied
macros that make the process as easy as implementing a function. You must
implement a callback by hand only if the callback manages internal data not
passed as arguments, or if the callback requires eight or more arguments.
This is how to implement a callback using macros. Since macros are not supported
in Java nor in .NET, this technique applies only to C++ applications.
Tip:
These macros make it easier to write callback functions by hiding some of the
implementation details, such as their use of a copy constructor and placement
syntax. Because the macros hide these implementation details, they also introduce
a risk of side effects that you may not anticipate, especially in the context of
multiple threads. On the other hand, in most situations, these macros greatly
simplify implementation of callbacks and may be well suited to your application.
Start by deciding which callback you want to implement and how many
arguments to pass to the callback function. These two pieces of information
determine the macro you need to use.
For example, to implement a simplex callback with one argument, the macro is
ILOSIMPLEXCALLBACK1 . Generally, for every callback type XXX and any number of
arguments n from 0 to 7 , there is a macro called ILOXXXCALLBACKn . The table
Table 63 lists the informational and query callbacks with the corresponding macros
and classes (where n is a placeholder for 0 to 7). The names of macros to write
control callbacks are similar and documented in the reference manual of the
Concert Technology C++ API.
Table 63. Informational and query callback macros
Callback Macro Class
presolve ILOPRESOLVECALLBACKn IloCplex::PresolveCallbackI
continuous ILOCONTINUOUSCALLBACKn IloCplex::ContinuousCallbackI
simplex ILOSIMPLEXCALLBACKn IloCplex::SimplexCallbackI
barrier ILOBARRIERCALLBACKn IloCplex::BarrierCallbackI
crossover ILOCROSSOVERCALLBACKn IloCplex::CrossoverCallbackI
network ILONETWORKCALLBACKn IloCplex::NetworkCallbackI
MIP info ILOMIPINFOCALLBACKn IloCplex::MIPInfoCallbackI
MIP ILOMIPCALLBACKn IloCplex::MIPCallbackI
probing info ILOPROBINGINFOCALLBACKn IloCplex::ProbingCallbackI
probing ILOPROBINGCALLBACKn IloCplex::ProbingCallbackI
fractional cut info ILOFRACTIONALCUTINFOCALLBACKn IloCplex::FractionalCutCallbackI
fractional cut ILOFRACTIONALCUTCALLBACKn IloCplex::FractionalCutCallbackI
disjunctive cut info ILODISJUNCTIVECUTINFOCALLBACKn IloCplex::DisjunctiveCutCallbackI
The protected methods of the corresponding class and its base classes specify the
functions that can be called for implementing your callback. See the Reference
Manual of the C++ API with respect to these classes for details of which functions
can be called.
This callback terminates the simplex algorithm at the iteration specified by the
number num. It queries the current iteration number by calling the function
getNiterations , a protected method of the class IloCplex::ContinuousCallbackI.
To use this callback with an instance of IloCplex declared as cplex, simply call:
IloCplex::Callback mycallback = cplex.use(MyCallback(env, 10));
The callback that is added to cplex is returned by the method use and stored in
the variable mycallback. This convention allows you to call mycallback.end to
remove the callback from cplex. If you do not intend to access your callback (for
example, in order to delete it before ending the environment), you may safely
leave out the declaration and initialization of the variable mycallback.
Callback interface
Describes the common callback interface.
Two callback classes in the hierarchy need extra attention. The first is the base class
IloCplex::CallbackI in the C++ API (IloCplex.Callback in the Java API or
Cplex.Callback in the .NET API or callbacks.Callback in the Python API). Since
there is no corresponding callback in the Callable Library (C API), this class cannot
be used for implementing user callbacks. Instead, its purpose is to provide an
interface common to all callback functions.
Further, methods getNrows and getNcols allow you to query the number of rows
and columns of the current cplex LP matrix. These methods can be called from all
callbacks.
For C++ users, no manipulation of the model or, more precisely, no manipulation
of any extracted modeling object is allowed during the execution of a callback. No
modification is allowed of any array or expression not local to the callback
function itself (that is, constructed in the callback and end ed in the callback). The
only exception is the modification of array elements. For example, x[i] = 0 is
permissible, whereas x.add(0) is not permissible unless x is a local array of the
callback.
defines the callback MyCallback without arguments with the code enclosed in the
outer {} . In Java, the same callback is defined like this:
static class MyCallback extends IloCplex.ContinuousCallback {
public void main() throws IloException {
System.out.print("Iteration " + getNiterations() + ": ");
if ( isFeasible() )
System.out.println("Objective = " + getObjValue());
else
System.out.println("Infeasibility measure = "
+ getInfeasibility());
}
}
The callback prints the iteration number. Then, depending on whether the current
solution is feasible or not, it prints the objective value or infeasibility measure. The
methods getNiterations, isFeasible, getObjValue, and getInfeasibility are
methods provided in the base class of the
callback, IloCplex::ContinuousCallbackI (IloCplex.ContinuousCallback or
Cplex.ContinuousCallback). See the Reference Manual of the C++ API for the
complete list of methods provided for each callback class.
void MyCallbackI::main() {
cout << "Iteration " << getNiterations() << ": ";
if ( isFeasible() ) {
cout << "Objective = " << getObjValue() << endl;
}
else {
cout << "Infeasibility measure = " << getInfeasibility() << endl;
}
}
The 0 (zero) in the macro indicates that no arguments are passed to the constructor
of the callback. For callbacks requiring up to 7 arguments, similar macros are
defined where the 0 is replaced by the number of arguments, ranging from 1
through 7. For an example using the cut callback, see Example: controlling cuts
iloadmipex5.cpp on page 494. If you need more than 7 arguments, you will need
to derive your callback class yourself without the help of a macro.
After the callback MyCallback is defined, it can be used with the line:
v cplex.use(MyCallback(env)); in C++
v cplex.use(new MyCallback()); in Java
v cplex.Use(new MyCallback()); in .NET
If your application defines more than one simplex callback object (possibly with
different subclasses), only the last one passed to CPLEX with the use method is
actually used during simplex. On the other hand, IloCplex can manage one
callback for each callback class at the same time. For example, a simplex callback
and a MIP callback can be used at the same time.
To implement and use a callback in your application, you must first write the
callback function and then tell CPLEX about it. For more information about the
Callable Library routines for callbacks, see the Callable Library Reference Manual. In
that reference manual, the group optim.cplex.callable.callbacks gives you direct
access to documentation about callback routines.
Setting callbacks
Describes organization of callbacks in the C API.
In the Callable Library, query or diagnostic callbacks are organized into two
groups: LP callbacks (that is, continuous callbacks) and MIP callbacks (that is,
discrete callbacks). For each group, one callback function can be set by the routine
CPXsetlpcallbackfunc and one by CPXsetmipcallbackfunc . You can distinguish
between the actual callbacks by querying the argument wherefrom passed to the
callback function as an argument by CPLEX.
The continuous callback is also called during the solution of problems of type LP,
QP, and QCP.
CPLEX will evaluate two user-defined callback functions, one during the solution
of continuous problems and one during the solution of discrete problems. CPLEX
calls the continuous callback once per iteration during the solution of an LP, QP, or
QCP problem and periodically during the presolve. CPLEX calls the discrete
callback periodically during the probing phase of MIP preprocessing, periodically
during cut generation, and periodically in the branch & cut process.
v The function mycallback at the end of the program is called by the optimizer.
This function tests whether the primal simplex optimizer has been called. If so,
then a call to CPXgetcallbackinfo gets the following information:
iteration count;
feasibility indicator;
sum of infeasibilities (if infeasible);
objective value (if feasible).
The function then prints these values to indicate progress.
v Before the program calls CPXlpopt, the default optimizer from the Callable
Library, it sets the callback function by calling CPXsetlpcallbackfunc. It unsets
the callback immediately after optimization.
This callback function offers a model for graphic user interfaces that display
optimization progress as well as those GUIs that allow a user to interrupt and stop
optimization. If you want to provide your end-user a facility like that to interrupt
and stop optimization, then you should make mycallback return a nonzero value
to indicate the end-user interrupt.
This example shows how to use the cut callback in the context of solving the
noswot model. This is a relatively small model from the MIPLIB 3.0 and MIPLIB
2003 test-sets, consisting only of 128 variables. This model is very hard to solve by
itself. In fact, until the release of CPLEX version 6.5, it appeared to be unsolvable
even after days of computation.
Of course the best way to solve the noswot model with these cuts is to simply add
the cuts to the model before calling the optimizer. In case you want to copy and
paste those cuts into a model in the Interactive Optimizer, for example, here are
the same cuts expressed in the conventions of the Interactive Optimizer with
uppercase variable names, as in the MPS data file:
X21 - X22 <= 0
X22 - X23 <= 0
X23 - X24 <= 0
2.08 X11 + 2.98 X21 + 3.47 X31 + 2.24 X41 + 2.08 X51 +
0.25 W11 + 0.25 W21 + 0.25 W31 + 0.25 W41 + 0.25 W51 <= 20.25
2.08 X12 + 2.98 X22 + 3.47 X32 + 2.24 X42 + 2.08 X52 +
0.25 W12 + 0.25 W22 + 0.25 W32 + 0.25 W42 + 0.25 W52 <= 20.25
2.08 X13 + 2.98 X23 + 3.47 X33 + 2.24 X43 + 2.08 X53 +
0.25 W13 + 0.25 W23 + 0.25 W33 + 0.25 W43 + 0.25 W53 <= 20.25
2.08 X14 + 2.98 X24 + 3.47 X34 + 2.24 X44 + 2.08 X54 +
0.25 W14 + 0.25 W24 + 0.25 W34 + 0.25 W44 + 0.25 W54 <= 20.25
2.08 X15 + 2.98 X25 + 3.47 X35 + 2.24 X45 + 2.08 X55 +
0.25 W15 + 0.25 W25 + 0.25 W35 + 0.25 W45 + 0.25 W55 <= 16.25
However, for demonstration purposes, this example adds the cuts, using a cut
callback, only when they are violated at a node. This cut callback takes a list of
cuts as an argument and adds individual cuts whenever they are violated by the
current LP solution. Notice that adding cuts does not change the extracted model,
but affects only the internal problem representation of the CPLEX object.
First consider the C++ implementation of the callback. In C++, the callback is
implemented with these lines:
ILOUSERCUTCALLBACK3(CtCallback, IloExprArray, lhs, IloNumArray, rhs, IloNum, eps) {
IloInt n = lhs.getSize();
for (IloInt i = 0; i < n; i++) {
IloNum xrhs = rhs[i];
if ( xrhs < IloInfinity && getValue(lhs[i]) > xrhs + eps ) {
IloRange cut;
try {
cut = (lhs[i] <= xrhs);
add(cut).end();
rhs[i] = IloInfinity;
}
catch (...) {
cut.end();
throw;
Similar macros are provided for other numbers of arguments ranging from 0
through 7 for all callback classes.
The first argument lhs is an array of expressions, and the argument rhs is an array
of values. These arguments are the lefthand side and righthand side values of cuts
of the form lhs rhs to be tested for violation and potentially added. The third
argument eps gives a tolerance by which a cut must at least be violated in order to
be added to the problem being solved.
The implementation of this example cut-callback looks for cuts that are violated by
the current LP solution of the node where the callback is invoked. It loops over the
potential cuts, checking each for violation by querying the value of the lhs
expression with respect to the current solution. This query calls getValue with this
expression as an argument. This value is tested for violation of more than the
tolerance argument eps with the corresponding righthand side value.
Tip:
A numeric tolerance is always a wise thing to consider when dealing with any
nontrivial model, to avoid certain logical inconsistencies that could otherwise occur
496 CPLEX Users Manual
due to numeric round-off. Here the standard simplex feasibility tolerance serves
this purpose, to make sure there is consistency with the way CPLEX is treating the
rest of the model.
It is important that all resources that have been allocated during a callback are
freed again before leaving the callback, even in the case of an exception. Here
exceptions could be thrown when the cut itself is created or when the application
tries to add it, for example, due to memory exhaustion. Thus, these operations are
enclosed in a try block to catch all exceptions that may occur. In the case of an
exception, the cut is deleted by a call to cut.end and whatever exception was
caught is then re-thrown. Re-throwing the exception can be omitted if you want to
continue the optimization without the cut.
After the cut has been added, the application sets the rhs value at IloInfinity to
avoid checking this cut for violation at the next invocation of the callback. Note
that it did not simply remove the i th element of arrays rhs and lhs, because doing
so is not supported if the cut callback is invoked from a parallel optimizer.
However, changing array elements is allowed.
Also, for the potential use of the callback in parallel, the variable xrhs makes sure
that the same value is used when checking for violation of the cut as when adding
the cut. Otherwise, another thread may have set the rhs value to IloInfinity just
between the two actions, and a useless cut would be added. CPLEX would actually
handle this correctly, as it handles adding the same cut from different threads.
The function makeCuts generates the arrays rhs and lhs to be passed to the cut
callback. It first declares the array of variables to be used for defining the cuts.
Since the environment is not passed to the constructor of that array, an array of
0-variable handles is created. In the following loop, these variable handles are
initialized to the correct variables in the noswot model which are passed to this
function as the argument vars. Variables are identified by querying their names.
After all the variables have been assigned, they are used to create the lhs
expressions and rhs values of the cuts.
Instead of receiving expressions and righthand side values, the application directly
passes an array of IloRange constraints to the callback; the constraints are stored in
cut . The main loops over all cuts and evaluates the constraint expressions at the
current solution by calling getValue(cut[i].getExpr). If this value exceeds the
constraint bounds by more than the tolerance of eps, the cut is added during the
search by a call to add(cut[i]), and cut[i] is set to null in order to avoid
unnecessarily evaluating it again.
As for the C++ implementation, the array of cuts passed to the callback is
initialized in a separate function makeCuts . The callback is then created and used
to with the noswot cuts by calling.
IloCplex provides an easier way to manage such cuts in a case like this, where all
cuts can be easily enumerated before starting the optimization. Calling the
methods cplex.addCut and cplex.addCuts allows you to copy the cuts to IloCplex
before the optimization. Thus, instead of creating and using the callback, a user
could have written:
makeCuts(cuts, vars);
cplex.addUserCuts(cuts);
When you use callback routines, and invoke the parallel implementation of CPLEX
optimizers, you need to be aware that the CPLEX environment passed to the
callback routine corresponds to an individual CPLEX thread rather than to the
original environment created. CPLEX frees this environment when finished with
the thread. This convention does not affect most uses of the callback function.
However, keep in mind that CPLEX associates problem objects, parameter settings,
and message channels with the environment that specifies them. CPLEX therefore
A user-written callback should return a nonzero value if the user wants to stop the
optimization and a value of zero otherwise.
For LP, QP, or QCP problems, if the callback returns a nonzero value, the solution
process terminates. If the process was not terminated during the presolve process,
the status returned by the function IloCplex::getStatus or the routines
CPXsolution or CPXgetstat will be CPX_STAT_ABORT_USER, specifying that the
process stopped because the user intervened..
For both LP, QP, QCP, and MIP problems, if the LP/QP/QCP callback returns a
nonzero value during preprocessing, the optimizer will return the value
CPXERR_PRESLV_ABORT, and no solution information will be available.
For MIP problems, if the callback returns a nonzero value, the solution process
terminates, and the status returned by IloCplex::getStatus or CPXgetstat is one
of the values in the table Table 65.
Table 65. Status of nonzero callbacks for MIPs.
Symbolic constant Meaning
CPXMIP_ABORT_FEAS current solution integer feasible
CPXMIP_ABORT_INFEAS no integer feasible solution found
Overview
Outlines advantages of goals and callbacks.
Goals and callbacks both provide an API within IBM ILOG CPLEX to allow you to
take control over the branch & cut search for solving MIP models. With one
exception, the same functionality is available in both goals and callbacks. In fact,
the goal API is built on top of callbacks. As a consequence, you can not use
callbacks and goals at the same time. To help you choose which API is more suited
to your needs, this topic examines commonalities and differences between both.
Both approaches (goals and callbacks) allow you to control the branch & cut
search used by CPLEX to solve MIP models. The following points distinguish
specific features of this control.
v Checking feasibility
With goals, you can discontinue the search at a node by returning a Fail goal.
Alternatively, you can continue searching, even though an integer feasible
solution has been found, by returning another nonempty goal.
With callbacks, you can use method prune of the branch callback to
discontinue the search, and an incumbent callback to accept or reject integer
feasible solutions.
v Creating branches
With goals, you create branches by using Or goals with local cut goals as
parameters.
With callbacks, you create branches by using a branch callback.
v Adding local or global cuts
With goals, you can add global and local cuts by using global and local cut
goals, respectively.
With callbacks, you need to implement either a cut callback (for global and
local cuts) or a branch callback for branching on local cuts
v Injecting solution candidates
With goals, you inject solutions by using a solution goal.
With callbacks, you need to implement a heuristic callback to inject solutions.
v Controlling the node selection strategy
With goals, you control node selection by applying node evaluators to your
search goal.
With callbacks, you control node selection by using a node callback.
v Supporting advanced starts
Since goals can enforce constraints, they do not support advanced starting
information. An optimization with goals starts from scratch.
Since each callback provides a specific functionality, callbacks support
advanced starts.
Thus, one of the main differences between goals and callbacks is that with goals,
all functionality is available from the execute method of the goal, whereas with
callbacks, you must implement different callbacks to access different functionality.
With goals, the feasibility test and the resulting branching can be implemented
with a single goal.
The second big difference between goals and callbacks is that with goals you can
easily specify different search strategies in different subtrees. To do this, simply
provide different search goals as a parameter to the Or goal when creating the root
nodes for the subtrees in question. To achieve a similar result with callbacks
requires an implementation that is too complex for a presentation here.
The only functionality that is not supported via goals is that provided by the solve
callback. Because of this, the solve callbacks can be used at the same time as goals.
However, this callback is very rarely used.
In summary, goals can be advantageous if you want to take control over several
steps of the branch & cut search simultaneously, or if you want to specify different
search strategies in different subtrees. On the other hand, if you only need to
control a single aspect of the search (for example, adding cuts) using the
appropriate callback may involve a smaller API and thus be quicker and easier to
understand and implement.
With callbacks, you need to use an incumbent callback and a branch callback. The
incumbent callback has to reject an otherwise integer feasible solution if it violates
such an additional constraint. In this case, the branch callback has to follow up
with an appropriate branch to enforce the constraint. The choice of the appropriate
branch may be quite difficult for constraints not modeled with linear expressions,
even though CPLEX supports branching on hyperplanes.
Introduction to presolve
Describes presolve with an example contrasting continuous and discrete
optimization.
This discussion of the advanced presolve interface begins with a quick introduction
to presolve. Most of the information in this section will be familiar to people who
are interested in the advanced interface, but everyone is encouraged to read
through it nonetheless.
As most users of IBM ILOG CPLEX know, presolve is a process whereby the
problem input by the user is examined for logical reduction opportunities. The
goal is to reduce the size of the problem passed to the requested optimizer. A
reduction in problem size typically translates to a reduction in total run time (even
including the time spent in presolve itself).
Iteration log . . .
Iteration: 1 Dual objective = 317.965093
CPLEX is presented with a problem with 388 constraints and 358 variables, and
after presolve the dual simplex method is presented with a problem with 57
constraints and 83 variables. Dual simplex solves this problem and passes the
solution back to the presolve routine, which then unpresolves the solution to
produce a solution to the original problem. During this process, presolve builds an
entirely new presolved problem and stores enough information to translate a
solution to this problem back to a solution to the original problem. This
information is hidden within the user's problem (in the CPLEX LP problem object,
for Callable Library users) and was inaccessible to the user in CPLEX releases prior
to 7.0.
The presolve process for a mixed integer program is similar, but has a few
important differences. First, the actual presolve reductions differ. Integrality
restrictions allow CPLEX to perform several classes of reductions that are invalid
Copyright IBM Corp. 1987, 2015 503
for continuous variables. A second difference is that the MIP solution process
involves a series of linear program solutions. In the MIP branch & cut tree, a linear
program is solved at each node. MIP presolve is performed at the beginning of the
optimization and applied a second time to the root relaxation, unless the relaxed
LP presolve switch (RelaxPreInd or CPX_PARAM_RELAXPREIND) is set to 0 (zero), in
which case the presolve is performed only once. All of the node relaxation
solutions use the presolved model. Again, presolve stores the presolved model and
the information required to convert a presolved solution to a solution for the
original problem within the LP problem object. Again, this information was
inaccessible to the user in CPLEX releases prior to version 7.0.
A proposed example
Describes special considerations about presolve.
Now consider an application where the user wants to solve a linear program using
the simplex method, then modify the problem slightly and solve the modified
problem. As an example, let's say a user wants to add a few new constraints to a
problem based on the results of the first solution. The second solution should
ideally start from the basis of the first, since starting from an advanced basis is
usually significantly faster if the problem is only modified slightly.
The advanced presolve interface can potentially make this and many other
sequences of operations more efficient. It provides facilities to restrict the set of
presolve reductions performed so that subsequent problem modifications can be
accommodated. It also provides routines to translate constraints on the original
problem to constraints on the presolved model, so new constraints can be added to
the presolved model. In short, it provides a variety of capabilities.
When processing mixed integer programs, the advanced presolve interface plays a
very different role. The branch & cut process needs to be restarted from scratch
when the problem is even slightly modified, so preserving advanced start
information isn't useful. The main benefit of the advanced presolve interface for
MIPs is that it allows a user to translate decisions made during the branch & cut
process (and based on the presolved model) back to the corresponding constraints
and variables in the original problem. This makes it easier for a user to control the
branch & cut process. Details on the advanced MIP callback interface are provided
in Advanced MIP Control Interface.
Consider adding a constraint to a problem after solving it. Imagine that you want
to optimize a linear program:
Primal: Dual:
max -x1 + x2 + x3 min 6y1 + 5y2
st x1 + x2 + 2x3 6 st y1 -1
x2 + x3 5 y1 + y2 1
0 2y1 + y2 1
x1, x2, x3 0 y1, y2, y3 0
Note that the first constraint in the dual (y1 -1) is redundant. Presolve can use
this information about the dual problem (and complementary slackness) to
conclude that variable x1 can be fixed to 0 and removed from the presolved model.
While it may be intuitively obvious from inspection of the primal problem that x1
can be fixed to 0, it is important to note that dual information (redundancy of the
first dual constraint) is used to prove it formally.
Primal: Dual:
max -x1 + x2 + x3 min 6y1 + 5y2
st x1 + x2 + 2x3 6 st y1 - 5y3 -1
x2 + x3 5 y1 + y2 + y3 1
- + x2 0 2y1 + y2 1
5x1
x1, x2, x3 0 y1, y2, y3 0
Our goal is to add the appropriate constraint to the presolved model and
re-optimize. Note, however, that the dual information presolve used to fix x1 to 0
was changed by the addition of the new constraint. The first constraint in the dual
is no longer guaranteed to be redundant, so the original fixing is no longer valid
(the optimal solution is now x1=1, x2=5, x3=0). As a result, CPLEX is unable to use
the presolved model to re-optimize.
Presolve reductions can be classified into several groups: those that rely on primal
information, those that rely on dual information, and those that rely on both.
Addition of new constraints, modifications to objective coefficients, and tightening
These reductions are controlled through the primal and dual reduction type
(CPX_PARAM_REDUCE) parameter. The parameter has four possible settings. The
default value CPX_PREREDUCE_PRIMALANDDUAL (3) indicates that presolve can rely on
primal and dual information. With setting CPX_PREREDUCE_DUALONLY (2), presolve
only uses dual information, with setting CPX_PREREDUCE_PRIMALONLY (1) it only uses
primal information, and with setting CPX_PREREDUCE_NO_PRIMALORDUAL (0) it uses
neither (which is equivalent to turning presolve off).
Setting the primal and dual reduction type (CPX_PARAM_REDUCE) parameter has one
additional effect on the optimization. Normally, the presolved model and the
presolved solution are freed at the end of an optimization call. However, when
CPX_PARAM_REDUCE is set to a value other than its default, CPLEX assumes that the
problem will subsequently be modified and reoptimized. It therefore retains the
presolved model and any presolved solution information (internally to the LP
problem object). If the user has set CPX_PARAM_REDUCE and is finished with problem
modification, the user can call CPXfreepresolve to free the presolved model and
reclaim the associated memory. The presolved model is automatically freed when
the user calls CPXfreeprob on the original problem.
However, for reasons that are complex and beyond the scope of this discussion,
dual reductions can be left on. The reasons relate to the fact that valid cuts never
exclude integer feasible solutions, so dual reductions performed for the original
problem are still valid after cutting planes are applied.
However, a small set of reductions does need to be turned off. Recall that presolve
must translate a new constraint on the original problem into a constraint on
variables in the presolved model. Most reductions performed by CPLEX presolve
replace variables with linear expressions of zero or more other variables (plus a
constant). A few do not. These latter reductions make it impossible to perform the
translation to the presolved model. Set the linear reduction switch
CPX_PARAM_PRELINEAR to 0 (zero) to forbid these latter reductions.
Restricting the type of presolve reductions will also allow presolve to conclude
more about infeasible and/or unbounded problems. Under the default setting of
the primal and dual reduction type (CPX_PARAM_REDUCE) parameter, presolve can
only conclude that a problem is infeasible and/or unbounded. If the
Note:
A final facility that modifies the set of reductions performed by presolve is the
CPXcopyprotected routine. The user provides as input a list of variables in the
original problem that should survive presolve (that is, should exist in the
presolved model). Presolve will avoid reductions that remove those variables, with
one exception. If a protected variable can be fixed, presolve will still remove it
from the problem. This command is useful in cases where the user wants to
explicitly control some aspect of the branch & cut process (for example, through
the branch callback) using knowledge about those variables.
You can set the advanced start switch (CPX_PARAM_ADVIND) to 2 in order to use
advanced starting information together with presolve. At this setting, CPLEX will
use starting information provided to it with CPXcopystart or CPXcopybase when it
solves an LP with the primal or dual simplex optimizer in the following way. If no
presolved model is available, presolve is invoked. Then the starting information is
crushed and installed in the presolved model. Finally, the presolved model is
solved from the advanced (crushed) starting point.
Another situation where a user might want to use CPXpresolve is if the user wants
to gather information about the presolve, possibly in preparation for using the
advanced MIP callback routines to control branch & cut. After CPXpresolve has
been called, the user can then call CPXgetprestat to obtain information about the
reductions performed on the problem. This routine provides information, for each
variable in the original problem, about whether the variable was fixed and
removed, aggregated out, removed for some other reason, or is still present in the
reduced model. It also gives information, for each row in the original problem,
about whether it was removed due to redundancy, aggregated out, or is still
present in the reduced model. For those rows and columns that are present in the
reduced model, this function provides a mapping from original row/column
number to row/column number in the reduced model, and vice-versa.
Another situation where a user might want to use CPXpresolve is to work directly
on the presolved model. By calling CPXgetredlp immediately after CPXpresolve, the
user can obtain a pointer to the presolved model. As an example of how this
pointer might be used, the user could call routines CPXcrushx and CPXcrushpi to
presolve primal and dual solution vectors, then call CPXgetredlp to get access to
the presolved model, then use CPXcopystart to copy the presolved solutions into
the presolved model, then optimize the problem, and finally call the routines
CPXuncrushx and CPXuncrushpi (CPXqpuncrushpi for QPs) to unpresolve solutions
from the presolved model, creating solutions for the original problem.
The routine CPXgetredlp provides the user access to internal CPLEX data
structures. The presolved model must not be modified by the user. If the user
wants to manipulate the reduced problem, the user should make a copy of it
(using CPXcloneprob) and manipulate the copy instead.
The interface allows the user to manually free the memory associated with the
presolved model using the routine CPXfreepresolve. The next optimization call (or
call to CPXpresolve) re-creates the presolved model.
Modifying a problem
Describes conditions in which to modify a model after presolve.
This section briefly discusses the mechanics of modifying a model after presolve
has been performed. This discussion applies only to linear programs; it does not
apply to quadratic programs, quadratically constrained programs, nor mixed
integer programs.
As noted earlier, the user must specify through the parameter CPX_PARAM_REDUCE,
documented in primal and dual reduction type, the types of modifications that are
going to be performed on the model. Recall that if primal reductions are turned
off, the user can add variables, change the righthand-side vector, or loosen variable
bounds without losing the presolved model. These changes are made through the
standard problem modification interface (CPXaddcols, CPXchgrhs, and CPXchgbds).
If dual reductions are turned off, the user can add constraints to the problem,
change the objective function, or tighten variable bounds. Variable bounds are
tightened through the standard interface (CPXchgbds). The addition of constraints or
changes to the objective value must be done through the two routines
CPXpreaddrows and CPXprechgobj. The constraints added by CPXpreaddrows are
equivalent to but sometimes different from those input by the user. The dual
variables associated with the added rows may take different values than those the
user might expect.
If a user makes a problem modification that is not consistent with the setting of
CPX_PARAM_REDUCE, CPLEX discards the presolved model, and presolve is
re-invoked at the next optimization call. Similarly, CPLEX discards the presolved
model if the user modifies a variable or constraint that presolve had previously
removed from the problem. You can use CPXpreaddrows or CPXprechgobj to make
sure that this will not happen. Note that CPXpreaddrows also permits changes to the
bounds of the presolved model. If the nature of the procedure dictates a real need
to modify the variables that presolve removed, you can use the CPXcopyprotected
routine to instruct CPLEX not to remove those variables from the problem.
In this manual, Chapter 35, Using optimization callbacks, on page 477 introduces
callbacks, their purpose, and conventions. Continuing from there, this topic
documents the IBM ILOG CPLEX advanced MIP control interface, describing
control callbacks in greater detail. It assumes that you are already familiar with
that introduction to callbacks in general.
These callbacks allow sophisticated users to control the details of the branch & cut
process. Specifically, users can choose the next node to explore, choose the
branching variable, add their own cutting planes, place additional restrictions on
integer solutions, or insert their own heuristic solutions. These functions are meant
for situations where other tactics to improve performance on a hard MIP problem,
such as nondefault parameter settings or priority orders, have failed. See
Troubleshooting MIP performance problems on page 260 for more information
about MIP parameters and priority orders.
Users of the advanced MIP control interface can work with the variables of the
presolved problem or, by following a few simple rules, can instead work with the
variables of the original problem.
Tip:
The advanced MIP control interface relies heavily on the advanced presolve
capabilities. It's a good idea to become familiar with Chapter 37, Advanced
presolve routines, on page 503 before reading this topic.
Control callbacks in Concert Technology use the variables of the original model.
These callbacks are fully documented in the reference manual of each application
programming interface (API).
When you use a control callback with parallel MIP, you must observe several
points about default parameter settings, thread-safety, and parallelism. This topic
addresses those points.
With respect to determinism, if the user sets the parallel mode switch
(ParallelMode, CPX_PARAM_PARALLELMODE) to 1 (one) thus invoking deterministic
parallel MIP optimization, it is up to the user to make sure that the control
callback does not interfere with the search in any way that would compromise
determinism. In particular, the control callback must be written in such a way that
it does not depend on the order in which callbacks are called, as no fixed order of
calling the callbacks can be guaranteed by parallel CPLEX. To make sure of that
condition, the control callback must use only information queried from CPLEX
itself within the callback as the basis for algorithmic decisions. In other words, no
information that accumulated in an external data structure over several invocations
of the control callback can be used.
This section addresses an important issue related to presolve that the user of MIP
control callbacks should be aware of.
Most of the decisions made within MIP relate to the variables of the problem. The
heuristic, for example, finds values for all the variables in the problem that
produce a feasible solution. Similarly, branching chooses a variable on which to
branch. When considering user callbacks, the difficulty that arises is that the user is
familiar with the variables in the original problem, while the branch & cut process
is performed on the presolved problem. Many of the variables in the original
problem may have been modified or removed by presolve.
CPLEX provides two options for handling the problem of mapping from the
original problem to the presolved problem. First, in an application of the Callable
Library (C API), the user may work directly with the presolved problem and
presolved solution vectors. This is the default. While this option may at first
The second option (available only in applications of the Callable Library (C API),
not in Concert Technology) is to set the MIP callback switch between original
model and reduced, presolved model (CPX_PARAM_MIPCBREDLP) to CPX_OFF (0), thus
requesting that the callback routines work exclusively with original variables.
CPLEX automatically translates the data between original and presolved data.
While the second option is simpler, the first provides more control. These two
options will be revisited at several points in this chapter.
Remember: Callbacks in applications of Concert Technology (that is, the C++, Java,
or .NET APIs) operate only on the original model, not the presolved model. That
is, only the first option, manually invoking advanced presolve methods, is
available and offers more control.
Heuristic callback
Describes the heuristic callback.
This topic introduces the heuristic callback. The first step in using this callback is
to call CPXsetheuristiccallbackfunc, with a pointer to a callback function and
optionally a pointer to user private data as arguments. Among the examples
distributed with the product, there is an advanced example admipex2.c showing
details of how this callback is used. After this routine has been called, CPLEX calls
the user callback function at every viable node in the branch & cut tree. (A node is
viable if its LP relaxation is feasible and its relaxation objective value is better than
that of the best available integer solution.) CPLEX calls the user callback routine
with the solution vector for the current relaxation as input. The callback function
should return a feasible solution vector, if one is found, as output.
The advanced MIP control interface provides several routines that allow the user
callback to gather information that may be useful in finding heuristic solutions.
The routines CPXgetcallbackgloballb and CPXgetcallbackglobalub, for example,
return the tightest known global lower and upper bounds on all the variables in
the problem. No feasible solution whose objective is better than that of the best
known solution can violate these bounds. Similarly, the routines
CPXgetcallbacknodelb and CPXgetcallbacknodeub return variable bounds at this
node. These reflect the bound adjustments made during branching. The routine
CPXgetcallbackincumbent returns the current incumbent - the best known feasible
solution. The routine CPXgetcallbacklp returns a pointer to the MIP problem
(presolved or unpresolved, depending on the MIP callback switch between original
model and reduced, presolved model, CPX_PARAM_MIPCBREDLP). This pointer can be
used to obtain various information about the problem (variable types, etc.), or as
an argument for the advanced presolve interface if the user wants to translate
manually between presolved and unpresolved values. In addition, the callback can
use the cbdata parameter passed to it, along with routine CPXgetcallbacknodelp, to
obtain a pointer to the node relaxation LP. This can be used to access information
about the relaxation (row count, column count, etc.). Note that in both cases, the
user should never use the pointers obtained from these callbacks to modify the
associated problems.
The user should be aware that the branch & cut process works with the presolved
problem, so the code will incur some cost when translating from presolved to
original values. This cost is usually small, but can sometimes be significant.
We should also note that if a user wants to solve linear programs as part of a
heuristic callback, the user must make a copy of the node LP (for example, using
CPXcloneprob ). The user should not modify the CPLEX node LP.
Cut callback
Describes special considerations of the cut callback.
The next example to consider is the user cut callback routine. The user calls
CPXsetusercutcallbackfunc to set a user cut callback. Then CPLEX calls the user's
callback routine at every viable node of the branch & cut tree. See the sample
admipex5.c for a detailed demonstration.
A likely sequence of events after the user cut callback function is called goes like
this. First, the routine calls CPXgetcallbacknodex to get the relaxation solution for
the current node. It possibly also gathers other information about the problem
(through such routines as CPXgetcallbacklp, CPXgetcallbackgloballb, and others).
It then calls a user separation routine to identify violated user cuts. These cuts are
then added to the problem by a call to CPXcutcallbackadd, and the callback
returns. You can add local cuts, that is, cuts that apply to the subtree of which the
current node is the root, by means of the routine CPXcutcallbackaddlocal.
CPLEX supports two rather different types of constraints that might both be
regarded as cuts in some sense.
v The first type is the conventional MIP cutting plane. A MIP cutting plane is a
constraint that can be derived from other constraints in the problem. Equally
important, a MIP cutting plane does not cut off any integer feasible solutions.
This type is known as a user cut in CPLEX. To add a MIP cutting plane (that is,
a user cut), use the routine CPXsetusercutcallbackfunc.
v The second type is a lazy constraint that is, a constraint that can not be derived
from other constraints and potentially cuts off integer feasible solutions. In other
words, a lazy constraint changes the feasible region of the model. To add a lazy
constraint, use the routine CPXsetlazyconstraintcallbackfunc.
As with the heuristic callback, the user can choose whether to work with presolved
values or original values of the model. If the user chooses to work with original
values, a few parameters must be modified:
v If the user adds only cutting planes to the original problem, the user can set the
advanced presolve linear reduction switch (CPX_PARAM_PRELINEAR) to CPX_OFF (0).
This parameter forbids certain presolve reductions that make translation from
original values to presolved values impossible. What happens if the user does
not turn off this parameter? If the user chooses to leave this parameter on, it can
happen that certain user cuts cannot be transformed into the presolved-problem
space; in that case, a call to the routine CPXcutcallbackadd to add such a cut
One scenario that merits special attention is when the user knows a large set of
cuts because of prior knowledge. Rather than adding them to the original problem
one by one, the user can add them only when they are violated. The CPLEX
advanced MIP control interface provides more than one mechanism for
accomplishing this. The first and probably most obvious at this point is to install a
lazy constraint callback that checks each of them at each node, adding those that
are violated.
Another, perhaps simpler alternative is to add them to a pool of user cuts or lazy
constraints before optimization begins. The topic Chapter 33, User-cut and
lazy-constraint pools, on page 453 discusses pools in greater detail.
Alternatively, the branch callback routine can be used to sculpt the search tree by
pruning nodes or adjusting variable bounds. Choosing zero children for a node
prunes that node, while choosing one node with a set of new variable bounds
adjusts bounds on those variables for the entire subtree rooted at this node. Note
that the user must be careful when using this routine for anything other than
choosing a different variable to branch on. Pruning a valid node or placing an
invalid bound on a variable can prune the optimal solution.
We should point out one important detail associated with the use of the MIP
callback switch between original model and reduced, presolved
model,CPX_PARAM_MIPCBREDLP, in a branch callback. If this parameter is set to
CPX_OFF (0), the user can choose branch variables (and add bounds) for the original
problem. However, not every fractional variable is actually available for branching.
Recall that some variables are replaced by linear combinations of other variables in
the presolved problem. Since branching involves adding new bounds to specific
variables in the presolved problem, a variable must be present in the presolved
problem for it to be branched on. The user should use the
CPXgetcallbacknodeintfeas routine from the Advanced Presolve Interface to find
branching candidates (those for which CPXgetcallbacknodeintfeas returns
CPX_INTEGER_INFEASIBLE ). The CPXcopyprotected routine can be used to prevent
presolve from removing specific variables from the presolved problem. (In Concert
Technology, this issue is handled for you automatically.) While restricting
branching may appear to limit your ability to solve a problem, in fact a problem
can always be solved to optimality by branching only on the variables of the
presolved problem.
By design, the CPLEX branch callback calculates and provides the branching
decisions that CPLEX would make in case the user does not create any branches in
the callback. Depending on variable selection and other details of your model, the
computation of these candidate branches can be time-consuming. Consequently, if
you know that you will never use the branching candidates suggested by CPLEX,
then you can save time by disabling such features as strong branching.
Incumbent callback
Describes special considerations about the incumbent callback.
The incumbent callback is used to reject integer feasible solutions that do not meet
additional restrictions the user imposes. The user-written callback will be called
each time a new incumbent solution has been found, including when solutions are
provided by the users heuristic callback routine. The user callback routine is called
with the new solution as input. Depending on the API, the callback function
changes an argument or invokes a method to indicate whether or not the new
solution should replace the incumbent solution.
For the object-oriented callback classes of the C++, Java, and .NET APIs, all
callback information about the model and solution vector pertains to the original,
unpresolved model. For the C API, the MIP callback switch between original
model and reduced, presolved model, CPX_PARAM_MIPCBREDLP, influences the
arguments to the user callback routine. If this parameter is set to its default value
of CPX_ON (1) , the solution vector that is input to the callback is a presolved
vector. It contains one value for each variable in the presolved problem. The same
is true of the various callback support routines (CPXcallbackglobalub , and so
forth.). If the parameter is set to CPX_OFF (0) , all these vectors relate to the
variables of the original problem. Note that this parameter should not be changed
in the middle of an optimization.
The user can influence the order in which nodes are explored by installing a node
selection callback (through CPXsetnodecallbackfunc). When CPLEX chooses the
node to explore next, it will call the user callback routine, with the choice of
CPLEX as an argument. The callback has the option of modifying this choice.
Solve callback
Describes special considerations about the solve callback.
Note:
The most common use of this callback is to craft a customer solution strategy out
of the set of available CPLEX algorithms. For example, a user might create a
hybrid strategy that checks for network status, calling CPXhybnetopt instead of
CPXdualopt when it finds it.
IBM ILOG CPLEX acknowledges use of the dtoa routine of the gdtoa package,
available at
http://www.netlib.org/fp/.
Permission to use, copy, modify, and distribute this software and its documentation
for any purpose and without fee is hereby granted, provided that the above
copyright notice appears in all copies and that both that the copyright notice and
this permission notice and warranty disclaimer appear in supporting
documentation, and that the name of Lucent or any of its entities not be used in
advertising or publicity pertaining to distribution of the software without specific,
written prior permission.
CPLEX acknowledges copyright, authorship, and the disclaimer of warranty for the
AMPL Driver for CPLEX.
Permission to use, copy, modify, and distribute this software and its documentation
for any purpose and without fee is hereby granted, provided that the above
copyright notice appear in all copies and that both that the copyright notice and
this permission notice and warranty disclaimer appear in supporting
documentation, and that the name of Lucent or any of its entities not be used in
advertising or publicity pertaining to distribution of the software without specific,
written prior permission.
CPLEX acknowledges copyright, authorship, and the disclaimer of warranty for the
AMPL Solver Library.
Permission to use, copy, modify, and distribute this software and its documentation
for any purpose and without fee is hereby granted, provided that the above
copyright notice appear in all copies and that both that the copyright notice and
this permission notice and warranty disclaimer appear in supporting
documentation, and that the name of Lucent or any of its entities not be used in
advertising or publicity pertaining to distribution of the software without specific,
written prior permission.
Permission to use, copy, modify, and distribute this software and its documentation
for any purpose and without fee is hereby granted, provided that the above
copyright notice appear in all copies and that both that the copyright notice and
this permission notice and warranty disclaimer appear in supporting
documentation.
The author disclaims all warranties with regard to this software, including all
implied warranties of merchantability and fitness. In no event shall the author be
liable for any special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether in an action of
contract, negligence or other tortious action, arising out of or in connection with
the use or performance of this software.
Permission to use, copy, modify, and distribute this software and its documentation
for any purpose and without fee is hereby granted, provided that the above
copyright notice appear in all copies and that both that the copyright notice and
this permission notice and warranty disclaimer appear in supporting
documentation.
The author and AMPL Optimization LLC disclaim all warranties with regard to
this software, including all implied warranties of merchantability and fitness. In no
event shall the author be liable for any special, indirect or consequential damages
or any damages whatsoever resulting from loss of use, data or profits, whether in
an action of contract, negligence or other tortious action, arising out of or in
connection with the use or performance of this software.
Based largely on suf_sos.c, which bears the following Copyright notice and
disclaimer. AMPL Optimization LLC similarly disclaims all warranties with regard
to this software and grants permission to use, copy, modify, and distribute it and
its documentation.
Index 527
CPXcheckchgcoeflist routine 68 CPXERR_PRESOLVE_BAD_PARAM 456 CPXnewrows routine 92
CPXcheckcopyctype routine 68 cpxerror message channel 114, 115 CPXopenCPLEX routine
CPXcheckcopylp routine 68 CPXfclose routine 69 data types and 64
CPXcheckcopylpwnames routine 68 CPXFILEptr data type 69 example lpex1.c 115
CPXcheckcopyqsep routine 68 CPXflushchannel routine 114 example lpex6.c 156
CPXcheckcopyquad routine 68 CPXfopen routine 69, 112 example netex1.c 182
CPXcheckcopysos routine 68 CPXfputs routine 69 example qpex1.c 194
CPXcheckvals routine 68 CPXfreepresolve 505 example qpex2.c 195
CPXchgbds 509 CPXfreeprob 505 initializing environment 60
CPXchgcoeflist routine 92 CPXfreeprob routine 62, 156, 194, 195, managing input and output 114
CPXchgprobtype routine 272 273 parameters and 71
CPXchgqpcoef routine 190 CPXgetcallbackgloballb 513, 514 role in application 74
changing quadratic terms 190 CPXgetcallbackglobalub 513 CPXordwrite routine 305
example 190 CPXgetcallbackincumbent 513 CPXpreaddrows 509
CPXchgrhs 509 CPXgetcallbackinfo routine 68, 492, 493, CPXpresolve 507
CPXcloneprob routine 494 CPXprimopt 507
advanced preprocessing and 507 CPXgetcallbacklp 513, 514 CPXprimopt routine 70, 272
copying node LPs 513 CPXgetcallbacknodeintfeas 515 CPXPROB_FIXEDMILP symbolic
CPXcloseCPLEX routine CPXgetcallbacknodelb 513 constant 272
example lpex6.c 156 CPXgetcallbacknodelp 513 CPXPUBLIC symbolic constant 69
example mipex2.c 273 CPXgetcallbacknodeub 513 CPXPUBVARARGS symbolic
example qpex1.c 194 CPXgetcallbacknodex 514 constant 69
example qpex2.c 195 CPXgetcallbackorder 515 CPXqpopt routine 194, 195
managing input and output 115 CPXgetcallbackpseudocosts 515 CPXreadcopyprob routine 61
network flow problems 182 CPXgetchannels routine 64, 114, 115 cpxresults message channel 114
purpose 62 CPXgetcolindex routine 66 CPXsetbranchcallbackfunc 515
CPXCLPptr 64 CPXgetcols routine 75 CPXsetcutcallbackfunc 514
CPXCNETptr 64 CPXgetctype routine 219 CPXsetdblparam routine 66, 71
CPXcopybase 507 CPXgetdblparam routine 66, 71 CPXsetdefaults routine 71
CPXcopybase routine 156 CPXgetdblquality routine 148, 154, 165 CPXsetheuristiccallbackfunc 513
CPXcopycttype routine 307 CPXgeterrorstring routine 182, 494 CPXsetintparam routine
CPXcopyctype routine CPXgetintparam routine 66, 71 arguments of 71
checking types of variables 96 CPXgetintquality routine 165 example lpex6.c 156
example mipex1.c 272 CPXgetnumcols routine 65 example netex1.c 182
specifying types of variables 218 CPXgetobjval routine 272 parameter types and 66
CPXcopylp routine 61, 92 CPXgetredlp 507 redirecting output to screen 98
CPXcopynettolp routine 183 CPXgetrowindex routine 66 selecting root algorithm 270
CPXcopyorder routine 305 CPXgetrowname routine 65 CPXsetlogfile routine 112, 161
CPXcopyprotected 507, 515 CPXgetslack routine 272 channels and 114
CPXcopyquad routine 194 CPXgetsos routine 219 collecting messages 68
CPXcopysos routine CPXgetstat routine 272, 499 file pointers and 69
example mipex3.c 305 CPXgetstrparam routine 66, 71 managing log files 112
CPXcopystart 507 CPXgettime 118 CPXsetlpcallbackfunc routine 69, 493,
advanced presolved solution and 507 CPXgetx routine 63, 272 494
crushing primal or dual CPXinfodblparam routine 66, 71 CPXsetmipcallbackfunc routine 69, 493
solutions 507 CPXinfointparam routine 66, 71 CPXsetnodecallbackfunc 517
CPXcreateprob 498 CPXinfostrparam routine 66, 71 CPXsetsolvecallbackfunc 517
CPXcreateprob routine 195 cpxlog message channel 114 CPXsetstrparam routine 66, 71
data types and 64 CPXlpopt 194, 195 CPXsolution routine 74, 272, 499
example lpex6.c 156 CPXlpopt routine 74, 494 CPXstrcpy routine 69
example mipex1.c 272 CPXLPptr data type 64 CPXstrlen routine 69
example mipex2.c 273 CPXmemcpy routine 69 CPXVOIDptr data type 69
example qpex1.c 194 CPXmipopt 507 cpxwarning message channel 114
problem object (C API) 61 CPXmipopt routine 272, 273 CPXwriteprob 95, 96, 149
role in application 74 CPXmsg routine 60, 69, 114, 115 SOS example 305
CPXcutcallbackadd 506, 514 CPXmsgstr routine 69 creating
CPXdelchannel routine 115 CPXNETaddarcs routine 182 application with Concert Technology
CPXdelfpdest routine 69, 114, 115 CPXNETaddnodes routine 182 (C++ API) 4
CPXdelfuncdest routine 114, 115 CPXNETcheckcopynet routine 68 array of variables (Java API) 31
CPXdelindconstr 321 CPXNETchgobjsen routine 182 arrays of variables (Java API) 31
CPXdisconnectchannel routine 114 CPXNETcreateprob routine 64, 182 Boolean variables (Java API) 31
CPXdualopt 507 CPXNETdelnodes routine 182 CPLEX environment 182
CPXENVptr data type 64 CPXNETfreeprob routine 182 log file 112
CPXERR_NEGATIVE_SURPLUS symbolic CPXNETprimopt routine 182, 184 modeling variables (Java API) 29, 31
constant 75 CPXNETptr data type 64 network flow problem object 182
CPXERR_PRESLV_INF 506 CPXNETsolution routine 182 new rows (Java API) 44
CPXERR_PRESLV_UNBD 506 CPXnewcols routine 74, 92 objective function (Java API) 29
Index 529
dynamic search error handling feasibility (continued)
incumbent callbacks and 301 in Concert Technology (C++ API) 20 primal 157
informational callbacks and 478 querying exceptions 98 progress toward 150, 177
query callbacks and 482 Error return status (C++) 15 feasibility tolerance
query callbacks incompatible Error return status (Java API) 35 default 153
with 484 example largest bound violation and 153
dynamic search algorithm Benders decomposition 398 network optimizer and 180
building blocks of 225 Column Generation 337 range of 153
definition 225 columnwise modeling (C API) 74 reducing 151
MIP performance and 225 columnwise modeling (C++ API) 22 Feasible return status (C++) 15
conflict refiner (Interactive Feasible return status (Java API) 35
Optimizer) 431 feasible solutions
E creating multi-dimensional arrays
(C++ API) 23
finding hidden (example) 348
FeasOpt 443
effort level
Cutting Stock 337 definition 443
incumbent and 248
dispatching data to remote displaying infeasibilities 445
MIP starts and 248
object 389 example 445
solution pool and 248
distributed concurrent MIP 387, 388 invoking 443
emphasis
distributed MIP 413 output 444
memory (barrier) 167
distributed MIP C++ 415 preferences 444
memory (LP) 144
distributed MIP Java 416 feasOpt method
numerical (barrier) 171
distributed MIP Python 418 Java API 42
numerical (LP) 146
FORTRAN 70 file format
empty goal 464, 467
lazy constraint callback 454 converting 109
encoding
message handler 115 described 107
multi-byte 106
MIP node log file 255 example QP problem data (C
NULL byte in 106
MIP optimization 272 API) 194
selecting 106
MIP problem from file 273 lazy constraints in LP format 457
end method
MIP with SOS and priority lazy constraints in MPS format 458
IloEnv C++ class 5
orders 304 MST and MIP restart 41
enter Interactive Optimizer
network optimization 178 solution pool filters 300
command 218
optimizing QP 193 virtual machine configuration
entering 218
output channels 115 (VMC) 406
LPs for barrier optimizer 159
Piecewise Linear 311 file reading routines in Callable
mixed integer programs (MIPs) 218
preprocessor macro 388 Library 59
network arcs 182
project staffing 431 file writing routines in Callable
network data 182
reading QP from file 194, 195 Library 59
network data from file 184
remote object 387, 388 finding hidden feasible solutions
network nodes 182
remote object on remote machine 388 example 348
enumeration
resource allocation 431 relevant problem types 348
Algorithm (C++ API) 11
rowwise modeling (C API) 73 flow cover cuts
BasisStatus (C++ API) 16
rowwise modeling (C++ API) 22 defined 232
BoolParam (C++ API) 13
user cut callback 454 flow path cuts
IntParam (C++ API) 13
using arrays for I/O (C++ API) 23 defined 233
NumParam (C++ API) 13
executing a goal 462 FORTRAN 70, 99
Quality (C++ API) 17
expression FracCand parameter
Status (C++ API) 15
building (C++ API) 6 controlling cuts 236
String Param (C++ API) 13
editable (Java API) 32 FracPass parameter
environment
in ranged constraints (Java API) 33 controlling cuts 236
callbacks and 498
linear (C++ API) 6 fractional cuts
constructing (C++ API) 5
logical (C++ API) 6 defined 233
initializing (C API) 60
piecewise linear (C++ API) 6 free row 108
multithreaded (C API) 60
square method (Java API) 32 free variable
releasing (C API) 62
sum method (Java API) 32 MPS files and 108
threads and 498
using modeling variables to construct reformulating QPs and 188
environment variables
(Java API) 29
distributed parallel optimization 404
external variables (C API) 63
EpAGap parameter
objective near zero and 263
extra rim vectors 109 G
EpGap parameter ge method (Java API) 32
when to change 263 generalized upper bound (GUB) cover
EpOpt parameter 154 F cuts 233
EpRHS parameter 153 FailGoal 463 getBasisStatus method
eq method (Java API) 32 feasibility IloCplex Java class 42
error checking analysis and barrier optimizer 175 getBasisStatuses method
diagnostic routines for (C API) 68 check 462 IloCplex C++ class 16
MPS file format 109 dual 143, 157 getBoundSA method
problem dimensions 99 network flows and 177 IloCplex C++ class 16
Index 531
IloNumVar class
modeling objects and (Java API) 29
infeasible solution
accessing information (Java API) 42
K
IloNumVarArray C++ class 6 analyzing (C++ API) 16 kappa
IloNumVarclass InfeasibleOrUnbounded definition (LP) 145
extension of IloNumExpr (Java return status (C++ API) 15 kappa condition number 148
API) 32 return status (Java API) 35 Karush-Kuhn-Tucker (KKT) conditions
IloObjective C++ class 9 infinite horizon quadratically constrained program
IloObjective class ramping up 419 (QCP) 208
addable objects (Java API) 33 info message knapsack constraint
as modeling object (C++ API) 9 definition 386 cover cuts and 232
declaring (C++ API) 7 tags in 386 GUB cover cuts and 233
modeling by column (Java API) 44 informational callback knapsack model
setExpr method in QP term 190 distributed MIP and 480 relevant parameter settings 348
IloObjectiveSense class distributed parallel optimization knapsack problem with reduced cost in
example (Java API) 33 and 480 objective 338
maximizing (Java API) 33 dynamic search and 478
minimizing (Java API) 33 initializing
objective function and (Java API) 33 CPLEX environment 182 L
iloqpex1.cpp example problem object 182 lazy constraint 455, 514
example problem object (C API) 61 definition 453
iloqpex1.cpp 193 input operator (C++ API) 8 Interactive Optimizer and 457
IloRange class instantiating LP file format and 457
adding constraints (C++ API) 7 CPLEX environment 182 MPS file format and 458
linear constraints and (C++ API) 9 problem object 182 pool 453, 459
modeling by column (Java API) 44 problem object (C API) 61 lazy constraint pool
modeling objects and (Java API) 33 integer programming (IP) contrasting user cut pool and 454
IloSemiContVar class 9 bibliography xx example 454
IloSolver as factory (Java API) 27 integrality constraints 461 identifying candidates for 455
IloSOS1 C++ class 9 integrality tolerance when CPLEX checks 454
IloSOS2 C++ class 9 MIP 265 le method
implied bound cuts parameter 265 in expressions (Java API) 32
defined 233 Interactive Optimizer lift-and-project cut 234
global 233 changing problem type (MIP) 219 limiting
local 233 debugging and 96 network iterations 180
include file 98 description xiii strong branching candidate list 262
incumbent distributed MIP 406 linear expression (C++ API) 6
node 226 experimenting with optimizers 93 linear objective function (C++ API) 9
solution 226 improving application linear programming (LP)
solution pool and 281 performance 95 aspect ratio 136, 358
incumbent callback 516 testing code in 91 bibliography xx
solution pool and 301 tuning tool 127 concurrent optimization and 358
indefinite 185 tuning tool time limit 125 sifting 136
index number (C API) 66 VMC file format 406 linear relaxation
indicator constraint 325 creating 406 as initial subproblem in MIP 270
definition 321 deleting 406 MIP and coefficients at nodes 244
restrictions 322 displaying 406 MIP and preprocessing 244
indicator variable 322 isDualFeasible method MIP and progress reports 255
infeasibility IloCplex C++ class 15 local optimum 185
barrier optimizer and 175 isolated point 315 local search heuristics
conflicts and 429 isPrimalFeasible method MIP emphasis parameter and 348
diagnosing in network flows 184 IloCplex C++ class 15 relevant parameter settings 348
displaying on screen 152 relevant problem types 348
dual 172, 175 solution polishing and 348
interpreting results 152
maximum bound 152, 153
J local transport protocol
Java API remote object and 371, 372
maximum reduced-cost 152, 154 log file
asynchronous execution 382
network flow 177 barrier optimizer 161
joining 382
network optimizer and 184 Cholesky factor in 164
remote object 382
norms 164 clones and 361
Java serialization 29
primal 164, 172, 175 closing 112
joining 378
ratio in barrier log file 164 contents 141, 165
joining (C API) 378
reports 150 creating 112
joining (C++ API) 380
scaling and 151 default 112
joining (Java API) 382
unboundedness and (LP) 152 description 111
unscaled 151 diagnostic routines and (C API) 68
Infeasible return status (C++ API) 15 iteration 144
Infeasible return status (Java API) 35 naming 112
Index 533
MPS file format (continued) NodeAlg objective function (continued)
lazy constraints in 458 controlling algorithm at subproblems maximize (C++ API) 7
saving modifications 109 (MIP) 229 minimize (C++ API) 7
saving QP 189 NodeAlg parameter modeling (Java API) 33
user cuts in 458 node relaxations and 271 network flows and 177
multi-byte encoding 106 NodeFileInd parameter optimality tolerance and 154
multi-commodity flow cuts (MCF) 235 effect on storage 267 preprocessing and 424
multicast node files and 267 primal reductions and 424
remote object and 375 nonlinear expression representing with IloObjective (C++
multithreaded definition 327 API) 7
optimizers 353 nonseparable 185 sign reversal in 109
parallel and 353 nonzero value (Python API) 83 objective value
multithreaded application nonzeros accessing slack in (C++ API) 15
needs multiple environments (C variables in solution (Python API) 83 in log file 179
API) 60 variables in solution and high network flows and 177
precision (Python API) 83 object range parameter 174
notation in this manual xvii unbounded 174
N notifying
changes to IloCplex object (C++
Open MPI
distributed MIP 407
namespace conflicts (C API) 63
API) 18 opportunist mode
naming
NULL byte encoding 106 MIP in parallel and 355
arcs in network flow 182
null goal 464 MIP parallel optimizer and 360
conventions 109
definition 464 parallel mode parameter 355
log file 112
when to use 464 opportunistic search
node file 267
numbering conventions component libraries and 357
nodes in network flow 182
C 99 control callbacks in parallel 487
negative method
FORTRAN 99 informational callbacks and 478
expressions and (Java API) 32
row, column order 108 Interactive Optimizer and 356
negative semi-definite objective 185
numeric difficulties mixed integer programming (MIP)
NetItLim 180
barrier growth parameter 174 and 355
network
barrier optimizer and 173 query callbacks and 484
converting to LP model 183
basis condition number and 148 synchronization and 355
embedded 180
complementarity 174 thread safety and 512
infeasibility in 177
convergence tolerance 174 threads parameter and 355
modeling variables 177
definition (LP) 146 wait time and 355
multi-commodity flow cuts
dense columns removed 173 Optimal return status (C++ API) 15
(MCF) 235
infeasibility and 150 Optimal return status (Java API) 35
problem formulation 177
sensitivity 148 optimality
network extractor 181
unbounded optimal face 174 basis condition number and 148
network object 177
numeric variable (C++ API) 9 cutoff parameters 263
network optimizer 135, 177, 181
numerical emphasis infeasibility ration 164
preprocessing and 181
barrier optimizer and 171 normalized error and 165
problem formulation 177
continuous (LP) 146 singularities and 149
turn off preprocessing 181
NumericalEmphasis parameter tolerance 151, 154
node 461
barrier 171 relative 263
demand 177
LP 146 optimality tolerance
from 177
absolute 263
head 177
changing relative or absolute 263
sink 177, 178
source 177, 178 O gap 263
maximum reduced-cost infeasibility
supply 177 ObjDif tolerance parameter 226
and 154
tail 177 objective coefficients
network and 180
to 177 crash parameter and 143
reducing 151
transshipment 177 modified in log file 179
relative 263
viable 513 network flows and 179
relative, default 263
node file 267 priority and 251
setting 154
cpx name convention 267 objective difference
when to change 263
parameters and 267 absolute 226, 263
optimality-based cut 454
using with MIP 230 relative 226, 263
when CPLEX checks 454
when to use 230, 267 objective function
optimization
node heuristic 238 accessing value of (C++ API) 15
interrupting 494
node log 255 changing sense 182
stopping 224, 494
node problem 461 constructor (Java API) 33
optimization limits
node selection callback 517 creating (Java API) 29
determinism and 359
node selection strategy free row as 108
parallel optimization and 359
best estimate 269 in log file 179
depth-first search 269 in MPS file format 109
maximization 109
Index 535
preprocessing (continued) problem formulation (continued) QP (continued)
dependency parameter 168 primal 157, 159 solution example 194, 195
dual reductions in 424 removing dense columns 169 solving 185, 195
lazy constraints and 456 switching from network to LP 183, QP relaxation 192
MIPs 244 184 quadratic
network optimizer and 181 problem modification routines in Callable constraints 197
primal reductions in 424 Library 59 convex constraints 197
second-order cone program (SOCP) problem object quadratic coefficient
and 199 creating (C API) 61 changing 190
simplex and 137 destroying (C API) 62 quadratic objective function (C++ API) 9
starting-point heuristics and 170 freeing (C API) 62 quadratic program (QP)
turning off 139 initializing (C API) 61 changing problem type 189
presolve 503 instantiating (C API) 61 quadratic programming (QP)
barrier preprocessing 168 network 177 bibliography xx
dependency checking in 138 populating 182 quadratically constrained program
final factorization after uncrush populating (C API) 61 (QCP) 197
in 138 problem query routines in Callable examples: dual values and 208
gathering information about 507 Library 59 examples: reduced costs and 208
interface 507 problem type Karush-Kuhn-Tucker (KKT) conditions
lazy constraints and 456 changing from network to LP 183, and 208
limited 507 184 Lagrangian of 208
process for MIP 503 changing in QCP 206 query callback
protecting variables during 507 changing to qp 190 dynamic search incompatible
restricting dual reductions 505 changing to zeroed_qp 190 with 484
restricting primal reductions 505 quadratic programming and 189 parallel search and 484
simplex and 137 quadratically constrained thread safety and 484
simplex preprocessing 137 programming and 202
turning off (Java API) 40 rotated cone programming and 202
presolved model
adding constraints to 504
second order cone programming
and 202
R
ramping up
building 503 process transport protocol
infinite horizon 419
freeing 505 remote object and 371, 372
rampup 401
freeing memory 507 prod method in expressions (Java
range filter
retaining 505 API) 32
example 300
pricing algorithms 180 programming paradigm
ranged constraint
primal feasibility 157 remote object and 368, 371
creating (Java API) 29
primal reduction 424 pruned node 461
definition (Java API) 32
primal simplex optimizer 135 PSD
name of (Java API) 32
perturbing variable bounds 150 positive semi-definite in objective
ranged row 109
stalling 150 function 185
reading
primal variables 143 quadratic constraints and 199
MIP problem data 273
primal-degenerate problem 135 second-order cone program (SOCP) as
MIP problem data from file 218
priority 251 exception to 199
network data from file 184
binary variables and 251 Python API
QP problem data from file 194, 195
integer variables and 251 closing log files 112
start values from MST file 246
order 251 creating log files 112
redirecting
parameter to control 251 message channels 112
diagnostic routines (C API) 68
reading from file 251 opening log files 112
log file output 113
semi-continuous variables and 251 streams 112
oputput 98
semi-integer variables and 251
screen output 113
special ordered set (SOS) and 251
Reduce parameter
priority order (Java API) 41
Probe parameter
Q lazy constraints and 456
QCP reduced cost
MIP 230
barrier optimizer and 199 accessing (C++ API) 15
probing parameter 230
convexity and 197 accessing (Java API) 42
problem
detecting problem type 202 choosing variables in column
analyzing infeasible (C++ API) 16
examples 213 generation 338
solving with Concert Technology (C++
file types and 203 column generation and 337
API) 3
modifying constraints in 207 pricing (LP) 141
problem formulation
PSD and 199 reduction
barrier 157
QP dual 424
dual 157, 159
example 193, 194, 195 primal 424
ill-conditioned 151
indefinite 185 refactoring frequency
infeasibility reports 150
portfolio optimization 188 dual simplex algorithm and 137
linear xiii
problem formulation 185 primal simplex algorithm and 137
network 177
reformulating large, dense reference counting 468
network-flow 177
models 188 reference row values 304
Index 537
setParam method solution (continued) status variables, using 494
IloCplex C++ class 13 differences between barrier, steepest-edge pricing 141, 267
setting simplex 158 step in piecewise linear function 313
algorithm in LP (C++ API) 11 example QP 194, 195 stopping criterion
all default parameters (C API) 71 feasible in MIPs 246 callbacks and 494
all default parameters (C++ API) 13 finding hidden feasible 348 optimization (MIP) 224
callbacks to null (C API) 71 incumbent 226 solution pools and 285
callbacks to null (C++ API) 13 infeasible basis 175 strong branching 262
parameters (C API) 71 midface 158 SubMIPNodeLim parameter
parameters in C++ API 13 nonbasis 158 RINS and 238
sifting 136 pool (MIP) 279 solution polishing and 238
simplex quality 156, 165, 172 subproblem
column generation and 337 serializing 110 definition (MIP) 229
dual 135 supplying first integer in MIPs 246 summary statistics 152
feasibility tolerance in MIP 265 using advanced presolved 507 suppressing output to the screen 116
optimizer 158 verifying 171 surplus argument (C API) 75
pricing phase and 337 XML representation of 110 symbolic constants (C API) 65, 71
primal 135 solution polishing symmetry-breaking constraint 454
singularity 149 example 348 when CPLEX checks 454
slack example in early tardy
accessing bound violations in (C++ scheduling 345
API) 17
accessing in constraints in active
relevant problem types 348
subproblems and 272
T
tail 177
model (Java API) 36 solution pool
TCP/IP
accessing slack variables in constraints changing fixed problem and 293
distributed MIP 412
(C++ API) 15 definition 279
terminating
accessing slack variables in objective fixed problem and 293
because of singularities 149
(C++ API) 15 incumbent in 281
MIP optimization 224
as indicator of ill-conditioning 154 MIP start and 293
network optimizer iterations 180
as reduced cost in infeasibility replaced solutions 291
without callbacks 499
analysis 154 stopping criteria 285
termination criterion
example CPXgetslack 272 writing MIP start file 293
multiple (Python API) 86
in primal formulation (barrier) 157 solution quality
simple (Python API) 86
in summary statistics 152 ill conditioning and (LP) 145
thread safety
infeasibilities as bound violations incumbent (Python API) 82
callbacks and 353
and 154 LP unboundedness and
parallel mode and 353
infeasibility in dual associated with infeasibility 152
thread-safe (C API) 63
reduced costs 154 MIP 263
threads 354
maximum bound violation and (Java solution pool (Python API) 82
callbacks and 498
API) 43 solve callback 517
clones 361
meaning in infeasible primal or dual solve method
environment and 498
LP 152 IloCplex C++ class 15, 16, 17, 18, 25
parallel optimizers 353
pivoted in when constraint is removed solving
parallelism and 498
(C++ API) 18 diet problem (Java API) 36
TiLim parameter
reducing computation of steepest edge model (C++ API) 11
solution polishing and 238
pricing 141 single LP (Java API) 38
time limit
role in inequality constraints subsequent LPs or QPs in a MIP (Java
concurrent optimizer and 137
(barrier) 164 API) 39
deterministic search and 355
role in infeasibility analysis 154 sparse matrix
effects all algorithms invoked by
using primal variables instead 143 IloLPMatrix and (Java API) 44
concurrent optimizer 137
variable needed in basis special ordered set (SOS)
possible reason for Unknown return
(network) 183 role in model (Java API) 29
status (C++ API) 15
variables and primal variables type 1 (C++ API) 9
possible reason for Unknown return
(dual) 143 type 2 (C++ API) 9
status (Java API) 35
SOCP using 303
TiLim parameter (MIP) 222
background 200, 213 weights in 304
tuning tool examples 125
detecting problem type 202 split cut 234
time stamp 118
examples 200, 213 stalling 150
callbacks and 119
SOCP second-order cone program 199 starting algorithm
determinism and 117
solution callbacks and 501
timing interface 118
accessing quality information (C++ goals and 501
callbacks and 119
API) 17 parallel processing 360
determinism and 117
accessing values of (C++ API) 15 static variables (C API) 63
tolerance
alternative (MIP) 279 status message
absolute objective difference and 226
basic infeasible primal 150 master 386
absolute optimality 263
basis 158 remote object and 386
complementarity convergence, default
complementary 157 tags in 386
of 174
Index 539
540 CPLEX Users Manual
Printed in USA