0

I'm new to constraint programming. Now I try to build weekly schedules to manage employee work time and to meet the labor demand. I am using ILOG CPLEX and try to fix the problem. I think this is an easy problem but I can't wrap my head around it. Here's the problem:

• We have multiple workers (i.e.47 workers), each with some constraints (let's say day min work hours, day max work hours, week min work hours, week max work hours)

• Each worker has at least one skill to do a task, with certain level

• We have some tasks (i.e. 10 tasks) every day, each day is split to 48 slots(0~47). a half hour one slot.

• A worker can only work on one task at the same slot (a half hour)

• A task at one slot has a labor demand (may be 2, means: need two workers to do this task at this slot), a min demand(may be 1), and a max demand(may be 10).

How do we assign the tasks to the workers to meet the demand and minimize the number of workers used and assign workers with higher level to do a task?

I'm just learning CP & ILOG CPLEX for two weeks. Any ideas on how to a constraint (i.e. day min work hours)? I write some code as below:

         CP cp = new CP();

            //objective
            INumExpr skillMatrixSum = cp.IntExpr();

            //day min work hours and so on 
            IList<SMAIlogStaffInfo> listStaffInfo = SMAScheduleByILogStaffInfoBusiness.GetStaffInfoList(unitId, startDate, endDate, connectionString);

            //skill level 
            IList<SMAIlogPersonSkillMatrixInfo> listPersonSkillMatrixInfo = SMAScheduleByILogSkillMatrixInfoBusiness.GetPersonSkillMatrixInfoList(unitId, startDate, endDate, connectionString);

            //demand for every half hour
            IList<SMAIlogDemandInfo> listDemandInfo = SMAScheduleByILogDemandInfoBusiness.GetDemandInfoList(unitId, startDate, endDate, connectionString);

            //staff unavailability (i.e. ask for a leave)
            IList<SMAIlogUnvailInfo> listUnvailInfo = SMAScheduleByILogUnvailInfoBusiness.GetUnvailInfoList(unitId, startDate, endDate, connectionString);
            int nbWorkers = listStaffInfo.Select(t => t.PersonId).Distinct().Count();
            IList<string> listPerson = listStaffInfo.Select(t => t.PersonId).ToList();

            //possible tasks by skill 
            List<IIntervalVar> allTasks = new List<IIntervalVar>();
            List<IIntervalVar>[] workerTasks = new List<IIntervalVar>[nbWorkers];
            for (int w = 0; w < nbWorkers; w++)
            {
                workerTasks[w] = new List<IIntervalVar>();
            }

            //schedule by day from sunday to saturday               
            for (int weekdayIndex = 0; weekdayIndex < 7; weekdayIndex++)
            {
                IList<SMAIlogDemandInfo> listWeekdayDemandInfo = listDemandInfo.Where(t => t.Weekday == weekdayIndex).ToList();
                IList<SMAIlogUnvailInfo> listWeekdayUnvailInfo = listUnvailInfo.Where(t => t.Weekday == weekdayIndex).ToList();

                SetScheduleByDay(cp, skillMatrixSum, allTasks, workerTasks, listWeekdayDemandInfo, listWeekdayUnvailInfo, listStaffInfo, listPersonSkillMatrixInfo);
            }

            //constraint: one worker can not do two tasks at the same time 
            for (int w = 0; w < nbWorkers; w++)
            {
                IIntervalSequenceVar seq = cp.IntervalSequenceVar(workerTasks[w].ToArray(), listPerson[w]);
                cp.Add(cp.NoOverlap(seq));
            }

            //Minimize skill Matrix
            cp.Add(cp.Minimize(skillMatrixSum));


    public static void SetScheduleByDay(CP cp, INumExpr skillMatrixSum, List<IIntervalVar> allTasks, List<IIntervalVar>[] workerTasks,
        IList<SMAIlogDemandInfo> listWeekdayDemandInfo, IList<SMAIlogUnvailInfo> listWeekdayUnvailInfo, IList<SMAIlogStaffInfo> listStaffInfo, IList<SMAIlogPersonSkillMatrixInfo> listPersonSkillMatrixInfo)
    {
        int weekdayIndex = listWeekdayDemandInfo.Select(t => t.Weekday).FirstOrDefault();
        int skillCount = listWeekdayDemandInfo.Select(t => t.SkillId).Distinct().Count(); //task count 
        int nbTasks = listWeekdayDemandInfo.Count(); //weekday demand            
        int nbWorkers = listStaffInfo.Count();//workers count 

        //decision variables
        IIntervalVar[] tasks = new IIntervalVar[nbTasks];
        IIntervalVar[,] taskMatrix = new IIntervalVar[nbTasks, nbWorkers];

        ICumulFunctionExpr[] arrayResourcesWorkHours = new ICumulFunctionExpr[nbWorkers];
        for (int j = 0; j < nbWorkers; j++)
        {
            arrayResourcesWorkHours[j] = cp.CumulFunctionExpr();
        }

        string taskFullName;
        string skillId = "";
        string personId = "";
        int levelId = 0;
        int demand = 0;
        int slot = 0;
        for (int i = 0; i < nbTasks; i++)
        {
            slot = listWeekdayDemandInfo[i].Slot;
            demand = listWeekdayDemandInfo[i].Demand;
            if (demand == 0)
            {
                continue;
            }

            skillId = listWeekdayDemandInfo[i].SkillId;

            List<IIntervalVar> alttasks = new List<IIntervalVar>();

            for (int w = 0; w < listStaffInfo.Count(); w++)
            {
                personId = listStaffInfo[w].PersonId;
                levelId = listPersonSkillMatrixInfo.Where(t => t.SkillId == skillId && t.PersonId == personId).ToList().ToList().FirstOrDefault().LevelId;

                //this worker can do this task 
                if (levelId > 0)
                {
                    taskFullName = personId + "-" + weekdayIndex + "-" + slot + "-" + skillId;
                    IIntervalVar wtask = cp.IntervalVar(1, taskFullName);
                    wtask.SetOptional(); // SetOptional
                    alttasks.Add(wtask);
                    taskMatrix[i, w] = wtask;
                    workerTasks[w].Add(wtask);
                    allTasks.Add(wtask);

                    listStaffInfo[w].listWorkerTasks.Add(wtask);

                    skillMatrixSum = cp.Sum(skillMatrixSum, cp.Prod(levelId, cp.PresenceOf(wtask)));
                }
            }
            cp.Add(cp.Alternative(tasks[i], alttasks.ToArray()));
        }

        //here is my problem. the way to generalize cumulative is wrong (DayMin and DayMax,also unavailability ).because tasks is Set Optional
        for (int j = 0; j < listStaffInfo.Count; j++)
        {
            IList<IIntervalVar> listWorkerTasks = listStaffInfo[j].listWorkerTasks;
            if (listWorkerTasks.Count == 0)
            {
                continue;
            }

            int dayMin = listStaffInfo[j].DayMin;
            int dayMax = listStaffInfo[j].DayMax;             

            IConstraint dayMinConstraint = cp.Ge(arrayResourcesWorkHours[j], dayMin);
            cp.Add(dayMinConstraint);

            IConstraint dayMaxConstraint = cp.Le(arrayResourcesWorkHours[j], dayMax);
            cp.Add(dayMaxConstraint);

            INumToNumStepFunction availablityNumStep = cp.NumToNumStepFunction();

            IList<SMAIlogUnvailInfo> listWeekdayPersonUnvailInfo = listWeekdayUnvailInfo.Where(t => t.PersonId == personId).ToList();

            if (listWeekdayPersonUnvailInfo.Count > 0)
            {
                for (int k = 0; k < listWeekdayPersonUnvailInfo.Count; k++)
                {
                    int startSlot = listWeekdayPersonUnvailInfo[k].StartSlot;
                    int endSlot = listWeekdayPersonUnvailInfo[k].EndSlot;

                    for (int i = 0; i < listWorkerTasks.Count; i++)
                    {
                        while (startSlot <= endSlot)
                        {
                            availablityNumStep.SetValue(startSlot, startSlot + 1, 0);
                            //cp.Add(cp.ForbidExtent(listWorkerTasks[i], availablityNumStep));

                            cp.Add(cp.ForbidStart(listWorkerTasks[i], availablityNumStep));
                            cp.Add(cp.ForbidEnd(listWorkerTasks[i], availablityNumStep));

                            startSlot++;
                        }

                    }
                }
            }

        }
    }

I have a set of input data as below:

Schedule Input Data

and also I have a example of out data as below: Schedule Output Data

  • Please do everybody a favor and format this extensive amount of data so that it is readable. Alternatively you can put a link to the data. – user3743222 Jan 09 '16 at 09:33
  • since the max length of posted content on this site.please send a email to me (enamewang@163.com or enamewang@gmail,com) get the input data. And the out data format like this ( personid weekday skillid slot ). – Frank Wang Jan 09 '16 at 09:35
  • Then upload your data somewhere, and put a link to it. There is no personal communication through emails going on here. Communication is best kept on the site as it can benefit other readers. Also, put the out data format in your question. It does not belong to a comment. http://stackoverflow.com/help/how-to-ask – user3743222 Jan 09 '16 at 09:37
  • [ScheduleProblemDesc](http://139.129.130.234/ConstraintProgram/ScheduleProblemDesc.docx) – Frank Wang Jan 09 '16 at 10:17

0 Answers0