public ConstraintSolverSolution SolveB(int maxSolutions = 100) { ConstraintSystem S = ConstraintSystem.CreateSolver(); //Define how many agents may work per shift CspDomain cspShiftsForceDomain = S.CreateIntegerInterval(first: 0, last: MaxAgents); //Decision variables, one per shift, that will hold the result of how may agents must work per shift to fullfil requirements CspTerm[] cspShiftsForce = S.CreateVariableVector(domain: cspShiftsForceDomain, key: "force", length: Shifts.Count); //index shifts, their variable CspTerm by the shift's relative no (0=first, 1=second, etc) ShiftsX = new Dictionary <Models.Shift, CspTerm>(); int i = 0; Shifts.ForEach(x => { ShiftsX.Add(x, cspShiftsForce[i]); i++; }); //Constraint - Agents on every half hour must be >= requirement for that half hour List <CspTerm> cspHalfHourExcess = new List <CspTerm>(); foreach (var halfHourRq in HalfHourRequirements) { //find shifts including that halftime, and calculate their sum of force List <CspTerm> cspActive = new List <CspTerm>(); foreach (var entry in ShiftsX) { if (entry.Key.IncludesHalfHour(halfHourRq.Start)) { cspActive.Add(entry.Value); } } //add constraint for sum of force of active shifts on that halfhour //if we need agents but no shifts exists for a halfhour, do not add a constraint if (cspActive.Count > 0) { var s = S.Sum(cspActive.ToArray()) - S.Constant(halfHourRq.RequiredForce); S.AddConstraints( S.LessEqual(constant: 0, inputs: s) ); cspHalfHourExcess.Add(s); } } //var goal = S.Sum(shiftsX.Values.ToArray()); //bool ok = S.TryAddMinimizationGoals(goal); var goalMinExcess = S.Sum(cspHalfHourExcess.ToArray()); bool ok = S.TryAddMinimizationGoals(goalMinExcess); ConstraintSolverSolution solution = S.Solve(); Console.WriteLine(); GetSolutionsAll(solution: solution, maxSolutions: maxSolutions); if (ShiftsForce == null || ShiftsForce.Count == 0) { Console.WriteLine("No solution found"); } if (ShiftsForce != null) { foreach (var shiftForceEntry in ShiftsForce) { ShowSolution(no: shiftForceEntry.Key, shiftsForce: shiftForceEntry.Value, showAgents: true, showSlots: false); } } return(solution); }
public SimplexSolver PrepareSimplexSolver(int maxAgents, out Dictionary <Models.Shift, int> shiftsExt, out int vidGoal) { var SX = new SimplexSolver(); var maxRq = HalfHourRequirements.Max(x => x.RequiredForce); var sumRq = HalfHourRequirements.Sum(x => x.RequiredForce); shiftsExt = null; ////goal: total no of agents //int ridgoal; //SX.AddRow(key: "goal", vid: out ridgoal); //SX.SetBounds(ridgoal, lower: 0, upper: maxAgents); //total agents must range from 0 to max agents available //SX.AddGoal(vid: ridgoal, pri: 1, fMinimize: true); //minimize total number of agents for that day //goal: total no of agents, minimize overtime int ridgoal; SX.AddRow(key: "goal", vid: out ridgoal); SX.SetBounds(ridgoal, lower: 0, upper: Rational.PositiveInfinity); // upper: maxAgents); //total agents must range from 0 to max agents available SX.AddGoal(vid: ridgoal, pri: 2, fMinimize: true); //minimize total number of agents for that day //goal2: Minimize excess force int ridexcess; SX.AddRow(key: "excess", vid: out ridexcess); SX.SetBounds(ridexcess, lower: 0, upper: sumRq); //allow excess from 0 to sum of all requirements SX.AddGoal(vid: ridexcess, pri: 1, fMinimize: true); //try to minimize excess time var shiftsX = new Dictionary <Models.Shift, int>(); int i = 0; Shifts.ForEach(x => { int vid; SX.AddVariable(key: string.Format("{0}. {1} {2:hh}:{2:mm} - {3:hh}:{3:mm}", i + 1, x.Name, x.Start, x.End), vid: out vid); //the no of agents on each shift may not exceed to max requirements SX.SetBounds(vid: vid, lower: 0, upper: maxRq); shiftsX.Add(x, vid); //SX.SetCoefficient(vidRow: ridgoal, vidVar: vid, num: 1); //normal weight: all shifts are the same SX.SetCoefficient(vidRow: ridgoal, vidVar: vid, num: x.Duration.Hours <= 8 ? 1 : 10); //weights per shift: overtime = *10 more expensive than normal shifts i++; }); //Constraint - Agents from every active shift on every half hour must be >= requirement for that half hour HalfHourRequirements.ForEach(hh => { List <int> vidShiftsActive = new List <int>(); foreach (var entry in shiftsX) { if (entry.Key.IncludesHalfHour(hh.Start)) { vidShiftsActive.Add(entry.Value); } } //add constraint for sum of force of active shifts on that halfhour //if we need agents but no shifts exists for a halfhour, do not add a constraint if (vidShiftsActive.Count > 0) { int ridHalf; SX.AddRow(key: string.Format("{0:hh}:{0:mm} [{1}]", hh.Start, hh.RequiredForce), vid: out ridHalf); //specify whichs shifts contributes to half hour's total force vidShiftsActive.ForEach(vidShift => { SX.SetCoefficient(vidRow: ridHalf, vidVar: vidShift, num: 1); SX.SetCoefficient(vidRow: ridexcess, vidVar: vidShift, num: 1); }); //each 30' span, must have at least as many required agents SX.SetBounds(vid: ridHalf, lower: hh.RequiredForce, upper: Rational.PositiveInfinity); } }); shiftsExt = shiftsX; vidGoal = ridgoal; return(SX); }
public ConstraintSystem PrepareCspSolver() { ConstraintSystem S = ConstraintSystem.CreateSolver(); //Define how many agents may work per shift var maxRq = HalfHourRequirements.Max(x => x.RequiredForce); CspDomain cspShiftsForceDomain = S.CreateIntegerInterval(first: 0, last: maxRq); //var cspShiftsForceDomain = S.CreateIntegerSet(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 }); //Decision variables, one per shift, that will hold the result of how may agents must work per shift to fullfil requirements CspTerm[] cspShiftsForce = S.CreateVariableVector(domain: cspShiftsForceDomain, key: "force", length: Shifts.Count); //index shifts, their variable CspTerm by the shift's relative no (0=first, 1=second, etc) ShiftsX = new Dictionary <Models.Shift, CspTerm>(); int i = 0; Shifts.ForEach(x => { ShiftsX.Add(x, cspShiftsForce[i]); i++; }); //Constraint - Agents from every active shift on every half hour must be >= requirement for that half hour List <CspTerm> cspHalfHourExcess = new List <CspTerm>(); foreach (var halfHourRq in HalfHourRequirements) { //find shifts including that halftime, and calculate their sum of force List <CspTerm> cspActive = new List <CspTerm>(); foreach (var entry in ShiftsX) { if (entry.Key.IncludesHalfHour(halfHourRq.Start)) { cspActive.Add(entry.Value); } } //add constraint for sum of force of active shifts on that halfhour //if we need agents but no shifts exists for a halfhour, do not add a constraint if (cspActive.Count > 0) { //var s = S.Sum(cspActive.ToArray()) - S.Constant(halfHourRq.RequiredForce); S.AddConstraints( S.LessEqual(constant: halfHourRq.RequiredForce, inputs: S.Sum(cspActive.ToArray())) ); //cspHalfHourExcess.Add(s); } } //if (false && cspHalfHourExcess.Count > 0) // S.AddConstraints( // S.LessEqual(constant: 0, inputs: S.Sum(cspHalfHourExcess.ToArray())) // ); bool xx = true; if (xx) { var goal = S.Sum(ShiftsX.Values.ToArray()); bool ok = S.TryAddMinimizationGoals(goal); } else { //S.RemoveAllMinimizationGoals(); var goalMinExcess = S.Sum(cspHalfHourExcess.ToArray()); bool ok = S.TryAddMinimizationGoals(goalMinExcess); } return(S); }
public Solution alternative_solve(out Dictionary <Models.Shift, Decision> shiftsN, out Dictionary <Models.Shift, Decision> shiftsO) { SolverContext SC = SolverContext.GetContext(); Model model = SC.CreateModel(); var maxRq = HalfHourRequirements.Max(x => x.RequiredForce); shiftsN = shiftsO = null; //split shifts into two Dictionaries (each dict holds each Shift as key and the Decision variable as Value) var normal_shifts = new Dictionary <Models.Shift, Decision>(); //for normal shifts int i = 0; Shifts.ForEach(x => { Decision des = new Decision(Domain.IntegerRange(0, maxRq), string.Format("{0}_{1}", x.Name, i)); normal_shifts.Add(x, des); model.AddDecision(des); i++; }); var overtime_shifts = new Dictionary <Models.Shift, Decision>(); //for overtime shifts i = 0; OvertimeShifts.ForEach(x => { Decision des = new Decision(Domain.IntegerRange(0, maxRq), string.Format("{0}_{1}", x.Name, i)); overtime_shifts.Add(x, des); model.AddDecision(des); i++; }); List <Decision> excess = new List <Decision>(); //used to calculate sum of excess values HalfHourRequirements.ForEach(hh => { var ShiftsActive = new List <Decision>(); foreach (var entry in normal_shifts) { if (entry.Key.IncludesHalfHour(hh.Start)) { ShiftsActive.Add(entry.Value); excess.Add(entry.Value); } } foreach (var entry in overtime_shifts) { if (entry.Key.IncludesHalfHour(hh.Start)) { ShiftsActive.Add(entry.Value); excess.Add(entry.Value); } } //add constraint for sum of force of active shifts on that halfhour //if we need agents but no shifts exists for a halfhour, do not add a constraint if (ShiftsActive.Count > 0) { var sum_constr = new SumTermBuilder(ShiftsActive.Count); ShiftsActive.ForEach(s => sum_constr.Add(s)); var constr = sum_constr.ToTerm() >= hh.RequiredForce; model.AddConstraint(string.Format("_{0:hh}{0:mm}", hh.Start), constr); //constr = sum_constr.ToTerm() <= hh.RequiredForce * 2.0; //model.AddConstraint(string.Format("limitconst_for_{0:hh}{0:mm}", hh.Start), constr); } }); //Constrain maximum number of agents working 8hour shifts var max_agents_constraint = new SumTermBuilder(Shifts.Count); foreach (var entry in normal_shifts) { max_agents_constraint.Add(entry.Value); } var ma_constr = max_agents_constraint.ToTerm() <= MaxAgents; model.AddConstraint("Max_agents_constraint", ma_constr); //1st goal: Minimize overtime shifts (if work manageable by normal shifts, no overtime shifts will be used) var overtime_obj = new SumTermBuilder(OvertimeShifts.Count); foreach (var entry in overtime_shifts) { overtime_obj.Add(entry.Value); } model.AddGoal("Minimize_overtime", GoalKind.Minimize, overtime_obj.ToTerm()); //2st goal: Minimize normal shifts var normal_obj = new SumTermBuilder(Shifts.Count); foreach (var entry in normal_shifts) { normal_obj.Add(entry.Value); } model.AddGoal("Minimize_normal", GoalKind.Minimize, normal_obj.ToTerm()); //3rd goal: Try to minimize excess var sumReqs = HalfHourRequirements.Sum(x => x.RequiredForce); model.AddGoal("Minimize_excess", GoalKind.Minimize, Model.Sum(excess.ToArray()) - sumReqs); SimplexDirective simplex = new SimplexDirective(); Solution solution = SC.Solve(simplex); shiftsN = normal_shifts; shiftsO = overtime_shifts; return(solution); }