FICO
FICO Xpress Optimization Examples Repository
FICO Optimization Community FICO Xpress Optimization Home
Back to examples browserPrevious exampleNext example

Capital budgeting - Using multi-objective optimization

Description
Capital budgeting example, 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

capbgt_dnet.zip[download all files]

Source Files
By clicking on a file name, a preview is opened at the bottom of this page.
CapitalBudgeting.cs[download]
CapitalBudgeting.csproj[download]





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

Back to examples browserPrevious exampleNext example