Gurobi from Java with ojAlgo

Gurobi 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-gurobi lets models built with ojAlgo be solved by Gurobi — whether you already use ojAlgo and want Gurobi's solving power, or are looking for a convenient way to use Gurobi from Java. Gurobi also solves models with quadratic constraints (QCLP and QCQP), including non-convex ones.

Already using ojAlgo: upgrade to Gurobi

Switching to Gurobi takes one dependency and one line of code. You need a Gurobi licence. Then:

  1. Add the dependency. ojalgo-gurobi brings Gurobi's own Java package from Maven Central, native libraries included, so there is nothing else to install. Keep your existing ojAlgo dependency.

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

    ExpressionsBasedModel.addIntegration(SolverGurobi.INTEGRATION);

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

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

New to ojAlgo: Gurobi 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 a Gurobi licence. Gurobi looks for gurobi.lic in its default locations, or wherever the GRB_LICENSE_FILE environment variable points; most licence types are activated with the grbgetkey tool, and a Web License Service (WLS) licence only needs the gurobi.lic file. Add ojalgo-gurobi. It brings both ojAlgo and Gurobi's Java package with it, and Gurobi's package includes the native libraries for Linux (x86-64 and ARM64), Windows (x86-64) and macOS, so nothing needs installing or configuring:

<dependency>
    <groupId>org.ojalgo</groupId>
    <artifactId>ojalgo-gurobi</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.gurobi.SolverGurobi;

public class ProductionPlan {

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

        // Register Gurobi once; from then on it solves the models
        ExpressionsBasedModel.addIntegration(SolverGurobi.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. No library path is needed. On recent JDKs, loading Gurobi's native library prints a warning about restricted methods; the JVM argument --enable-native-access=ALL-UNNAMED silences it. 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.

Versions

ojalgo-gurobi is built and tested with Gurobi 13.0.3, and brings that version with it. It also 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>

Gurobi's package from Maven Central is large, about 120 MB, because it contains the native libraries for all platforms. With Gurobi installed locally, the core classifier has the same Java classes without the native libraries. The local installation must be the same version, and if the JVM can't find the native library, point -Djava.library.path at the installation's lib directory:

<dependency>
    <groupId>org.ojalgo</groupId>
    <artifactId>ojalgo-gurobi</artifactId>
    <version>3.3.0</version>
    <exclusions>
        <exclusion>
            <groupId>com.gurobi</groupId>
            <artifactId>gurobi</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.gurobi</groupId>
    <artifactId>gurobi</artifactId>
    <version>13.0.3</version>
    <classifier>core</classifier>
</dependency>

Parameters

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

ojAlgo option (model.options) Gurobi parameter
time_suffice / time_abort (the shorter one) TimeLimit
parallelism(n) Threads
feasibility, when changed from the default FeasibilityTol, IntFeasTol
the integer strategy's gap tolerance, when changed MIPGap, MIPGapAbs

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

There are two configure methods: one for the Gurobi environment, which is shared by all models and is the place for settings such as licence parameters, and one for each model.

import com.gurobi.gurobi.*;
import org.ojalgo.optimisation.Optimisation;
import org.ojalgo.optimisation.solver.gurobi.SolverGurobi;

public class MyGurobiConfigurator extends SolverGurobi.Configurator {

    @Override
    protected void configure(final GRBModel model, final Optimisation.Options options) {
        super.configure(model, options);
        try {
            model.set(GRB.IntParam.MIPFocus, 1);
        } catch (GRBException cause) {
            throw new RuntimeException(cause);
        }
    }
}

ExpressionsBasedModel.setConfigurator(new MyGurobiConfigurator());

More