diff --git a/examples/dotnet/gate_scheduling_sat.cs b/examples/dotnet/GateSchedulingSat.cs similarity index 98% rename from examples/dotnet/gate_scheduling_sat.cs rename to examples/dotnet/GateSchedulingSat.cs index 67d03c3bbf..3f7d90190e 100644 --- a/examples/dotnet/gate_scheduling_sat.cs +++ b/examples/dotnet/GateSchedulingSat.cs @@ -28,7 +28,7 @@ using Google.OrTools.Sat; public class GateSchedulingSat { - static void Solve() + static void Main() { CpModel model = new CpModel(); @@ -146,8 +146,4 @@ public class GateSchedulingSat Console.WriteLine(" - branches : " + solver.NumBranches()); Console.WriteLine(" - wall time : " + solver.WallTime() + " ms"); } - - static void Main() { - Solve(); - } } diff --git a/examples/dotnet/slow_scheduling.csproj b/examples/dotnet/GateSchedulingSat.csproj similarity index 93% rename from examples/dotnet/slow_scheduling.csproj rename to examples/dotnet/GateSchedulingSat.csproj index d1b124bdcf..da957e6223 100644 --- a/examples/dotnet/slow_scheduling.csproj +++ b/examples/dotnet/GateSchedulingSat.csproj @@ -14,7 +14,7 @@ - + diff --git a/examples/dotnet/JobshopSat.cs b/examples/dotnet/JobshopSat.cs new file mode 100644 index 0000000000..c8f65c3f50 --- /dev/null +++ b/examples/dotnet/JobshopSat.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + + +class Task +{ + public Task(int taskId, int jobId, int duration, int machine) + { + TaskId = taskId; + JobId = jobId; + Duration = duration; + Machine = machine; + Name = "T" + taskId + "J" + jobId + "M" + + machine + "D" + duration; + } + public int TaskId { get; set; } + public int JobId { get; set; } + public int Machine { get; set; } + public int Duration { get; set; } + public string Name { get; } +} + + + +class JobshopSat +{ + //Number of machines. + public const int machinesCount = 3; + //horizon is the upper bound of the start time of all tasks. + public const int horizon = 300; + //this will be set to the size of myJobList variable. + public static int jobsCount; + /*Search time limit in milliseconds. if it's equal to 0, + then no time limit will be used.*/ + public const int timeLimitInSeconds = 0; + public static List> myJobList = new List>(); + public static void InitTaskList() + { + List taskList = new List(); + taskList.Add(new Task(0, 0, 65, 0)); + taskList.Add(new Task(1, 0, 5, 1)); + taskList.Add(new Task(2, 0, 15, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 1, 15, 0)); + taskList.Add(new Task(1, 1, 25, 1)); + taskList.Add(new Task(2, 1, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 2, 25, 0)); + taskList.Add(new Task(1, 2, 30, 1)); + taskList.Add(new Task(2, 2, 40, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 3, 20, 0)); + taskList.Add(new Task(1, 3, 35, 1)); + taskList.Add(new Task(2, 3, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 4, 15, 0)); + taskList.Add(new Task(1, 4, 25, 1)); + taskList.Add(new Task(2, 4, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 5, 25, 0)); + taskList.Add(new Task(1, 5, 30, 1)); + taskList.Add(new Task(2, 5, 40, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 6, 20, 0)); + taskList.Add(new Task(1, 6, 35, 1)); + taskList.Add(new Task(2, 6, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 7, 10, 0)); + taskList.Add(new Task(1, 7, 15, 1)); + taskList.Add(new Task(2, 7, 50, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 8, 50, 0)); + taskList.Add(new Task(1, 8, 10, 1)); + taskList.Add(new Task(2, 8, 20, 2)); + myJobList.Add(taskList); + + jobsCount = myJobList.Count; + } + + public static void Main(String[] args) + { + InitTaskList(); + CpModel model = new CpModel(); + + // ----- Creates all intervals and integer variables ----- + + // Stores all tasks attached interval variables per job. + List> + jobsToTasks = new List>(jobsCount); + List> + jobsToStarts = new List>(jobsCount); + List> + jobsToEnds = new List>(jobsCount); + + // machinesToTasks stores the same interval variables as above, but + // grouped my machines instead of grouped by jobs. + List> + machinesToTasks = new List>(machinesCount); + List> + machinesToStarts = new List>(machinesCount); + for (int i = 0; i < machinesCount; i++) + { + machinesToTasks.Add(new List()); + machinesToStarts.Add(new List()); + } + + // Creates all individual interval variables. + foreach (List job in myJobList) + { + jobsToTasks.Add(new List()); + jobsToStarts.Add(new List()); + jobsToEnds.Add(new List()); + foreach (Task task in job) + { + IntVar start = model.NewIntVar(0, horizon, task.Name); + IntVar end = model.NewIntVar(0, horizon, task.Name); + IntervalVar oneTask = + model.NewIntervalVar(start, task.Duration, end, task.Name); + jobsToTasks[task.JobId].Add(oneTask); + jobsToStarts[task.JobId].Add(start); + jobsToEnds[task.JobId].Add(end); + machinesToTasks[task.Machine].Add(oneTask); + machinesToStarts[task.Machine].Add(start); + } + } + + // ----- Creates model ----- + + // Creates precedences inside jobs. + for (int j = 0; j < jobsToTasks.Count; ++j) + { + for (int t = 0; t < jobsToTasks[j].Count - 1; ++t) + { + model.Add(jobsToEnds[j][t] <= jobsToStarts[j][t + 1]); + } + } + + // Adds no_overkap constraints on unary resources. + for (int machineId = 0; machineId < machinesCount; ++machineId) + { + model.AddNoOverlap(machinesToTasks[machineId].ToArray()); + } + + // Creates array of end_times of jobs. + IntVar[] allEnds = new IntVar[jobsCount]; + for (int i = 0; i < jobsCount; i++) + { + allEnds[i] = jobsToEnds[i].Last(); + } + + // Objective: minimize the makespan (maximum end times of all tasks) + // of the problem. + IntVar makespan = model.NewIntVar(0, horizon, "makespan"); + model.AddMaxEquality(makespan, allEnds); + model.Minimize(makespan); + + + // Createe the solver. + CpSolver solver = new CpSolver(); + // Set the time limit. + if (timeLimitInSeconds > 0) + { + solver.StringParameters = "max_time_in_seconds:" + timeLimitInSeconds; + } + // Solve the problem. + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Optimal) + { + Console.WriteLine("Makespan = " + solver.ObjectiveValue); + for (int m = 0; m < machinesCount; ++m) + { + Console.WriteLine($"Machine {m}:"); + SortedDictionary starts = new SortedDictionary(); + foreach (IntVar var in machinesToStarts[m]) + { + starts[solver.Value(var)] = var.Name(); + } + foreach (KeyValuePair p in starts) + { + Console.WriteLine($" Task {p.Value} starts at {p.Key}"); + } + } + } + else + { + Console.WriteLine("No solution found!"); + } + } +} diff --git a/examples/dotnet/csjobshop.csproj b/examples/dotnet/JobshopSat.csproj similarity index 94% rename from examples/dotnet/csjobshop.csproj rename to examples/dotnet/JobshopSat.csproj index c33f44c24c..74199a7056 100644 --- a/examples/dotnet/csjobshop.csproj +++ b/examples/dotnet/JobshopSat.csproj @@ -14,7 +14,7 @@ - + diff --git a/examples/dotnet/SpeakerSchedulingSat.cs b/examples/dotnet/SpeakerSchedulingSat.cs new file mode 100644 index 0000000000..6bbc6e5a8e --- /dev/null +++ b/examples/dotnet/SpeakerSchedulingSat.cs @@ -0,0 +1,190 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Google.OrTools.Sat; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System; + +class SpeakerScheduling +{ + struct Entry + { + public IntVar var; + public int start; + + public Entry(IntVar v, int s) + { + var = v; + start = 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[] entries = new List[number_of_speakers]; + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + entries[speaker] = new List(); + } + + List[] contributions_per_slot = new List[last_slot + 1]; + for (int slot = 1; slot <= last_slot; ++slot) + { + contributions_per_slot[slot] = new List(); + } + + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + List all_vars = new List(); + 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(all_vars.ToArray().Sum() == 1); + } + // Force the schedule to be consistent. + for (int slot = first_slot; slot <= last_slot; ++slot) + { + model.Add(contributions_per_slot[slot].ToArray().Sum() <= 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(); + 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) + { + int start = 1; + if (args.Length == 1) + { + start = int.Parse(args[0]); + } + Stopwatch s = new Stopwatch(); + s.Start(); + for (int i = start; i < 40; i++) + { + Solve(i); + } + + s.Stop(); + Console.WriteLine("Finished in " + s.ElapsedMilliseconds + " ms"); + } +} \ No newline at end of file diff --git a/examples/dotnet/techtalk_scheduling.csproj b/examples/dotnet/SpeakerSchedulingSat.csproj similarity index 93% rename from examples/dotnet/techtalk_scheduling.csproj rename to examples/dotnet/SpeakerSchedulingSat.csproj index 67f36d360b..f311fe013d 100644 --- a/examples/dotnet/techtalk_scheduling.csproj +++ b/examples/dotnet/SpeakerSchedulingSat.csproj @@ -14,7 +14,7 @@ - + diff --git a/examples/dotnet/csjobshop.cs b/examples/dotnet/csjobshop.cs deleted file mode 100644 index f1a4ed4a4a..0000000000 --- a/examples/dotnet/csjobshop.cs +++ /dev/null @@ -1,229 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Google.OrTools.ConstraintSolver; - - -class Task { - public Task(int taskId, int jobId, int duration, int machine) { - TaskId = taskId; - JobId = jobId; - Duration = duration; - Machine = machine; - Name = "T" + taskId + "J" + jobId + "M" + - machine + "D" + duration; - } - public int TaskId {get; set;} - public int JobId {get; set;} - public int Machine {get; set;} - public int Duration {get; set;} - public string Name {get;} -} - - - -class FlexibleJobshop -{ - //Number of machines. - public const int machinesCount = 3; - //horizon is the upper bound of the start time of all tasks. - public const int horizon = 300; - //this will be set to the size of myJobList variable. - public static int jobsCount; - /*Search time limit in milliseconds. if it's equal to 0, - then no time limit will be used.*/ - public const int timeLimitInMs = 0; - public static List> myJobList = new List>(); - public static void InitTaskList() { - List taskList = new List(); - taskList.Add(new Task(0, 0, 65, 0)); - taskList.Add(new Task(1 ,0, 5, 1)); - taskList.Add(new Task(2 ,0, 15, 2)); - myJobList.Add(taskList); - - taskList = new List(); - taskList.Add(new Task(0, 1, 15, 0)); - taskList.Add(new Task(1 ,1, 25, 1)); - taskList.Add(new Task(2 ,1, 10, 2)); - myJobList.Add(taskList); - - taskList = new List(); - taskList.Add(new Task(0 ,2, 25, 0)); - taskList.Add(new Task(1 ,2, 30, 1)); - taskList.Add(new Task(2 ,2, 40, 2)); - myJobList.Add(taskList); - - taskList = new List(); - taskList.Add(new Task(0, 3, 20, 0)); - taskList.Add(new Task(1 ,3, 35, 1)); - taskList.Add(new Task(2 ,3, 10, 2)); - myJobList.Add(taskList); - - taskList = new List(); - taskList.Add(new Task(0, 4, 15, 0)); - taskList.Add(new Task(1 ,4, 25, 1)); - taskList.Add(new Task(2 ,4, 10, 2)); - myJobList.Add(taskList); - - taskList = new List(); - taskList.Add(new Task(0, 5, 25, 0)); - taskList.Add(new Task(1 ,5, 30, 1)); - taskList.Add(new Task(2 ,5, 40, 2)); - myJobList.Add(taskList); - - taskList = new List(); - taskList.Add(new Task(0, 6, 20, 0)); - taskList.Add(new Task(1 ,6, 35, 1)); - taskList.Add(new Task(2 ,6, 10, 2)); - myJobList.Add(taskList); - - taskList = new List(); - taskList.Add(new Task(0, 7, 10, 0)); - taskList.Add(new Task(1 ,7, 15, 1)); - taskList.Add(new Task(2 ,7, 50, 2)); - myJobList.Add(taskList); - - taskList = new List(); - taskList.Add(new Task(0, 8, 50, 0)); - taskList.Add(new Task(1 ,8, 10, 1)); - taskList.Add(new Task(2 ,8, 20, 2)); - myJobList.Add(taskList); - - jobsCount = myJobList.Count; - } - - public static void Main(String[] args) - { - InitTaskList(); - Solver solver = new Solver("Jobshop"); - - // ----- Creates all Intervals and vars ----- - - // All tasks - List allTasks = new List(); - // Stores all tasks attached interval variables per job. - List> - jobsToTasks = new List>(jobsCount); - // machinesToTasks stores the same interval variables as above, but - // grouped my machines instead of grouped by jobs. - List> - machinesToTasks = new List>(machinesCount); - for (int i=0; i()); - } - - // Creates all individual interval variables. - foreach (List job in myJobList) { - jobsToTasks.Add(new List()); - foreach (Task task in job) { - IntervalVar oneTask = solver.MakeFixedDurationIntervalVar( - 0, horizon, task.Duration, false, task.Name); - jobsToTasks[task.JobId].Add(oneTask); - allTasks.Add(oneTask); - machinesToTasks[task.Machine].Add(oneTask); - } - } - - // ----- Creates model ----- - - // Creates precedences inside jobs. - foreach (List jobToTask in jobsToTasks) { - int tasksCount = jobToTask.Count; - for (int task_index = 0; task_index < tasksCount - 1; ++task_index) { - IntervalVar t1 = jobToTask[task_index]; - IntervalVar t2 = jobToTask[task_index + 1]; - Constraint prec = - solver.MakeIntervalVarRelation(t2, Solver.STARTS_AFTER_END, t1); - solver.Add(prec); - } - } - - // Adds disjunctive constraints on unary resources, and creates - // sequence variables. A sequence variable is a dedicated variable - // whose job is to sequence interval variables. - SequenceVar[] allSequences = new SequenceVar[machinesCount]; - for (int machineId = 0; machineId < machinesCount; ++machineId) { - string name = "Machine_" + machineId; - DisjunctiveConstraint ct = - solver.MakeDisjunctiveConstraint(machinesToTasks[machineId].ToArray(), - name); - solver.Add(ct); - allSequences[machineId] = ct.SequenceVar(); - } - // Creates array of end_times of jobs. - IntVar[] allEnds = new IntVar[jobsCount]; - for (int i=0; i 0) { - limit = solver.MakeTimeLimit(timeLimitInMs); - } - - - SolutionCollector collector = solver.MakeLastSolutionCollector(); - collector.Add(allSequences); - collector.Add(allTasks.ToArray()); - // Search. - bool solutionFound = solver.Solve(mainPhase, searchLog, objectiveMonitor, - limit, collector); - if(solutionFound) { - //The index of the solution from the collector - const int SOLUTION_INDEX = 0; - Assignment solution = collector.Solution(SOLUTION_INDEX); - for (int m = 0; m < machinesCount; ++m) { - Console.WriteLine("Machine " + m + " :"); - SequenceVar seq = allSequences[m]; - int[] storedSequence = collector.ForwardSequence(SOLUTION_INDEX, seq); - foreach (int taskIndex in storedSequence) { - IntervalVar task = seq.Interval(taskIndex); - long startMin = solution.StartMin(task); - long startMax = solution.StartMax(task); - if(startMin == startMax) { - Console.WriteLine("Task " + task.Name() + " starts at " + - startMin + "."); - } - else { - Console.WriteLine("Task " + task.Name() + " starts between " + - startMin + " and " + startMax + "."); - } - - } - } - } - else { - Console.WriteLine("No solution found!"); - } - } -} diff --git a/examples/dotnet/gate_scheduling_sat.csproj b/examples/dotnet/gate_scheduling_sat.csproj deleted file mode 100644 index f36b1e6d5b..0000000000 --- a/examples/dotnet/gate_scheduling_sat.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - Exe - 7.2 - netcoreapp2.1 - false - ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json - - - - full - true - true - - - - - - - diff --git a/examples/dotnet/jobshop_ft06_sat.cs b/examples/dotnet/jobshop_ft06_sat.cs index 4e889110dc..6acfc7acc2 100644 --- a/examples/dotnet/jobshop_ft06_sat.cs +++ b/examples/dotnet/jobshop_ft06_sat.cs @@ -33,7 +33,7 @@ public class JobshopFt06Sat public IntervalVar interval; } - static void Solve() + static void Main() { int[,] durations = new int[,] { {1, 3, 6, 7, 3, 6}, {8, 5, 10, 10, 10, 4}, @@ -131,8 +131,4 @@ public class JobshopFt06Sat // Statistics. Console.WriteLine(solver.ResponseStats()); } - - static void Main() { - Solve(); - } } diff --git a/examples/dotnet/slow_scheduling.cs b/examples/dotnet/slow_scheduling.cs deleted file mode 100644 index 2a7697fe0b..0000000000 --- a/examples/dotnet/slow_scheduling.cs +++ /dev/null @@ -1,284 +0,0 @@ -// -// Copyright 2013 Google -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Google.OrTools.ConstraintSolver; -using Google.OrTools.Graph; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System; - -public class SpeakerScheduling -{ - - public class FlowAssign : NetDecisionBuilder - { - public FlowAssign(IntVar[] vars, int first_slot, IntVar last_slot_var) - { - vars_ = vars; - first_slot_ = first_slot; - last_slot_var_ = last_slot_var; - } - - public override Decision Next(Solver solver) - { - int large = 100000; - int number_of_variables = vars_.Length; - long last_slot = last_slot_var_.Max(); - // Lets build a bipartite graph with equal number of nodes left and right. - // Variables will be on the left, slots on the right. - // We will add dummy variables when needed. - // Arcs will have a cost x is slot x is possible for a variable, a large - // number otherwise. For dummy variables, the cost will be 0 always. - LinearSumAssignment matching = new LinearSumAssignment(); - for (int speaker = 0; speaker < number_of_variables; ++speaker) - { - IntVar var = vars_[speaker]; - for (int value = first_slot_; value <= last_slot; ++value) - { - if (var.Contains(value)) - { - matching.AddArcWithCost(speaker, value - first_slot_, value); - } - else - { - matching.AddArcWithCost(speaker, value - first_slot_, large); - } - } - } - // The Matching algorithms expect the same number of left and right nodes. - // So we fill the rest with dense zero-cost arcs. - for (int dummy = number_of_variables; - dummy <= last_slot - first_slot_; ++dummy) { - for (int value = first_slot_; value <= last_slot; ++value) - { - matching.AddArcWithCost(dummy, value - first_slot_, 0); - } - } - if (matching.Solve() == LinearSumAssignment.OPTIMAL && - matching.OptimalCost() < large) // No violated arcs. - { - for (int speaker = 0; speaker < number_of_variables; ++speaker) - { - vars_[speaker].SetValue(matching.RightMate(speaker) + first_slot_); - } - } else { - solver.Fail(); - } - return null; - } - - private IntVar[] vars_; - private int first_slot_; - private IntVar last_slot_var_; - } - - private static void Solve(int first_slot) - { - Console.WriteLine("----------------------------------------------------"); - Solver solver = new Solver("SpeakerScheduling"); - - // 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 {0} speakers, for a total of {1} slots, during [{2}..{3}]", - number_of_speakers, sum_of_durations, first_slot, last_slot); - - // Start variable for all talks. - IntVar[] starts = new IntVar[number_of_speakers]; - // We store the possible starts for all talks filtered from the - // duration and the speaker availability. - int[][] possible_starts = new int[number_of_speakers][]; - - for (int speaker = 0; speaker < number_of_speakers; ++speaker) - { - int duration = durations[speaker]; - // Let's filter the possible starts. - List filtered_starts = new List(); - 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 < durations[speaker]; ++offset) - { - if (index + offset >= availability || - speaker_availability[speaker][index + offset] != slot + offset) - { - // discontinuity. - ok = false; - break; - } - } - if (ok) - { - filtered_starts.Add(slot); - } - possible_starts[speaker] = filtered_starts.ToArray(); - } - starts[speaker] = - solver.MakeIntVar(possible_starts[speaker], "start[" + speaker + "]"); - } - - List[] contributions_per_slot = - new List[last_slot + 1]; - for (int slot = first_slot; slot <= last_slot; ++slot) - { - contributions_per_slot[slot] = new List(); - } - for (int speaker = 0; speaker < number_of_speakers; ++speaker) - { - int duration = durations[speaker]; - IntVar start_var = starts[speaker]; - foreach (int start in possible_starts[speaker]) - { - for (int offset = 0; offset < duration; ++offset) - { - contributions_per_slot[start + offset].Add(start_var.IsEqual(start)); - } - } - } - // Force the schedule to be consistent. - for (int slot = first_slot; slot <= last_slot; ++slot) - { - solver.Add( - solver.MakeSumLessOrEqual(contributions_per_slot[slot].ToArray(), 1)); - } - - // Add minimum start info. - for (int speaker = 0; speaker < number_of_speakers; ++speaker) - { - solver.Add(starts[speaker] >= first_slot); - } - - // Creates makespan. - IntVar[] end_times = new IntVar[number_of_speakers]; - for (int speaker = 0; speaker < number_of_speakers; speaker++) - { - end_times[speaker] = (starts[speaker] + (durations[speaker] - 1)).Var(); - } - IntVar last_slot_var = end_times.Max().VarWithName("last_slot"); - - // Add trivial bound to objective. - last_slot_var.SetMin(first_slot + sum_of_durations - 1); - - // Redundant scheduling constraint. - IntervalVar[] intervals = - solver.MakeFixedDurationIntervalVarArray(starts, durations, "intervals"); - DisjunctiveConstraint disjunctive = - solver.MakeDisjunctiveConstraint(intervals, "disjunctive"); - solver.Add(disjunctive); - - // - // Search - // - List short_talks = new List(); - List long_talks = new List(); - for (int speaker = 0; speaker < number_of_speakers; ++speaker) - { - if (durations[speaker] == 1) - { - short_talks.Add(starts[speaker]); - } - else - { - long_talks.Add(starts[speaker]); - } - } - OptimizeVar objective_monitor = solver.MakeMinimize(last_slot_var, 1); - DecisionBuilder long_phase = - solver.MakePhase(long_talks.ToArray(), - Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, - Solver.ASSIGN_MIN_VALUE); - DecisionBuilder short_phase = - new FlowAssign(short_talks.ToArray(), first_slot, last_slot_var); - DecisionBuilder obj_phase = - solver.MakePhase(last_slot_var, - Solver.CHOOSE_FIRST_UNBOUND, - Solver.ASSIGN_MIN_VALUE); - DecisionBuilder main_phase = - solver.Compose(long_phase, short_phase, obj_phase); - - solver.NewSearch(main_phase, objective_monitor); - while (solver.NextSolution()) - { - Console.WriteLine("\nLast used slot: " + (last_slot_var.Value())); - Console.WriteLine("Speakers (start..end):"); - for (int s = 0; s < number_of_speakers; s++) - { - long sstart = starts[s].Value(); - Console.WriteLine(" - speaker {0,2}: {1,2}..{2,2}", (s + 1), - sstart, (sstart + durations[s] - 1)); - } - } - - Console.WriteLine("\nSolutions: {0}", solver.Solutions()); - Console.WriteLine("WallTime: {0}ms", solver.WallTime()); - Console.WriteLine("Failures: {0}", solver.Failures()); - Console.WriteLine("Branches: {0} ", solver.Branches()); - solver.EndSearch(); - } - - public static void Main(String[] args) - { - int start = 1; - if (args.Length == 1) - { - start = int.Parse(args[0]); - } - Stopwatch s = new Stopwatch(); - s.Start(); - for (int i = start; i < 40; i++) - { - Solve(i); - } - - s.Stop(); - Console.WriteLine("Finished in " + s.ElapsedMilliseconds + " ms"); - } -} diff --git a/examples/dotnet/techtalk_scheduling.cs b/examples/dotnet/techtalk_scheduling.cs deleted file mode 100644 index 14ff36e155..0000000000 --- a/examples/dotnet/techtalk_scheduling.cs +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Google.OrTools.ConstraintSolver; -using Google.OrTools.Graph; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System; - -public class SpeakerScheduling -{ - - public class FlowAssign : NetDecisionBuilder - { - public FlowAssign(IntVar[] vars, int first_slot, IntVar last_slot_var) - { - vars_ = vars; - first_slot_ = first_slot; - last_slot_var_ = last_slot_var; - } - - public override Decision Next(Solver solver) - { - int large = 100000; - int number_of_variables = vars_.Length; - long last_slot = last_slot_var_.Max(); - // Lets build a bipartite graph with equal number of nodes left and right. - // Variables will be on the left, slots on the right. - // We will add dummy variables when needed. - // Arcs will have a cost x is slot x is possible for a variable, a large - // number otherwise. For dummy variables, the cost will be 0 always. - LinearSumAssignment matching = new LinearSumAssignment(); - for (int speaker = 0; speaker < number_of_variables; ++speaker) - { - IntVar var = vars_[speaker]; - for (int value = first_slot_; value <= last_slot; ++value) - { - if (var.Contains(value)) - { - matching.AddArcWithCost(speaker, value - first_slot_, value); - } - else - { - matching.AddArcWithCost(speaker, value - first_slot_, large); - } - } - } - // The Matching algorithms expect the same number of left and right nodes. - // So we fill the rest with dense zero-cost arcs. - for (int dummy = number_of_variables; - dummy <= last_slot - first_slot_; ++dummy) { - for (int value = first_slot_; value <= last_slot; ++value) - { - matching.AddArcWithCost(dummy, value - first_slot_, 0); - } - } - if (matching.Solve() == LinearSumAssignment.OPTIMAL && - matching.OptimalCost() < large) // No violated arcs. - { - for (int speaker = 0; speaker < number_of_variables; ++speaker) - { - vars_[speaker].SetValue(matching.RightMate(speaker) + first_slot_); - } - } else { - solver.Fail(); - } - return null; - } - - private IntVar[] vars_; - private int first_slot_; - private IntVar last_slot_var_; - } - - private static void Solve(int first_slot) - { - Console.WriteLine("----------------------------------------------------"); - Solver solver = new Solver("SpeakerScheduling"); - - // 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 {0} speakers, for a total of {1} slots, during [{2}..{3}]", - number_of_speakers, sum_of_durations, first_slot, last_slot); - - // Start variable for all talks. - IntVar[] starts = new IntVar[number_of_speakers]; - // We store the possible starts for all talks filtered from the - // duration and the speaker availability. - int[][] possible_starts = new int[number_of_speakers][]; - - for (int speaker = 0; speaker < number_of_speakers; ++speaker) - { - int duration = durations[speaker]; - // Let's filter the possible starts. - List filtered_starts = new List(); - 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) - { - filtered_starts.Add(slot); - } - possible_starts[speaker] = filtered_starts.ToArray(); - } - starts[speaker] = - solver.MakeIntVar(possible_starts[speaker], "start[" + speaker + "]"); - } - - List[] contributions_per_slot = - new List[last_slot + 1]; - for (int slot = first_slot; slot <= last_slot; ++slot) - { - contributions_per_slot[slot] = new List(); - } - for (int speaker = 0; speaker < number_of_speakers; ++speaker) - { - int duration = durations[speaker]; - IntVar start_var = starts[speaker]; - foreach (int start in possible_starts[speaker]) - { - for (int offset = 0; offset < duration; ++offset) - { - contributions_per_slot[start + offset].Add(start_var.IsEqual(start)); - } - } - } - // Force the schedule to be consistent. - for (int slot = first_slot; slot <= last_slot; ++slot) - { - solver.Add( - solver.MakeSumLessOrEqual(contributions_per_slot[slot].ToArray(), 1)); - } - - // Add minimum start info. - for (int speaker = 0; speaker < number_of_speakers; ++speaker) - { - solver.Add(starts[speaker] >= first_slot); - } - - // Creates makespan. - IntVar[] end_times = new IntVar[number_of_speakers]; - for (int speaker = 0; speaker < number_of_speakers; speaker++) - { - end_times[speaker] = (starts[speaker] + (durations[speaker] - 1)).Var(); - } - IntVar last_slot_var = end_times.Max().VarWithName("last_slot"); - - // Add trivial bound to objective. - last_slot_var.SetMin(first_slot + sum_of_durations - 1); - - // Redundant scheduling constraint. - IntervalVar[] intervals = - solver.MakeFixedDurationIntervalVarArray(starts, durations, "intervals"); - DisjunctiveConstraint disjunctive = - solver.MakeDisjunctiveConstraint(intervals, "disjunctive"); - solver.Add(disjunctive); - - // - // Search - // - List short_talks = new List(); - List long_talks = new List(); - for (int speaker = 0; speaker < number_of_speakers; ++speaker) - { - if (durations[speaker] == 1) - { - short_talks.Add(starts[speaker]); - } - else - { - long_talks.Add(starts[speaker]); - } - } - OptimizeVar objective_monitor = solver.MakeMinimize(last_slot_var, 1); - DecisionBuilder long_phase = - solver.MakePhase(long_talks.ToArray(), - Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, - Solver.ASSIGN_MIN_VALUE); - DecisionBuilder short_phase = - new FlowAssign(short_talks.ToArray(), first_slot, last_slot_var); - DecisionBuilder obj_phase = - solver.MakePhase(last_slot_var, - Solver.CHOOSE_FIRST_UNBOUND, - Solver.ASSIGN_MIN_VALUE); - DecisionBuilder main_phase = - solver.Compose(long_phase, short_phase, obj_phase); - - solver.NewSearch(main_phase, objective_monitor); - while (solver.NextSolution()) - { - Console.WriteLine("\nLast used slot: " + (last_slot_var.Value())); - Console.WriteLine("Speakers (start..end):"); - for (int s = 0; s < number_of_speakers; s++) - { - long sstart = starts[s].Value(); - Console.WriteLine(" - speaker {0,2}: {1,2}..{2,2}", (s + 1), - sstart, (sstart + durations[s] - 1)); - } - } - - Console.WriteLine("\nSolutions: {0}", solver.Solutions()); - Console.WriteLine("WallTime: {0}ms", solver.WallTime()); - Console.WriteLine("Failures: {0}", solver.Failures()); - Console.WriteLine("Branches: {0} ", solver.Branches()); - solver.EndSearch(); - } - - public static void Main(String[] args) - { - int start = 1; - if (args.Length == 1) - { - start = int.Parse(args[0]); - } - Stopwatch s = new Stopwatch(); - s.Start(); - for (int i = start; i < 40; i++) - { - Solve(i); - } - - s.Stop(); - Console.WriteLine("Finished in " + s.ElapsedMilliseconds + " ms"); - } -} diff --git a/makefiles/Makefile.dotnet.mk b/makefiles/Makefile.dotnet.mk index 363f7fa830..c14020c725 100644 --- a/makefiles/Makefile.dotnet.mk +++ b/makefiles/Makefile.dotnet.mk @@ -568,7 +568,6 @@ test_dotnet_examples_csharp: \ rdotnet_cscvrptw.cs \ rdotnet_csflow.cs \ rdotnet_csintegerprogramming.cs \ - rdotnet_csjobshop.cs \ rdotnet_csknapsack.cs \ rdotnet_cslinearprogramming.cs \ rdotnet_csls_api.cs \ @@ -587,11 +586,12 @@ test_dotnet_examples_csharp: \ rdotnet_furniture_moving.cs \ rdotnet_furniture_moving_intervals.cs \ rdotnet_futoshiki.cs \ - rdotnet_gate_scheduling_sat.cs \ + rdotnet_GateSchedulingSat.cs \ rdotnet_golomb_ruler.cs \ rdotnet_grocery.cs \ rdotnet_hidato_table.cs \ rdotnet_jobshop_ft06_sat.cs \ + rdotnet_JobshopSat.cs \ rdotnet_just_forgotten.cs \ rdotnet_kakuro.cs \ rdotnet_kenken2.cs \ @@ -641,14 +641,13 @@ test_dotnet_examples_csharp: \ rdotnet_set_partition.cs \ rdotnet_sicherman_dice.cs \ rdotnet_ski_assignment.cs \ - rdotnet_slow_scheduling.cs \ + rdotnet_SpeakerSchedulingSat.cs \ rdotnet_stable_marriage.cs \ rdotnet_strimko2.cs \ rdotnet_subset_sum.cs \ rdotnet_sudoku.cs \ rdotnet_survo_puzzle.cs \ rdotnet_TaskScheduling.cs \ - rdotnet_techtalk_scheduling.cs \ rdotnet_to_num.cs \ rdotnet_traffic_lights.cs \ rdotnet_tsp.cs \