Initializing help system before first use

# Migrating from BCL

Topics covered in this chapter:

- Replacing the XPRBprob factory class
- Creating variables
- Expressions
- Creating constraints
- Specifying the objective function
- Operators
- Interacting with Optimizer controls and attributes
- Solution objects
- Special ordered sets
- Indicator constraints
- Index sets
- Cuts
- Deletion
- Synchronization
- Mapping from BCL to the new API
  - Objects
  - Functions
  - Constants

As described above, in Xpress 9.4 the C++ API to the Optimizer was extended to allow the creation of an optimization problem in a fully object oriented fashion. Instead of specifying the problem as a matrix with row and column indices, you can use variable and constraint objects to state the problem.

The new API is fully integrated with the low-level Optimizer API, and has been designed for high performance. The new API is a replacement for BCL, which will be deprecated in future Xpress releases.

We will only give very short code example snippets here. Please refer to the reference documentation for more details.

## Replacing the XPRBprob factory class

Any BCL application requires an instance of class XPRBprob as factory to create variables and/or constraints. For the new API you also need a factory class. The fully qualified name of this class is xpress::objects::XpressProblem.

All classes required to formulate optimization problems with the new API are located in the xpress::objects namespace. In the following we assume that you imported all names from this and will no longer provide fully qualified names.

## Creating variables

In BCL only a single variable can be created at a time. This is done using function XPRBprob::newVar().

With the new API you can also create variables one by one using XpressProblem::addVariable() family of functions. Alternatively, you can create multiple variables with a single statement (this is more efficient than creating variables one by one). The overloads of function XpressProblem::addVariables() achieve this. These functions allow creating single and multidimensional arrays or maps of variables with a single function call.

Creating individual variables in the new API is similar to creating variables in BCL. All properties of the variable (bounds, type, name, ...) are specified as arguments to the function that creates the variable:

    //Unnamed continous variable in [0,infinity)
    Variable x = prob.addVariable();
    // Integer variable in [2,3] named "x"
    Variable x = prob.addVariable(2, 3, ColumnType::Integer, "x");

Please refer to the reference documentation for an exhaustive list of overloads.

When creating multiple variables at once then the builder pattern is applied to modify the default properties of the variables. Each property can be modified by passing a constant value that applies to all variables created or by passing a function that generates the respective property based on the indices/objects it is created for:

    // Create a 3x4 matrix of binary variables.
    	// The names of the variables are "x_0_1", "x_0_2", ...
    auto x = prob.addVariables(3,4)
      .withType(ColumnType::Binary)
      .withName([](auto i, auto j){ return xpress::format("x_%d_%d", i, j); })
      .toMap();
    // Create a map of variables that is indexed by pairs of objects from
    // the cartesian product of c1 and c2. All variables are integer and
    // the upper bounds are computed from the properties of the objects
    std::vector<C1Type> c1 = ...;
    std::vector<C2Type> c2 = ...;
    HashMap2<C1Type,C2Type,Variable> y = prob.addVariables(c1, c2)
      .withType(ColumnType::Integer)
      .withUB([](auto c1object, auto c2object){ return c1object.getUB() * c2object.getUB(); })
      .toMap();

Again, the reference documentation provides more details.

## Expressions

In BCL any kind of expression is represented by class XPRBexpr and its subclasses XPRBlinExp (for linear expressions) and XPRBquadExp (for quadratic expressions).

The new API provides analogous classes Expression, LinExpression and QuadExpression. These expressions work in the same way in all APIs:

    Variable x = ..., y = ..., z = ...
    // Create 3*x + 4*y + z
    LinExpression l = LinExpression.create();
    l.addTerm(3, x);
    l.addTerm(y, 4);
    l.addTerm(z);
    // Create 3*x^2 + 4*x*y + 5*z
    QuadExpression q = QuadExpression.create();
    q.addTerm(3, x, x);
    q.addTerm(4, x, y);
    q.addTerm(5, z);

## Creating constraints

In BCL a constraint is created using XPRBprob::newCtr(). This function creates a constraint and returns an object representing the newly created constraint.

Adding constraints with the new API works similarly. Here you use function addConstraint() to create a single constraint and function addConstraints() to create multiple constraints with a single function call (this is more efficient). The latter function has a number of overloads, in particular for creating multi-dimensional constraints, for creating constraints directly from data etc. See the reference documentation for further details.

In order to create a less-than-or-equal, equal, greater-than-or-equal or a range constraint, use functions leq(), eq(), geq(), or in(), respectively, of the Expression class:

    // Add constraint 2*x+3*y <= 5
    prob.addConstraint(x.mul(2).plus(y.mul(3)).leq(5));
    // Add constraint 6*y >= 7*z
    prob.addConstraint(y.mul(6).geq(z.mul(7)));
    // Require x+y to be in [2,3]
    prob.addConstraint(x.plus(y).in(2, 3));

You can also directly use operators <=, >= and == instead of functions leq(), geq() or eq().

## Specifying the objective function

In BCL the objective function is set by calling XPRBprob::setObj() which expects as argument a constraint expression without relational operator.

In the new API the objective function is set using function XpressProblem::setObjective() which expects an expression as argument. The objective sense is set by picking an overload for setObjective that also allows setting the sense.

    // Minimize x+y
    prob.setObjectve(x + y);
    // Maximize x+y
    prob.setObjective(x + y, ObjSense::Maximize);

## Operators

Like BCL, the new API makes it possible to create linear and quadratic expressions by combining constants, variables and expressions using the plus(), minus(), mul(), and div() member functions. As opposed to BCL, these operations are always guaranteed to work in constant time. None of the operations require deep copies of their arguments.

In addition, there are convenience functions sum() and scalarProduct() in xpress::objects::utils. These functions make creating expressions somewhat easier and more readable in some cases.

    // Create 3*x[0] + 2*x[1] + 4*x[2] in different ways:
    Variable[] x = ...
    e = x[0].mul(3).plus(x[1].mul(2)).plus(x[2].mul(4))
    e = sum(x[0].mul(3), x[1].mul(2), x[2].mul(4))
    e = scalarProduct(x, std::vector<double>{ 3, 2, 4 })
    e = 3*x[0] + 2*x[1] + 4*x[2]

As seen above, operators +, -, *, / are overloaded. So instead of x.plus(y) you can also write x+y.

## Interacting with Optimizer controls and attributes

In order to interact with Optimizer attributes or controls or with special solver features in BCL, you first have to obtain a representation of the solver problem, an instance of class XPRSprob. In addition, when using this instance, you have to make sure that data is properly synchronized between BCL and the Optimizer.

None of this is necessary in the new API. The XpressProblem is a subclass of XPRSprob and it no longer holds a representation of the problem model that is separate from the solver. In the new API there is only a single representation of the problem. Any change you make to the problem formulation via the new API directly changes the problem held in the solver. Also, all solver controls and attributes are directly available as members of the XpressProblem class.

    XpressProblem prob;
    // Set maximum number of nodes and query the solution status
    prob.controls.setMaxNode(1);
    std::cout << prob.attributes.getSolStatus() << std::endl;

## Solution objects

BCL provides XPRBsol objects to capture a solution.

The new API does not provide such a class. Instead a solution is represented as an array of double values, one for each variable. You retrieve such an array using XpressProblem::getSolution() and then pass the array to a variable's getValue() function:

    Variable[] x = ...;
    ... // build the model here
    prob.optimize();
    auto sol = prob.getSolution();
    for (Variable v : x)
        std::cout << x.getName() << " = " << x.getValue(sol);

## Special ordered sets

In BCL special ordered set constraints are specified using XPRBprob::newSos() which expects the variables and their weights to be passed as an expression (XPRBexpr).

In the new API a special ordered set constraint is created by first creating an SOS description, for example using function SOS::sos() and then passing this result to XpressProblem::addConstraint():

    std::vector<Variable> x = ...;
    std::vector<double> weigths = ...;
    prob.addConstraint(SOS::sos(SetType::SOS1, x, weights));

## Indicator constraints

In BCL indicator constraints are defined by setting the "indicator" property of an instance of class XPRBctr.

In the new API you can still set the indicator variable for a particular constraint but in a slightly different way:

    // if x == 1 then y == 5
    Variable x = ...;
    Variable y = ...;
    Inequality i = prob.addConstraint(y == 5);
    prob.setIndicatorVariable(x, true, i);

The boolean argument to setIndicatorVariable() specifies whether the constraint should be activated if the indicator variable is non-zero (true) or zero (false).

The same indicator constraint can also be created in a different way:

    prob.addConstraint(x.ifThen(y == 5));

There are also functions ifThen() and ifNotThen() in the Variable class to create a constraint that only becomes active if the indicator variable is set to one or zero, respectively.

## Index sets

In the new API there is no equivalent to BCL's index sets (as created by XPRBprob::newIndexSet()).

Instead you should use the collections that are provided by the programming language.

## Cuts

In the new API there is no equivalent to BCL's cut data type (as created by XPRBprob::newCut()).

A cut in the new API is just an instance of Inequality (like any other row constraint) and can be added using function XpressProblem::addCut().

Note that the addCut() function automatically presolves the cut if necessary. In other words, you can provide the cut as an expression in Variable instances and the API will correctly translate this cut to the presolved model. It is no longer necessary to call a function like XPRBprob::setCutMode() to enable adding of cuts from callbacks.

## Deletion

As in BCL, the new API allows for deleting rows and SOSs. The functions for this are XpressProblem::delInequalities() and XpressProblem::delSOS().

As opposed to BCL, the new API also allows for deletion of variables by means of function delVariables().

## Synchronization

Since BCL keeps two separate copies of the problem (one with BCL objects, one inside the Optimizer) these copies must be kept in sync. This requires calling methods like XPRBprob::loadMat() or XPRBprob::sync(). It also puts some limitations on what things can be done in parallel in callbacks.

Since the new API only ever has a single copy of the problem, no synchronization is required at all. Moreover, no special considerations for parallel callbacks are required.

In callbacks there is no need to call functions like XPRBprob::beginCB() and/or XPRBprob::endCB(). The problem instance passed into the callback is always up to date.

## Mapping from BCL to the new API

### Objects

BCL | New API
--- | ---
XPRBvar | xpress::objects::Variable
XPRBctr, XPRBcut, XPRBrelation | xpress::objects::Inequality
XPRBexpr | xpress::objects::Expression
XPRBbasis | Not required, use plain arrays of double and int to interact with the basis get and set methods of the XpressProblem class (more precisely, its XPRSprob superclass)
XPRBsol | Not required, use plain arrays of double to interact with the solution query methods of the XpressProblem class (more precisely, its XPRSprob superclass)
XPRBexpr, XPRBlinExp, XPRBquadExp, XPRBterm, XPRBqterm | Expression and all its subclasses (in most cases the actual concrete subclass should not matter)
XPRBlinExp | xpress::objects::LinExpression
XPRBquadExp | xpress::objects::QuadExpression
XPRBindexSet | Use collections provided by the programming language
XPRBsos | xpress::objects::SOS

### Functions

BCL | New API
--- | ---
XPRBprob::newVar (with overloads) | XpressProblem::addVariable and XpressProblem::addVariables (with overloads)
XPRBprob::newCtr (with overloads) | XpressProblem::addConstraint and XpressProblem::addConstraints (with overloads)
XPRBprob::delCtr | XpressProblem::delInequalities
XPRBprob::delSos | XpressProblem::delSOS
XPRBprob::getLPStat, XPRBprob::getMIPStat | Directly query the solver's LPSTATUS and MIPSTATUS attributes
XPRBprob::loadMat, XPRBsync | Not required since there is only one problem representation
XPRBprob::isValid | Not required since the model is always in a valid state
XPRBprob::readBinSol, XPRBprob::readSlxSol, XPRBprob::writeBinSol, XPRBprob::writeSlxSol | Use the Optimizer's functions to interact with solution files
XPRBprob::setOutputStream | Use the Optimizer's message callback instead
XPRBprob::solve, XPRB::mipOptimize, XPRB::lpOptimize | XpressProblem::optimize, XpressProblem::mipOptimize, XpressProblem::lpOptimize (which are all in fact members of the XPRSprob superclass), preferably use optimize() unless you explicitly need one of the other functions.
XPRBprob::loadMipSol | Use XpressProblem::addMipSol
XPRBprob::getProbStat | Not required, solution status can be queried directly from the Optimizer, see the SolStatus attribute

### Constants

BCL | New API
--- | ---
Variable type XPRB::BV, XPRB::PI, XPRB::PL, ... | xpress::ColumnType
Row type XPRB::E, XPRB::G, XPRB::L, XPRB::N | xpress::RowType
LP solution status LP_* | Use the corresponding constants from the XPRSprob class directly.

Parent Topic Solver C++ User Guide

© 2001-2026 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.