All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ojalgo.optimisation.solver.cplex.SolverCPLEX Maven / Gradle / Ivy

There is a newer version: 3.0.3
Show newest version
/*
 * Copyright 1997-2021 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.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.ojalgo.array.Primitive64Array;
import org.ojalgo.netio.CharacterRing;
import org.ojalgo.netio.CharacterRing.PrinterBuffer;
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.Status;

public final class SolverCPLEX implements Optimisation.Solver {

    @FunctionalInterface
    public interface Configurator {

        void configure(final IloCplex cplex, final Optimisation.Options options);

    }

    static final class Integration extends ExpressionsBasedModel.Integration {

        private final PrinterBuffer myLog = new CharacterRing().asPrinter();

        Integration() {
            super();
        }

        @Override
        public SolverCPLEX build(final ExpressionsBasedModel model) {

            boolean mip = model.isAnyVariableInteger();

            final SolverCPLEX retVal = new SolverCPLEX(model.options);
            final IloCplex delegateSolver = retVal.getDelegateSolver();

            try {

                final List freeModVars = model.getFreeVariables();
                final Set fixedModVars = model.getFixedVariables();

                final Expression modObj = model.objective().compensate(fixedModVars);

                for (final Variable var : freeModVars) {

                    IloNumVarType type = IloNumVarType.Float;
                    if (mip) {
                        // When relaxing the MIP property of the model
                        // the individual variables maintain the 'info' that
                        // they where modelled as integer/binary.
                        // This is because of how the ojAlgo IntegerSolver works.
                        if (var.isBinary()) {
                            type = IloNumVarType.Bool;
                        } else if (var.isInteger()) {
                            type = IloNumVarType.Int;
                        }
                    }

                    double unadjustedLowerLimit = var.getUnadjustedLowerLimit();
                    double unadjustedUpperLimit = var.getUnadjustedUpperLimit();
                    final IloNumVar solverVar = delegateSolver.numVar(unadjustedLowerLimit, unadjustedUpperLimit, type, var.getName());
                    retVal.getDelegateVariables().add(solverVar);
                }

                for (final Expression expr : model.constraints().map(e -> e.compensate(fixedModVars)).collect(Collectors.toList())) {

                    final IloNumExpr solExpr = SolverCPLEX.buildExpression(model, expr, delegateSolver, retVal.getDelegateVariables());

                    SolverCPLEX.setBounds(solExpr, expr, delegateSolver);
                }

                final IloNumExpr solObj = SolverCPLEX.buildExpression(model, modObj, delegateSolver, retVal.getDelegateVariables());

                if (model.isMaximisation()) {
                    delegateSolver.addMaximize(solObj);
                } else {
                    delegateSolver.addMinimize(solObj);
                }

            } catch (final IloException exception) {
                exception.printStackTrace();
            }

            return retVal;
        }

        @Override
        public boolean isCapable(final ExpressionsBasedModel model) {
            return true; // CPLEX can handle anything/everything ExpressionsBasedModel can model.
        }

        @Override
        protected boolean isSolutionMapped() {
            return true;
        }

    }

    public static final SolverCPLEX.Integration INTEGRATION = new Integration();

    static final Configurator DEFAULT = new Configurator() {

        public void configure(final IloCplex cplex, final Options options) {
            //            if (options.logger_appender != null) {
            //                cplex.setOut(new OutputStream() {
            //
            //                    @Override
            //                    public void write(final int b) throws IOException {
            //                        options.logger_appender.print(b);
            //                    }
            //                });
            //            } else {
            //                cplex.setOut(new OutputStream() {
            //
            //                    @Override
            //                    public void write(final int b) throws IOException {
            //                    }
            //                });
            //            }
        }
    };

    static void addLinear(final Expression source, final IloLinearNumExpr destination, final ExpressionsBasedModel model, final List variables)
            throws IloException {

        for (final IntIndex key : source.getLinearKeySet()) {

            final int freeInd = model.indexOfFreeVariable(key.index);

            if (freeInd >= 0) {
                destination.addTerm(source.getAdjustedLinearFactor(key), variables.get(freeInd));
            }
        }
    }

    static void addQuadratic(final Expression source, final IloQuadNumExpr destination, final ExpressionsBasedModel model, final List variables)
            throws IloException {

        for (final IntRowColumn key : source.getQuadraticKeySet()) {

            final int freeRow = model.indexOfFreeVariable(key.row);
            final int freeCol = model.indexOfFreeVariable(key.column);

            if ((freeRow >= 0) && (freeCol >= 0)) {
                destination.addTerm(source.getAdjustedQuadraticFactor(key), variables.get(freeRow), variables.get(freeCol));
            }
        }
    }

    static IloNumExpr buildExpression(final ExpressionsBasedModel model, final Expression expression, final IloCplex solver, final List variables)
            throws IloException {

        if (expression.isFunctionQuadratic()) {

            final IloLQNumExpr tmpIloLQNumExpr = solver.lqNumExpr();

            SolverCPLEX.addQuadratic(expression, tmpIloLQNumExpr, model, variables);
            SolverCPLEX.addLinear(expression, tmpIloLQNumExpr, model, variables);

            return tmpIloLQNumExpr;

        } else if (expression.isFunctionPureQuadratic()) {

            final IloQuadNumExpr tmpIloQuadNumExpr = solver.quadNumExpr();

            SolverCPLEX.addQuadratic(expression, tmpIloQuadNumExpr, model, variables);

            return tmpIloQuadNumExpr;

        } else if (expression.isFunctionLinear()) {

            final IloLinearNumExpr tmpIloLinearNumExpr = solver.linearNumExpr();

            SolverCPLEX.addLinear(expression, tmpIloLinearNumExpr, model, variables);

            return tmpIloLinearNumExpr;
        }

        return null;
    }

    static void setBounds(final IloNumExpr expression, final Expression model, final IloCplex solver) throws IloException {

        if (model.isEqualityConstraint()) {
            solver.addEq(model.getAdjustedLowerLimit(), expression);
        } else {
            if (model.isLowerConstraint()) {
                solver.addLe(model.getAdjustedLowerLimit(), expression);
            }
            if (model.isUpperConstraint()) {
                solver.addGe(model.getAdjustedUpperLimit(), expression);
            }
        }
    }

    private final IloCplex myDelegateSolver;

    private final List myDelegateVariables;
    private final Optimisation.Options myOptions;

    SolverCPLEX(final Optimisation.Options options) {

        super();

        myOptions = options;

        IloCplex tmpDelegate = null;

        try {
            tmpDelegate = new IloCplex();
        } catch (final IloException ex) {
            ex.printStackTrace();
            tmpDelegate = null;
        }

        myDelegateSolver = tmpDelegate;
        myDelegateVariables = new ArrayList<>();
    }

    @Override
    public void dispose() {

        Solver.super.dispose();

        if (myDelegateSolver != null) {
            myDelegateSolver.end();
        }
    }

    @Override
    public Result solve(final Result kickStarter) {

        try {

            final List solVariables = this.getDelegateVariables();

            Optimisation.State retState = Optimisation.State.UNEXPLORED;
            double retValue = Double.NaN;
            final Primitive64Array retSolution = Primitive64Array.make(solVariables.size());

            DEFAULT.configure(myDelegateSolver, myOptions);
            final Optional optional = myOptions.getConfigurator(Configurator.class);
            if (optional.isPresent()) {
                optional.get().configure(myDelegateSolver, myOptions);
            }

            if (myDelegateSolver.solve()) {
                // Feasible or Optimal

                for (int i = 0; i < solVariables.size(); i++) {
                    retSolution.set(i, myDelegateSolver.getValue(solVariables.get(i)));
                }

                retValue = myDelegateSolver.getObjValue();
            }

            retState = this.translate(myDelegateSolver.getStatus());

            return new Result(retState, retValue, retSolution);

        } catch (final IloException exception) {

            exception.printStackTrace();
        }

        return new Result(Optimisation.State.FAILED, Double.NaN, kickStarter);
    }

    @Override
    protected void finalize() throws Throwable {

        this.dispose();

        super.finalize();
    }

    IloCplex getDelegateSolver() {
        return myDelegateSolver;
    }

    List getDelegateVariables() {
        return myDelegateVariables;
    }

    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;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy