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

Project planning - Defining SOS

Description
Alternative formulations using either binary variables (Pplan) or SOS-1 (Pplan2)

pplan_dnet.zip[download all files]

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





Pplan.cs

// (c) 2023-2024 Fair Isaac Corporation

using Optimizer;
using Optimizer.Objects;
using static Optimizer.Objects.Utils;
using System;
using System.Linq;

namespace XpressExamples
{
    /// <summary>Project planning: MIP example</summary>
    /// <remarks>
    ///   A company has several projects that it must undertake in the next
    ///   few months.
    ///   Each project lasts for a given time (its duration) and uses up one
    ///   resource as soon as it starts.
    ///   The resource profile is the amount of the resource that is used in
    ///   the months following the start of the project.
    ///   For instance, project 1 uses up 3 units of resource in the month it
    ///   starts, 4 units in its second month, and 2 units in its last month.
    ///   The problem is to decide when to start each project, subject to not
    ///   using more of any resource in a given month than is available.
    ///   The benefit from the project only starts to accrue when the project
    ///   has been completed, and then it accrues at BEN[p] per month for
    ///   project p, up to the end of the time horizon.
    /// </remarks>
    class Pplan
    {
        static readonly string[] PROJ = { "A", "B", "C" };                  /* Set of projects */
        const int NM = 6;                                                   /* Time horizon (months) */
        static readonly int[] MONTHS = System.Linq.Enumerable.Range(1, NM).ToArray();   /* Set of time periods (months) to plan for */
        static readonly int[] DUR = { 3, 3, 4 };                            /* Duration of project p */
        static readonly int[] RESMAX = { 5, 6, 7, 7, 6, 6 };                /* Resource available in month m */
        static readonly double[] BEN = { 10.2, 12.3, 11.2 };                /* Benefit per month once project finished */
        static readonly int[][] RESUSE =                                    /* Res. usage of proj. p in its t'th month */
            new int[][] { new int[] { 3, 4, 2, 0, 0, 0 }, new int[] { 4, 1, 5, 0, 0, 0 }, new int[] { 3, 2, 1, 2, 0, 0 } };

        public static void Main(string[] args)
        {
            using (XpressProblem prob = new XpressProblem())
            {
                Variable[,] start = prob.AddVariables(PROJ.Length, NM)
                                  .WithType(ColumnType.Binary)
                                  .WithName((p, m) => $"start({PROJ[p]})({MONTHS[m]})")
                                  .ToArray();

                // Each project starts once and only once
                prob.AddConstraints(PROJ.Length, p => Sum(NM, m => start[p, m]) == 1);

                // Resource availability. A project starting in month m is in its k-m month in month k:
                // sum(p in PROJ, m in 0..k) RESUSE(p,k-m)*start(p,m) <= RESMAX(k)
                prob.AddConstraints(NM, k => Sum(PROJ.Length, p => Sum(k + 1, m => start[p, m] * RESUSE[p][k - m])) <= RESMAX[k]);

                /* Objective: Maximize Benefit
                    If project p starts in month m, it finishes in month m+DUR(p)-1 and
                    contributes a benefit of BEN(p) for the remaining NM-(m+DUR(p)-1) months:  */
                LinExpression obj = LinExpression.Create();
                for (int p = 0; p < PROJ.Length; p++)
                {
                    for (int m = 0; m < NM - DUR[p]; m++)
                    {
                        obj.AddTerm(start[p, m], BEN[p] * (NM - m - DUR[p]));
                    }
                }
                prob.SetObjective(obj, ObjSense.Maximize);

                // Solve the problem
                prob.MipOptimize("");

                System.Console.WriteLine("Problem status: " + prob.MIPStatus);
                if (prob.MIPStatus != Optimizer.MIPStatus.Solution &&
                        prob.MIPStatus != Optimizer.MIPStatus.Optimal)
                    throw new System.Exception("optimization failed with status " + prob.MIPStatus);

                // Solution printing
                System.Console.WriteLine("Solution value is: " + prob.ObjVal);
                double[] sol = prob.GetSolution();
                for (int p = 0; p < PROJ.Length; p++)
                    for (int m = 0; m < NM; m++)
                        if (start[p, m].GetValue(sol) > 0.5)
                            System.Console.WriteLine("Project " + PROJ[p] + " starts in month " + MONTHS[m]);
            }
        }
    }
}

Back to examples browserPrevious exampleNext example