// (c) 2023-2025 Fair Isaac Corporation
import static com.dashoptimization.objects.Utils.scalarProduct;
import static com.dashoptimization.objects.Utils.sum;
import java.io.IOException;
import com.dashoptimization.ColumnType;
import com.dashoptimization.XPRSenumerations;
import com.dashoptimization.objects.Inequality;
import com.dashoptimization.objects.LinExpression;
import com.dashoptimization.objects.Variable;
import com.dashoptimization.objects.XpressProblem;
/**
* Capital budgeting example. The problem is solved using three multi-objective
* approaches: - Lexicographic approach, solving first to minimize capital
* expended and second to maximize return - Blended approach, solving a weighted
* combination of both objectives simultaneously - Lexicographic approach, with
* the objective priorities reversed
*/
public class CapitalBudgeting {
/**
* Required capital for each project
*/
static final double[] CAPITAL = { 104000, 53000, 29000, 187000, 98000, 32000, 75000, 200000 };
/**
* Required personnel for each project
*/
static final double[] PERSONNEL = { 22, 12, 7, 36, 24, 10, 20, 41 };
/**
* Expected return of each project
*/
static final double[] RETURN = { 124000, 74000, 42000, 188000, 108000, 56000, 88000, 225000 };
/**
* Target capital to invest
*/
static final double CAPITAL_TARGET = 478000;
/**
* Target return on investment
*/
static final double ROI_TARGET = 550000;
/**
* Available personnel
*/
static final int PERSONNEL_MAX = 106;
private final XpressProblem prob;
/**
* Binary decision variables indicating which projects will be implemented
*/
private Variable[] select;
/**
* Constraint for the number of personnel available
*/
private Inequality personnel;
/**
* Primary objective: minimize capital expended
*/
private LinExpression capital;
/**
* Secondary objective: maximize return on investment
*/
private LinExpression roi;
public CapitalBudgeting(XpressProblem prob) {
this.prob = prob;
}
public static void main(String[] args) throws IOException {
try (XpressProblem prob = new XpressProblem()) {
new CapitalBudgeting(prob).solve();
}
}
public void solve() {
// Binary decision variables indicating which projects will be implemented
select = prob.addVariables(CAPITAL.length).withType(ColumnType.Binary).withName("select_%d").toArray();
// Constraint: at least 3 projects must be implemented
prob.addConstraint(sum(select).geq(3));
// Constraint for the number of personnel available
personnel = prob.addConstraint(scalarProduct(select, PERSONNEL).leq(PERSONNEL_MAX));
// Primary objective: minimize capital expended
capital = scalarProduct(select, CAPITAL);
// The first objective has priority=1 (weight=1 by default)
prob.setObjective(capital, XPRSenumerations.ObjSense.MINIMIZE);
prob.setObjIntControl(0, XPRSenumerations.ObjControl.PRIORITY, 1);
// Secondary objective: maximize return on investment
roi = scalarProduct(select, RETURN);
// The second objective has weight=-1 to maximize this expression, and
// priority=0
// to cause this objective to be solved in a second optimization
prob.addObjective(roi, 0, -1);
prob.optimize();
printSolution("Higher priority for 'Minimize Capital' objective:");
// Now set the same priority for both objectives. This will result in a single
// solve
// using the weighted sum of the two objectives.
prob.setObjIntControl(1, XPRSenumerations.ObjControl.PRIORITY, 1);
prob.optimize();
printSolution("Equal priority for objectives:");
// Finally, give the first objective a lower priority
prob.setObjIntControl(0, XPRSenumerations.ObjControl.PRIORITY, 0);
prob.optimize();
printSolution("Higher priority for 'Maximize Return' objective:");
}
/**
* Prints the current solution to the problem.
*
* @param title a title to print before the solution
*/
private void printSolution(String title) {
System.out.println(title);
if (prob.attributes().getSolveStatus() != XPRSenumerations.SolveStatus.COMPLETED
|| prob.attributes().getSolStatus() != XPRSenumerations.SolStatus.OPTIMAL) {
System.out.println(" Problem not solved");
} else {
System.out.println(" Objectives: " + prob.attributes().getObjectives() + " solved objectives: "
+ prob.attributes().getSolvedObjs());
double[] sol = prob.getSolution();
double capitalUsed = capital.evaluate(sol);
double roiGained = roi.evaluate(sol);
if (capitalUsed > CAPITAL_TARGET) {
System.out.println(" Unable to meet Capital target");
}
if (roiGained < ROI_TARGET) {
System.out.println(" Unable to meet Return target");
}
System.out.print(" Projects undertaken:");
for (int p = 0; p < select.length; p++) {
if (select[p].getSolution() >= 0.99) {
System.out.print(" " + p);
}
}
System.out.println();
System.out.println(
" Used capital: $" + capitalUsed + (CAPITAL_TARGET >= capitalUsed ? " unused: $" : " overused: $")
+ Math.abs(CAPITAL_TARGET - capitalUsed));
System.out.println(" Total return: $" + roiGained);
System.out.println(" Unused personnel: " + (int) personnel.getSlack() + " persons");
}
}
}
|
// (c) 2023-2025 Fair Isaac Corporation
import static com.dashoptimization.objects.Utils.sum;
import com.dashoptimization.ColumnType;
import com.dashoptimization.DefaultMessageListener;
import com.dashoptimization.XPRSenumerations;
import com.dashoptimization.objects.Expression;
import com.dashoptimization.objects.Inequality;
import com.dashoptimization.objects.Variable;
import com.dashoptimization.objects.XpressProblem;
/**
* Capital budgeting problem. Illustrates - Logical conditions, formulation using logic constraints -
*/
public class Capbgt2l {
/** A project. */
public static final class Project {
/** The name of this project. */
public final String name;
/** Payout for the project. */
public final double payout;
/** Capital investment for the project. */
public final double investment;
/** Number of personnel required for the project. */
public final int personnel;
public Project(String name, double payout, double investment, int personnel) {
this.name = name;
this.payout = payout;
this.investment = investment;
this.personnel = personnel;
}
@Override
public String toString() {
return name;
}
}
/** The projects used in this example. */
private static final Project[] projectArray = new Project[] { new Project("Alpha", 124000.0, 104000.0, 22),
new Project("Beta", 74000.0, 53000.0, 12), new Project("Gamma", 42000.0, 29000.0, 7),
new Project("Delta", 188000.0, 187000.0, 36), new Project("Epsilon", 108000.0, 98000.0, 24),
new Project("Zeta", 56000.0, 32000.0, 10), new Project("Eta", 88000.0, 75000.0, 20),
new Project("Theta", 225000.0, 200000.0, 41) };
/** The resource constraints used in this example. */
static final double budget = 478000.0;
static final int workforce = 106;
public static void main(String[] args) {
try (XpressProblem prob = new XpressProblem()) {
// Output all messages.
prob.callbacks.addMessageCallback(DefaultMessageListener::console);
/**** VARIABLES ****/
// Whether to undertake a specific project
Variable[] x = prob.addVariables(projectArray.length).withName(i -> String.format("x_%d", i))
.withType(ColumnType.Binary).toArray();
// objective function: sum of payouts of all undertaken projects
Expression totalProfit = sum(projectArray.length, i -> (x[i].mul(projectArray[i].payout)));
prob.setObjective(totalProfit, XPRSenumerations.ObjSense.MAXIMIZE);
// limits on resource availability
// sum of investments of all undertaken projects should not exceed budget
Inequality investmentLimit = prob
.addConstraint(sum(projectArray.length, i -> (x[i].mul(projectArray[i].investment))).leq(budget));
// sum of personnel committed of all undertaken projects should not exceed
// workforce
Inequality workforceLimit = prob
.addConstraint(sum(projectArray.length, i -> (x[i].mul(projectArray[i].personnel))).leq(workforce));
// project alpha can only be done if both gamma and zeta happen
prob.addConstraint(x[0].leq(x[2]));
prob.addConstraint(x[0].leq(x[5]));
// project zeta can only be done if project epsilon happens
prob.addConstraint(x[5].leq(x[4]));
// projects alpha and beta as well as gamma and delta can only happen together
prob.addConstraint(x[0].eq(x[1]));
prob.addConstraint(x[2].eq(x[3]));
// exactly one of those pairs should be invested in, i.e., if project alpha is
// performed,
// neither gamma nor delta can be invested in, and if project alpha does not
// happen, then
// projects gamma and delta have to be performed
prob.addConstraint(x[0].ifThen(sum(x[2], x[3]).eq(0.0)));
prob.addConstraint(x[0].ifNotThen(sum(x[2], x[3]).eq(2.0)));
// Dump the problem to disk so that we can inspect it.
prob.writeProb("capbgt2l.lp", "l");
// Solve
prob.optimize();
if (prob.attributes().getSolStatus() != XPRSenumerations.SolStatus.OPTIMAL
&& prob.attributes().getSolStatus() != XPRSenumerations.SolStatus.FEASIBLE)
throw new RuntimeException("optimization failed with status " + prob.attributes().getSolStatus());
double[] sol = prob.getSolution();
// Print the objective
System.out.println("Objective: " + prob.attributes().getObjVal());
// Print the interesting slacks
System.out.println("Remaining Budget: " + investmentLimit.getSlack());
System.out.println("Remaining Workers: " + (int) workforceLimit.getSlack());
// Print out the solution
for (int i = 0; i < projectArray.length; ++i) {
if (x[i].getValue(sol) > 0.0) {
System.out.println("Undertaking project " + projectArray[i].toString() + ".");
}
}
}
}
}
|