1

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 Answers 2

1

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);
        }
    }
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! What is the quadraticParameter here? I understand that I can convert my objective function to (a_i - b_i * x_i)^2, but don't understand what will I need to set it to? a_i and b_i are numbers and x_i is the variable to be optimized. I want something like - ` Expression objective = model.addExpression("Objective Part").weight(BigDecimal.ONE);` for (Variable variableR : variables) { variableR.weight(-b_i); objective.set(a_i, variableR, (a_i + variableR) ^2); // How do I set a power 2 expression as objective without constraining it? } }`
Sorry about the floppy formatting. I was trying to fix it when editing the comments timed out. variableR.weight(-b_i); objective.set(**a_i**, variableR, **(a_i + variableR) ^2**); I'm not sure how the areas in bold should be written. Since a_i is not a variable but a number and I cannot "set" a value to the objective function but just need it to be raised to power 2.
quadraticParameter and linearParameter are numeric scalars - they can't be anything else. You have to do some algebra to figure out what those numbers are, then just set them.
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.