| |||||||||||||
Capital budgeting - Using multi-objective optimization Description Capital budgeting example, solved using three multi-objective approaches:
Source Files By clicking on a file name, a preview is opened at the bottom of this page.
CapitalBudgeting.cs // (c) 2023-2024 Fair Isaac Corporation using System; using Optimizer; using Optimizer.Objects; using static Optimizer.Objects.Utils; namespace XpressExamples { /// <summary>Capital budgeting example, solved using three multi-objective approaches</summary> /// <remarks> /// <list type='number'> /// <item><description> /// Lexicographic approach, solving first to minimize capital expended /// and second to maximize return /// </description></item> /// <item><description> /// Blended approach, solving a weighted combination of both objectives /// simultaneously /// </description></item> /// <item><description> /// Lexicographic approach, with the objective priorities reversed /// </description></item> /// </list> class CapitalBudgeting : IDisposable { /// Required capital for each project static readonly double[] CAPITAL = { 104000, 53000, 29000, 187000, 98000, 32000, 75000, 200000 }; /// Required personnel for each project static readonly double[] PERSONNEL = { 22, 12, 7, 36, 24, 10, 20, 41 }; /// Expected return of each project static readonly double[] RETURN = { 124000, 74000, 42000, 188000, 108000, 56000, 88000, 225000 }; /// Target capital to invest static readonly double CAPITAL_TARGET = 478000; /// Target return on investment static readonly double ROI_TARGET = 550000; /// Available personnel static readonly int PERSONNEL_MAX = 106; private readonly 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() { prob = new XpressProblem(); } public void Dispose() { prob.Dispose(); } public static void Main(string[] args) { using (CapitalBudgeting capBudget = new CapitalBudgeting()) { capBudget.Solve(); } } public void Solve() { // Binary decision variables indicating which projects will be implemented select = prob.AddVariables(CAPITAL.Length) .WithType(ColumnType.Binary) .WithName("select_{0}") .ToArray(); // Constraint: at least 3 projects must be implemented prob.AddConstraint(Sum(select) <= 3); // Constraint for the number of personnel available personnel = prob.AddConstraint(ScalarProduct(select, PERSONNEL) <= PERSONNEL_MAX); // Primary objective: minimize capital expended capital = ScalarProduct(select, CAPITAL); // The first objective has priority=1 (weight=1 by default) prob.SetObjective(capital, ObjSense.Minimize); prob.SetObjIntControl(0, 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, ObjControl.Priority, 1); prob.Optimize(); PrintSolution("Equal priority for objectives:"); // Finally, give the first objective a lower priority prob.SetObjIntControl(0, ObjControl.Priority, 0); prob.Optimize(); PrintSolution("Higher priority for 'Maximize Return' objective:"); } /// <summary> /// Prints the current solution to the problem. /// </summary> /// <param name="title">a title to print before the solution</param> private void PrintSolution(String title) { Console.WriteLine(title); if (prob.SolveStatus != SolveStatus.Completed || prob.SolStatus != SolStatus.Optimal) { Console.WriteLine(" Problem not solved"); } else { Console.WriteLine($" Objectives: {prob.Objectives}, solved objectives: {prob.SolvedObjs}"); double[] sol = prob.GetSolution(); double capitalUsed = capital.Evaluate(sol); double roiGained = roi.Evaluate(sol); if (capitalUsed > CAPITAL_TARGET) { Console.WriteLine(" Unable to meet Capital target"); } if (roiGained < ROI_TARGET) { Console.WriteLine(" Unable to meet Return target"); } Console.Write(" Projects undertaken:"); for (int p = 0; p < select.Length; p++) { if (select[p].GetSolution() >= 0.99) { Console.Write($" {p}"); } } Console.WriteLine(); Console.WriteLine($" Used capital: ${capitalUsed}," + (CAPITAL_TARGET >= capitalUsed ? " unused: $" : " overused: $") + Math.Abs(CAPITAL_TARGET - capitalUsed)); Console.WriteLine($" Total return: ${roiGained}"); Console.WriteLine($" Unused personnel: {(int)personnel.GetSlack()} persons"); } } } } | |||||||||||||
© Copyright 2024 Fair Isaac Corporation. |