CPLEX from Java with ojAlgo

CPLEX (IBM ILOG CPLEX) 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-cplex lets models built with ojAlgo be solved by CPLEX — whether you already use ojAlgo and want CPLEX's solving power, or are looking for a convenient way to use CPLEX from Java.

Already using ojAlgo: upgrade to CPLEX

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

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

    <dependency>
        <groupId>org.ojalgo</groupId>
        <artifactId>ojalgo-cplex</artifactId>
        <version>3.3.0</version>
    </dependency>
  2. Register CPLEX once, at start-up:

    ExpressionsBasedModel.addIntegration(SolverCPLEX.INTEGRATION);

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

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

New to ojAlgo: CPLEX 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 CPLEX (part of IBM ILOG CPLEX Optimization Studio) installed and licensed; ojalgo-cplex is built and tested with CPLEX 22.2.0.0. Add ojalgo-cplex, which brings ojAlgo with it, and CPLEX's Java package, described below:

<dependency>
    <groupId>org.ojalgo</groupId>
    <artifactId>ojalgo-cplex</artifactId>
    <version>3.3.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.cplex.SolverCPLEX;

public class ProductionPlan {

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

        // Register CPLEX once; from then on it solves the models
        ExpressionsBasedModel.addIntegration(SolverCPLEX.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 CPLEX's native libraries on the library path (-Djava.library.path=<CPLEX directory>/bin/<platform>). 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.

CPLEX's Java package

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

mvn install:install-file -Dfile=<CPLEX directory>/lib/cplex.jar \
    -DgroupId=cplex -DartifactId=cplex -Dversion=22.2.0.0 -Dpackaging=jar

and add it to your project:

<dependency>
    <groupId>cplex</groupId>
    <artifactId>cplex</artifactId>
    <version>22.2.0.0</version>
</dependency>

At run time the JVM must find CPLEX's native libraries: -Djava.library.path=<CPLEX directory>/bin/<platform>. Typical locations:

Platform CPLEX directory <platform>
macOS (ARM) /Applications/CPLEX_Studio<version>/cplex arm64_osx
macOS (x86) /Applications/CPLEX_Studio<version>/cplex x86-64_osx
Linux /opt/ibm/ILOG/CPLEX_Studio<version>/cplex x86-64_linux
Windows C:\Program Files\IBM\ILOG\CPLEX_Studio<version>\cplex x64_win64

Versions

ojalgo-cplex 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-cplex passes these model options on to CPLEX. CPLEX's log is shown with model.options.debug(SolverCPLEX.class).

ojAlgo option (model.options) CPLEX parameter
time_suffice / time_abort (the shorter one) TiLim
parallelism(n) Threads
feasibility, when changed from the default EpRHS, EpInt
the integer strategy's gap tolerance, when changed EpGap, EpAGap

Any other CPLEX parameter is set with a configurator: extend SolverCPLEX.Configurator, override newIloCplex, and register it.

import ilog.concert.IloException;
import ilog.cplex.IloCplex;
import org.ojalgo.optimisation.Optimisation;
import org.ojalgo.optimisation.solver.cplex.SolverCPLEX;

public class MyCPLEXConfigurator extends SolverCPLEX.Configurator {

    @Override
    protected IloCplex newIloCplex(final Optimisation.Options options) throws IloException {
        IloCplex cplex = super.newIloCplex(options);
        cplex.setParam(IloCplex.Param.Emphasis.MIP, 1);
        return cplex;
    }
}

ExpressionsBasedModel.setConfigurator(new MyCPLEXConfigurator());

More