public static void Main(String[] args) { // [START data] const int numNurses = 5; const int numDays = 7; const int numShifts = 3; int[] allNurses = Enumerable.Range(0, numNurses).ToArray(); int[] allDays = Enumerable.Range(0, numDays).ToArray(); int[] allShifts = Enumerable.Range(0, numShifts).ToArray(); int[,,] shiftRequests = new int[, , ] { { { 0, 0, 1 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 1 }, { 0, 1, 0 }, { 0, 0, 1 }, }, { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 1, 0, 0 }, { 0, 0, 0 }, { 0, 0, 1 }, }, { { 0, 1, 0 }, { 0, 1, 0 }, { 0, 0, 0 }, { 1, 0, 0 }, { 0, 0, 0 }, { 0, 1, 0 }, { 0, 0, 0 }, }, { { 0, 0, 1 }, { 0, 0, 0 }, { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 0 }, { 1, 0, 0 }, { 0, 0, 0 }, }, { { 0, 0, 0 }, { 0, 0, 1 }, { 0, 1, 0 }, { 0, 0, 0 }, { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 0 }, }, }; // [END data] // Creates the model. // [START model] CpModel model = new CpModel(); // [END model] // Creates shift variables. // shifts[(n, d, s)]: nurse 'n' works shift 's' on day 'd'. // [START variables] Dictionary <Tuple <int, int, int>, IntVar> shifts = new Dictionary <Tuple <int, int, int>, IntVar>(); foreach (int n in allNurses) { foreach (int d in allDays) { foreach (int s in allShifts) { shifts.Add(Tuple.Create(n, d, s), model.NewBoolVar($"shifts_n{n}d{d}s{s}")); } } } // [END variables] // Each shift is assigned to exactly one nurse in the schedule period. // [START exactly_one_nurse] foreach (int d in allDays) { foreach (int s in allShifts) { IntVar[] x = new IntVar[numNurses]; foreach (int n in allNurses) { var key = Tuple.Create(n, d, s); x[n] = shifts[key]; } model.Add(LinearExpr.Sum(x) == 1); } } // [END exactly_one_nurse] // Each nurse works at most one shift per day. // [START at_most_one_shift] foreach (int n in allNurses) { foreach (int d in allDays) { IntVar[] x = new IntVar[numShifts]; foreach (int s in allShifts) { var key = Tuple.Create(n, d, s); x[s] = shifts[key]; } model.Add(LinearExpr.Sum(x) <= 1); } } // [END at_most_one_shift] // [START assign_nurses_evenly] // Try to distribute the shifts evenly, so that each nurse works // minShiftsPerNurse shifts. If this is not possible, because the total // number of shifts is not divisible by the number of nurses, some nurses will // be assigned one more shift. int minShiftsPerNurse = (numShifts * numDays) / numNurses; int maxShiftsPerNurse; if ((numShifts * numDays) % numNurses == 0) { maxShiftsPerNurse = minShiftsPerNurse; } else { maxShiftsPerNurse = minShiftsPerNurse + 1; } foreach (int n in allNurses) { IntVar[] numShiftsWorked = new IntVar[numDays * numShifts]; foreach (int d in allDays) { foreach (int s in allShifts) { var key = Tuple.Create(n, d, s); numShiftsWorked[d * numShifts + s] = shifts[key]; } } model.AddLinearConstraint(LinearExpr.Sum(numShiftsWorked), minShiftsPerNurse, maxShiftsPerNurse); } // [END assign_nurses_evenly] // [START objective] IntVar[] flatShifts = new IntVar[numNurses * numDays * numShifts]; int[] flatShiftRequests = new int[numNurses * numDays * numShifts]; foreach (int n in allNurses) { foreach (int d in allDays) { foreach (int s in allShifts) { var key = Tuple.Create(n, d, s); flatShifts[n * numDays * numShifts + d * numShifts + s] = shifts[key]; flatShiftRequests[n * numDays * numShifts + d * numShifts + s] = shiftRequests[n, d, s]; } } } model.Maximize(LinearExpr.ScalProd(flatShifts, flatShiftRequests)); // [END objective] // Solve // [START solve] CpSolver solver = new CpSolver(); CpSolverStatus status = solver.Solve(model); Console.WriteLine($"Solve status: {status}"); // [END solve] // [START print_solution] if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) { Console.WriteLine("Solution:"); foreach (int d in allDays) { Console.WriteLine($"Day {d}"); foreach (int n in allNurses) { bool isWorking = false; foreach (int s in allShifts) { var key = Tuple.Create(n, d, s); if (solver.Value(shifts[key]) == 1L) { if (shiftRequests[n, d, s] == 1) { Console.WriteLine($" Nurse {n} work shift {s} (requested)."); } else { Console.WriteLine($" Nurse {n} work shift {s} (not requested)."); } } } } } Console.WriteLine( $"Number of shift requests met = {solver.ObjectiveValue} (out of {numNurses * minShiftsPerNurse})."); } else { Console.WriteLine("No solution found."); } // [END print_solution] // [START statistics] Console.WriteLine("Statistics"); Console.WriteLine($" conflicts: {solver.NumConflicts()}"); Console.WriteLine($" branches : {solver.NumBranches()}"); Console.WriteLine($" wall time: {solver.WallTime()}s"); // [END statistics] }
static void RankTasks(CpModel model, IntVar[] starts, ILiteral[] presences, IntVar[] ranks) { int num_tasks = starts.Length; // Creates precedence variables between pairs of intervals. ILiteral[,] precedences = new ILiteral[num_tasks, num_tasks]; for (int i = 0; i < num_tasks; ++i) { for (int j = 0; j < num_tasks; ++j) { if (i == j) { precedences[i, i] = presences[i]; } else { BoolVar prec = model.NewBoolVar(String.Format("{0} before {1}", i, j)); precedences[i, j] = prec; model.Add(starts[i] < starts[j]).OnlyEnforceIf(prec); } } } // Treats optional intervals. for (int i = 0; i < num_tasks - 1; ++i) { for (int j = i + 1; j < num_tasks; ++j) { List <ILiteral> tmp_array = new List <ILiteral>(); tmp_array.Add(precedences[i, j]); tmp_array.Add(precedences[j, i]); tmp_array.Add(presences[i].Not()); // Makes sure that if i is not performed, all precedences are false. model.AddImplication(presences[i].Not(), precedences[i, j].Not()); model.AddImplication(presences[i].Not(), precedences[j, i].Not()); tmp_array.Add(presences[j].Not()); // Makes sure that if j is not performed, all precedences are false. model.AddImplication(presences[j].Not(), precedences[i, j].Not()); model.AddImplication(presences[j].Not(), precedences[j, i].Not()); // The following bool_or will enforce that for any two intervals: // i precedes j or j precedes i or at least one interval is not // performed. model.AddBoolOr(tmp_array); // Redundant constraint: it propagates early that at most one precedence // is true. model.AddImplication(precedences[i, j], precedences[j, i].Not()); model.AddImplication(precedences[j, i], precedences[i, j].Not()); } } // Links precedences and ranks. for (int i = 0; i < num_tasks; ++i) { List <IntVar> tasks = new List <IntVar>(); for (int j = 0; j < num_tasks; ++j) { tasks.Add((IntVar)precedences[j, i]); } model.Add(ranks[i] == LinearExpr.Sum(tasks) - 1); } }
static void Main() { CpModel model = new CpModel(); // Three weeks. int horizon = 100; int num_tasks = 4; IntVar[] starts = new IntVar[num_tasks]; IntVar[] ends = new IntVar[num_tasks]; IntervalVar[] intervals = new IntervalVar[num_tasks]; ILiteral[] presences = new ILiteral[num_tasks]; IntVar[] ranks = new IntVar[num_tasks]; ILiteral true_var = model.TrueLiteral(); // Creates intervals, half of them are optional. for (int t = 0; t < num_tasks; ++t) { starts[t] = model.NewIntVar(0, horizon, String.Format("start_{0}", t)); int duration = t + 1; ends[t] = model.NewIntVar(0, horizon, String.Format("end_{0}", t)); if (t < num_tasks / 2) { intervals[t] = model.NewIntervalVar(starts[t], duration, ends[t], String.Format("interval_{0}", t)); presences[t] = true_var; } else { presences[t] = model.NewBoolVar(String.Format("presence_{0}", t)); intervals[t] = model.NewOptionalIntervalVar(starts[t], duration, ends[t], presences[t], String.Format("o_interval_{0}", t)); } // Ranks = -1 if and only if the tasks is not performed. ranks[t] = model.NewIntVar(-1, num_tasks - 1, String.Format("rank_{0}", t)); } // Adds NoOverlap constraint. model.AddNoOverlap(intervals); // Adds ranking constraint. RankTasks(model, starts, presences, ranks); // Adds a constraint on ranks. model.Add(ranks[0] < ranks[1]); // Creates makespan variable. IntVar makespan = model.NewIntVar(0, horizon, "makespan"); for (int t = 0; t < num_tasks; ++t) { model.Add(ends[t] <= makespan).OnlyEnforceIf(presences[t]); } // Minimizes makespan - fixed gain per tasks performed. // As the fixed cost is less that the duration of the last interval, // the solver will not perform the last interval. IntVar[] presences_as_int_vars = new IntVar[num_tasks]; for (int t = 0; t < num_tasks; ++t) { presences_as_int_vars[t] = (IntVar)presences[t]; } model.Minimize(2 * makespan - 7 * LinearExpr.Sum(presences_as_int_vars)); // Creates a solver and solves the model. CpSolver solver = new CpSolver(); CpSolverStatus status = solver.Solve(model); if (status == CpSolverStatus.Optimal) { Console.WriteLine(String.Format("Optimal cost: {0}", solver.ObjectiveValue)); Console.WriteLine(String.Format("Makespan: {0}", solver.Value(makespan))); for (int t = 0; t < num_tasks; ++t) { if (solver.BooleanValue(presences[t])) { Console.WriteLine(String.Format("Task {0} starts at {1} with rank {2}", t, solver.Value(starts[t]), solver.Value(ranks[t]))); } else { Console.WriteLine( String.Format("Task {0} in not performed and ranked at {1}", t, solver.Value(ranks[t]))); } } } else { Console.WriteLine(String.Format("Solver exited with nonoptimal status: {0}", status)); } }
public static void Main(String[] args) { // Instantiate the data problem. // [START data] int[] Weights = { 48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36 }; int[] Values = { 10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25 }; int NumItems = Weights.Length; int[] allItems = Enumerable.Range(0, NumItems).ToArray(); int[] BinCapacities = { 100, 100, 100, 100, 100 }; int NumBins = BinCapacities.Length; int[] allBins = Enumerable.Range(0, NumBins).ToArray(); // [END data] // Model. // [START model] CpModel model = new CpModel(); // [END model] // Variables. // [START variables] IntVar[,] x = new IntVar[NumItems, NumBins]; foreach (int i in allItems) { foreach (int b in allBins) { x[i, b] = model.NewBoolVar($"x_{i}_{b}"); } } // [END variables] // Constraints. // [START constraints] // Each item is assigned to at most one bin. foreach (int i in allItems) { IntVar[] vars = new IntVar[NumBins]; foreach (int b in allBins) { vars[b] = x[i, b]; } model.Add(LinearExpr.Sum(vars) <= 1); } // The amount packed in each bin cannot exceed its capacity. foreach (int b in allBins) { IntVar[] vars = new IntVar[NumItems]; foreach (int i in allItems) { vars[i] = x[i, b]; } model.Add(LinearExpr.ScalProd(vars, Weights) <= BinCapacities[b]); } // [END constraints] // Objective. // [START objective] IntVar[] objectiveVars = new IntVar[NumItems * NumBins]; int[] objectiveValues = new int[NumItems * NumBins]; foreach (int i in allItems) { foreach (int b in allBins) { int k = i * NumBins + b; objectiveVars[k] = x[i, b]; objectiveValues[k] = Values[i]; } } model.Maximize(LinearExpr.ScalProd(objectiveVars, objectiveValues)); // [END objective] // Solve // [START solve] CpSolver solver = new CpSolver(); CpSolverStatus status = solver.Solve(model); // [END solve] // Print solution. // [START print_solution] // Check that the problem has a feasible solution. if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) { Console.WriteLine($"Total packed value: {solver.ObjectiveValue}"); double TotalWeight = 0.0; foreach (int b in allBins) { double BinWeight = 0.0; double BinValue = 0.0; Console.WriteLine($"Bin {b}"); foreach (int i in allItems) { if (solver.Value(x[i, b]) == 1) { Console.WriteLine($"Item {i} weight: {Weights[i]} values: {Values[i]}"); BinWeight += Weights[i]; BinValue += Values[i]; } } Console.WriteLine("Packed bin weight: " + BinWeight); Console.WriteLine("Packed bin value: " + BinValue); TotalWeight += BinWeight; } Console.WriteLine("Total packed weight: " + TotalWeight); } else { Console.WriteLine("No solution found."); } // [END print_solution] // [START statistics] Console.WriteLine("Statistics"); Console.WriteLine($" conflicts: {solver.NumConflicts()}"); Console.WriteLine($" branches : {solver.NumBranches()}"); Console.WriteLine($" wall time: {solver.WallTime()}s"); // [END statistics] }
static void Main(string[] args) { int numberGroups = 10; int numberItems = 100; int numberColors = 3; int minItemsOfSameColorPerGroup = 4; var allGroups = Enumerable.Range(0, numberGroups).ToArray(); var allItems = Enumerable.Range(0, numberItems).ToArray(); var allColors = Enumerable.Range(0, numberColors).ToArray(); var values = allItems.Select(i => 1 + i + (i * i / 200)).ToArray(); var colors = allItems.Select(i => i % numberColors).ToArray(); var sumOfValues = values.Sum(); var averageSumPerGroup = sumOfValues / numberGroups; var numItemsPerGroup = numberItems / numberGroups; var itemsPerColor = new Dictionary <int, List <int> >(); foreach (var color in allColors) { itemsPerColor[color] = new List <int>(); foreach (var item in allItems) { if (colors[item] == color) { itemsPerColor[color].Add(item); } } } Console.WriteLine($"Model has {numberItems}, {numberGroups} groups and {numberColors} colors"); Console.WriteLine($" Average sum per group = {averageSumPerGroup}"); var model = new CpModel(); var itemInGroup = new BoolVar[numberItems, numberGroups]; foreach (var item in allItems) { foreach (var @group in allGroups) { itemInGroup[item, @group] = model.NewBoolVar($"item {item} in group {@group}"); } } // Each group must have the same size. foreach (var @group in allGroups) { var itemsInGroup = allItems.Select(x => itemInGroup[x, @group]).ToArray(); model.AddLinearConstraint(LinearExpr.Sum(itemsInGroup), numItemsPerGroup, numItemsPerGroup); } //# One item must belong to exactly one group. foreach (var item in allItems) { var groupsForItem = allGroups.Select(x => itemInGroup[item, x]).ToArray(); model.Add(LinearExpr.Sum(groupsForItem) == 1); } // The deviation of the sum of each items in a group against the average. var e = model.NewIntVar(0, 550, "epsilon"); // Constrain the sum of values in one group around the average sum per // group. foreach (var @group in allGroups) { var itemValues = allItems.Select(x => itemInGroup[x, @group]).ToArray(); var sum = LinearExpr.WeightedSum(itemValues, values); model.Add(sum <= averageSumPerGroup + e); model.Add(sum >= averageSumPerGroup - e); } // colorInGroup variables. var colorInGroup = new BoolVar[numberColors, numberGroups]; foreach (var @group in allGroups) { foreach (var color in allColors) { colorInGroup[color, @group] = model.NewBoolVar($"color {color} is in group {@group}"); } } // Item is in a group implies its color is in that group. foreach (var item in allItems) { foreach (var @group in allGroups) { model.AddImplication(itemInGroup[item, @group], colorInGroup[colors[item], @group]); } } // If a color is in a group, it must contains at least // min_items_of_same_color_per_group items from that color. foreach (var color in allColors) { foreach (var @group in allGroups) { var literal = colorInGroup[color, @group]; var items = itemsPerColor[color].Select(x => itemInGroup[x, @group]).ToArray(); model.Add(LinearExpr.Sum(items) >= minItemsOfSameColorPerGroup).OnlyEnforceIf(literal); } } // Compute the maximum number of colors in a group. int maxColor = numItemsPerGroup / minItemsOfSameColorPerGroup; // Redundant constraint: The problem does not solve in reasonable time // without it. if (maxColor < numberColors) { foreach (var @group in allGroups) { var all = allColors.Select(x => colorInGroup[x, @group]).ToArray(); model.Add(LinearExpr.Sum(all) <= maxColor); } } // Minimize epsilon model.Minimize(e); var solver = new CpSolver(); var solutionPrinter = new SolutionPrinter(values, colors, allGroups, allItems, itemInGroup); var status = solver.Solve(model, solutionPrinter); }
public static void Main(String[] args) { // Data. // [START data] int[,] costs = { { 90, 76, 75, 70, 50, 74 }, { 35, 85, 55, 65, 48, 101 }, { 125, 95, 90, 105, 59, 120 }, { 45, 110, 95, 115, 104, 83 }, { 60, 105, 80, 75, 59, 62 }, { 45, 65, 110, 95, 47, 31 }, { 38, 51, 107, 41, 69, 99 }, { 47, 85, 57, 71, 92, 77 }, { 39, 63, 97, 49, 118, 56 }, { 47, 101, 71, 60, 88, 109 }, { 17, 39, 103, 64, 61, 92 }, { 101, 45, 83, 59, 92, 27 }, }; int numWorkers = costs.GetLength(0); int numTasks = costs.GetLength(1); int[] allWorkers = Enumerable.Range(0, numWorkers).ToArray(); int[] allTasks = Enumerable.Range(0, numTasks).ToArray(); // [END data] // Allowed groups of workers: // [START allowed_groups] long[,] group1 = { { 0, 0, 1, 1 }, // Workers 2, 3 { 0, 1, 0, 1 }, // Workers 1, 3 { 0, 1, 1, 0 }, // Workers 1, 2 { 1, 1, 0, 0 }, // Workers 0, 1 { 1, 0, 1, 0 }, // Workers 0, 2 }; long[,] group2 = { { 0, 0, 1, 1 }, // Workers 6, 7 { 0, 1, 0, 1 }, // Workers 5, 7 { 0, 1, 1, 0 }, // Workers 5, 6 { 1, 1, 0, 0 }, // Workers 4, 5 { 1, 0, 0, 1 }, // Workers 4, 7 }; long[,] group3 = { { 0, 0, 1, 1 }, // Workers 10, 11 { 0, 1, 0, 1 }, // Workers 9, 11 { 0, 1, 1, 0 }, // Workers 9, 10 { 1, 0, 1, 0 }, // Workers 8, 10 { 1, 0, 0, 1 }, // Workers 8, 11 }; // [END allowed_groups] // Model. // [START model] CpModel model = new CpModel(); // [END model] // Variables. // [START variables] BoolVar[,] x = new BoolVar[numWorkers, numTasks]; // Variables in a 1-dim array. foreach (int worker in allWorkers) { foreach (int task in allTasks) { x[worker, task] = model.NewBoolVar($"x[{worker},{task}]"); } } // [END variables] // Constraints // [START constraints] // Each worker is assigned to at most one task. foreach (int worker in allWorkers) { List <ILiteral> tasks = new List <ILiteral>(); foreach (int task in allTasks) { tasks.Add(x[worker, task]); } model.AddAtMostOne(tasks); } // Each task is assigned to exactly one worker. foreach (int task in allTasks) { List <ILiteral> workers = new List <ILiteral>(); foreach (int worker in allWorkers) { workers.Add(x[worker, task]); } model.AddExactlyOne(workers); } // [END constraints] // [START assignments] // Create variables for each worker, indicating whether they work on some task. BoolVar[] work = new BoolVar[numWorkers]; foreach (int worker in allWorkers) { work[worker] = model.NewBoolVar($"work[{worker}]"); } foreach (int worker in allWorkers) { List <ILiteral> tasks = new List <ILiteral>(); foreach (int task in allTasks) { tasks.Add(x[worker, task]); } model.Add(work[worker] == LinearExpr.Sum(tasks)); } // Define the allowed groups of worders model.AddAllowedAssignments(new IntVar[] { work[0], work[1], work[2], work[3] }).AddTuples(group1); model.AddAllowedAssignments(new IntVar[] { work[4], work[5], work[6], work[7] }).AddTuples(group2); model.AddAllowedAssignments(new IntVar[] { work[8], work[9], work[10], work[11] }).AddTuples(group3); // [END assignments] // Objective // [START objective] LinearExprBuilder obj = LinearExpr.NewBuilder(); foreach (int worker in allWorkers) { foreach (int task in allTasks) { obj.AddTerm(x[worker, task], costs[worker, task]); } } model.Minimize(obj); // [END objective] // Solve // [START solve] CpSolver solver = new CpSolver(); CpSolverStatus status = solver.Solve(model); Console.WriteLine($"Solve status: {status}"); // [END solve] // Print solution. // [START print_solution] // Check that the problem has a feasible solution. if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) { Console.WriteLine($"Total cost: {solver.ObjectiveValue}\n"); foreach (int worker in allWorkers) { foreach (int task in allTasks) { if (solver.Value(x[worker, task]) > 0.5) { Console.WriteLine($"Worker {worker} assigned to task {task}. " + $"Cost: {costs[worker, task]}"); } } } } else { Console.WriteLine("No solution found."); } // [END print_solution] Console.WriteLine("Statistics"); Console.WriteLine($" - conflicts : {solver.NumConflicts()}"); Console.WriteLine($" - branches : {solver.NumBranches()}"); Console.WriteLine($" - wall time : {solver.WallTime()}s"); }
// [END solution_printer] public static void Main(String[] args) { // [START data] const int numNurses = 4; const int numDays = 3; const int numShifts = 3; int[] allNurses = Enumerable.Range(0, numNurses).ToArray(); int[] allDays = Enumerable.Range(0, numDays).ToArray(); int[] allShifts = Enumerable.Range(0, numShifts).ToArray(); // [END data] // Creates the model. // [START model] CpModel model = new CpModel(); // [END model] // Creates shift variables. // shifts[(n, d, s)]: nurse 'n' works shift 's' on day 'd'. // [START variables] Dictionary <Tuple <int, int, int>, IntVar> shifts = new Dictionary <Tuple <int, int, int>, IntVar>(); foreach (int n in allNurses) { foreach (int d in allDays) { foreach (int s in allShifts) { shifts.Add(Tuple.Create(n, d, s), model.NewBoolVar($"shifts_n{n}d{d}s{s}")); } } } // [END variables] // Each shift is assigned to exactly one nurse in the schedule period. // [START exactly_one_nurse] foreach (int d in allDays) { foreach (int s in allShifts) { IntVar[] x = new IntVar[numNurses]; foreach (int n in allNurses) { var key = Tuple.Create(n, d, s); x[n] = shifts[key]; } model.Add(LinearExpr.Sum(x) == 1); } } // [END exactly_one_nurse] // Each nurse works at most one shift per day. // [START at_most_one_shift] foreach (int n in allNurses) { foreach (int d in allDays) { IntVar[] x = new IntVar[numShifts]; foreach (int s in allShifts) { var key = Tuple.Create(n, d, s); x[s] = shifts[key]; } model.Add(LinearExpr.Sum(x) <= 1); } } // [END at_most_one_shift] // [START assign_nurses_evenly] // Try to distribute the shifts evenly, so that each nurse works // minShiftsPerNurse shifts. If this is not possible, because the total // number of shifts is not divisible by the number of nurses, some nurses will // be assigned one more shift. int minShiftsPerNurse = (numShifts * numDays) / numNurses; int maxShiftsPerNurse; if ((numShifts * numDays) % numNurses == 0) { maxShiftsPerNurse = minShiftsPerNurse; } else { maxShiftsPerNurse = minShiftsPerNurse + 1; } foreach (int n in allNurses) { IntVar[] numShiftsWorked = new IntVar[numDays * numShifts]; foreach (int d in allDays) { foreach (int s in allShifts) { var key = Tuple.Create(n, d, s); numShiftsWorked[d * numShifts + s] = shifts[key]; } } model.AddLinearConstraint(LinearExpr.Sum(numShiftsWorked), minShiftsPerNurse, maxShiftsPerNurse); } // [END assign_nurses_evenly] // [START parameters] CpSolver solver = new CpSolver(); solver.StringParameters += "linearization_level:0 "; // Tell the solver to enumerate all solutions. solver.StringParameters += "enumerate_all_solutions:true "; // [END parameters] // Display the first five solutions. // [START solution_printer_instantiate] const int solutionLimit = 5; SolutionPrinter cb = new SolutionPrinter(allNurses, allDays, allShifts, shifts, solutionLimit); // [END solution_printer_instantiate] // Solve // [START solve] CpSolverStatus status = solver.Solve(model, cb); Console.WriteLine($"Solve status: {status}"); // [END solve] // [START statistics] Console.WriteLine("Statistics"); Console.WriteLine($" conflicts: {solver.NumConflicts()}"); Console.WriteLine($" branches : {solver.NumBranches()}"); Console.WriteLine($" wall time: {solver.WallTime()}s"); // [END statistics] }
private static void Witi_SOLUTION() { Solver solver = Solver.CreateSolver("SimpleMipProgram", "CBC_MIXED_INTEGER_PROGRAMMING"); string[] text = File.ReadAllLines(@"test.txt"); //SPD/SPD/BIN/DEBUG string dataStart = text[0]; string[] result = dataStart.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); HowManyTasks = Convert.ToInt32(result[0]); List <WiTi> witiTasks = new List <WiTi>(); for (int i = 1; i <= HowManyTasks; i++) { result = text[i].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); witiTasks.Add(new WiTi { P = Convert.ToInt32(result[0]), W = Convert.ToInt32(result[1]), D = Convert.ToInt32(result[2]) }); } int Time = 0; int variablesMaxValue = 0; foreach (var element in witiTasks) { Time += element.P; int kara = 0; if (element.D < Time) { kara = element.W * (Time - element.D); } variablesMaxValue += kara; Console.WriteLine(variablesMaxValue + " | " + Time); } var alfas = solver.MakeIntVarMatrix(witiTasks.Count, witiTasks.Count, 0, 1); var starts = solver.MakeIntVarArray(witiTasks.Count, 0, Time); var ends = solver.MakeIntVarArray(witiTasks.Count, 0, Time); var cmax = solver.MakeIntVar(0, variablesMaxValue, "cmax"); //int helpTime = 0; var suma = new LinearExpr(); for (int i = 0; i < witiTasks.Count; i++) { solver.Add(ends[i] >= starts[i] + witiTasks[i].P); solver.Add(ends[i] >= witiTasks[i].D); solver.Add(cmax >= witiTasks[i].W * (ends[i] - witiTasks[i].D) + suma); suma += witiTasks[i].W * (ends[i] - witiTasks[i].D); } for (int i = 0; i < witiTasks.Count; i++) { for (int j = i + 1; j < witiTasks.Count; j++) { solver.Add(starts[i] + witiTasks[i].P <= starts[j] + alfas[i, j] * variablesMaxValue); solver.Add(starts[j] + witiTasks[j].P <= starts[i] + alfas[j, i] * variablesMaxValue); solver.Add(alfas[i, j] + alfas[j, i] == 1); } } solver.Minimize(cmax); Solver.ResultStatus resultStatus = solver.Solve(); if (resultStatus != Solver.ResultStatus.OPTIMAL) { Console.WriteLine("Solve nie znalazl optymala"); } else { Console.WriteLine("Obiect_value = " + solver.Objective().Value()); } }
public static LinearExpr Sum <TSource>(this IEnumerable <TSource> source, Func <TSource, LinearExpr> selector) { var zeroExpr = new LinearExpr(); return(source.Aggregate(zeroExpr, (current, entry) => current + selector(entry))); }
/** <summary>The size expression of the interval</summary> */ public LinearExpr SizeExpr() { return(LinearExpr.RebuildLinearExprFromLinearExpressionProto(interval_.Size, model_)); }
/** <summary>The end expression of the interval</summary> */ public LinearExpr EndExpr() { return(LinearExpr.RebuildLinearExprFromLinearExpressionProto(interval_.End, model_)); }
static void Main() { // Data. int bin_capacity = 100; int slack_capacity = 20; int num_bins = 5; int[,] items = new int[, ] { { 20, 6 }, { 15, 6 }, { 30, 4 }, { 45, 3 } }; int num_items = items.GetLength(0); // Model. CpModel model = new CpModel(); // Main variables. IntVar[,] x = new IntVar[num_items, num_bins]; for (int i = 0; i < num_items; ++i) { int num_copies = items[i, 1]; for (int b = 0; b < num_bins; ++b) { x[i, b] = model.NewIntVar(0, num_copies, String.Format("x_{0}_{1}", i, b)); } } // Load variables. IntVar[] load = new IntVar[num_bins]; for (int b = 0; b < num_bins; ++b) { load[b] = model.NewIntVar(0, bin_capacity, String.Format("load_{0}", b)); } // Slack variables. IntVar[] slacks = new IntVar[num_bins]; for (int b = 0; b < num_bins; ++b) { slacks[b] = model.NewBoolVar(String.Format("slack_{0}", b)); } // Links load and x. int[] sizes = new int[num_items]; for (int i = 0; i < num_items; ++i) { sizes[i] = items[i, 0]; } for (int b = 0; b < num_bins; ++b) { IntVar[] tmp = new IntVar[num_items]; for (int i = 0; i < num_items; ++i) { tmp[i] = x[i, b]; } model.Add(load[b] == LinearExpr.ScalProd(tmp, sizes)); } // Place all items. for (int i = 0; i < num_items; ++i) { IntVar[] tmp = new IntVar[num_bins]; for (int b = 0; b < num_bins; ++b) { tmp[b] = x[i, b]; } model.Add(LinearExpr.Sum(tmp) == items[i, 1]); } // Links load and slack. int safe_capacity = bin_capacity - slack_capacity; for (int b = 0; b < num_bins; ++b) { // slack[b] => load[b] <= safe_capacity. model.Add(load[b] <= safe_capacity).OnlyEnforceIf(slacks[b]); // not(slack[b]) => load[b] > safe_capacity. model.Add(load[b] > safe_capacity).OnlyEnforceIf(slacks[b].Not()); } // Maximize sum of slacks. model.Maximize(LinearExpr.Sum(slacks)); // Solves and prints out the solution. CpSolver solver = new CpSolver(); CpSolverStatus status = solver.Solve(model); Console.WriteLine(String.Format("Solve status: {0}", status)); if (status == CpSolverStatus.Optimal) { Console.WriteLine(String.Format("Optimal objective value: {0}", solver.ObjectiveValue)); for (int b = 0; b < num_bins; ++b) { Console.WriteLine(String.Format("load_{0} = {1}", b, solver.Value(load[b]))); for (int i = 0; i < num_items; ++i) { Console.WriteLine(string.Format(" item_{0}_{1} = {2}", i, b, solver.Value(x[i, b]))); } } } Console.WriteLine("Statistics"); Console.WriteLine(String.Format(" - conflicts : {0}", solver.NumConflicts())); Console.WriteLine(String.Format(" - branches : {0}", solver.NumBranches())); Console.WriteLine(String.Format(" - wall time : {0} s", solver.WallTime())); }
public static void Main(String[] args) { // Data. // [START data] int[,] costs = { { 90, 76, 75, 70, 50, 74, 12, 68 }, { 35, 85, 55, 65, 48, 101, 70, 83 }, { 125, 95, 90, 105, 59, 120, 36, 73 }, { 45, 110, 95, 115, 104, 83, 37, 71 }, { 60, 105, 80, 75, 59, 62, 93, 88 }, { 45, 65, 110, 95, 47, 31, 81, 34 }, { 38, 51, 107, 41, 69, 99, 115, 48 }, { 47, 85, 57, 71, 92, 77, 109, 36 }, { 39, 63, 97, 49, 118, 56, 92, 61 }, { 47, 101, 71, 60, 88, 109, 52, 90 }, }; int numWorkers = costs.GetLength(0); int numTasks = costs.GetLength(1); int[] allWorkers = Enumerable.Range(0, numWorkers).ToArray(); int[] allTasks = Enumerable.Range(0, numTasks).ToArray(); int[] taskSizes = { 10, 7, 3, 12, 15, 4, 11, 5 }; // Maximum total of task sizes for any worker int totalSizeMax = 15; // [END data] // Model. // [START model] CpModel model = new CpModel(); // [END model] // Variables. // [START variables] BoolVar[,] x = new BoolVar[numWorkers, numTasks]; foreach (int worker in allWorkers) { foreach (int task in allTasks) { x[worker, task] = model.NewBoolVar($"x[{worker},{task}]"); } } // [END variables] // Constraints // [START constraints] // Each worker is assigned to at most max task size. foreach (int worker in allWorkers) { BoolVar[] vars = new BoolVar[numTasks]; foreach (int task in allTasks) { vars[task] = x[worker, task]; } model.Add(LinearExpr.WeightedSum(vars, taskSizes) <= totalSizeMax); } // Each task is assigned to exactly one worker. foreach (int task in allTasks) { List <ILiteral> workers = new List <ILiteral>(); foreach (int worker in allWorkers) { workers.Add(x[worker, task]); } model.AddExactlyOne(workers); } // [END constraints] // Objective // [START objective] LinearExprBuilder obj = LinearExpr.NewBuilder(); foreach (int worker in allWorkers) { foreach (int task in allTasks) { obj.AddTerm(x[worker, task], costs[worker, task]); } } model.Minimize(obj); // [END objective] // Solve // [START solve] CpSolver solver = new CpSolver(); CpSolverStatus status = solver.Solve(model); Console.WriteLine($"Solve status: {status}"); // [END solve] // Print solution. // [START print_solution] // Check that the problem has a feasible solution. if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) { Console.WriteLine($"Total cost: {solver.ObjectiveValue}\n"); foreach (int worker in allWorkers) { foreach (int task in allTasks) { if (solver.Value(x[worker, task]) > 0.5) { Console.WriteLine($"Worker {worker} assigned to task {task}. " + $"Cost: {costs[worker, task]}"); } } } } else { Console.WriteLine("No solution found."); } // [END print_solution] Console.WriteLine("Statistics"); Console.WriteLine($" - conflicts : {solver.NumConflicts()}"); Console.WriteLine($" - branches : {solver.NumBranches()}"); Console.WriteLine($" - wall time : {solver.WallTime()}s"); }
private void ATSP_Flow() { var M = solverData.NumberOfVisits; foreach (var santa in Enumerable.Range(0, solverData.NumberOfSantas)) { var santaWayFlow = solverData.Variables.SantaWayFlow[santa]; var santaWayHasFlow = solverData.Variables.SantaWayHasFlow[santa]; var santaUsesWay = solverData.Variables.SantaUsesWay[santa]; var numberOfVisitsInCluster = NumberOfSantaVisit(santa); // flow for start location if santa is in use foreach (var destination in Enumerable.Range(0, solverData.NumberOfVisits)) { Solver.Add(santaWayFlow[0, destination] == santaUsesWay[0, destination] * M); } //flow only possible if santa uses way foreach (var source in Enumerable.Range(0, solverData.NumberOfVisits)) { foreach (var destination in Enumerable.Range(0, solverData.NumberOfVisits)) { Solver.Add(santaWayFlow[source, destination] <= santaUsesWay[source, destination] * M); //ClusteringILPSolver.Add(santaWayFlow[source, destination] >= santaUsesWay[source, destination]); } } // strengthen formulasation for (var source = 1; source < solverData.NumberOfVisits; source++) { for (var destination = 1; source < solverData.NumberOfVisits; source++) { Solver.Add(santaWayFlow[source, destination] <= numberOfVisitsInCluster); } } // flow only possible if neighbours have flow for (var source = 1; source < solverData.NumberOfVisits; source++) { var sumFlowNeightbours = new LinearExpr(); foreach (var incomingNeighbours in Enumerable.Range(0, solverData.NumberOfVisits)) { sumFlowNeightbours += santaWayFlow[incomingNeighbours, source]; } // node has value of incoming flow foreach (var destination in Enumerable.Range(0, solverData.NumberOfVisits)) { Solver.Add(santaWayFlow[source, destination] <= sumFlowNeightbours - 1 * solverData.Variables.SantaVisit[santa, source]); } } // hasFlow Variable foreach (var source in Enumerable.Range(0, solverData.NumberOfVisits)) { foreach (var destination in Enumerable.Range(0, solverData.NumberOfVisits)) { Solver.Add(santaWayHasFlow[source, destination] <= santaWayFlow[source, destination]); //ClusteringILPSolver.Add(santaWayFlow[source, destination] <= santaWayHasFlow[source, destination] * M); // this constraint makes it terrible slow } } var sumOfFlow = new LinearExpr(); var sumOfEdges = new LinearExpr(); foreach (var source in Enumerable.Range(0, solverData.NumberOfVisits)) { foreach (var destination in Enumerable.Range(0, solverData.NumberOfVisits)) { sumOfFlow += santaWayHasFlow[source, destination]; sumOfEdges += santaUsesWay[source, destination]; } } Solver.Add(sumOfFlow == sumOfEdges); } }
static void Main() { // Data. int num_nurses = 4; // Nurse assigned to shift 0 means not working that day. int num_shifts = 4; int num_days = 7; var all_nurses = Enumerable.Range(0, num_nurses); var all_shifts = Enumerable.Range(0, num_shifts); var all_working_shifts = Enumerable.Range(1, num_shifts - 1); var all_days = Enumerable.Range(0, num_days); // Creates the model. CpModel model = new CpModel(); // Creates shift variables. // shift[n, d, s]: nurse "n" works shift "s" on day "d". IntVar[,,] shift = new IntVar[num_nurses, num_days, num_shifts]; foreach (int n in all_nurses) { foreach (int d in all_days) { foreach (int s in all_shifts) { shift[n, d, s] = model.NewBoolVar(String.Format("shift_n{0}d{1}s{2}", n, d, s)); } } } // Makes assignments different on each day, that is each shift is // assigned at most one nurse. As we have the same number of // nurses and shifts, then each day, each shift is assigned to // exactly one nurse. foreach (int d in all_days) { foreach (int s in all_shifts) { IntVar[] tmp = new IntVar[num_nurses]; foreach (int n in all_nurses) { tmp[n] = shift[n, d, s]; } model.Add(LinearExpr.Sum(tmp) == 1); } } // Nurses do 1 shift per day. foreach (int n in all_nurses) { foreach (int d in all_days) { IntVar[] tmp = new IntVar[num_shifts]; foreach (int s in all_shifts) { tmp[s] = shift[n, d, s]; } model.Add(LinearExpr.Sum(tmp) == 1); } } // Each nurse works 5 or 6 days in a week. // That is each nurse works shift 0 at most 2 times. foreach (int n in all_nurses) { IntVar[] tmp = new IntVar[num_days]; foreach (int d in all_days) { tmp[d] = shift[n, d, 0]; } model.AddLinearConstraint(LinearExpr.Sum(tmp), 1, 2); } // works_shift[(n, s)] is 1 if nurse n works shift s at least one day in // the week. IntVar[,] works_shift = new IntVar[num_nurses, num_shifts]; foreach (int n in all_nurses) { foreach (int s in all_shifts) { works_shift[n, s] = model.NewBoolVar(String.Format("works_shift_n{0}s{1}", n, s)); IntVar[] tmp = new IntVar[num_days]; foreach (int d in all_days) { tmp[d] = shift[n, d, s]; } model.AddMaxEquality(works_shift[n, s], tmp); } } // For each working shift, at most 2 nurses are assigned to that shift // during the week. foreach (int s in all_working_shifts) { IntVar[] tmp = new IntVar[num_nurses]; foreach (int n in all_nurses) { tmp[n] = works_shift[n, s]; } model.Add(LinearExpr.Sum(tmp) <= 2); } // If a nurse works shifts 2 or 3 on, she must also work that // shift the previous day or the following day. This means that // on a given day and shift, either she does not work that shift // on that day, or she works that shift on the day before, or the // day after. foreach (int n in all_nurses) { for (int s = 2; s <= 3; ++s) { foreach (int d in all_days) { int yesterday = d == 0 ? num_days - 1 : d - 1; int tomorrow = d == num_days - 1 ? 0 : d + 1; model.AddBoolOr(new ILiteral[] { shift[n, yesterday, s], shift[n, d, s].Not(), shift[n, tomorrow, s] }); } } } // Creates the solver and solve. CpSolver solver = new CpSolver(); // Display a few solutions picked at random. HashSet <int> to_print = new HashSet <int>(); to_print.Add(859); to_print.Add(2034); to_print.Add(5091); to_print.Add(7003); NurseSolutionObserver cb = new NurseSolutionObserver( shift, num_nurses, num_days, num_shifts, to_print); CpSolverStatus status = solver.SearchAllSolutions(model, cb); // Statistics. Console.WriteLine("Statistics"); Console.WriteLine(String.Format(" - solve status : {0}", status)); Console.WriteLine(" - conflicts : " + solver.NumConflicts()); Console.WriteLine(" - branches : " + solver.NumBranches()); Console.WriteLine(" - wall time : " + solver.WallTime() + " ms"); Console.WriteLine(" - #solutions : " + cb.SolutionCount()); }
public static void Main(String[] args) { // Data. // [START data_model] int[,] costs = { { 90, 80, 75, 70 }, { 35, 85, 55, 65 }, { 125, 95, 90, 95 }, { 45, 110, 95, 115 }, { 50, 100, 90, 100 }, }; int numWorkers = costs.GetLength(0); int numTasks = costs.GetLength(1); // [END data_model] // Model. // [START model] CpModel model = new CpModel(); // [END model] // Variables. // [START variables] IntVar[,] x = new IntVar[numWorkers, numTasks]; // Variables in a 1-dim array. IntVar[] xFlat = new IntVar[numWorkers * numTasks]; int[] costsFlat = new int[numWorkers * numTasks]; for (int i = 0; i < numWorkers; ++i) { for (int j = 0; j < numTasks; ++j) { x[i, j] = model.NewIntVar(0, 1, $"worker_{i}_task_{j}"); int k = i * numTasks + j; xFlat[k] = x[i, j]; costsFlat[k] = costs[i, j]; } } // [END variables] // Constraints // [START constraints] // Each worker is assigned to at most one task. for (int i = 0; i < numWorkers; ++i) { IntVar[] vars = new IntVar[numTasks]; for (int j = 0; j < numTasks; ++j) { vars[j] = x[i, j]; } model.Add(LinearExpr.Sum(vars) <= 1); } // Each task is assigned to exactly one worker. for (int j = 0; j < numTasks; ++j) { IntVar[] vars = new IntVar[numWorkers]; for (int i = 0; i < numWorkers; ++i) { vars[i] = x[i, j]; } model.Add(LinearExpr.Sum(vars) == 1); } // [END constraints] // Objective // [START objective] model.Minimize(LinearExpr.ScalProd(xFlat, costsFlat)); // [END objective] // Solve // [START solve] CpSolver solver = new CpSolver(); CpSolverStatus status = solver.Solve(model); Console.WriteLine($"Solve status: {status}"); // [END solve] // Print solution. // [START print_solution] // Check that the problem has a feasible solution. if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) { Console.WriteLine($"Total cost: {solver.ObjectiveValue}\n"); for (int i = 0; i < numWorkers; ++i) { for (int j = 0; j < numTasks; ++j) { if (solver.Value(x[i, j]) > 0.5) { Console.WriteLine($"Worker {i} assigned to task {j}. Cost: {costs[i, j]}"); } } } } else { Console.WriteLine("No solution found."); } // [END print_solution] Console.WriteLine("Statistics"); Console.WriteLine($" - conflicts : {solver.NumConflicts()}"); Console.WriteLine($" - branches : {solver.NumBranches()}"); Console.WriteLine($" - wall time : {solver.WallTime()}s"); }
static void Solve(int first_slot) { Console.WriteLine("----------------------------------------------------"); // the slots each speaker is available int[][] speaker_availability = { new int[] { 1, 3, 4, 6, 7, 10, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 54, 55, 56, 57, 58, 59 }, new int[] { 1, 2, 7, 8, 10, 11, 16, 17, 18, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 52, 53, 54, 55, 56, 57, 58, 59, 60 }, new int[] { 5, 6, 7, 10, 12, 14, 16, 17, 21, 22, 23, 24, 33, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 51, 53, 55, 56, 57, 58, 59 }, new int[] { 1, 2, 3, 4, 5, 6, 7, 11, 13, 14, 15, 16, 20, 24, 25, 33, 34, 35, 37, 38, 39, 40, 41, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 60 }, new int[] { 4, 7, 8, 9, 16, 17, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 49, 50, 51, 53, 55, 56, 57, 58, 59, 60 }, new int[] { 4, 7, 9, 11, 12, 13, 14, 15, 16, 17, 18, 22, 23, 24, 33, 34, 35, 36, 38, 39, 42, 44, 46, 48, 49, 51, 53, 54, 55, 56, 57 }, new int[] { 1, 2, 3, 4, 5, 6, 7, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 54, 55, 56, 57, 58,59, 60 }, new int[] { 1, 3, 11, 14, 15, 16, 17, 21, 22, 23, 24, 25, 33, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45, 47, 48, 49, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 }, new int[] { 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 13, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 50, 51, 52, 53, 54, 55, 56, 57, 59, 60 }, new int[] { 24, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 45, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 }, new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 52, 53, 55, 56, 57, 58, 59, 60 }, new int[] { 3, 4, 5, 6, 13, 15, 16, 17, 18, 19, 21, 22, 24, 25, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45, 47, 52, 53, 55, 57, 58, 59, 60 }, new int[] { 4, 5, 6, 8, 11, 12, 13, 14, 17, 19, 20, 22, 23, 24, 25, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 47, 48, 49, 50, 51, 52, 55, 56, 57 }, new int[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 }, new int[] { 12, 25, 33, 35, 36, 37, 39, 41, 42, 43, 48, 51, 52, 53, 54, 57, 59,60 }, new int[] { 4, 8, 16, 17, 19, 23, 25, 33, 34, 35, 37, 41, 44, 45, 47, 48, 49,50 }, new int[] { 3, 23, 24, 25, 33, 35, 36, 37, 38, 39, 40, 42, 43, 44, 49, 50, 53, 54, 55, 56, 57, 58,60 }, new int[] { 7, 13, 19, 20, 22, 23, 24, 25, 33, 34, 35, 38, 40, 41, 42, 44, 45, 46, 47, 48, 49, 52, 53, 54, 58, 59, 60 } }; // how long each talk lasts for each speaker int[] durations = { 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; int sum_of_durations = durations.Sum(); int number_of_speakers = durations.Length; // calculate the total number of slots (maximum in the availability array) // (and add the max durations) int last_slot = (from s in Enumerable.Range(0, number_of_speakers) select speaker_availability[s].Max()).Max(); Console.WriteLine($"Scheduling {number_of_speakers} speakers, for a total of " + $"{sum_of_durations} slots, during [{first_slot}..{last_slot}]"); CpModel model = new CpModel(); // We store the possible entries (var, start) for all talks filtered // from the duration and the speaker availability. List <Entry>[] entries = new List <Entry> [number_of_speakers]; for (int speaker = 0; speaker < number_of_speakers; ++speaker) { entries[speaker] = new List <Entry>(); } List <IntVar>[] contributions_per_slot = new List <IntVar> [last_slot + 1]; for (int slot = 1; slot <= last_slot; ++slot) { contributions_per_slot[slot] = new List <IntVar>(); } for (int speaker = 0; speaker < number_of_speakers; ++speaker) { List <IntVar> all_vars = new List <IntVar>(); int duration = durations[speaker]; // Let's filter the possible starts. int availability = speaker_availability[speaker].Length; for (int index = 0; index < availability; ++index) { bool ok = true; int slot = speaker_availability[speaker][index]; if (slot < first_slot) { continue; } for (int offset = 1; offset < duration; ++offset) { if (index + offset >= availability || speaker_availability[speaker][index + offset] != slot + offset) { // discontinuity. ok = false; break; } } if (ok) { IntVar var = model.NewBoolVar("speaker " + (speaker + 1) + " starts at " + slot); entries[speaker].Add(new Entry(var, slot)); all_vars.Add(var); for (int offset = 0; offset < duration; ++offset) { contributions_per_slot[slot + offset].Add(var); } } } model.Add(LinearExpr.Sum(all_vars) == 1); } // Force the schedule to be consistent. for (int slot = first_slot; slot <= last_slot; ++slot) { model.Add(LinearExpr.Sum(contributions_per_slot[slot]) <= 1); } // Creates last_slot. IntVar last_slot_var = model.NewIntVar(first_slot + sum_of_durations - 1, last_slot, "last_slot"); for (int speaker = 0; speaker < number_of_speakers; speaker++) { int duration = durations[speaker]; foreach (Entry e in entries[speaker]) { model.Add(last_slot_var >= e.start + duration - 1).OnlyEnforceIf(e.var); } } model.Minimize(last_slot_var); // Creates the solver and solve. CpSolver solver = new CpSolver(); solver.StringParameters = "num_search_workers:8"; CpSolverStatus status = solver.Solve(model); if (status == CpSolverStatus.Optimal) { Console.WriteLine("\nLast used slot: " + solver.Value(last_slot_var)); Console.WriteLine("Speakers (start..end):"); for (int speaker = 0; speaker < number_of_speakers; speaker++) { foreach (Entry e in entries[speaker]) { if (solver.BooleanValue(e.var)) { Console.WriteLine(" - speaker {0,2}: {1,2}..{2,2}", (speaker + 1), e.start, (e.start + durations[speaker] - 1)); } } } } // Statistics. Console.WriteLine(solver.ResponseStats()); }
public static void Main(String[] args) { // Data. // [START data] int[,] costs = { { 90, 76, 75, 70 }, { 35, 85, 55, 65 }, { 125, 95, 90, 105 }, { 45, 110, 95, 115 }, { 60, 105, 80, 75 }, { 45, 65, 110, 95 }, }; int numWorkers = costs.GetLength(0); int numTasks = costs.GetLength(1); int[] allWorkers = Enumerable.Range(0, numWorkers).ToArray(); int[] allTasks = Enumerable.Range(0, numTasks).ToArray(); int[] team1 = { 0, 2, 4 }; int[] team2 = { 1, 3, 5 }; // Maximum total of tasks for any team int teamMax = 2; // [END data] // Model. // [START model] CpModel model = new CpModel(); // [END model] // Variables. // [START variables] BoolVar[,] x = new BoolVar[numWorkers, numTasks]; foreach (int worker in allWorkers) { foreach (int task in allTasks) { x[worker, task] = model.NewBoolVar($"x[{worker},{task}]"); } } // [END variables] // Constraints // [START constraints] // Each worker is assigned to at most one task. foreach (int worker in allWorkers) { List <ILiteral> tasks = new List <ILiteral>(); foreach (int task in allTasks) { tasks.Add(x[worker, task]); } model.AddAtMostOne(tasks); } // Each task is assigned to exactly one worker. foreach (int task in allTasks) { List <ILiteral> workers = new List <ILiteral>(); foreach (int worker in allWorkers) { workers.Add(x[worker, task]); } model.AddExactlyOne(workers); } // Each team takes at most two tasks. List <IntVar> team1Tasks = new List <IntVar>(); foreach (int worker in team1) { foreach (int task in allTasks) { team1Tasks.Add(x[worker, task]); } } model.Add(LinearExpr.Sum(team1Tasks.ToArray()) <= teamMax); List <IntVar> team2Tasks = new List <IntVar>(); foreach (int worker in team2) { foreach (int task in allTasks) { team2Tasks.Add(x[worker, task]); } } model.Add(LinearExpr.Sum(team2Tasks.ToArray()) <= teamMax); // [END constraints] // Objective // [START objective] LinearExprBuilder obj = LinearExpr.NewBuilder(); foreach (int worker in allWorkers) { foreach (int task in allTasks) { obj.AddTerm(x[worker, task], costs[worker, task]); } } model.Minimize(obj); // [END objective] // Solve // [START solve] CpSolver solver = new CpSolver(); CpSolverStatus status = solver.Solve(model); Console.WriteLine($"Solve status: {status}"); // [END solve] // Print solution. // [START print_solution] // Check that the problem has a feasible solution. if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) { Console.WriteLine($"Total cost: {solver.ObjectiveValue}\n"); foreach (int worker in allWorkers) { foreach (int task in allTasks) { if (solver.Value(x[worker, task]) > 0.5) { Console.WriteLine($"Worker {worker} assigned to task {task}. " + $"Cost: {costs[worker, task]}"); } } } } else { Console.WriteLine("No solution found."); } // [END print_solution] Console.WriteLine("Statistics"); Console.WriteLine($" - conflicts : {solver.NumConflicts()}"); Console.WriteLine($" - branches : {solver.NumBranches()}"); Console.WriteLine($" - wall time : {solver.WallTime()}s"); }