ExpressionsBasedModel model = new ExpressionsBasedModel();
ExpressionsBasedModel and OptModel, side by side
Two ways to build an optimisation model in Java and solve it on the Optimisation Service. They are separate implementations, but can be used almost the same way. An OptModel has one way to do each thing; this page shows that way, side by side with the same thing done with an ExpressionsBasedModel — most of the time in the very same code. Where they differ, it says so. The ExpressionsBasedModel code needs ojAlgo 58 or later; if yours is written the traditional way, the last section shows what to change.
ExpressionsBasedModel
ojAlgo’s model, in use for many years. It solves with ojAlgo’s own solvers, with any solver an integration brings in, or remotely, on the Optimisation Service. ojAlgo requires a recent Java version, and keeps moving ahead to use what new versions bring.
Choose it if you already use ojAlgo, want to move between solving locally and on the Service, or need what only it can do.
OptModel
The model of the Service’s client library, which has no dependencies at all. It solves on the Optimisation Service only, and has one way to do each thing. It is more conservative about Java versions, and currently requires only Java 11.
Choose it if all your solving is done on the Service, you want nothing else on the classpath, or you are on an older Java.
Creating a model
An ExpressionsBasedModel can be created simply with its constructor. It then uses ojAlgo’s default environment. For more advanced features, such as solving remotely on the Service, you have to create an environment first, and get the models from it.
An OptModel always comes from a client, which knows the Service’s address, and your licence key if you have one.
OptClientV1 client = OptClientV1.newInstance(
"https://service-api.optimatika.se");
OptModel model = client.newModel();OptClientV1 client = OptClientV1.newInstance(
"https://service-api.optimatika.se", licenceKey);
Optimisation.Environment environment = Optimisation.newEnvironment();
environment.setRemoteSolver(client::putOnQueue, client::pollResult);
ExpressionsBasedModel model = environment.newModel();OptClientV1 client = OptClientV1.newInstance(
"https://service-api.optimatika.se", licenceKey);
OptModel model = client.newModel();The environment and the client play the same part: each knows how its models are solved, and hands them out with newModel(). An ExpressionsBasedModel that solves on the Service is given the client’s two methods — the same client an OptModel comes from.
Variables
Both have a factory for each kind of variable: real, integer and binary. A real or integer variable is free when it is created — no lower and no upper bound — and a binary one is between 0 and 1.
An ExpressionsBasedModel has one class of variable, Variable, whichever factory made it. An OptModel’s variables come in three classes, and only real and integer ones have bounds you can set.
Variable x = model.newRealVariable("x");RealVariable x = model.newRealVariable("x");Variable n = model.newIntegerVariable("n");IntegerVariable n = model.newIntegerVariable("n");Variable b = model.newBinaryVariable("b");BinaryVariable b = model.newBinaryVariable("b");Variable x = model.newRealVariable("x").lower(0).upper(10);
x.upper(null); // no upper bound againRealVariable x = model.newRealVariable("x").lower(0).upper(10);
x.upper(null); // no upper bound againA bound that is null is no bound, in both. Most models need their variables to be non-negative, so remember the lower(0).
Constraints
A constraint is a linear expression in the variables, with a lower bound, an upper bound, or both — the same bound as both makes it an equality. In an ExpressionsBasedModel it is an Expression, in an OptModel an OptConstraint. The code is the same.
Expression budget = model.newConstraint("budget").set(x, 1).set(y, 1).upper(10);OptConstraint budget = model.newConstraint("budget").set(x, 1).set(y, 1).upper(10);model.newConstraint("range").set(x, 1).set(y, -1).lower(2).upper(8);model.newConstraint("range").set(x, 1).set(y, -1).lower(2).upper(8);model.newConstraint("exact").set(x, 2).set(y, 3).level(12);model.newConstraint("exact").set(x, 2).set(y, 3).level(12);The objective
How the objective is put together differs by design between the two, but with a recent ojAlgo they can be used the same way: getObjective() returns the objective, and you set its terms. Whether it is minimised or maximised is decided when solving, in both.
model.getObjective().set(x, 3).set(y, 5);
model.getObjective().set(x, 3).set(y, 5);
model.getObjective().set(x, x, 1).set(y, y, 1).set(x, -2).set(y, -4);
model.getObjective().set(x, x, 1).set(y, y, 1).set(x, -2).set(y, -4);
Solving
Both send the model to the Service and return a Future of the result. An OptModel always solves on the Service, so that is what its minimise() and maximise() do. An ExpressionsBasedModel does it with submit(Optimisation.Sense.MIN) or submit(Optimisation.Sense.MAX), which hands the model to its environment’s remote solver.
Here the code differs, for a reason: an ExpressionsBasedModel’s minimise() and maximise() solve right there, in your JVM, with ojAlgo’s own solvers or an integration, and return the result when they are done. They never use a remote solver, not even in an environment set up with one.
Future<Optimisation.Result> future = model.submit(Optimisation.Sense.MAX); Optimisation.Result result = future.get();
Future<OptResult> future = model.maximise(); OptResult result = future.get();
Both futures can be cancelled with future.cancel(true). An OptModel’s cancel also aborts the solve on the Service; an ExpressionsBasedModel’s only stops the waiting, and the Service solves on.
Reading the result
The result says whether the solution is optimal, or at least feasible, and holds the objective function’s value and the variables’ values — read the same way in both: the objective function’s value without an index, a variable’s value with one, in the order the variables were created. The variables have their values too, once the solve is done.
if (result.isOptimal()) { … }
if (result.isFeasible()) { … }if (result.isOptimal()) { … }
if (result.isFeasible()) { … }double value = result.doubleValue();
double value = result.doubleValue();
double xValue = x.doubleValue(); int nValue = n.intValue(); boolean bValue = b.booleanValue();
double xValue = x.doubleValue(); int nValue = n.intValue(); boolean bValue = b.booleanValue();
double xValue = result.doubleValue(0); BigDecimal exactly = result.get(0);
double xValue = result.doubleValue(0); BigDecimal exactly = result.get(0);
Where they differ
Most of a model reads the same in both. These are the differences to know about when moving a model from one to the other.
| ExpressionsBasedModel | OptModel | |
|---|---|---|
| Where it solves | Locally, with ojAlgo’s solvers or an integration, or remotely, on the Service | On the Service only |
| Solving | minimise() returns the result when done; submit(…) returns a Future | minimise() returns a Future |
| Cancelling | Stops the waiting; the Service solves on | Also aborts the solve on the Service |
| The objective | Weights on variables and expressions, or getObjective() — they add up | getObjective() only |
| Constant terms | – | constant(…) on the objective and on constraints |
| Coefficients | set(x, 2) replaces, add(x, 2) adds to what is there | set(x, 2) only |
| Quadratic terms | In the objective and in constraints — given a solver that handles quadratic constraints | In the objective only |
| Variable classes | One, Variable, made integer or binary as it is created or afterwards | Three, RealVariable, IntegerVariable and BinaryVariable, fixed when created |
| Values as they arrive | – | A receiver given when a variable is created: model.newRealVariable("x", value -> …) |
| Java | A recent version, and moving ahead | Java 11 or later |
| Dependencies | ojAlgo, and the client library to solve on the Service | The client library only |
From traditional ExpressionsBasedModel code
Code written for an ExpressionsBasedModel the traditional way still works as it always has. To make it read like the code on this page — the way an OptModel is written — and to solve it on the Service, this is what you change. The typed variable factories, newConstraint, getObjective() and the new ways to read values are in ojAlgo 58 and later.
Variable x = model.newVariable("x");
Variable n = model.newVariable("n").integer();
Variable b = model.newVariable("b").binary();Variable x = model.newRealVariable("x");
Variable n = model.newIntegerVariable("n");
Variable b = model.newBinaryVariable("b");model.newExpression("budget").set(x, 1).set(y, 1).upper(10);model.newConstraint("budget").set(x, 1).set(y, 1).upper(10);x.weight(3); y.weight(5);
model.getObjective().set(x, 3).set(y, 5);
ExpressionsBasedModel model = new ExpressionsBasedModel(); // … Optimisation.Result result = model.maximise();
ExpressionsBasedModel model = environment.newModel();
// …
Optimisation.Result result =
model.submit(Optimisation.Sense.MAX).get();if (result.getState().isOptimal()) { … }
double value = result.getValue();
double xValue = x.getValue().doubleValue();if (result.isOptimal()) { … }
double value = result.doubleValue();
double xValue = x.doubleValue();Weights and getObjective() add up, so when an objective moves to getObjective(), take the weights off the variables and expressions. The environment is the one set up with the client, as in Creating a model; to go on solving in your JVM, keep new ExpressionsBasedModel() and maximise(). From ojAlgo 58 the variables have their values after submit(…) too, not only after minimise() and maximise().