COPT from Java with ojAlgo

COPT (Cardinal Optimizer) is a commercial solver for LP, QP, MIP and more. ojAlgo's modelling API, ExpressionsBasedModel, builds LP, QP and MIP models in Java and other JVM languages such as Kotlin and Scala. ojAlgo solves them with its own pure Java solvers, and the integration module ojalgo-copt lets the same models be solved by COPT instead, without changing the model code. For an application that already uses ojAlgo, moving to COPT takes two dependencies and one line of code.

Upgrading an existing ojAlgo application

When an application's models outgrow ojAlgo's built-in solvers, they can be handed to COPT without rewriting them:

  1. Add ojalgo-copt and COPT's copt.jar to the project, as described below. Keep your existing ojAlgo dependency.
  2. Register COPT once, at start-up: ExpressionsBasedModel.addIntegration(SolverCOPT.INTEGRATION);
  3. Nothing else changes. Models are built, solved and read as before, and the time limit, gap tolerance and number of threads set in model.options are passed on to COPT.

To try COPT on one model first, leave out the registration and call model.maximise(SolverCOPT.INTEGRATION) (or minimise) on that model. The same model solved with model.maximise() uses ojAlgo's own solvers, so the two results can be compared directly.

Installation

Prerequisites. Java 11 or later, and COPT installed and licensed as described in COPT's installation guide. ojalgo-copt is built and tested with COPT 8.0.

Add the integration to your project. The module is on Maven Central and brings ojAlgo with it:

<dependency>
    <groupId>org.ojalgo</groupId>
    <artifactId>ojalgo-copt</artifactId>
    <version>1.1.0</version>
</dependency>

ojAlgo is released more often than the integration. To use a newer ojAlgo than the one ojalgo-copt was built with, declare ojAlgo as well; the version you declare takes precedence:

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

Add the COPT Java package. The COPT Java API, copt.jar, is part of the COPT installation and is found in $COPT_HOME/lib. It is not on Maven Central, and ojalgo-copt does not bring it with it. Install it in your local Maven repository, using your COPT version:

mvn install:install-file -Dfile=$COPT_HOME/lib/copt.jar \
    -DgroupId=copt -DartifactId=copt -Dversion=8.0.7 -Dpackaging=jar

and add it to your project:

<dependency>
    <groupId>copt</groupId>
    <artifactId>copt</artifactId>
    <version>8.0.7</version>
</dependency>

Native libraries. At run time the JVM must find COPT's native libraries. Pass the library directory to the JVM:

-Djava.library.path=$COPT_HOME/lib

Example

Register COPT once; from then on ExpressionsBasedModel uses it to solve the models.

import org.ojalgo.optimisation.*;
import org.ojalgo.optimisation.solver.copt.SolverCOPT;

ExpressionsBasedModel.addIntegration(SolverCOPT.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();   // OPTIMAL 6475.0: 75 chairs, 50 tables

Models can also be read from MPS or LP files with ExpressionsBasedModel.parse(file).

Parameters

ojalgo-copt passes these model options on to COPT. COPT logging is switched off.

ojAlgo option (model.options) COPT parameter
time_suffice / time_abort (the shorter one) TimeLimit
parallelism(n) Threads
feasibility, when changed from the default FeasTol, IntTol
the integer strategy's gap tolerance, when changed RelGap, AbsGap

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

import copt.*;
import org.ojalgo.optimisation.Optimisation;
import org.ojalgo.optimisation.solver.copt.SolverCOPT;

public class MyCOPTConfigurator extends SolverCOPT.Configurator {

    @Override
    protected void configure(final copt.Model model, final Optimisation.Options options) {
        super.configure(model, options);
        try {
            model.setIntParam(IntParam.Logging, 1);
            model.setDblParam(DblParam.RelGap, 1e-6);
        } catch (CoptException cause) {
            throw new RuntimeException(cause);
        }
    }
}

ExpressionsBasedModel.setConfigurator(new MyCOPTConfigurator());

More