Decision Optimization

 View Only
  • 1.  Modifying Sched_tasks example

    Posted 10 days ago

    Hello,
    I am modifying the Sched_tasks example to make it applicable to optimize several tasks across scattered units with different durations.

    Here is my .mod file:

    using CP;
    int NbHouses = ...; // Number of units
    range HousesRange = 1..NbHouses;
     
    int NoTasks = ...; // Number of tasks
    range TasksRange = 1..NoTasks;
     
    {string} WorkerNames = ...;  // List of workers' names
    string TaskNames[TasksRange] = ...; // List of task names as an array of strings
    {string} ActivityNames = {}; // List of task names as a set of strings
     
    // Use the data from TaskNames to create the ActivityNames
    execute {
      for (var i in TasksRange) {
        ActivityNames.add(TaskNames[i]);
      }
    }
     
    // Create a tuple containing id and name of tasks
    tuple ListOfTask {
      int id;
      string TaskName;
    };
    {ListOfTask} tasksAsTuples = {<i, TaskNames[i]> | i in TasksRange};
     
    execute {
      writeln("tasksAsTuples: ", tasksAsTuples);
    }
     
    int Duration[HousesRange][TasksRange] = ...; // Duration for each task in each house as a matrix
     
    // Data for workers
    tuple ResourceDat {
      key int id;
      string type;
      string name;
    };
     
    {ResourceDat} resources = ...;
     
    // Create a list of the resource types
    {string} resourceTypes = {r.type | r in resources};
    // Data for task & worker requirements (Which crew to complete a task and how many required)
    tuple Requirement {
      string task;
      string resource;
      int quantity;
    };
     
    {Requirement} requirements = ...;
    // Create a tuple with data of list of tasks & requirements for each one & available crews
    tuple Allocation {
      ListOfTask dmd;
      Requirement req;
      ResourceDat resource;
    };
     
    {Allocation} allocs = 
      {<tn, m, r> | tn in tasksAsTuples, m in requirements, r in resources : 
       tn.TaskName == m.task && r.type == m.resource};
    // Relationships among tasks
    tuple Precedence {
      string pre;
      string post;
      string type;
      int delay;
    };
     
    // Import the relationships from the .dat file
    {Precedence} Precedences = ...;
    int ReleaseDate[HousesRange] = ...; 
    int DueDate[HousesRange] = ...; 
    float Weight[HousesRange] = ...; 
     
    // Define the matrix to import the values
    int transitionTimes[HousesRange][HousesRange] = ...;
     
    // Transition time across the units
    tuple triplet {
      int loc1;
      int loc2;
      int value;
    };
     
    // Create a set of tuples from the traveling matrix
    {triplet} transitionAsTuples = {<i, j, transitionTimes[i][j]> | i in HousesRange, j in HousesRange};
     
    // Create the house interval variables
    dvar interval houses[h in HousesRange] in ReleaseDate[h]..(maxint div 2)-1;
     
    // Create the task interval variables
    dvar interval itvs[h in HousesRange][t in TasksRange] size Duration[h][t];
     
    // Create the allocation interval variables
    dvar interval tiallocs[allocs] optional;
     
    // Create the sequence variables
    dvar sequence workers[r in resources] in all(a in allocs: a.resource == r) tiallocs[a];
     
    int levels[rt in resourceTypes] = sum(r in resources : r.type == rt) 1;
     
    cumulFunction cumuls[rt in resourceTypes] =
      sum(rc in requirements, tn in tasksAsTuples, h in HousesRange, t in TasksRange : rc.resource == rt && tn.TaskName == rc.task) pulse(itvs[h][t], rc.quantity);
    // Declare the mapping from task names to task indices
    int taskIndex[ActivityNames];
     
    // Populate the mapping
    execute {
      for (var i in TasksRange) {
        taskIndex[TaskNames[i]] = i;
      }
    }
     
    // Define the objective Functions:
    // The total duration
    dexpr float TotalDuration = max(h in HousesRange, t in TasksRange) endOf(itvs[h][t]);
     
    // Determine the earliest start time among all houses
    dexpr float firstStartTime = min(h in HousesRange) startOf(houses[h]);
    // Objective Function:
    minimize TotalDuration;
     
    subject to {
     
    // Ensure the first task in the first house starts at day 0
    firstStartTime == 0;
     
    // Add the precedence constraints
    forall(h in HousesRange)
      forall(p in Precedences) 
        if (p.type == "FS") endBeforeStart(itvs[h][taskIndex[p.pre]], itvs[h][taskIndex[p.post]], p.delay);
        else if (p.type == "SS") startAtStart(itvs[h][taskIndex[p.pre]], itvs[h][taskIndex[p.post]], p.delay);
        else if (p.type == "FF") endAtEnd(itvs[h][taskIndex[p.pre]], itvs[h][taskIndex[p.post]], p.delay);
        else if (p.type == "SF") startBeforeEnd(itvs[h][taskIndex[p.pre]], itvs[h][taskIndex[p.post]], p.delay);
     
    // Add the house span constraints
    forall(h in HousesRange)
    span(houses[h], all(t in TasksRange) itvs[h][t]);
     
    // Add the no overlap constraints
    forall(r in resources) {
      noOverlap(workers[r], transitionAsTuples);
    }
     
    // Ensure resource levels are not exceeded
    forall(r in resourceTypes : levels[r] > 10) {
      cumuls[r] <= levels[r];
    }
    }
    // Set solver parameters
    execute {
      cp.param.FailLimit = 10000;
      cp.param.TimeLimit = 300;
      cp.param.TimeMode = "CPUTime";
      cp.param.SearchType = "auto";
      writeln("Solver parameters set.");
    }


    Here is my .dat file

    NbHouses = 3;
     
    NoTasks = 10;
     
    WorkerNames = {"Crew1-1", "Crew1-2","Crew2", "Crew3-1", "Crew3-2","Crew3-3","Crew4", "Crew5", "Crew6", "Crew7", "Crew8", "Crew9", "Crew10" };
     
    resources = {
      <1, MasonaryLayer,     "Crew1-1" >,
      <2, MasonaryLayer,     "Crew1-2" >,
      <3, Carpenter,         "Crew2" >,
      <4, Plumber,           "Crew3-1" >,
      <5, Plumber,           "Crew3-2" >,
      <6, Plumber,           "Crew3-3" >, 
      <7, CeilingWorker,     "Crew4" >,
      <8, RoofWorker,        "Crew5" >,
      <9, Painter,           "Crew6" >,
      <10, WindowInstaller,  "Crew7" >,
      <11, FacadeWorker,     "Crew8" >,
      <12, GardenWorker,     "Crew9" >,
      <13, Mover,            "Crew10" >
    };
     
    TaskNames = [ 
      "masonry", 
      "carpentry", 
      "plumbing", 
      "ceiling",
      "roofing", 
      "painting", 
      "windows", 
      "facade",
      "garden", 
      "moving" 
    ];
     
    // Each row represents a building (unit) with duration of tasks in the same order as in TaskNames list
     
    Duration =  [[35, 30, 25, 20, 25, 15, 20, 15, 20, 15],
        [10, 15, 10, 15, 10, 5, 10, 5, 10, 5],
        [5, 10, 5, 10, 5, 15, 20, 15, 20, 15]];
     
    requirements = {
      <masonry,      MasonaryLayer,    1>,
      <carpentry,    Carpenter,        1>,
      <plumbing,     Plumber,          1>,
      <ceiling,      CeilingWorker,    1>, 
      <roofing,      RoofWorker,       1>, 
      <painting,     Painter,          1>,
      <windows,      WindowInstaller,  1>, 
      <facade,       FacadeWorker,     1>,
      <garden,       GardenWorker,     1>,
      <moving,       Mover,            1>
    };
     
    // transitionTimes to be entered as a fraction of day (e.g., 2 hrs = 0.25 day)
     
    transitionTimes = [[0, 2, 1],
                                 [2, 0, 1],
                                 [1, 1, 0]
    ];
     
    ReleaseDate = [  0,     0,   0];
    DueDate     = [120,   212,   304];
    Weight      = [100.0, 100.0, 100.0];
     
    // Format <"Predecessor", "Successor", "Relation type", Lag time>,
    // FS = Finish to Start; SS = Start to Start; FF = Finish to Finish; SF = Start to Finish
    // Lag = + Lag time ; Lead = - Lag time
     
    Precedences = {
      <"masonry",   "carpentry", "SS", 2>,
      <"masonry",   "plumbing", "FS", -2>,
      <"masonry",   "ceiling", "FS", 0>,
      <"carpentry", "roofing", "FS", 0>,
      <"ceiling",   "painting", "FS", 0>,
      <"roofing",   "windows", "FF", 0>,
      <"roofing",   "facade", "FS", 0>,
      <"plumbing",  "facade", "FS", 0>,
      <"roofing",   "garden", "FS", 0>,
      <"plumbing",  "garden", "SF", 0>,
      <"windows",   "moving", "FS", 0>,
      <"facade",    "moving", "FS", 0>,
      <"garden",    "moving", "FS", 0>,
      <"painting",  "moving", "FS", 0>
    };
    I have issues with lines highlighted in Bold (in the .mod file):
    1- Create the sequence variable for the workers
    2- cumulFunction for the resources consumption.
    Also, I need to calculate the idle time for each worker and the total idle time

    Any help would be much appreciated,
    Regards,


    ------------------------------
    Fam Saeed
    ------------------------------


  • 2.  RE: Modifying Sched_tasks example

    Posted yesterday

    Hello Fam,

                 In your example "tasksAsTuples" corresponds to "operations" in the Sched_tasks example.

    You can update your definition as follows:

    {ListOfTask} tasksAsTuples = {<(h-1)*NoTasks + i, TaskNames[i]> | i in TasksRange, h in HousesRange};

    You want to build NbHouses houses.

    Regarding the sequence variables, if you want to take into account the transition times, you have to define a type for each interval vars.

    dvar sequence workers[r in resources] in all(a in allocs: a.resource == r) tiallocs[a] types all(a in allocs: a.resource == r) (((a.dmd.id-1) div NoTasks)+1) ;

    Regarding the cumul functions, "tasksAsTuples" contains now all the operations for all the houses, you can update your code as follows:

    cumulFunction cumuls[rt in resourceTypes] =

      sum(rc in requirements, tn in tasksAsTuples : rc.resource == rt && tn.TaskName == rc.task) pulse(itvs[((tn.id-1) div NoTasks) + 1][((tn.id mod NoTasks) == 0 ? NoTasks:(tn.id mod NoTasks))], rc.quantity);

    In the Sched_tasks example, the tiops interval variables are linked to the tiallocs interval variables using alternatives.

    You need to do the same thing between itvs and tiallocs interval variables:

    forall(h in HousesRange) {

      forall(t in TasksRange) {

        forall (rc in requirements : rc.task == TaskNames[t]) {

                 alternative(itvs[h][t], all(a in allocs : a.dmd.id == (h-1)*NoTasks+t) tiallocs[a], rc.quantity);

       }        

      }

    } 

    Regarding the redundant cumul function, you can keep the same as in Sched_tasks example:

      forall(r in resourceTypes: levels[r] > 1) {

        cumuls[r] <= levels[r];

      } 

    In your example you set some parameters. For them to be taken into account, you must define them before defining your objective function.

    Best regards,



    ------------------------------
    Thierry Sola
    ------------------------------