FICO Xpress from Java with ojAlgo

FICO Xpress is a high-performance commercial solver for LP, QP, MIP and more. ojAlgo is an open source, pure Java library for mathematical optimisation, with a modelling API, ExpressionsBasedModel. The integration module ojalgo-xpress lets models built with ojAlgo be solved by Xpress — whether you already use ojAlgo and want Xpress's solving power, or are looking for a convenient way to use Xpress from Java.

Already using ojAlgo: upgrade to Xpress

Switching to Xpress takes two dependencies and one line of code. You need Xpress installed and licensed. Then:

  1. Add two dependencies: ojalgo-xpress, and Xpress's own Java package, xprs.jar, which comes with the Xpress installation (details below). Keep your existing ojAlgo dependency.

    <dependency>
        <groupId>org.ojalgo</groupId>
        <artifactId>ojalgo-xpress</artifactId>
        <version>1.2.0</version>
    </dependency>
  2. Register Xpress once, at start-up:

    ExpressionsBasedModel.addIntegration(SolverXpress.INTEGRATION);

That is all. Your model code stays exactly as it is, and Xpress now solves your models.

To try Xpress on one model first, leave out the registration and call model.maximise(SolverXpress.INTEGRATION) (or minimise) on that model.

New to ojAlgo: Xpress from Java

ojAlgo is made for the Java ecosystem:

  • One Maven dependency, pure Java, no further dependencies, MIT licensed. It works with Maven, Gradle and every other JVM build tool, and from Kotlin, Scala and any other JVM language.
  • Models are plain Java objects. No modelling language, no model files, no code generation: variables and constraints are created in code, with your IDE's completion and refactoring, from your application's own data.

Set up. You need Java 11 or later, and FICO Xpress installed and licensed; ojalgo-xpress is built and tested with Xpress 47.01. Add ojalgo-xpress, which brings ojAlgo with it, and Xpress's Java package, described below:

<dependency>
    <groupId>org.ojalgo</groupId>
    <artifactId>ojalgo-xpress</artifactId>
    <version>1.2.0</version>
</dependency>

Write a model. A furniture maker decides how many chairs and tables to make. Each variable has bounds and a weight, its contribution to the objective (here the profit per unit); each expression with a limit is a constraint. The table line has a fixed cost, modelled with a binary variable.

import org.ojalgo.optimisation.ExpressionsBasedModel;
import org.ojalgo.optimisation.Optimisation;
import org.ojalgo.optimisation.Variable;
import org.ojalgo.optimisation.solver.xpress.SolverXpress;

public class ProductionPlan {

    public static void main(final String[] args) {

        // Register Xpress once; from then on it solves the models
        ExpressionsBasedModel.addIntegration(SolverXpress.INTEGRATION);

        ExpressionsBasedModel model = new ExpressionsBasedModel();

        // Whole units only; the weight is the profit per unit
        Variable chairs = model.newVariable("Chairs").lower(0).integer().weight(45);
        Variable tables = model.newVariable("Tables").lower(0).integer().weight(80);
        // Making any tables means running the table line, at a fixed cost of 900
        Variable tableLine = model.newVariable("TableLine").binary().weight(-900);

        model.newExpression("MachineHours").upper(400).set(chairs, 2).set(tables, 5);
        model.newExpression("Wood").upper(450).set(chairs, 4).set(tables, 3);
        model.newExpression("LineCapacity").upper(0).set(tables, 1).set(tableLine, -60);

        Optimisation.Result result = model.maximise();

        System.out.println(result.getState() + " " + result.getValue());
        System.out.println("Chairs: " + chairs.getValue().toPlainString());
        System.out.println("Tables: " + tables.getValue().toPlainString());
        System.out.println("TableLine: " + tableLine.getValue().toPlainString());
    }
}

Run it. Run it with Xpress's native libraries on the library path (-Djava.library.path=<xpressmp>/lib) and the two environment variables described below set. The result is OPTIMAL with a profit of 6475: 75 chairs and 50 tables, with the table line running.

Models can also be read from MPS or LP files with ExpressionsBasedModel.parse(file). The Optimisation Cookbook has complete models for common problem shapes — assignment, scheduling, bin packing, blending and more.

Xpress's Java package

The Xpress Java API, xprs.jar, is part of the Xpress installation and is found in <xpressmp>/lib. It is not on Maven Central, and ojalgo-xpress does not bring it with it. Install it in your local Maven repository (here with the Xpress version ojalgo-xpress is built with; use your own):

mvn install:install-file -Dfile=<xpressmp>/lib/xprs.jar \
    -DgroupId=com.fico.xpress -DartifactId=xprs -Dversion=47.01 -Dpackaging=jar

and add it to your project:

<dependency>
    <groupId>com.fico.xpress</groupId>
    <artifactId>xprs</artifactId>
    <version>47.01</version>
</dependency>

At run time the JVM must find Xpress's native libraries, which are in the same directory: -Djava.library.path=<xpressmp>/lib. Xpress also needs two environment variables:

Variable Value
XPRESSDIR the installation directory, <xpressmp>
XPAUTH_PATH the licence file, <xpressmp>/bin/xpauth.xpr

Typical installation directories are /Applications/FICO Xpress/xpressmp on macOS, /opt/xpressmp on Linux and C:\xpressmp on Windows.

Versions

ojalgo-xpress brings the ojAlgo version it was built with. ojAlgo is released more often, and a version of ojAlgo you declare yourself takes precedence:

<dependency>
    <groupId>org.ojalgo</groupId>
    <artifactId>ojalgo</artifactId>
    <version>57.5.0</version>
</dependency>

Parameters

ojalgo-xpress passes these model options on to Xpress. Xpress's own output is switched off.

ojAlgo option (model.options) Xpress parameter
time_suffice / time_abort (the shorter one) MAXTIME
parallelism(n) THREADS
feasibility, when changed from the default FEASTOL, MIPTOL
the integer strategy's gap tolerance, when changed MIPRELSTOP, MIPABSSTOP

Any other Xpress parameter is set with a configurator: extend SolverXpress.Configurator, override configure, and register it.

import com.dashoptimization.XPRSconstants;
import com.dashoptimization.XPRSprob;
import org.ojalgo.optimisation.Optimisation;
import org.ojalgo.optimisation.solver.xpress.SolverXpress;

public class MyXpressConfigurator extends SolverXpress.Configurator {

    @Override
    protected void configure(final XPRSprob problem, final Optimisation.Options options) {
        super.configure(problem, options);
        problem.setIntControl(XPRSconstants.CUTSTRATEGY, 2);
    }
}

ExpressionsBasedModel.setConfigurator(new MyXpressConfigurator());

More