Integer Optimization using OjAlgo objective function

314 Views Asked by At

I'm trying to use ojAlgo library in Java for Integer Optimization but I'm unable to provide it the objective function I intend to.
I'd like to minimize the function: (A - B.X)'(A - B.X), where A is a (n x 1) matrix, B is a (n x n) diagonal matrix and X is a (n x 1) matrix with the optimization variables. I want the result in X to consist of only integers .
I was able to set a different objective function which was to maximize B.X. How do I change it to (A - B.X)'(A - B.X)? Here is the code so far.

import org.apache.log4j.Logger;
import org.ojalgo.optimisation.Expression;
import org.ojalgo.optimisation.ExpressionsBasedModel;
import org.ojalgo.optimisation.Optimisation;
import org.ojalgo.optimisation.Variable;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.List;

public class AllocationOptimization {
    protected Logger log = Logger.getLogger(AllocationOptimization.class);

    // This is the objective function, since weight() is assigned to it. How to change this objective function to what I want?
    private List<Variable> makeVariables(HashMap<String, BigDecimal> matrixB) {
        List<Variable> result = new ArrayList<>();
        for (String assetName : matrixB.keySet()) {
            result.add(new Variable(assetName).weight(matrixB.get(assetName)));
        }
        return result;
    }

    private ExpressionsBasedModel createExpressionModel(List<Variable> variables) {
        final ExpressionsBasedModel model = new ExpressionsBasedModel();
        for (Variable v : variables) {
            model.addVariable(v);
        }
        return model;
    }

    private void addExpressionConstraints(ExpressionsBasedModel model, List<Variable> variables,
                                          HashMap<String, BigDecimal> matrixB,
                                          HashMap<String, BigDecimal> wantedAbsoluteSharesMap,
                                          BigDecimal idealTotalPrice) {
        Expression expression = model.addExpression("C1").upper(idealTotalPrice);
        int i = 0;
        for (String assetName : matrixB.keySet()) {
            expression.set(variables.get(i), matrixB.get(assetName));
            i += 1;
        }

        for (Variable v : variables) {
            long absShares = wantedAbsoluteSharesMap.get(v.getName()).longValue();
            v.lower((long) Math.max(0, 0.8 * absShares)).upper((long) Math.max(Math.max(0, 1.2 * absShares), 5));
        }
    }

    private void setIntegerSolving(ExpressionsBasedModel model) {
        for (Variable v : model.getVariables()) {
            v.setInteger(true);
        }
    }

    private HashMap<String, Long> getIntegerOptimizationResult(ExpressionsBasedModel model, HashMap<String, BigDecimal> matrixB) {
        Optimisation.Result result = model.maximise();

        return prepareResult(result, matrixB);
    }


    private HashMap<String, Long> prepareResult(Optimisation.Result result, HashMap<String, BigDecimal> matrixB) {
        int i = 0;
        HashMap<String, Long> optimizedResult = new HashMap<>();
        BigDecimal sumAssetPrices = new BigDecimal("0.0");

        for (String assetName : matrixB.keySet()) {
            long sharesCount = result.get(i).longValue();
            log.debug(assetName + ": " + sharesCount);
            optimizedResult.put(assetName, sharesCount);
            sumAssetPrices = sumAssetPrices.add(matrixB.get(assetName).multiply(BigDecimal.valueOf(sharesCount)));
            i += 1;
        }
        log.debug("Total assets value after converting shares to integer numbers: " + sumAssetPrices);

        return optimizedResult;
    }

    public HashMap<String, Long> optimizeSharesCount(HashMap<String, BigDecimal> constraint1,
                                                     HashMap<String, BigDecimal> matrixB,
                                                     BigDecimal constraint2) throws InputMismatchException {
        List<Variable> variableList = makeVariables(matrixB);
        ExpressionsBasedModel model = createExpressionModel(variableList);
        addExpressionConstraints(model, variableList, matrixB, constraint1, constraint2);
        setIntegerSolving(model);
        HashMap<String, Long> resultMap = getIntegerOptimizationResult(model, matrixB);

        return resultMap;

    }

    private HashMap<String, BigDecimal> createWantedAbsoluteSharesTest1() {
        HashMap<String, BigDecimal> absShares = new HashMap<>();
        absShares.put("NFLX", new BigDecimal("2"));
        absShares.put("MSFT", new BigDecimal("4"));
        absShares.put("GOOG", new BigDecimal("0"));
        absShares.put("AAPL", new BigDecimal("25"));

        return absShares;
    }

    private HashMap<String, BigDecimal> createAssetPricesMapTest1() {
        HashMap<String, BigDecimal> assetPrices = new HashMap<>();
        assetPrices.put("NFLX", new BigDecimal("601.06"));
        assetPrices.put("MSFT", new BigDecimal("296.75"));
        assetPrices.put("GOOG", new BigDecimal("2843.78"));
        assetPrices.put("AAPL", new BigDecimal("149.07"));

        return assetPrices;
    }


    public static void main(String[] args) {
        AllocationOptimization allocationOptimization = new AllocationOptimization();
        // For testing
        HashMap<String, BigDecimal> constr1 = allocationOptimization.createWantedAbsoluteSharesTest1();
        HashMap<String, BigDecimal> matrixB = allocationOptimization.createAssetPricesMapTest1();
        BigDecimal constr2 = new BigDecimal("5348.25");

        HashMap<String, Long> optimizedResult = null;
        try {
            optimizedResult = allocationOptimization.optimizeSharesCount(constr1, matrixB, constr2);
        } catch (Exception e) {
            e.printStackTrace();
        }
        assert optimizedResult != null;
        allocationOptimization.log.info("optimizedResult size: " + optimizedResult.size());
    }

}

2

There are 2 best solutions below

3
On

You assigned weights to the Variable:s. That makes them part of the objective function. You can also assign weights to Expression:s. Anything/everything that has a weight is summed up to form the objective function.

    Expression objective = model.addExpression("Whole Objective").weight(BigDecimal.ONE);
    for (Variable variableR : variables) {
        objective.set(variableR, linearParameter);
        for (Variable variableC : variables) {
            objective.set(variableR, variableC, quadraticParameter);
        }
    }

Is equivalent to:

    Expression objective = model.addExpression("Objective Part").weight(BigDecimal.ONE);
    for (Variable variableR : variables) {
        variableR.weight(linearParameter);
        for (Variable variableC : variables) {
            objective.set(variableR, variableC, quadraticParameter);
        }
    }
0
On

I modified the objective function and added necessary constraints, following @apete's comments. Posting my solution here for others.

private List<Variable> makeVariables(HashMap<String, BigDecimal> matrixB) {
    List<Variable> result = new ArrayList<>();
    for (String assetName : matrixB.keySet()) {
        result.add(new Variable(assetName));
    }
    return result;
}

private ExpressionsBasedModel createObjective(ExpressionsBasedModel model, List<Variable> variables,
                                              HashMap<String, BigDecimal> matrixA,
                                              HashMap<String, BigDecimal> matrixB) {
    // Anything and everything with that has a weight is summed up to form the objective function
    Expression objective = model.addExpression("Objective function").weight(BigDecimal.ONE);
    for (Variable variable : variables) {
        String assetName = variable.getName();
        objective.set(variable, new BigDecimal("-2").multiply(matrixA.get(assetName)).multiply(matrixB.get(assetName)));
        objective.set(variable, variable, matrixB.get(assetName).pow(2));
    }
    return model;
}

private void addExpressionConstraints(ExpressionsBasedModel model, List<Variable> variables,
                                      HashMap<String, BigDecimal> matrixB,
                                      HashMap<String, BigDecimal> wantedAbsoluteSharesMap,
                                      HashMap<String, BigDecimal> matrixA,
                                      BigDecimal idealTotalPrice, BigDecimal accountBalance) {
    Expression expression1 = model.addExpression("C1").upper(idealTotalPrice);
    for (Variable variable : variables) {
        expression1.set(variable, matrixB.get(variable.getName()));
    }

    for (Variable v : variables) {
        // No negative values constraint
        v.lower(0);
    }

    // This constraint is used to compensate for the constants arising in the quadratic objective function
    BigDecimal sumSquaresUserAllocation = new BigDecimal("0.0");
    for (String assetName : this.assetsList) {
        sumSquaresUserAllocation = sumSquaresUserAllocation.add(matrixA.get(assetName).pow(2));
    }

    Expression expression2 = model.addExpression("C2").upper(new BigDecimal("1.01").multiply(sumSquaresUserAllocation.multiply(new BigDecimal("-1"))));
    expression2.lower(new BigDecimal("0.99").multiply(sumSquaresUserAllocation.multiply(new BigDecimal("-1"))));
    for (Variable variable : variables) {
        String assetName = variable.getName();
        expression2.set(variable, new BigDecimal("-2").multiply(matrixA.get(assetName)).multiply(matrixB.get(assetName)));
        expression2.set(variable, variable, matrixB.get(assetName).pow(2));
    }
}

Finally, instead of using the model.maximise() function, I used model.minimise() to minimize the objective function.