SGI
SGI

Reputation: 313

Integer Optimization using OjAlgo objective function

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

}

Upvotes: 1

Views: 411

Answers (2)

SGI
SGI

Reputation: 313

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.

Upvotes: 1

apete
apete

Reputation: 1320

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

Upvotes: 1

Related Questions