
org.ojalgo.optimisation.solver.cplex.SolverCPLEX Maven / Gradle / Ivy
The newest version!
/*
* Copyright 1997-2025 Optimatika
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.ojalgo.optimisation.solver.cplex;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import org.ojalgo.netio.BasicLogger;
import org.ojalgo.optimisation.Expression;
import org.ojalgo.optimisation.ExpressionsBasedModel;
import org.ojalgo.optimisation.Optimisation;
import org.ojalgo.optimisation.Variable;
import org.ojalgo.structure.Structure1D.IntIndex;
import org.ojalgo.structure.Structure2D.IntRowColumn;
import ilog.concert.IloException;
import ilog.concert.IloLQNumExpr;
import ilog.concert.IloLinearNumExpr;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
import ilog.concert.IloNumVarType;
import ilog.concert.IloQuadNumExpr;
import ilog.cplex.IloCplex;
import ilog.cplex.IloCplex.IntParam;
import ilog.cplex.IloCplex.Status;
/**
* SolverCPLEX
*
* @author apete
*/
public final class SolverCPLEX implements Optimisation.Solver {
public static class Configurator {
protected IloCplex newIloCplex(final Optimisation.Options options) throws IloException {
IloCplex retVal = new IloCplex();
if (options.logger_appender != null) {
retVal.setParam(IntParam.MIPDisplay, 5);
retVal.setParam(IntParam.MIPInterval, 5);
retVal.setOut(new OutputStream() {
@Override
public void write(final int b) throws IOException {
options.logger_appender.print((char) b);
}
});
} else {
retVal.setOut(OutputStream.nullOutputStream());
}
return retVal;
}
}
static final class Integration extends ExpressionsBasedModel.Integration {
Integration() {
super();
}
@Override
public SolverCPLEX build(final ExpressionsBasedModel model) {
try {
Configurator configurator = model.options.getConfigurator(Configurator.class).orElse(DEFAULT);
IloCplex solver = configurator.newIloCplex(model.options);
boolean mip = model.isAnyVariableInteger();
List mVars = model.getVariables();
int nbVars = mVars.size();
IloNumVar[] sVars = new IloNumVar[nbVars];
for (int i = 0; i < nbVars; i++) {
Variable mVar = mVars.get(i);
IloNumVarType type = IloNumVarType.Float;
if (mip) {
if (mVar.isBinary()) {
type = IloNumVarType.Bool;
} else if (mVar.isInteger()) {
type = IloNumVarType.Int;
}
}
double lb = mVar.getLowerLimit(false, Double.NEGATIVE_INFINITY);
double ub = mVar.getUpperLimit(false, Double.POSITIVE_INFINITY);
String name = mVar.getName();
solver.add(sVars[i] = solver.numVar(lb, ub, type, name));
}
model.constraints().forEach(mConstr -> {
try {
IloNumExpr sConstr = this.buildExpression(mConstr, solver, sVars);
if (mConstr.isEqualityConstraint()) {
solver.addEq(mConstr.getLowerLimit(false, 0.0), sConstr);
} else {
if (mConstr.isLowerConstraint()) {
solver.addLe(mConstr.getLowerLimit(false, Double.NEGATIVE_INFINITY), sConstr);
}
if (mConstr.isUpperConstraint()) {
solver.addGe(mConstr.getUpperLimit(false, Double.POSITIVE_INFINITY), sConstr);
}
}
} catch (IloException cause) {
throw new RuntimeException(cause);
}
});
Expression mObj = model.objective();
IloNumExpr sObj = this.buildExpression(mObj, solver, sVars);
if (model.getOptimisationSense() == Optimisation.Sense.MAX) {
solver.addMaximize(sObj);
} else {
solver.addMinimize(sObj);
}
return new SolverCPLEX(solver, sVars);
} catch (IloException cause) {
throw new RuntimeException(cause);
}
}
/**
* CPLEX can handle anything/everything ExpressionsBasedModel can model.
*
* @see org.ojalgo.optimisation.Optimisation.Integration#isCapable(org.ojalgo.optimisation.Optimisation.Model)
*/
@Override
public boolean isCapable(final ExpressionsBasedModel model) {
return true;
}
private void copyLinear(final Expression source, final IloNumVar[] variables, final IloLinearNumExpr destination) throws IloException {
for (IntIndex key : source.getLinearKeySet()) {
destination.addTerm(source.doubleValue(key, false), variables[key.index]);
}
}
private void copyQuadratic(final Expression source, final IloNumVar[] variables, final IloQuadNumExpr destination) throws IloException {
for (IntRowColumn key : source.getQuadraticKeySet()) {
destination.addTerm(source.doubleValue(key, false), variables[key.row], variables[key.column]);
}
}
IloNumExpr buildExpression(final Expression expression, final IloCplex cplex, final IloNumVar[] variables) throws IloException {
if (expression.isFunctionQuadratic()) {
IloLQNumExpr tmpIloLQNumExpr = cplex.lqNumExpr();
this.copyQuadratic(expression, variables, tmpIloLQNumExpr);
this.copyLinear(expression, variables, tmpIloLQNumExpr);
return tmpIloLQNumExpr;
} else if (expression.isFunctionPureQuadratic()) {
IloQuadNumExpr tmpIloQuadNumExpr = cplex.quadNumExpr();
this.copyQuadratic(expression, variables, tmpIloQuadNumExpr);
return tmpIloQuadNumExpr;
} else if (expression.isFunctionLinear()) {
IloLinearNumExpr tmpIloLinearNumExpr = cplex.linearNumExpr();
this.copyLinear(expression, variables, tmpIloLinearNumExpr);
return tmpIloLinearNumExpr;
} else {
return cplex.linearNumExpr();
}
}
}
public static final ExpressionsBasedModel.Integration INTEGRATION = new Integration();
static final Configurator DEFAULT = new Configurator();
static Optimisation.State translate(final IloCplex.Status status) {
if (status.equals(Status.Bounded)) {
return State.VALID;
} else if (status.equals(Status.Error)) {
return State.FAILED;
} else if (status.equals(Status.Feasible)) {
return State.FEASIBLE;
} else if (status.equals(Status.Infeasible)) {
return State.INFEASIBLE;
} else if (status.equals(Status.InfeasibleOrUnbounded)) {
return State.INVALID;
} else if (status.equals(Status.Optimal)) {
return State.OPTIMAL;
} else if (status.equals(Status.Unbounded)) {
return State.UNBOUNDED;
} else if (status.equals(Status.Unknown)) {
return State.UNEXPLORED;
} else {
return State.FAILED;
}
}
private final IloCplex mySolver;
private final IloNumVar[] myVariables;
SolverCPLEX(final IloCplex solver, final IloNumVar[] variables) {
super();
mySolver = solver;
myVariables = variables;
}
@Override
public void dispose() {
Solver.super.dispose();
if (mySolver != null) {
mySolver.end();
}
}
@Override
public Optimisation.Result solve(final Result kickStarter) {
double retValue = Double.NaN;
Optimisation.State retState = Optimisation.State.UNEXPLORED;
double[] retSolution = new double[myVariables.length];
try {
if (mySolver.solve()) {
for (int i = 0; i < myVariables.length; i++) {
retSolution[i] = mySolver.getValue(myVariables[i]);
}
retValue = mySolver.getObjValue();
}
retState = SolverCPLEX.translate(mySolver.getStatus());
} catch (IloException cause) {
BasicLogger.error(cause, "CPLEX solve failed!");
retValue = Double.NaN;
retState = Optimisation.State.FAILED;
}
return Optimisation.Result.of(retValue, retState, retSolution);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy