Switching from XPRESS™
This page covers migrating from XPRESS™ to Gurobi. While the steps are similar, if you are interested in migrating from CPLEX to Gurobi please visit our switching from CPLEX page.
Any program that uses optimization must start by building an optimization model. While the exact details of how models are built depend on the programming language, the general concepts are similar in all of our supported languages. We'll focus on the concepts, and provide details when appropriate.
The first two steps in describing an optimization problem in Gurobi are to create an environment and a model.
These are objects in our object-oriented interfaces, created using the GRBEnv and GRBModel constructors, respectively. They are pointers in our C interface, returned by our GRBloadenv() and GRBnewmodel() routines. XPRESS programs start in a similar way, but with slightly different terminology. In XPRESS, you initialize the optimizer (using XPRSinit/XPRBinit), and then create a problem (XPRScreateprob()/XPRBnewprob()).
The next step is generally to list your decision variables. This is done using the GRBaddvar() routine in C, and the addVar() method in our object-oriented interfaces. You can add one variable at a time, or you can add multiple variables (using GRBaddvars() or addVars()). We generally find it more convenient to add one variable at a time, and there is no performance penalty for doing so, but you may want to add multiple variables if your XPRESS program already does so.
One difference between Gurobi and XPRESS is that we use a "lazy update" approach. After making changes to a model, you need to call GRBupdatemodel/GRBModel.update in order for those changes to be visible. To be more specific, you'll need to call "update" after adding variables to the model, in order to use those variables in constraints. Our lazy update approach makes it easier and more efficient to build or modify a model, since you have complete control over when the actual changes take place. However, this difference between our interface and the XPRESS interface is something you'll need to remember.
Decision variables have a number of attributes, including variable type (continuous, binary, etc.), lower bound, upper bound, etc.. You have two choices for specifying these. The first is to input the desired attribute values when you create the variable (i.e., as arguments to addVar()). The second is to modify the attributes after the variable has been added to the model, using one of the various Gurobi set routines (e.g., GRBsetintattr() in C, GRBVar.set() in C++).
Attributes are an important concept in the Gurobi interface. Rather than providing dozens of different routines for accessing and modifying the various attributes of a model, as is done in XPRESS, we handle them through a single interface.
To give an example, below is the command, shown in a range of languages, you would use to change the upper bound on variable x to 1.0:
|C||GRBsetdblattrelement(model, GRB_DBL_ATTR_UB, x_index, 1.0);|
|Python||x.ub = 1.0|
Similarly, to change the lower bound:
|C||GRBsetdblattrelement(model, GRB_DBL_ATTR_LB, x_index, 1.0);|
|Python||x.lb = 1.0|
This attribute interface serves to both unify and simplify the various Gurobi interfaces. In general, if you are searching for a routine to match a XPRESS get/set routine, you are likely to find that capability in our attribute interface.
The next step in building an optimization model is generally to describe the linear constraints on your decision variables. Depending on the interface you are using, you can describe these constraints using a single constraint matrix, by adding groups of constraints, or by adding constraints one at a time. Again, we find it more convenient to add constraints one at a time, but we understand that it may simplify migration if you mimic your existing XPRESS approach.
You will generally only use a single constraint matrix to specify your constraints if you are using the Gurobi C interface. You would do this with the GRBloadmodel() routine. The arguments to this routine are quite similar to the arguments to the XPRESS XPRSloadlp() routine. Note that you can also add constraints individually in our C interface, using GRBaddconstr().
In our object-oriented interfaces, you can add individual linear constraints using the addConstr() method. The details depend on the language you are using. Our C++, .NET, and Python interfaces allow operator overloading, so you can add a linear constraint as follows:
|C++||model.addConstr(x + y + 2*z <= 2);|
|C#||model.AddConstr(x + y + 2*z <= 2);|
|Python||model.addConstr(x + y + 2*z <= 2)|
You can also build linear expression (GRBExpr) objects, add linear terms to these expressions (expr.addTerm()), and then add constraints using these expressions. In Java:
GRBLinExpr expr = new GRBLinExpr();
expr.addTerm(1.0, x); expr.addTerm(1.0, y); expr.addTerm(2.0, z);
model.addConstr(expr, GRB.LESS_EQUAL, 2.0);
Your model might contain other constraint types, including Special Ordered Set (SOS) constraints or quadratic constraints. The Gurobi interface contains routines that are quite similar to those in the XPRESS interface for each of these. We encourage you to browse our examples for details.
When migrating an optimization model from XPRESS to Gurobi, you may need to set certain Gurobi parameters to match the parameters you have modified in XPRESS. One important point we'd like to make is that you shouldn't assume that you'll need to find a matching Gurobi parameter for every XPRESS parameter you've changed. Gurobi and XPRESS use different strategies and algorithms. Gurobi strategy tuning may differ from the XPRESS tuning you've done, and often it may not be necessary at all. We recommend that you start with default settings, and only change parameters when you observe specific behavior that you'd like to modify.
The following table gives a high-level mapping for the most commonly used XPRESS parameters...
|XPRESS parameter (C API)||Gurobi Parameter|
Again, this is just a partial list of the parameters you might be using in XPRESS. If you are modifying a parameter that isn't on this list, we encourage you to browse the list of Gurobi parameters in our Reference Manual.
Once you know which parameters you would like to change, the code required to change them is straightforward. In C, you call GRBsetintparam/GRBsetdblparam/GRBsetstringparam. In our object-oriented interfaces, you call the set() method on the GRBEnv object.
One point that sometimes trips people up when migrating from XPRESS to Gurobi is that Gurobi gives each model its own copy of a Gurobi environment, thus allowing each model to have its own parameter settings. In XPRESS, all models use the same parameter values.
The simplest approach to managing this is to set parameters for a model as follows:
|Python||model.params.solutionLimit = 1|
Once you have formulated your optimization model and set solver parameters, the last step is to compute a solution. You do this by calling GRBoptimize() in C, or GRBModel.optimize() in our object-oriented interfaces. The optimization will complete when one of your requested termination conditions (optimality gap, time limit, solution limit, etc.) are met.
You should always check the status of the optimization once it completes. Again, rather than providing dozens of get/set routines, we return the status as an attribute of the model. You query it with the appropriate attribute get routine.
|C++||if (model.get(GRB_IntAttr_Status) == GRB_OPTIMAL) ...|
|C#||if (model.Get(GRB.IntAttr.Status) == GRB.OPTIMAL) ...|
|Java||if (model.get(GRB.IntAttr.Status) == GRB.OPTIMAL) ...|
|Python||if model.status == GRB.OPTIMAL: ...|
The computed values for the decision variables are also attributes:
|C||cout << "Value for variable x is: " << x.get(GRB_DoubleAttr_X) << endl;|
|C#||Console.WriteLine("Value for variable x is: " + x.Get(GRB.DoubleAttr.X));|
|Java||System.out.println("Value for variable x is: " + x.get(GRB.DoubleAttr.X));|
|Python||print 'Value for variable x is:', x.x|
You can also retrieve other solution information, including the objective value, constraint slacks, and dual variables (when available), all using the attribute interface. Let us reiterate that if you are looking to map a XPRESS get/set function to Gurobi, you will probably find its equivalent in our attribute interface.
Of course, finding a proven optimal solution isn't the only possible outcome. For example, you may find that your model is infeasible. If you are using the XPRESS IIS routines to help diagnose infeasibility, Gurobi also provides an IIS feature (GRBcomputeIIS() in C; model.computeIIS() in the object-oriented interfaces). If you would like to find a minimum-cost relaxation of the constraints in your model, you can use the Gurobi feasRelax feature (GRBfeasrelax() in C; model.feasRelax in the object-oriented interfaces).
We encourage you to browse the Gurobi examples in the Example Tour to get a more concrete sense of how the Gurobi interfaces are structured. Customers have told us that, once you understand a few key concepts, the migration process is straightforward and typically quite quick.