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

org.ojalgo.optimisation.solver.mosek.SolverMosek 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.mosek;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.ojalgo.array.ArrayR064;
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 org.ojalgo.type.ForgetfulMap;

import mosek.*;
import mosek.Exception;

/**
 * TODO: Clean up / refactor, look at SolverORTools and SolverCPLEX.
 *
 * @author apete
 */
public final class SolverMosek implements Optimisation.Solver {

    public static class Configurator {

        protected Task newTask(final Env environment, final int nbConstraints, final int nbVariables, final Optimisation.Options options) {

            Task task = new Task(environment, nbConstraints, nbVariables);

            task.appendcons(nbConstraints);
            task.appendvars(nbVariables);

            if (options.logger_appender != null) {

                Stream stream = new Stream() {

                    @Override
                    public void stream(final String message) {
                        options.logger_appender.print(message);
                    }
                };

                environment.set_Stream(streamtype.log, stream);
                task.set_Stream(streamtype.log, stream);
            }

            return task;
        }

    }

    public static final class Integration extends ExpressionsBasedModel.Integration {

        private final ForgetfulMap.ValueCache myEnvironmentCache;

        Integration() {

            super();

            myEnvironmentCache = ForgetfulMap.newBuilder().expireAfterAccess(1, TimeUnit.HOURS).build(this::newEnv, this::dispose);
        }

        @Override
        public SolverMosek build(final ExpressionsBasedModel model) {

            Options options = model.options;

            Env environment = this.getEnvironment();

            Configurator configurator = options.getConfigurator(Configurator.class).orElse(DEFAULT);

            List freeVariables = model.getFreeVariables();
            Set fixedVariables = model.getFixedVariables();

            List constraints = model.constraints().collect(Collectors.toList());

            int nbVariables = freeVariables.size();
            int nbConstraints = constraints.size();

            Task task = configurator.newTask(environment, nbConstraints, nbVariables, options);

            SolverMosek retVal = new SolverMosek(task, options);

            for (int v = 0; v < nbVariables; v++) {
                Variable tmpVariable = freeVariables.get(v);
                retVal.putVariable(v, tmpVariable);
            }

            for (int c = 0; c < nbConstraints; c++) {
                Expression tmpConstraint = constraints.get(c).compensate(fixedVariables);
                retVal.putConstraint(c, tmpConstraint, model);
            }

            Expression tmpObjective = model.objective().compensate(fixedVariables);
            retVal.putObjective(tmpObjective, model);

            retVal.setSolutionType(model);

            return retVal;
        }

        @Override
        public boolean isCapable(final ExpressionsBasedModel model) {
            return true; // Can handle any variation of an ExpressionsBasedModel
        }

        @Override
        public Result toModelState(final Result solverState, final ExpressionsBasedModel model) {

            List freeVariables = model.getFreeVariables();
            Set fixedVariables = model.getFixedVariables();
            int nbFreeVars = freeVariables.size();
            int nbModelVars = model.countVariables();

            ArrayR064 modelSolution = ArrayR064.make(nbModelVars);

            for (int i = 0; i < nbFreeVars; i++) {
                modelSolution.set(model.indexOf(freeVariables.get(i)), solverState.doubleValue(i));
            }

            for (IntIndex fixed : fixedVariables) {
                modelSolution.set(fixed.index, model.getVariable(fixed.index).getValue());
            }

            return new Result(solverState.getState(), modelSolution);
        }

        @Override
        public Result toSolverState(final Result modelState, final ExpressionsBasedModel model) {

            List freeVariables = model.getFreeVariables();
            int nbFreeVars = freeVariables.size();

            ArrayR064 solverSolution = ArrayR064.make(nbFreeVars);

            for (int i = 0; i < nbFreeVars; i++) {
                Variable variable = freeVariables.get(i);
                int modelIndex = model.indexOf(variable);
                solverSolution.set(i, modelState.doubleValue(modelIndex));
            }

            return new Result(modelState.getState(), solverSolution);
        }

        private void dispose(final Env env) {
            env.dispose();
        }

        private Env newEnv() {

            Env env = new Env();

            env.set_Stream(streamtype.err, new Stream() {
                @Override
                public void stream(final String message) {
                    BasicLogger.ERROR.print(message);
                }
            });

            return env;
        }

        Env getEnvironment() {
            return myEnvironmentCache.getCachedObject();
        }

    }

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

    static final Configurator DEFAULT = new Configurator();

    private final Optimisation.Options myOptions;
    private soltype mySolutionType = soltype.bas;
    private final Task myTask;

    SolverMosek(final Task task, final Optimisation.Options options) {

        super();

        myTask = task;
        myOptions = options;
    }

    @Override
    public void dispose() {

        Solver.super.dispose();

        if (myTask != null) {
            myTask.dispose();
        }
    }

    @Override
    public Result solve(final Result kickStarter) {

        int tmpNumberOfVariables = myTask.getnumvar();

        Optimisation.State tmpSate = Optimisation.State.FAILED;
        double tmpValue = Double.NaN;
        double[] tmpSolution = new double[tmpNumberOfVariables];

        try {

            if (myTask.optimize() == rescode.ok) {

                solsta[] tmpSolverState = new solsta[1];
                myTask.getsolsta(mySolutionType, tmpSolverState);

                myTask.getxx(mySolutionType, tmpSolution);

                switch (tmpSolverState[0]) {
                    case optimal:
                        tmpSate = Optimisation.State.OPTIMAL;
                        tmpValue = myTask.getprimalobj(mySolutionType);
                        break;
                    case dual_infeas_cer:
                    case prim_infeas_cer:
                        tmpSate = Optimisation.State.INFEASIBLE;
                        break;
                    default:
                        tmpSate = Optimisation.State.FAILED;
                        break;
                }
            }

        } catch (Exception xcptn) {
            throw xcptn;
        }

        return new Optimisation.Result(tmpSate, tmpValue, ArrayR064.wrap(tmpSolution));
    }

    boundkey getBoundKey(final Optimisation.Constraint modelEntity) {

        if (modelEntity.getLowerLimit() != null) {
            if (modelEntity.getUpperLimit() == null) {
                return boundkey.lo;
            }
            if (modelEntity.getLowerLimit().compareTo(modelEntity.getUpperLimit()) == 0) {
                return boundkey.fx;
            }
            return boundkey.ra;
        }
        if (modelEntity.getUpperLimit() != null) {
            return boundkey.up;
        }
        return boundkey.fr;
    }

    void putConstraint(final int index, final Expression constraint, final ExpressionsBasedModel model) {

        Set tmpLinearFactorKeys = constraint.getLinearKeySet();
        int tmpLinearSize = tmpLinearFactorKeys.size();
        if (tmpLinearSize > 0) {

            int[] tmpCols = new int[tmpLinearSize];
            double[] tmpVals = new double[tmpLinearSize];

            int i = 0;
            for (IntIndex tmpKey : tmpLinearFactorKeys) {
                int tmpRow = tmpKey.index;
                if (tmpRow >= 0) {
                    tmpCols[i] = tmpRow;
                    tmpVals[i] = constraint.doubleValue(tmpKey, true);
                } else {
                    tmpCols[i] = 0;
                    tmpVals[i] = 0.0;
                }
                i++;
            }
            myTask.putarow(index, tmpCols, tmpVals);
        }

        Set tmpQuadraticFactorKeys = constraint.getQuadraticKeySet();
        int tmpQuadraticSize = tmpQuadraticFactorKeys.size();
        if (tmpQuadraticSize > 0) {

            int[] tmpRows = new int[tmpQuadraticSize];
            int[] tmpCols = new int[tmpQuadraticSize];
            double[] tmpVals = new double[tmpQuadraticSize];

            int i = 0;
            for (IntRowColumn tmpKey : tmpQuadraticFactorKeys) {
                int tmpRow = tmpKey.row;
                int tmpCol = tmpKey.column;
                if (tmpRow >= 0 && tmpCol >= 0) {
                    if (tmpRow == tmpCol) {
                        tmpRows[i] = tmpRow;
                        tmpCols[i] = tmpCol;
                        tmpVals[i] = 2.0 * constraint.doubleValue(tmpKey, true);
                    } else {
                        tmpRows[i] = Math.max(tmpRow, tmpCol);
                        tmpCols[i] = Math.min(tmpRow, tmpCol);
                        tmpVals[i] = constraint.doubleValue(tmpKey, true);
                    }
                } else {
                    tmpRows[i] = 0;
                    tmpCols[i] = 0;
                    tmpVals[i] = 0.0;
                }
                i++;
            }
            myTask.putqconk(index, tmpRows, tmpCols, tmpVals);
        }

        boundkey tmpBoundType = this.getBoundKey(constraint);
        double tmpLowerBound = constraint.getLowerLimit(true, Double.NEGATIVE_INFINITY);
        double tmpUpperBound = constraint.getUpperLimit(true, Double.POSITIVE_INFINITY);

        myTask.putconbound(index, tmpBoundType, tmpLowerBound, tmpUpperBound);
    }

    void putObjective(final Expression objective, final ExpressionsBasedModel model) {

        Set tmpLinearFactorKeys = objective.getLinearKeySet();
        int tmpLinearSize = tmpLinearFactorKeys.size();
        if (tmpLinearSize > 0) {

            int[] tmpRows = new int[tmpLinearSize];
            double[] tmpVals = new double[tmpLinearSize];

            int i = 0;
            for (IntIndex tmpKey : tmpLinearFactorKeys) {
                int tmpRow = tmpKey.index;
                if (tmpRow >= 0) {
                    tmpRows[i] = tmpRow;
                    tmpVals[i] = objective.doubleValue(tmpKey, true);
                } else {
                    tmpRows[i] = 0;
                    tmpVals[i] = 0.0;
                }
                i++;
            }
            myTask.putclist(tmpRows, tmpVals);
        }

        Set tmpQuadraticFactorKeys = objective.getQuadraticKeySet();
        int tmpQuadraticSize = tmpQuadraticFactorKeys.size();
        if (tmpQuadraticSize > 0) {

            int[] tmpRows = new int[tmpQuadraticSize];
            int[] tmpCols = new int[tmpQuadraticSize];
            double[] tmpVals = new double[tmpQuadraticSize];

            int i = 0;
            for (IntRowColumn tmpKey : tmpQuadraticFactorKeys) {
                int tmpRow = tmpKey.row;
                int tmpCol = tmpKey.column;
                if (tmpRow >= 0 && tmpCol >= 0) {
                    if (tmpRow == tmpCol) {
                        tmpRows[i] = tmpRow;
                        tmpCols[i] = tmpCol;
                        tmpVals[i] = 2.0 * objective.doubleValue(tmpKey, true);
                    } else {
                        tmpRows[i] = Math.max(tmpRow, tmpCol);
                        tmpCols[i] = Math.min(tmpRow, tmpCol);
                        tmpVals[i] = objective.doubleValue(tmpKey, true);
                    }
                } else {
                    tmpRows[i] = 0;
                    tmpCols[i] = 0;
                    tmpVals[i] = 0.0;
                }
                i++;
            }
            myTask.putqobj(tmpRows, tmpCols, tmpVals);
        }

        myTask.putobjsense(model.getOptimisationSense() == Optimisation.Sense.MIN ? objsense.minimize : objsense.maximize);
    }

    void putVariable(final int index, final Variable variable) {

        boundkey boundType = this.getBoundKey(variable);
        double lowerBound = variable.getLowerLimit(false, Double.NEGATIVE_INFINITY);
        double upperBound = variable.getUpperLimit(false, Double.POSITIVE_INFINITY);
        variabletype variableType = variable.isInteger() ? variabletype.type_int : variabletype.type_cont;

        myTask.putvarbound(index, boundType, lowerBound, upperBound);
        myTask.putvartype(index, variableType);
    }

    void setSolutionType(final ExpressionsBasedModel model) {
        mySolutionType = model.isAnyVariableInteger() ? soltype.itg : model.isAnyExpressionQuadratic() ? soltype.itr : soltype.bas;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy