Blog

Switching from FICO Xpress to Gurobi

A practical migration guide for moving optimization applications from FICO Xpress to Gurobi, from model construction and parameter translation through solution validation and benchmarking.

Blog

Switching from FICO Xpress to Gurobi

A practical migration guide for moving optimization applications from FICO Xpress to Gurobi, from model construction and parameter translation through solution validation and benchmarking.

Blog

Switching from FICO Xpress to Gurobi

A practical migration guide for moving optimization applications from FICO Xpress to Gurobi, from model construction and parameter translation through solution validation and benchmarking.

author

Everett Dutton

Optimization Strategist

Everett Dutton

author

Everett Dutton

Optimization Strategist

Everett Dutton

Switching from FICO Xpress to Gurobi

Migrating an optimization application from FICO Xpress to Gurobi can be straightforward, as long as you remember to map concepts, not necessarily exact usage. The products expose similar modeling concepts, but their APIs, parameter values, default behavior, and stopping criteria are not always directly interchangeable. If you’ve used AMPL, GAMS, or another 3rd-party modeling language, you will likely have an easier time switching, perhaps even in one line of code.

As you scroll through this guide, you'll notice that most of the code snippets look very similar if you squint. This is by design, because at the end of the day we are doing the same mathematical modeling regardless of which solver it's written for.

This guide covers the main stages of a migration:

  1. Building the model

  2. Translating solver parameters

  3. Optimizing and extracting solutions

  4. Validating the migrated application

The examples focus on the Gurobi C, C++, C#, Java, and Python APIs. The exact migration path depends on whether your application uses the Xpress Optimizer API, Xpress Python, BCL, or Mosel.

Before you translate the code

Start by separating three parts of the existing application in your mind: the mathematical model; solver configuration; and surrounding application logic such as data loading, callbacks, logging, and deployment.

Migrate and validate the mathematical model before trying to reproduce Xpress performance. A parameter setting that helps Xpress may be unnecessary, or have a different effect, in Gurobi. Begin with Gurobi defaults and port only requirements that are required as part of the application, such as a time limit, accepted optimality gap, thread limit, or numerical tolerance.

Building the model

Environments and models

In the object-oriented Gurobi APIs, an environment manages shared resources and configuration, while a model represents one optimization problem. In C, the corresponding objects are created with routines such as GRBloadenv() and GRBnewmodel(). In C++, C#, and Java, they are represented by GRBEnv and GRBModel. Below (and for the rest of the guide) we'll be showing Python.

# Xpress Python
import xpress as xp

problem = xp.problem()
# Build and solve the problem here
# Xpress Python
import xpress as xp

problem = xp.problem()
# Build and solve the problem here
# Xpress Python
import xpress as xp

problem = xp.problem()
# Build and solve the problem here
# Gurobi Python
import gurobipy as gp
from gurobipy import GRB

with gp.Model("migration_example") as model:
    # Build and solve the problem here
# Gurobi Python
import gurobipy as gp
from gurobipy import GRB

with gp.Model("migration_example") as model:
    # Build and solve the problem here
# Gurobi Python
import gurobipy as gp
from gurobipy import GRB

with gp.Model("migration_example") as model:
    # Build and solve the problem here

Create an explicit Python environment when you need to configure licensing or Remote Services before startup, manage cleanup, or manage separate environments in a multithreaded application. Environments (and thus license entitlements) can be tricky when attempting to rapidly open and close them for fast jobs, so be sure to review our documentation thoroughly if this is in your use case.

Adding variables

Gurobi supports continuous, binary, integer, semi-continuous, and semi-integer variables. Bounds, objective coefficients, types, and names can be supplied when a variable is created or changed later through attributes.

# Xpress Python
x = problem.addVariable(
    lb=0.0,
    ub=1.0,
    obj=2.0,
    vartype=xp.continuous,
    name="x",
)
# Xpress Python
x = problem.addVariable(
    lb=0.0,
    ub=1.0,
    obj=2.0,
    vartype=xp.continuous,
    name="x",
)
# Xpress Python
x = problem.addVariable(
    lb=0.0,
    ub=1.0,
    obj=2.0,
    vartype=xp.continuous,
    name="x",
)
# Gurobi Python
x = model.addVar(
    lb=0.0,
    ub=1.0,
    obj=2.0,
    vtype=GRB.CONTINUOUS,
    name="x",
)
# Gurobi Python
x = model.addVar(
    lb=0.0,
    ub=1.0,
    obj=2.0,
    vtype=GRB.CONTINUOUS,
    name="x",
)
# Gurobi Python
x = model.addVar(
    lb=0.0,
    ub=1.0,
    obj=2.0,
    vtype=GRB.CONTINUOUS,
    name="x",
)

For indexed variables, Python offers addVars(), which returns a tupledict, and addMVar(), which provides a NumPy-compatible matrix interface. Choose the interface that best matches your data. For very large models, measure model-construction time and memory rather than assuming individual and bulk operations perform identically.

Properties such as a variable’s bounds, objective coefficient, or type are represented as attributes. For example, in Python: x.UB = 1.0. The same attribute system retrieves model statistics and solution information, so many Xpress query or modification calls translate to attribute access rather than a dedicated Gurobi method.

Adding constraints

C++, C#, and Python support operator overloading for common expressions. For example, the following Python statement represents x + y + 2z ≤ 2. In Java, build a GRBLinExpr and add terms before calling model.addConstr(). In C, specify variable indices and coefficients explicitly with GRBaddconstr(). Review quadratic, SOS, indicator, piecewise-linear, and other specialized constructs individually rather than assuming identical semantics.

# Xpress Python
problem.addConstraint(x + y + 2.0 * z <= 2.0)
# Xpress Python
problem.addConstraint(x + y + 2.0 * z <= 2.0)
# Xpress Python
problem.addConstraint(x + y + 2.0 * z <= 2.0)
# Gurobi Python
model.addConstr(x + y + 2.0 * z <= 2.0, name="capacity")
# Gurobi Python
model.addConstr(x + y + 2.0 * z <= 2.0, name="capacity")
# Gurobi Python
model.addConstr(x + y + 2.0 * z <= 2.0, name="capacity")

Understanding lazy updates

Gurobi queues many model modifications and processes them when you call update(), optimize(), or write(). With the default UpdateMode=1, newly created variables and linear constraints can normally be used immediately while building the model. An explicit update is usually unnecessary.

You may still need update() when querying model information before optimizing or writing, copying a model with pending changes, setting attributes on SOS, quadratic, or general constraints, or when UpdateMode is 0. See the UpdateMode documentation for the current behavior.

Translating solver parameters

The following table identifies the closest Gurobi settings for commonly used Xpress controls.

Xpress control

Closest Gurobi setting

Migration guidance

TIMELIMIT

TimeLimit

Both are wall-clock limits in seconds. Confirm whether setup time and surrounding application work are included.

THREADS

Threads

Positive values represent thread counts in both products, but the automatic sentinel differs: Xpress uses -1 and Gurobi uses 0.

PRESOLVE

Presolve

Setting 0 disables presolve in both products; other levels are not equivalent.

MIPRELSTOP

MIPGap

The concepts are similar, but the formulas differ near zero and when incumbent and bound have different signs.

MIPABSSTOP

MIPGapAbs

Both stop when the absolute difference between incumbent objective and objective bound reaches the specified tolerance.

MIPTOL

IntFeasTol

Both define when an integer variable is considered integral. Review the value explicitly because defaults differ.

DEFAULTALG

Method and NodeMethod

Method controls continuous models and the initial MIP relaxation; NodeMethod controls later MIP node relaxations.

CUTSTRATEGY

Cuts

Both control overall cut aggressiveness, but their positive levels are defined differently.

HEURSEARCHEFFORT

Heuristics, sometimes SubMIPNodes

Xpress uses a multiplier on local-search effort; Gurobi Heuristics targets a fraction of total MIP runtime.

COVERCUTS

CoverCuts

Xpress specifies root-node rounds; Gurobi uses an aggressiveness level. Do not copy the value directly.

CROSSOVER

Crossover

The concepts are similar, but the positive numeric codes differ and Gurobi also distinguishes cleanup methods.

DUALIZE

PreDual

The automatic, prohibit, and force concepts align reasonably for -1, 0, and 1. Gurobi also provides setting 2.

OPTIMALITYTOL

OptimalityTol

Closest match for the simplex reduced-cost optimality tolerance.

BARGAPSTOP

BarConvTol or PDHGConvTol

Use BarConvTol for barrier. If the selected method is PDHG, review PDHGConvTol and the related feasibility tolerances.

BARCRASH

CrossoverBasis

Both affect basis construction around crossover, but the strategies and numeric values are not equivalent.

Legacy MAXTIME

TimeLimit, possibly custom termination logic

Xpress deprecated MAXTIME in version 9.0. Positive legacy behavior is not reproduced by simply copying the value.

These are migration guides, not value-for-value conversions. For settings not listed here, consult the complete Gurobi Parameter Reference.

Set parameters on the model

# Xpress Python
problem.controls.maxtime = 300
problem.controls.miprelstop = 1e-4
problem.controls.threads = 8
# Xpress Python
problem.controls.maxtime = 300
problem.controls.miprelstop = 1e-4
problem.controls.threads = 8
# Xpress Python
problem.controls.maxtime = 300
problem.controls.miprelstop = 1e-4
problem.controls.threads = 8
# Gurobi Python
model.Params.TimeLimit = 300.0
model.Params.MIPGap = 1e-4
model.Params.Threads = 8
# Gurobi Python
model.Params.TimeLimit = 300.0
model.Params.MIPGap = 1e-4
model.Params.Threads = 8
# Gurobi Python
model.Params.TimeLimit = 300.0
model.Params.MIPGap = 1e-4
model.Params.Threads = 8

Optimizing and extracting a solution

# Xpress Python
problem.solve()

if problem.getProbStatus() == xp.mip_optimal:
    print("Optimization met the configured optimality criteria.")
if problem.getSolutionCount() > 0:
    print(f"Objective value: {problem.getObjVal():g}")
# Xpress Python
problem.solve()

if problem.getProbStatus() == xp.mip_optimal:
    print("Optimization met the configured optimality criteria.")
if problem.getSolutionCount() > 0:
    print(f"Objective value: {problem.getObjVal():g}")
# Xpress Python
problem.solve()

if problem.getProbStatus() == xp.mip_optimal:
    print("Optimization met the configured optimality criteria.")
if problem.getSolutionCount() > 0:
    print(f"Objective value: {problem.getObjVal():g}")
# Gurobi Python
model.optimize()

if model.Status == GRB.OPTIMAL:
    print("Optimization met the configured optimality criteria.")
elif model.Status == GRB.INFEASIBLE:
    print("The model is infeasible.")
if model.SolCount > 0:
    print(f"Objective value: {model.ObjVal:g}")
# Gurobi Python
model.optimize()

if model.Status == GRB.OPTIMAL:
    print("Optimization met the configured optimality criteria.")
elif model.Status == GRB.INFEASIBLE:
    print("The model is infeasible.")
if model.SolCount > 0:
    print(f"Objective value: {model.ObjVal:g}")
# Gurobi Python
model.optimize()

if model.Status == GRB.OPTIMAL:
    print("Optimization met the configured optimality criteria.")
elif model.Status == GRB.INFEASIBLE:
    print("The model is infeasible.")
if model.SolCount > 0:
    print(f"Objective value: {model.ObjVal:g}")

Diagnosing infeasibility

If the migrated model is infeasible, compute an irreducible inconsistent subsystem:

# Xpress Python
problem.iisall()
problem.write("model.ilp")
# Xpress Python
problem.iisall()
problem.write("model.ilp")
# Xpress Python
problem.iisall()
problem.write("model.ilp")
# Gurobi Python
model.computeIIS()
model.write("model.ilp")
# Gurobi Python
model.computeIIS()
model.write("model.ilp")
# Gurobi Python
model.computeIIS()
model.write("model.ilp")

Gurobi also provides feasibility-relaxation routines when you want to find a minimum-cost relaxation of selected bounds or constraints. Review the infeasibility-analysis documentation before applying a relaxation because these routines may modify the model.

Validate the migration

A successful solve is not proof that the model was translated correctly. Compare objective sense and coefficients; variable counts, bounds, and types; linear, quadratic, SOS, indicator, and piecewise-linear constraints; multiple objectives; MIP starts; branching priorities; callback-added constraints; solver status; incumbent and bound; gaps; important variable values; feasibility and integrality violations; and stopping reasons.

Small numerical differences are normal. Large differences, different feasibility conclusions, or objective values outside configured tolerances require investigation. Write the translated model to LP or MPS when file-based inspection will help.

Benchmark fairly

Use the same formulation and input data on comparable hardware. Keep thread and resource limits consistent, separate construction time from solver time, compare equivalent termination requirements, run representative models, and avoid comparing a heavily tuned Xpress configuration only against Gurobi defaults. Establish a valid baseline first, then tune each solver independently on a representative training set. Remember that even free trials get access to our Experts for tuning.

Next steps

Once the migrated model has been validated, explore the current Gurobi Example Tour and Optimizer Reference Manual for callbacks, matrix modeling, multiple objectives, solution pools, infeasibility analysis, tuning, and deployment options.

Ready to benchmark your models with Gurobi? Request a free evaluation license or speak with a Gurobi expert.

Start Solving with Gurobi

Try Gurobi on your own optimization models and see how it performs on real decision problems.

Start Solving with Gurobi

Try Gurobi on your own optimization models and see how it performs on real decision problems.

Start Solving with Gurobi

Try Gurobi on your own optimization models and see how it performs on real decision problems.

Join our newsletter to stay up to date on features releases and more.

By subscribing you agree to with our Privacy Policy

© Gurobi Optimization, LLC. All Rights Reserved.

Join our newsletter to stay up to date on features releases and more.

By subscribing you agree to with our Privacy Policy

© Gurobi Optimization, LLC. All Rights Reserved.