Reputation: 313
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
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
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