diff --git a/examples/dotnet/CoverRectangleSat.cs b/examples/dotnet/CoverRectangleSat.cs index 77b38d89d07..ee4d92ec2e2 100644 --- a/examples/dotnet/CoverRectangleSat.cs +++ b/examples/dotnet/CoverRectangleSat.cs @@ -1,150 +1,150 @@ -// Copyright 2010-2025 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 System; -using System.Collections.Generic; -using System.Linq; -using Google.OrTools.Sat; - -/// -/// Fill a 60x50 rectangle exactly using a minimum number of non-overlapping squares.""" -/// -class CoverRectangleSat -{ - static int sizeX = 60; - static int sizeY = 50; - - static bool CoverRectangle(int numSquares) - { - CpModel model = new CpModel(); - - var areas = new List(); - var sizes = new List(); - var xIntervals = new List(); - var yIntervals = new List(); - var xStarts = new List(); - var yStarts = new List(); - - // Creates intervals for the NoOverlap2D and size variables. - foreach (var i in Enumerable.Range(0, numSquares)) - { - var size = model.NewIntVar(1, sizeY, String.Format("size_{0}", i)); - var startX = model.NewIntVar(0, sizeX, String.Format("startX_{0}", i)); - var endX = model.NewIntVar(0, sizeX, String.Format("endX_{0}", i)); - var startY = model.NewIntVar(0, sizeY, String.Format("startY_{0}", i)); - var endY = model.NewIntVar(0, sizeY, String.Format("endY_{0}", i)); - - var intervalX = model.NewIntervalVar(startX, size, endX, String.Format("intervalX_{0}", i)); - var intervalY = model.NewIntervalVar(startY, size, endY, String.Format("intervalY_{0}", i)); - - var area = model.NewIntVar(1, sizeY * sizeY, String.Format("area_{0}", i)); - model.AddMultiplicationEquality(area, size, size); - - areas.Add(area); - xIntervals.Add(intervalX); - yIntervals.Add(intervalY); - sizes.Add(size); - xStarts.Add(startX); - yStarts.Add(startY); - } - - // Main constraint. - NoOverlap2dConstraint noOverlap2d = model.AddNoOverlap2D(); - foreach (var i in Enumerable.Range(0, numSquares)) - { - noOverlap2d.AddRectangle(xIntervals[i], yIntervals[i]); - } - - // Redundant constraints. - model.AddCumulative(sizeY).AddDemands(xIntervals, sizes); - model.AddCumulative(sizeX).AddDemands(yIntervals, sizes); - - // Forces the rectangle to be exactly covered. - model.Add(LinearExpr.Sum(areas) == sizeX * sizeY); - - // Symmetry breaking 1: sizes are ordered. - foreach (var i in Enumerable.Range(0, numSquares - 1)) - { - model.Add(sizes[i] <= sizes[i + 1]); - - // Define same to be true iff sizes[i] == sizes[i + 1] - var same = model.NewBoolVar(""); - model.Add(sizes[i] == sizes[i + 1]).OnlyEnforceIf(same); - model.Add(sizes[i] < sizes[i + 1]).OnlyEnforceIf(same.Not()); - - // Tie break with starts. - model.Add(xStarts[i] <= xStarts[i + 1]).OnlyEnforceIf(same); - } - - // Symmetry breaking 2: first square in one quadrant. - model.Add(xStarts[0] < (sizeX + 1) / 2); - model.Add(yStarts[0] < (sizeY + 1) / 2); - - // Creates a solver and solves. - var solver = new CpSolver(); - solver.StringParameters = "num_search_workers:16, log_search_progress: false, max_time_in_seconds:10"; - var status = solver.Solve(model); - Console.WriteLine(string.Format("{0} found in {1:0.00}s", status, solver.WallTime())); - - // Prints solution. - bool solution_found = status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible; - if (solution_found) - { - char[][] output = new char [sizeY][]; - foreach (var y in Enumerable.Range(0, sizeY)) - { - - output[y] = new char[sizeX]; - foreach (var x in Enumerable.Range(0, sizeX)) - { - output[y][x] = ' '; - } - } - - foreach (var s in Enumerable.Range(0, numSquares)) - { - int startX = (int)solver.Value(xStarts[s]); - int startY = (int)solver.Value(yStarts[s]); - int size = (int)solver.Value(sizes[s]); - char c = (char)(65 + s); - foreach (var x in Enumerable.Range(startX, size)) - { - foreach (var y in Enumerable.Range(startY, size)) - { - if (output[y][x] != ' ') - { - Console.WriteLine( - string.Format("Error at position x={0} y{1}, found {2}", x, y, output[y][x])); - } - output[y][x] = c; - } - } - } - foreach (var y in Enumerable.Range(0, sizeY)) - { - Console.WriteLine(new String(output[y], 0, sizeX)); - } - } - return solution_found; - } - - static void Main() - { - foreach (int numSquares in Enumerable.Range(1, 15)) - { - Console.WriteLine("Trying with size = {0}", numSquares); - if (CoverRectangle(numSquares)) - break; - } - } -} +// Copyright 2010-2025 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 System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + +/// +/// Fill a 60x50 rectangle exactly using a minimum number of non-overlapping squares.""" +/// +class CoverRectangleSat +{ + static int sizeX = 60; + static int sizeY = 50; + + static bool CoverRectangle(int numSquares) + { + CpModel model = new CpModel(); + + var areas = new List(); + var sizes = new List(); + var xIntervals = new List(); + var yIntervals = new List(); + var xStarts = new List(); + var yStarts = new List(); + + // Creates intervals for the NoOverlap2D and size variables. + foreach (var i in Enumerable.Range(0, numSquares)) + { + var size = model.NewIntVar(1, sizeY, String.Format("size_{0}", i)); + var startX = model.NewIntVar(0, sizeX, String.Format("startX_{0}", i)); + var endX = model.NewIntVar(0, sizeX, String.Format("endX_{0}", i)); + var startY = model.NewIntVar(0, sizeY, String.Format("startY_{0}", i)); + var endY = model.NewIntVar(0, sizeY, String.Format("endY_{0}", i)); + + var intervalX = model.NewIntervalVar(startX, size, endX, String.Format("intervalX_{0}", i)); + var intervalY = model.NewIntervalVar(startY, size, endY, String.Format("intervalY_{0}", i)); + + var area = model.NewIntVar(1, sizeY * sizeY, String.Format("area_{0}", i)); + model.AddMultiplicationEquality(area, size, size); + + areas.Add(area); + xIntervals.Add(intervalX); + yIntervals.Add(intervalY); + sizes.Add(size); + xStarts.Add(startX); + yStarts.Add(startY); + } + + // Main constraint. + NoOverlap2dConstraint noOverlap2d = model.AddNoOverlap2D(); + foreach (var i in Enumerable.Range(0, numSquares)) + { + noOverlap2d.AddRectangle(xIntervals[i], yIntervals[i]); + } + + // Redundant constraints. + model.AddCumulative(sizeY).AddDemands(xIntervals, sizes); + model.AddCumulative(sizeX).AddDemands(yIntervals, sizes); + + // Forces the rectangle to be exactly covered. + model.Add(LinearExpr.Sum(areas) == sizeX * sizeY); + + // Symmetry breaking 1: sizes are ordered. + foreach (var i in Enumerable.Range(0, numSquares - 1)) + { + model.Add(sizes[i] <= sizes[i + 1]); + + // Define same to be true iff sizes[i] == sizes[i + 1] + var same = model.NewBoolVar(""); + model.Add(sizes[i] == sizes[i + 1]).OnlyEnforceIf(same); + model.Add(sizes[i] < sizes[i + 1]).OnlyEnforceIf(same.Not()); + + // Tie break with starts. + model.Add(xStarts[i] <= xStarts[i + 1]).OnlyEnforceIf(same); + } + + // Symmetry breaking 2: first square in one quadrant. + model.Add(xStarts[0] < (sizeX + 1) / 2); + model.Add(yStarts[0] < (sizeY + 1) / 2); + + // Creates a solver and solves. + var solver = new CpSolver(); + solver.StringParameters = "num_search_workers:16, log_search_progress: false, max_time_in_seconds:10"; + var status = solver.Solve(model); + Console.WriteLine(string.Format("{0} found in {1:0.00}s", status, solver.WallTime())); + + // Prints solution. + bool solution_found = status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible; + if (solution_found) + { + char[][] output = new char [sizeY][]; + foreach (var y in Enumerable.Range(0, sizeY)) + { + + output[y] = new char[sizeX]; + foreach (var x in Enumerable.Range(0, sizeX)) + { + output[y][x] = ' '; + } + } + + foreach (var s in Enumerable.Range(0, numSquares)) + { + int startX = (int)solver.Value(xStarts[s]); + int startY = (int)solver.Value(yStarts[s]); + int size = (int)solver.Value(sizes[s]); + char c = (char)(65 + s); + foreach (var x in Enumerable.Range(startX, size)) + { + foreach (var y in Enumerable.Range(startY, size)) + { + if (output[y][x] != ' ') + { + Console.WriteLine( + string.Format("Error at position x={0} y{1}, found {2}", x, y, output[y][x])); + } + output[y][x] = c; + } + } + } + foreach (var y in Enumerable.Range(0, sizeY)) + { + Console.WriteLine(new String(output[y], 0, sizeX)); + } + } + return solution_found; + } + + static void Main() + { + foreach (int numSquares in Enumerable.Range(1, 15)) + { + Console.WriteLine("Trying with size = {0}", numSquares); + if (CoverRectangle(numSquares)) + break; + } + } +} diff --git a/examples/dotnet/TaskSchedulingSat.cs b/examples/dotnet/TaskSchedulingSat.cs index bc020f0db71..dddd2bb3975 100644 --- a/examples/dotnet/TaskSchedulingSat.cs +++ b/examples/dotnet/TaskSchedulingSat.cs @@ -1,199 +1,199 @@ -// Copyright 2010-2025 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 System; -using System.Collections.Generic; -using Google.OrTools.Sat; - -class Job -{ - public Job(List tasks) - { - AlternativeTasks = tasks; - } - public Job Successor { get; set; } - public List AlternativeTasks { get; set; } -} - -class Task -{ - public Task(string name, long duration, long equipment) - { - Name = name; - Duration = duration; - Equipment = equipment; - } - - public string Name { get; set; } - public long StartTime { get; set; } - public long EndTime - { - get { - return StartTime + Duration; - } - } - public long Duration { get; set; } - public long Equipment { get; set; } - - public override string ToString() - { - return Name + " [ " + Equipment + " ]\tstarts: " + StartTime + " ends:" + EndTime + ", duration: " + Duration; - } -} - -class TaskSchedulingSat -{ - public static List myJobList = new List(); - public static Dictionary> tasksToEquipment = new Dictionary>(); - public static Dictionary taskIndexes = new Dictionary(); - - public static void InitTaskList() - { - List taskList = new List(); - taskList.Add(new Task("Job1Task0a", 15, 0)); - taskList.Add(new Task("Job1Task0b", 25, 1)); - taskList.Add(new Task("Job1Task0c", 10, 2)); - myJobList.Add(new Job(taskList)); - - taskList = new List(); - taskList.Add(new Task("Job1Task1a", 25, 0)); - taskList.Add(new Task("Job1Task1b", 30, 1)); - taskList.Add(new Task("Job1Task1c", 40, 2)); - myJobList.Add(new Job(taskList)); - - taskList = new List(); - taskList.Add(new Task("Job1Task2a", 20, 0)); - taskList.Add(new Task("Job1Task2b", 35, 1)); - taskList.Add(new Task("Job1Task2c", 10, 2)); - myJobList.Add(new Job(taskList)); - - taskList = new List(); - taskList.Add(new Task("Job2Task0a", 15, 0)); - taskList.Add(new Task("Job2Task0b", 25, 1)); - taskList.Add(new Task("Job2Task0c", 10, 2)); - myJobList.Add(new Job(taskList)); - - taskList = new List(); - taskList.Add(new Task("Job2Task1a", 25, 0)); - taskList.Add(new Task("Job2Task1b", 30, 1)); - taskList.Add(new Task("Job2Task1c", 40, 2)); - myJobList.Add(new Job(taskList)); - - taskList = new List(); - taskList.Add(new Task("Job2Task2a", 20, 0)); - taskList.Add(new Task("Job2Task2b", 35, 1)); - taskList.Add(new Task("Job2Task2c", 10, 2)); - myJobList.Add(new Job(taskList)); - - taskList = new List(); - taskList.Add(new Task("Job3Task0a", 10, 0)); - taskList.Add(new Task("Job3Task0b", 15, 1)); - taskList.Add(new Task("Job3Task0c", 50, 2)); - myJobList.Add(new Job(taskList)); - - taskList = new List(); - taskList.Add(new Task("Job3Task1a", 50, 0)); - taskList.Add(new Task("Job3Task1b", 10, 1)); - taskList.Add(new Task("Job3Task1c", 20, 2)); - myJobList.Add(new Job(taskList)); - - taskList = new List(); - taskList.Add(new Task("Job3Task2a", 65, 0)); - taskList.Add(new Task("Job3Task2b", 5, 1)); - taskList.Add(new Task("Job3Task2c", 15, 2)); - myJobList.Add(new Job(taskList)); - - myJobList[0].Successor = myJobList[1]; - myJobList[1].Successor = myJobList[2]; - myJobList[2].Successor = null; - - myJobList[3].Successor = myJobList[4]; - myJobList[4].Successor = myJobList[5]; - myJobList[5].Successor = null; - - myJobList[6].Successor = myJobList[7]; - myJobList[7].Successor = myJobList[8]; - myJobList[8].Successor = null; - } - - public static int GetTaskCount() - { - int c = 0; - foreach (Job j in myJobList) - foreach (Task t in j.AlternativeTasks) - { - taskIndexes[t.Name] = c; - c++; - } - - return c; - } - - public static int GetEndTaskCount() - { - int c = 0; - foreach (Job j in myJobList) - if (j.Successor == null) - c += j.AlternativeTasks.Count; - return c; - } - - static void Main() - { - InitTaskList(); - int taskCount = GetTaskCount(); - - CpModel model = new CpModel(); - - IntervalVar[] tasks = new IntervalVar[taskCount]; - BoolVar[] taskChoosed = new BoolVar[taskCount]; - IntVar[] allEnds = new IntVar[GetEndTaskCount()]; - - int endJobCounter = 0; - foreach (Job j in myJobList) - { - BoolVar[] tmp = new BoolVar[j.AlternativeTasks.Count]; - int i = 0; - foreach (Task t in j.AlternativeTasks) - { - long ti = taskIndexes[t.Name]; - taskChoosed[ti] = model.NewBoolVar(t.Name + "_choose"); - tmp[i++] = taskChoosed[ti]; - IntVar start = model.NewIntVar(0, 10000, t.Name + "_start"); - IntVar end = model.NewIntVar(0, 10000, t.Name + "_end"); - tasks[ti] = model.NewIntervalVar(start, t.Duration, end, t.Name + "_interval"); - if (j.Successor == null) - allEnds[endJobCounter++] = end; - if (!tasksToEquipment.ContainsKey(t.Equipment)) - tasksToEquipment[t.Equipment] = new List(); - tasksToEquipment[t.Equipment].Add(tasks[ti]); - } - model.AddExactlyOne(tmp); - } - - foreach (KeyValuePair> pair in tasksToEquipment) - { - model.AddNoOverlap(pair.Value); - } - - IntVar makespan = model.NewIntVar(0, 100000, "makespan"); - model.AddMaxEquality(makespan, allEnds); - model.Minimize(makespan); - - // Create the solver. - CpSolver solver = new CpSolver(); - // Solve the problem. - solver.Solve(model); - Console.WriteLine(solver.ResponseStats()); - } -} +// Copyright 2010-2025 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 System; +using System.Collections.Generic; +using Google.OrTools.Sat; + +class Job +{ + public Job(List tasks) + { + AlternativeTasks = tasks; + } + public Job Successor { get; set; } + public List AlternativeTasks { get; set; } +} + +class Task +{ + public Task(string name, long duration, long equipment) + { + Name = name; + Duration = duration; + Equipment = equipment; + } + + public string Name { get; set; } + public long StartTime { get; set; } + public long EndTime + { + get { + return StartTime + Duration; + } + } + public long Duration { get; set; } + public long Equipment { get; set; } + + public override string ToString() + { + return Name + " [ " + Equipment + " ]\tstarts: " + StartTime + " ends:" + EndTime + ", duration: " + Duration; + } +} + +class TaskSchedulingSat +{ + public static List myJobList = new List(); + public static Dictionary> tasksToEquipment = new Dictionary>(); + public static Dictionary taskIndexes = new Dictionary(); + + public static void InitTaskList() + { + List taskList = new List(); + taskList.Add(new Task("Job1Task0a", 15, 0)); + taskList.Add(new Task("Job1Task0b", 25, 1)); + taskList.Add(new Task("Job1Task0c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job1Task1a", 25, 0)); + taskList.Add(new Task("Job1Task1b", 30, 1)); + taskList.Add(new Task("Job1Task1c", 40, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job1Task2a", 20, 0)); + taskList.Add(new Task("Job1Task2b", 35, 1)); + taskList.Add(new Task("Job1Task2c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job2Task0a", 15, 0)); + taskList.Add(new Task("Job2Task0b", 25, 1)); + taskList.Add(new Task("Job2Task0c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job2Task1a", 25, 0)); + taskList.Add(new Task("Job2Task1b", 30, 1)); + taskList.Add(new Task("Job2Task1c", 40, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job2Task2a", 20, 0)); + taskList.Add(new Task("Job2Task2b", 35, 1)); + taskList.Add(new Task("Job2Task2c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job3Task0a", 10, 0)); + taskList.Add(new Task("Job3Task0b", 15, 1)); + taskList.Add(new Task("Job3Task0c", 50, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job3Task1a", 50, 0)); + taskList.Add(new Task("Job3Task1b", 10, 1)); + taskList.Add(new Task("Job3Task1c", 20, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job3Task2a", 65, 0)); + taskList.Add(new Task("Job3Task2b", 5, 1)); + taskList.Add(new Task("Job3Task2c", 15, 2)); + myJobList.Add(new Job(taskList)); + + myJobList[0].Successor = myJobList[1]; + myJobList[1].Successor = myJobList[2]; + myJobList[2].Successor = null; + + myJobList[3].Successor = myJobList[4]; + myJobList[4].Successor = myJobList[5]; + myJobList[5].Successor = null; + + myJobList[6].Successor = myJobList[7]; + myJobList[7].Successor = myJobList[8]; + myJobList[8].Successor = null; + } + + public static int GetTaskCount() + { + int c = 0; + foreach (Job j in myJobList) + foreach (Task t in j.AlternativeTasks) + { + taskIndexes[t.Name] = c; + c++; + } + + return c; + } + + public static int GetEndTaskCount() + { + int c = 0; + foreach (Job j in myJobList) + if (j.Successor == null) + c += j.AlternativeTasks.Count; + return c; + } + + static void Main() + { + InitTaskList(); + int taskCount = GetTaskCount(); + + CpModel model = new CpModel(); + + IntervalVar[] tasks = new IntervalVar[taskCount]; + BoolVar[] taskChoosed = new BoolVar[taskCount]; + IntVar[] allEnds = new IntVar[GetEndTaskCount()]; + + int endJobCounter = 0; + foreach (Job j in myJobList) + { + BoolVar[] tmp = new BoolVar[j.AlternativeTasks.Count]; + int i = 0; + foreach (Task t in j.AlternativeTasks) + { + long ti = taskIndexes[t.Name]; + taskChoosed[ti] = model.NewBoolVar(t.Name + "_choose"); + tmp[i++] = taskChoosed[ti]; + IntVar start = model.NewIntVar(0, 10000, t.Name + "_start"); + IntVar end = model.NewIntVar(0, 10000, t.Name + "_end"); + tasks[ti] = model.NewIntervalVar(start, t.Duration, end, t.Name + "_interval"); + if (j.Successor == null) + allEnds[endJobCounter++] = end; + if (!tasksToEquipment.ContainsKey(t.Equipment)) + tasksToEquipment[t.Equipment] = new List(); + tasksToEquipment[t.Equipment].Add(tasks[ti]); + } + model.AddExactlyOne(tmp); + } + + foreach (KeyValuePair> pair in tasksToEquipment) + { + model.AddNoOverlap(pair.Value); + } + + IntVar makespan = model.NewIntVar(0, 100000, "makespan"); + model.AddMaxEquality(makespan, allEnds); + model.Minimize(makespan); + + // Create the solver. + CpSolver solver = new CpSolver(); + // Solve the problem. + solver.Solve(model); + Console.WriteLine(solver.ResponseStats()); + } +} diff --git a/examples/python/testdata/salbp_20_1.alb b/examples/python/testdata/salbp_20_1.alb index 6b780dae89b..e3d93785827 100644 --- a/examples/python/testdata/salbp_20_1.alb +++ b/examples/python/testdata/salbp_20_1.alb @@ -1,51 +1,51 @@ - -20 - - -1000 - - -0,268 - - - -1 142 -2 34 -3 140 -4 214 -5 121 -6 279 -7 50 -8 282 -9 129 -10 175 -11 97 -12 132 -13 107 -14 132 -15 69 -16 169 -17 73 -18 231 -19 120 -20 186 - - -1,6 -2,7 -4,8 -5,9 -6,10 -7,11 -8,12 -10,13 -11,13 -12,14 -12,15 -13,16 -13,17 -13,18 -14,20 -15,19 - - + +20 + + +1000 + + +0,268 + + + +1 142 +2 34 +3 140 +4 214 +5 121 +6 279 +7 50 +8 282 +9 129 +10 175 +11 97 +12 132 +13 107 +14 132 +15 69 +16 169 +17 73 +18 231 +19 120 +20 186 + + +1,6 +2,7 +4,8 +5,9 +6,10 +7,11 +8,12 +10,13 +11,13 +12,14 +12,15 +13,16 +13,17 +13,18 +14,20 +15,19 + + diff --git a/examples/tests/issue33.cs b/examples/tests/issue33.cs index 5c78383a347..2e888ca2072 100644 --- a/examples/tests/issue33.cs +++ b/examples/tests/issue33.cs @@ -1,676 +1,676 @@ -// Authors: Johan Wessén -// Copyright 2010-2025 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 System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System; - -public class Task -{ - public int Id { get; private set; } - public int TaskType { get; private set; } - public int LocationId { get; private set; } - public Dictionary Durations { get; private set; } - public int TaskPosition { get; private set; } - - public Task(int id, int taskType, int locationIndex, int taskPosition, Dictionary durations) - { - Id = id; - TaskType = taskType; - LocationId = locationIndex; - Durations = durations; - TaskPosition = taskPosition; - } - - public Task(int id, int taskType, int locationIndex, int taskPosition) - { - Id = id; - TaskType = taskType; - LocationId = locationIndex; - TaskPosition = taskPosition; - Durations = new Dictionary(); - } -} - -public class WorkLocation -{ - public int Id { get; private set; } - public int NbTasks - { - get { - Debug.Assert(Tasks != null); - return Tasks.Length; - } - set { - Debug.Assert(Tasks == null); - Tasks = new Task[value]; - } - } - public Task[] Tasks { get; private set; } - - public WorkLocation(int index) - { - Id = index; - } -} - -public class Tool -{ - public int Id { get; private set; } - public HashSet TaskTypes { get; set; } - public int[,] TravellingTime { get; set; } - public int InitialLocationId { get; set; } - - public Tool(int index, int initialLocation = 0) - { - Id = index; - InitialLocationId = initialLocation; - TaskTypes = new HashSet(); - } - - public void AddTaskType(int t) - { - TaskTypes.Add(t); - } - - public bool CanPerformTaskType(int taskType) - { - return TaskTypes.Contains(taskType); - } -} - -public class FactoryDescription -{ - public Tool[] Tools { get; private set; } - public WorkLocation[] Locations { get; private set; } - - public int NbWorkLocations - { - get { - return Locations.Length; - } - } - public int NbTools - { - get { - return Tools.Length; - } - } - - public int NbTaskPerCycle { get; private set; } - // TaskType go typically from 0 to 6. InspectionType indicates which - // is the TaskType that correspond to Inspection. - public int Inspection { get; private set; } - // All the time within the schedule horizon in which the blast can start. - public long[] InspectionStarts { get; private set; } - - public int Horizon { get; private set; } - - // horizon equal to 2 weeks (in minutes). - public FactoryDescription(int nbTools, int nbLocations, int nbTaskPerCycle, int horizon = 14 * 24 * 60) - { - Debug.Assert(nbTools > 0); - Debug.Assert(nbLocations > 0); - Debug.Assert(nbTaskPerCycle > 0); - Debug.Assert(horizon > 0); - NbTaskPerCycle = nbTaskPerCycle; - Inspection = NbTaskPerCycle - 1; - Tools = new Tool[nbTools]; - Horizon = horizon; - for (int i = 0; i < nbTools; i++) - Tools[i] = new Tool(i); - Locations = new WorkLocation[nbLocations]; - for (int i = 0; i < nbLocations; i++) - Locations[i] = new WorkLocation(i); - - InspectionStarts = new long[] { -1, 600, 1200, 1800, 2400, 2800 }; - } - - public Tool[] getToolPerTaskType(int taskType) - { - var elements = from tool in Tools - where tool.CanPerformTaskType(taskType) select tool; - return elements.ToArray(); - } - - public Task[] getFlatTaskList() - { - return (from location in Locations from task in location.Tasks orderby task.Id select task).ToArray(); - } - - public int[] getTaskTypes() - { - return (from location in Locations from task in location.Tasks select task.TaskType).Distinct().ToArray(); - } - - // TODO: This should be enhanced - public void SanityCheck() - { - foreach (Tool tool in Tools) - { - Debug.Assert(tool.TravellingTime.GetLength(0) == NbWorkLocations); - Debug.Assert(tool.TravellingTime.GetLength(1) == NbWorkLocations); - for (int i = 0; i < NbWorkLocations; i++) - Debug.Assert(tool.TravellingTime[i, i] == 0); - } - } -} - -interface DataReader -{ - FactoryDescription FetchData(); -} - -public class SmallSyntheticData : DataReader -{ - public SmallSyntheticData() - { - } - - public FactoryDescription FetchData() - { - // deterministic seed for result reproducibility - Random randomDuration = new Random(2); - - // FactoryDescription(nbTools, nblocations, nbTasks per cycle) - FactoryDescription factoryDescription = new FactoryDescription(5, 4, 3); - - // Travelling time and distance are temporarily identical and they - // are no different for different tools - int[,] travellingTime = new int[factoryDescription.NbWorkLocations, factoryDescription.NbWorkLocations]; - for (int i = 0; i < travellingTime.GetLength(0); i++) - { - for (int j = 0; j < travellingTime.GetLength(1); j++) - { - if (i == j) - travellingTime[i, j] = 0; - else - travellingTime[i, j] = (5 * Math.Abs(i - j)) * 10; - } - } - - factoryDescription.Tools[0].AddTaskType(0); - factoryDescription.Tools[1].AddTaskType(0); - factoryDescription.Tools[2].AddTaskType(1); - factoryDescription.Tools[3].AddTaskType(1); - factoryDescription.Tools[4].AddTaskType(2); - factoryDescription.Tools[1].AddTaskType(1); - - foreach (Tool tool in factoryDescription.Tools) - tool.TravellingTime = travellingTime; - - int c = 0; - int nbCyclePerWorkLocation = 2; - int[] boll = new int[100]; - for (int i = 0; i < factoryDescription.NbWorkLocations; i++) - { - factoryDescription.Locations[i].NbTasks = nbCyclePerWorkLocation * factoryDescription.NbTaskPerCycle; - for (int j = 0; j < nbCyclePerWorkLocation; j++) - { - for (int k = 0; k < factoryDescription.NbTaskPerCycle; k++) - { - Task t = new Task(c, k, i, k + j * factoryDescription.NbTaskPerCycle); - - // Filling in tool-dependent durations - Tool[] compatibleTools = factoryDescription.getToolPerTaskType(k); - foreach (Tool tool in compatibleTools) - { - boll[c] = randomDuration.Next(13, 17) * 10; - ; - t.Durations[tool.Id] = boll[c]; - } - factoryDescription.Locations[i].Tasks[t.TaskPosition] = t; - c++; - } - } - } - - factoryDescription.SanityCheck(); - return factoryDescription; - } -} - -public class RandomSelectToolHeuristic : NetDecisionBuilder -{ - private FactoryScheduling factoryScheduling; - private Random rnd; - - public RandomSelectToolHeuristic(FactoryScheduling factoryScheduling, int seed) - { - this.factoryScheduling = factoryScheduling; - // deterministic seed for result reproducibility - this.rnd = new Random(seed); - } - - public override Decision Next(Solver solver) - { - foreach (IntVar var in factoryScheduling.SelectedTool) - { - if (!var.Bound()) - { - int min = (int)var.Min(); - int max = (int)var.Max(); - int rndVal = rnd.Next(min, max + 1); - while (!var.Contains(rndVal)) - rndVal = rnd.Next(min, max + 1); - return solver.MakeAssignVariableValue(var, rndVal); - } - } - return null; - } -} - -class TaskAlternative -{ - public Task Task { get; private set; } - public IntVar ToolVar { get; set; } - public List Intervals { get; private set; } - - public TaskAlternative(Task t) - { - Task = t; - Intervals = new List(); - } -} - -public class FactoryScheduling -{ - private FactoryDescription factoryData; - private Solver solver; - - private Task[] tasks; - private int[] taskTypes; - - /* Flat list of all the tasks */ - private TaskAlternative[] taskStructures; - - /* Task per WorkLocation: location2Task[d][i]: the i-th task of the - * d-th location */ - private TaskAlternative[][] location2Task; - - /* Task per Tool: tool2Task[t][i]: the i-th task of the t-th tool. - Note that it does NOT imply that the it will be the i-th - executed. In other words, it should be considered as an unordered - set. Furthermore, tool2Task[t][i] can also be *unperformed* */ - private List[] tool2Task; - - /* All the transition times for the tools. - tool2TransitionTimes[t][i]: the transition time of the t-th tool - from the i-th task to the next */ - private List[] tool2TransitionTimes; - - /* Map between the interval var of a tool to its related task id. - toolIntervalVar2TaskId[t][k] = i: in the t-th tool, the k-th - interval var correspond to tasks[i] */ - private List[] toolIntervalVar2TaskId; - - /* Tools per task type: taskType2Tool[tt][t]: the t-th tool capable - * of doing the tt-th task type */ - private List[] taskType2Tool; - - /* For each task which tools is performed upon */ - private List selectedTool; - public List SelectedTool - { - get { - return selectedTool; - } - } - - /* Sequence of task for each tool */ - private SequenceVar[] allToolSequences; - public SequenceVar[] AllToolSequences - { - get { - return allToolSequences; - } - } - - /* Makespan var */ - private IntVar makespan; - - /* Objective */ - private OptimizeVar objective; - - /* maximum horizon */ - private int horizon; - - /* Start & End times of IntervalVars*/ - IntVar[][] startingTimes; - IntVar[][] endTimes; - - public FactoryScheduling(FactoryDescription data) - { - factoryData = data; - } - - private void Init() - { - horizon = factoryData.Horizon; - solver = new Solver("Factory Scheduling"); - tasks = factoryData.getFlatTaskList(); - taskTypes = factoryData.getTaskTypes(); - taskStructures = new TaskAlternative[tasks.Length]; - location2Task = new TaskAlternative[factoryData.NbWorkLocations][]; - tool2Task = new List[factoryData.NbTools]; - toolIntervalVar2TaskId = new List[factoryData.NbTools]; - tool2TransitionTimes = new List[factoryData.NbTools]; - - taskType2Tool = new List[taskTypes.Length]; - selectedTool = new List(); - for (int tt = 0; tt < taskTypes.Length; tt++) - taskType2Tool[tt] = new List(); - - foreach (Tool tool in factoryData.Tools) - foreach (int taskType in tool.TaskTypes) - taskType2Tool[taskType].Add(tool); - for (int d = 0; d < factoryData.NbWorkLocations; d++) - location2Task[d] = new TaskAlternative[factoryData.Locations[d].NbTasks]; - for (int t = 0; t < factoryData.NbTools; t++) - { - tool2Task[t] = new List(); - toolIntervalVar2TaskId[t] = new List(); - tool2TransitionTimes[t] = new List(); - } - - allToolSequences = new SequenceVar[factoryData.NbTools - 1]; - - startingTimes = new IntVar[factoryData.NbTools - 1][]; - endTimes = new IntVar[factoryData.NbTools - 1][]; - } - - private void PostTransitionTimeConstraints(int t, bool postTransitionsConstraint = true) - { - Tool tool = factoryData.Tools[t]; - // if it is a inspection, we make sure there are no transitiontimes - if (tool.CanPerformTaskType(factoryData.Inspection)) - tool2TransitionTimes[t].Add(null); - else - { - int[,] tt = tool.TravellingTime; - - SequenceVar seq = allToolSequences[t]; - long s = seq.Size(); - IntVar[] nextLocation = new IntVar[s + 1]; - - // The seq.Next(i) represents the task performed after the i-th - // task in the sequence seq.Next(0) represents the first task - // performed for extracting travelling times we need to get the - // related location In case a task is not performed (seq.Next(i) - // == i), i.e. it's pointing to itself The last performed task - // (or pre-start task, if no tasks are performed) will have - // seq.Next(i) == s + 1 therefore we add a virtual location - // whose travelling time is equal to 0 - // - // NOTE: The index of a SequenceVar are 0..n, but the domain - // range is 1..(n+1), this is due to that the start node = 0 is - // a dummy node, and the node where seq.Next(i) == n+1 is the - // end node - - // Extra elements for the unreachable start node (0), and the - // end node whose next task takes place in a virtual location - int[] taskIndex2locationId = new int[s + 2]; - taskIndex2locationId[0] = -10; - for (int i = 0; i < s; i++) - taskIndex2locationId[i + 1] = tasks[toolIntervalVar2TaskId[t][i]].LocationId; - - // this is the virtual location for unperformed tasks - taskIndex2locationId[s + 1] = factoryData.NbWorkLocations; - - // Build the travelling time matrix with the additional virtual location - int[][] ttWithVirtualLocation = new int [factoryData.NbWorkLocations + 1][]; - for (int d1 = 0; d1 < ttWithVirtualLocation.Length; d1++) - { - ttWithVirtualLocation[d1] = new int[factoryData.NbWorkLocations + 1]; - for (int d2 = 0; d2 < ttWithVirtualLocation.Length; d2++) - if (d1 == factoryData.NbWorkLocations) - { - ttWithVirtualLocation[d1][d2] = 0; - } - else - { - ttWithVirtualLocation[d1][d2] = (d2 == factoryData.NbWorkLocations) ? 0 : tt[d1, d2]; - } - } - - for (int i = 0; i < nextLocation.Length; i++) - { - // this is the next-location associated with the i-th task - nextLocation[i] = solver.MakeElement(taskIndex2locationId, seq.Next(i)).Var(); - - int d = (i == 0) ? tool.InitialLocationId : tasks[toolIntervalVar2TaskId[t][i - 1]].LocationId; - if (i == 0) - { - // To be changed - right now we don't have meaningful indata - // of previous location Ugly way of setting initial travel - // time to = 0, as this is how we find common grounds - // between benchmark algorithm and this - tool2TransitionTimes[t].Add( - solver.MakeElement(new int[ttWithVirtualLocation[d].Length], nextLocation[i]).Var()); - } - else - { - tool2TransitionTimes[t].Add(solver.MakeElement(ttWithVirtualLocation[d], nextLocation[i]).Var()); - } - } - - // Extra elements for the unreachable start node (0), and the - // end node whose next task takes place in a virtual location - startingTimes[t] = new IntVar[s + 2]; - endTimes[t] = new IntVar[s + 2]; - - startingTimes[t][0] = solver.MakeIntConst(0); - // Tbd: Set this endtime to the estimated time of finishing - // previous task for the current tool - endTimes[t][0] = solver.MakeIntConst(0); - - for (int i = 0; i < s; i++) - { - startingTimes[t][i + 1] = tool2Task[t][i].SafeStartExpr(-1).Var(); - endTimes[t][i + 1] = tool2Task[t][i].SafeEndExpr(-1).Var(); - } - startingTimes[t][s + 1] = solver.MakeIntConst(factoryData.Horizon); - endTimes[t][s + 1] = solver.MakeIntConst(factoryData.Horizon); - - // Enforce (or not) that each task is separated by the - // transition time to the next task - for (int i = 0; i < nextLocation.Length; i++) - { - IntVar nextStart = solver.MakeElement(startingTimes[t], seq.Next(i).Var()).Var(); - if (postTransitionsConstraint) - solver.Add(endTimes[t][i] + tool2TransitionTimes[t][i] <= nextStart); - } - } - } - - private void Model() - { - /* Building basic task data structures */ - for (int i = 0; i < tasks.Length; i++) - { - /* Create a new set of possible IntervalVars & IntVar to decide - * which one (and only 1) is performed */ - taskStructures[i] = new TaskAlternative(tasks[i]); - - /* Container to use when posting constraints */ - location2Task[tasks[i].LocationId][tasks[i].TaskPosition] = taskStructures[i]; - - /* Get task type */ - int taskType = tasks[i].TaskType; - - /* Possible tool for this task */ - List tools = taskType2Tool[taskType]; - bool optional = tools.Count > 1; - - /* List of boolean variables. If performedOnTool[t] == true then - * the task is performed on tool t */ - List performedOnTool = new List(); - for (int t = 0; t < tools.Count; t++) - { - /* Creating an IntervalVar. If tools.Count > 1 the intervalVar - * is *OPTIONAL* */ - int toolId = tools[t].Id; - Debug.Assert(tasks[i].Durations.ContainsKey(toolId)); - int duration = tasks[i].Durations[toolId]; - string name = "J " + tasks[i].Id + " [" + toolId + "]"; - - IntervalVar intervalVar; - if (taskType == factoryData.Inspection) - { - /* We set a 0 time if the task is an inspection */ - duration = 0; - intervalVar = solver.MakeFixedDurationIntervalVar(0, horizon, duration, optional, name); - IntVar start = intervalVar.SafeStartExpr(-1).Var(); - - intervalVar.SafeStartExpr(-1).Var().SetValues(factoryData.InspectionStarts); - } - else - { - intervalVar = solver.MakeFixedDurationIntervalVar(0, horizon, duration, optional, name); - } - - taskStructures[i].Intervals.Add(intervalVar); - tool2Task[toolId].Add(intervalVar); - toolIntervalVar2TaskId[toolId].Add(i); - - /* Collecting all the bool vars, even if they are optional */ - performedOnTool.Add(intervalVar.PerformedExpr().Var()); - } - - /* Linking the bool var to a single integer variable: */ - /* if alternativeToolVar == t <=> performedOnTool[t] == true */ - string alternativeName = "J " + tasks[i].Id; - IntVar alternativeToolVar = solver.MakeIntVar(0, tools.Count - 1, alternativeName); - taskStructures[i].ToolVar = alternativeToolVar; - - solver.Add(solver.MakeMapDomain(alternativeToolVar, performedOnTool.ToArray())); - Debug.Assert(performedOnTool.ToArray().Length == alternativeToolVar.Max() + 1); - - selectedTool.Add(alternativeToolVar); - } - - /* Creates precedences on a work Location in order to enforce a - * fully ordered set within the same location - */ - for (int d = 0; d < location2Task.Length; d++) - { - for (int i = 0; i < location2Task[d].Length - 1; i++) - { - TaskAlternative task1 = location2Task[d][i]; - TaskAlternative task2 = location2Task[d][i + 1]; - /* task1 must end before task2 starts */ - /* Adding precedence for each possible alternative pair */ - for (int t1 = 0; t1 < task1.Intervals.Count(); t1++) - { - IntervalVar task1Alternative = task1.Intervals[t1]; - for (int t2 = 0; t2 < task2.Intervals.Count(); t2++) - { - IntervalVar task2Alternative = task2.Intervals[t2]; - Constraint precedence = - solver.MakeIntervalVarRelation(task2Alternative, Solver.STARTS_AFTER_END, task1Alternative); - solver.Add(precedence); - } - } - } - } - - /* Adds disjunctive constraints on unary resources, and creates - * sequence variables. */ - for (int t = 0; t < factoryData.NbTools; t++) - { - string name = "Tool " + t; - - if (!factoryData.Tools[t].CanPerformTaskType(factoryData.Inspection)) - { - DisjunctiveConstraint ct = solver.MakeDisjunctiveConstraint(tool2Task[t].ToArray(), name); - solver.Add(ct); - allToolSequences[t] = ct.SequenceVar(); - } - PostTransitionTimeConstraints(t, true); - } - - /* Collecting all tasks end for makespan objective function */ - List intervalEnds = new List(); - for (int i = 0; i < tasks.Length; i++) - foreach (IntervalVar var in taskStructures[i].Intervals) - intervalEnds.Add(var.SafeEndExpr(-1).Var()); - - /* Objective: minimize the makespan (maximum end times of all tasks) */ - makespan = solver.MakeMax(intervalEnds.ToArray()).Var(); - objective = solver.MakeMinimize(makespan, 1); - } - - private void Search() - { - int seed = 2; // This is a good seed to show the crash - - /* Assigning first tools */ - DecisionBuilder myToolAssignmentPhase = new RandomSelectToolHeuristic(this, seed); - - /* Ranking of the tools */ - DecisionBuilder sequencingPhase = solver.MakePhase(allToolSequences, Solver.SEQUENCE_DEFAULT); - - /* Then fixing time of tasks as early as possible */ - DecisionBuilder timingPhase = solver.MakePhase(makespan, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); - - /* Overall phase */ - DecisionBuilder mainPhase = solver.Compose(myToolAssignmentPhase, sequencingPhase, timingPhase); - - /* Logging */ - const int logFrequency = 1000000; - SearchMonitor searchLog = solver.MakeSearchLog(logFrequency, objective); - - /* Restarts */ - SearchMonitor searchRestart = solver.MakeLubyRestart(100); - - /* Search Limit in ms */ - SearchLimit limit = solver.MakeTimeLimit(180 * 1000); - - /* Collecting best solution */ - SolutionCollector collector = solver.MakeLastSolutionCollector(); - collector.AddObjective(makespan); - - // collector.Add( pile.ToArray() ); - solver.NewSearch(mainPhase, searchLog, searchRestart, objective, limit); - while (solver.NextSolution()) - { - Console.WriteLine("MAKESPAN: " + makespan.Value()); - } - } - - public void Solve() - { - Init(); - Model(); - Search(); - } -} - -public class Issue33Test -{ - public static void FactorySchedulingTest() - { - FactoryScheduling scheduling = new FactoryScheduling(new SmallSyntheticData().FetchData()); - scheduling.Solve(); - } - static void Main() - { - FactorySchedulingTest(); - } -} +// Authors: Johan Wessén +// Copyright 2010-2025 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 System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System; + +public class Task +{ + public int Id { get; private set; } + public int TaskType { get; private set; } + public int LocationId { get; private set; } + public Dictionary Durations { get; private set; } + public int TaskPosition { get; private set; } + + public Task(int id, int taskType, int locationIndex, int taskPosition, Dictionary durations) + { + Id = id; + TaskType = taskType; + LocationId = locationIndex; + Durations = durations; + TaskPosition = taskPosition; + } + + public Task(int id, int taskType, int locationIndex, int taskPosition) + { + Id = id; + TaskType = taskType; + LocationId = locationIndex; + TaskPosition = taskPosition; + Durations = new Dictionary(); + } +} + +public class WorkLocation +{ + public int Id { get; private set; } + public int NbTasks + { + get { + Debug.Assert(Tasks != null); + return Tasks.Length; + } + set { + Debug.Assert(Tasks == null); + Tasks = new Task[value]; + } + } + public Task[] Tasks { get; private set; } + + public WorkLocation(int index) + { + Id = index; + } +} + +public class Tool +{ + public int Id { get; private set; } + public HashSet TaskTypes { get; set; } + public int[,] TravellingTime { get; set; } + public int InitialLocationId { get; set; } + + public Tool(int index, int initialLocation = 0) + { + Id = index; + InitialLocationId = initialLocation; + TaskTypes = new HashSet(); + } + + public void AddTaskType(int t) + { + TaskTypes.Add(t); + } + + public bool CanPerformTaskType(int taskType) + { + return TaskTypes.Contains(taskType); + } +} + +public class FactoryDescription +{ + public Tool[] Tools { get; private set; } + public WorkLocation[] Locations { get; private set; } + + public int NbWorkLocations + { + get { + return Locations.Length; + } + } + public int NbTools + { + get { + return Tools.Length; + } + } + + public int NbTaskPerCycle { get; private set; } + // TaskType go typically from 0 to 6. InspectionType indicates which + // is the TaskType that correspond to Inspection. + public int Inspection { get; private set; } + // All the time within the schedule horizon in which the blast can start. + public long[] InspectionStarts { get; private set; } + + public int Horizon { get; private set; } + + // horizon equal to 2 weeks (in minutes). + public FactoryDescription(int nbTools, int nbLocations, int nbTaskPerCycle, int horizon = 14 * 24 * 60) + { + Debug.Assert(nbTools > 0); + Debug.Assert(nbLocations > 0); + Debug.Assert(nbTaskPerCycle > 0); + Debug.Assert(horizon > 0); + NbTaskPerCycle = nbTaskPerCycle; + Inspection = NbTaskPerCycle - 1; + Tools = new Tool[nbTools]; + Horizon = horizon; + for (int i = 0; i < nbTools; i++) + Tools[i] = new Tool(i); + Locations = new WorkLocation[nbLocations]; + for (int i = 0; i < nbLocations; i++) + Locations[i] = new WorkLocation(i); + + InspectionStarts = new long[] { -1, 600, 1200, 1800, 2400, 2800 }; + } + + public Tool[] getToolPerTaskType(int taskType) + { + var elements = from tool in Tools + where tool.CanPerformTaskType(taskType) select tool; + return elements.ToArray(); + } + + public Task[] getFlatTaskList() + { + return (from location in Locations from task in location.Tasks orderby task.Id select task).ToArray(); + } + + public int[] getTaskTypes() + { + return (from location in Locations from task in location.Tasks select task.TaskType).Distinct().ToArray(); + } + + // TODO: This should be enhanced + public void SanityCheck() + { + foreach (Tool tool in Tools) + { + Debug.Assert(tool.TravellingTime.GetLength(0) == NbWorkLocations); + Debug.Assert(tool.TravellingTime.GetLength(1) == NbWorkLocations); + for (int i = 0; i < NbWorkLocations; i++) + Debug.Assert(tool.TravellingTime[i, i] == 0); + } + } +} + +interface DataReader +{ + FactoryDescription FetchData(); +} + +public class SmallSyntheticData : DataReader +{ + public SmallSyntheticData() + { + } + + public FactoryDescription FetchData() + { + // deterministic seed for result reproducibility + Random randomDuration = new Random(2); + + // FactoryDescription(nbTools, nblocations, nbTasks per cycle) + FactoryDescription factoryDescription = new FactoryDescription(5, 4, 3); + + // Travelling time and distance are temporarily identical and they + // are no different for different tools + int[,] travellingTime = new int[factoryDescription.NbWorkLocations, factoryDescription.NbWorkLocations]; + for (int i = 0; i < travellingTime.GetLength(0); i++) + { + for (int j = 0; j < travellingTime.GetLength(1); j++) + { + if (i == j) + travellingTime[i, j] = 0; + else + travellingTime[i, j] = (5 * Math.Abs(i - j)) * 10; + } + } + + factoryDescription.Tools[0].AddTaskType(0); + factoryDescription.Tools[1].AddTaskType(0); + factoryDescription.Tools[2].AddTaskType(1); + factoryDescription.Tools[3].AddTaskType(1); + factoryDescription.Tools[4].AddTaskType(2); + factoryDescription.Tools[1].AddTaskType(1); + + foreach (Tool tool in factoryDescription.Tools) + tool.TravellingTime = travellingTime; + + int c = 0; + int nbCyclePerWorkLocation = 2; + int[] boll = new int[100]; + for (int i = 0; i < factoryDescription.NbWorkLocations; i++) + { + factoryDescription.Locations[i].NbTasks = nbCyclePerWorkLocation * factoryDescription.NbTaskPerCycle; + for (int j = 0; j < nbCyclePerWorkLocation; j++) + { + for (int k = 0; k < factoryDescription.NbTaskPerCycle; k++) + { + Task t = new Task(c, k, i, k + j * factoryDescription.NbTaskPerCycle); + + // Filling in tool-dependent durations + Tool[] compatibleTools = factoryDescription.getToolPerTaskType(k); + foreach (Tool tool in compatibleTools) + { + boll[c] = randomDuration.Next(13, 17) * 10; + ; + t.Durations[tool.Id] = boll[c]; + } + factoryDescription.Locations[i].Tasks[t.TaskPosition] = t; + c++; + } + } + } + + factoryDescription.SanityCheck(); + return factoryDescription; + } +} + +public class RandomSelectToolHeuristic : NetDecisionBuilder +{ + private FactoryScheduling factoryScheduling; + private Random rnd; + + public RandomSelectToolHeuristic(FactoryScheduling factoryScheduling, int seed) + { + this.factoryScheduling = factoryScheduling; + // deterministic seed for result reproducibility + this.rnd = new Random(seed); + } + + public override Decision Next(Solver solver) + { + foreach (IntVar var in factoryScheduling.SelectedTool) + { + if (!var.Bound()) + { + int min = (int)var.Min(); + int max = (int)var.Max(); + int rndVal = rnd.Next(min, max + 1); + while (!var.Contains(rndVal)) + rndVal = rnd.Next(min, max + 1); + return solver.MakeAssignVariableValue(var, rndVal); + } + } + return null; + } +} + +class TaskAlternative +{ + public Task Task { get; private set; } + public IntVar ToolVar { get; set; } + public List Intervals { get; private set; } + + public TaskAlternative(Task t) + { + Task = t; + Intervals = new List(); + } +} + +public class FactoryScheduling +{ + private FactoryDescription factoryData; + private Solver solver; + + private Task[] tasks; + private int[] taskTypes; + + /* Flat list of all the tasks */ + private TaskAlternative[] taskStructures; + + /* Task per WorkLocation: location2Task[d][i]: the i-th task of the + * d-th location */ + private TaskAlternative[][] location2Task; + + /* Task per Tool: tool2Task[t][i]: the i-th task of the t-th tool. + Note that it does NOT imply that the it will be the i-th + executed. In other words, it should be considered as an unordered + set. Furthermore, tool2Task[t][i] can also be *unperformed* */ + private List[] tool2Task; + + /* All the transition times for the tools. + tool2TransitionTimes[t][i]: the transition time of the t-th tool + from the i-th task to the next */ + private List[] tool2TransitionTimes; + + /* Map between the interval var of a tool to its related task id. + toolIntervalVar2TaskId[t][k] = i: in the t-th tool, the k-th + interval var correspond to tasks[i] */ + private List[] toolIntervalVar2TaskId; + + /* Tools per task type: taskType2Tool[tt][t]: the t-th tool capable + * of doing the tt-th task type */ + private List[] taskType2Tool; + + /* For each task which tools is performed upon */ + private List selectedTool; + public List SelectedTool + { + get { + return selectedTool; + } + } + + /* Sequence of task for each tool */ + private SequenceVar[] allToolSequences; + public SequenceVar[] AllToolSequences + { + get { + return allToolSequences; + } + } + + /* Makespan var */ + private IntVar makespan; + + /* Objective */ + private OptimizeVar objective; + + /* maximum horizon */ + private int horizon; + + /* Start & End times of IntervalVars*/ + IntVar[][] startingTimes; + IntVar[][] endTimes; + + public FactoryScheduling(FactoryDescription data) + { + factoryData = data; + } + + private void Init() + { + horizon = factoryData.Horizon; + solver = new Solver("Factory Scheduling"); + tasks = factoryData.getFlatTaskList(); + taskTypes = factoryData.getTaskTypes(); + taskStructures = new TaskAlternative[tasks.Length]; + location2Task = new TaskAlternative[factoryData.NbWorkLocations][]; + tool2Task = new List[factoryData.NbTools]; + toolIntervalVar2TaskId = new List[factoryData.NbTools]; + tool2TransitionTimes = new List[factoryData.NbTools]; + + taskType2Tool = new List[taskTypes.Length]; + selectedTool = new List(); + for (int tt = 0; tt < taskTypes.Length; tt++) + taskType2Tool[tt] = new List(); + + foreach (Tool tool in factoryData.Tools) + foreach (int taskType in tool.TaskTypes) + taskType2Tool[taskType].Add(tool); + for (int d = 0; d < factoryData.NbWorkLocations; d++) + location2Task[d] = new TaskAlternative[factoryData.Locations[d].NbTasks]; + for (int t = 0; t < factoryData.NbTools; t++) + { + tool2Task[t] = new List(); + toolIntervalVar2TaskId[t] = new List(); + tool2TransitionTimes[t] = new List(); + } + + allToolSequences = new SequenceVar[factoryData.NbTools - 1]; + + startingTimes = new IntVar[factoryData.NbTools - 1][]; + endTimes = new IntVar[factoryData.NbTools - 1][]; + } + + private void PostTransitionTimeConstraints(int t, bool postTransitionsConstraint = true) + { + Tool tool = factoryData.Tools[t]; + // if it is a inspection, we make sure there are no transitiontimes + if (tool.CanPerformTaskType(factoryData.Inspection)) + tool2TransitionTimes[t].Add(null); + else + { + int[,] tt = tool.TravellingTime; + + SequenceVar seq = allToolSequences[t]; + long s = seq.Size(); + IntVar[] nextLocation = new IntVar[s + 1]; + + // The seq.Next(i) represents the task performed after the i-th + // task in the sequence seq.Next(0) represents the first task + // performed for extracting travelling times we need to get the + // related location In case a task is not performed (seq.Next(i) + // == i), i.e. it's pointing to itself The last performed task + // (or pre-start task, if no tasks are performed) will have + // seq.Next(i) == s + 1 therefore we add a virtual location + // whose travelling time is equal to 0 + // + // NOTE: The index of a SequenceVar are 0..n, but the domain + // range is 1..(n+1), this is due to that the start node = 0 is + // a dummy node, and the node where seq.Next(i) == n+1 is the + // end node + + // Extra elements for the unreachable start node (0), and the + // end node whose next task takes place in a virtual location + int[] taskIndex2locationId = new int[s + 2]; + taskIndex2locationId[0] = -10; + for (int i = 0; i < s; i++) + taskIndex2locationId[i + 1] = tasks[toolIntervalVar2TaskId[t][i]].LocationId; + + // this is the virtual location for unperformed tasks + taskIndex2locationId[s + 1] = factoryData.NbWorkLocations; + + // Build the travelling time matrix with the additional virtual location + int[][] ttWithVirtualLocation = new int [factoryData.NbWorkLocations + 1][]; + for (int d1 = 0; d1 < ttWithVirtualLocation.Length; d1++) + { + ttWithVirtualLocation[d1] = new int[factoryData.NbWorkLocations + 1]; + for (int d2 = 0; d2 < ttWithVirtualLocation.Length; d2++) + if (d1 == factoryData.NbWorkLocations) + { + ttWithVirtualLocation[d1][d2] = 0; + } + else + { + ttWithVirtualLocation[d1][d2] = (d2 == factoryData.NbWorkLocations) ? 0 : tt[d1, d2]; + } + } + + for (int i = 0; i < nextLocation.Length; i++) + { + // this is the next-location associated with the i-th task + nextLocation[i] = solver.MakeElement(taskIndex2locationId, seq.Next(i)).Var(); + + int d = (i == 0) ? tool.InitialLocationId : tasks[toolIntervalVar2TaskId[t][i - 1]].LocationId; + if (i == 0) + { + // To be changed - right now we don't have meaningful indata + // of previous location Ugly way of setting initial travel + // time to = 0, as this is how we find common grounds + // between benchmark algorithm and this + tool2TransitionTimes[t].Add( + solver.MakeElement(new int[ttWithVirtualLocation[d].Length], nextLocation[i]).Var()); + } + else + { + tool2TransitionTimes[t].Add(solver.MakeElement(ttWithVirtualLocation[d], nextLocation[i]).Var()); + } + } + + // Extra elements for the unreachable start node (0), and the + // end node whose next task takes place in a virtual location + startingTimes[t] = new IntVar[s + 2]; + endTimes[t] = new IntVar[s + 2]; + + startingTimes[t][0] = solver.MakeIntConst(0); + // Tbd: Set this endtime to the estimated time of finishing + // previous task for the current tool + endTimes[t][0] = solver.MakeIntConst(0); + + for (int i = 0; i < s; i++) + { + startingTimes[t][i + 1] = tool2Task[t][i].SafeStartExpr(-1).Var(); + endTimes[t][i + 1] = tool2Task[t][i].SafeEndExpr(-1).Var(); + } + startingTimes[t][s + 1] = solver.MakeIntConst(factoryData.Horizon); + endTimes[t][s + 1] = solver.MakeIntConst(factoryData.Horizon); + + // Enforce (or not) that each task is separated by the + // transition time to the next task + for (int i = 0; i < nextLocation.Length; i++) + { + IntVar nextStart = solver.MakeElement(startingTimes[t], seq.Next(i).Var()).Var(); + if (postTransitionsConstraint) + solver.Add(endTimes[t][i] + tool2TransitionTimes[t][i] <= nextStart); + } + } + } + + private void Model() + { + /* Building basic task data structures */ + for (int i = 0; i < tasks.Length; i++) + { + /* Create a new set of possible IntervalVars & IntVar to decide + * which one (and only 1) is performed */ + taskStructures[i] = new TaskAlternative(tasks[i]); + + /* Container to use when posting constraints */ + location2Task[tasks[i].LocationId][tasks[i].TaskPosition] = taskStructures[i]; + + /* Get task type */ + int taskType = tasks[i].TaskType; + + /* Possible tool for this task */ + List tools = taskType2Tool[taskType]; + bool optional = tools.Count > 1; + + /* List of boolean variables. If performedOnTool[t] == true then + * the task is performed on tool t */ + List performedOnTool = new List(); + for (int t = 0; t < tools.Count; t++) + { + /* Creating an IntervalVar. If tools.Count > 1 the intervalVar + * is *OPTIONAL* */ + int toolId = tools[t].Id; + Debug.Assert(tasks[i].Durations.ContainsKey(toolId)); + int duration = tasks[i].Durations[toolId]; + string name = "J " + tasks[i].Id + " [" + toolId + "]"; + + IntervalVar intervalVar; + if (taskType == factoryData.Inspection) + { + /* We set a 0 time if the task is an inspection */ + duration = 0; + intervalVar = solver.MakeFixedDurationIntervalVar(0, horizon, duration, optional, name); + IntVar start = intervalVar.SafeStartExpr(-1).Var(); + + intervalVar.SafeStartExpr(-1).Var().SetValues(factoryData.InspectionStarts); + } + else + { + intervalVar = solver.MakeFixedDurationIntervalVar(0, horizon, duration, optional, name); + } + + taskStructures[i].Intervals.Add(intervalVar); + tool2Task[toolId].Add(intervalVar); + toolIntervalVar2TaskId[toolId].Add(i); + + /* Collecting all the bool vars, even if they are optional */ + performedOnTool.Add(intervalVar.PerformedExpr().Var()); + } + + /* Linking the bool var to a single integer variable: */ + /* if alternativeToolVar == t <=> performedOnTool[t] == true */ + string alternativeName = "J " + tasks[i].Id; + IntVar alternativeToolVar = solver.MakeIntVar(0, tools.Count - 1, alternativeName); + taskStructures[i].ToolVar = alternativeToolVar; + + solver.Add(solver.MakeMapDomain(alternativeToolVar, performedOnTool.ToArray())); + Debug.Assert(performedOnTool.ToArray().Length == alternativeToolVar.Max() + 1); + + selectedTool.Add(alternativeToolVar); + } + + /* Creates precedences on a work Location in order to enforce a + * fully ordered set within the same location + */ + for (int d = 0; d < location2Task.Length; d++) + { + for (int i = 0; i < location2Task[d].Length - 1; i++) + { + TaskAlternative task1 = location2Task[d][i]; + TaskAlternative task2 = location2Task[d][i + 1]; + /* task1 must end before task2 starts */ + /* Adding precedence for each possible alternative pair */ + for (int t1 = 0; t1 < task1.Intervals.Count(); t1++) + { + IntervalVar task1Alternative = task1.Intervals[t1]; + for (int t2 = 0; t2 < task2.Intervals.Count(); t2++) + { + IntervalVar task2Alternative = task2.Intervals[t2]; + Constraint precedence = + solver.MakeIntervalVarRelation(task2Alternative, Solver.STARTS_AFTER_END, task1Alternative); + solver.Add(precedence); + } + } + } + } + + /* Adds disjunctive constraints on unary resources, and creates + * sequence variables. */ + for (int t = 0; t < factoryData.NbTools; t++) + { + string name = "Tool " + t; + + if (!factoryData.Tools[t].CanPerformTaskType(factoryData.Inspection)) + { + DisjunctiveConstraint ct = solver.MakeDisjunctiveConstraint(tool2Task[t].ToArray(), name); + solver.Add(ct); + allToolSequences[t] = ct.SequenceVar(); + } + PostTransitionTimeConstraints(t, true); + } + + /* Collecting all tasks end for makespan objective function */ + List intervalEnds = new List(); + for (int i = 0; i < tasks.Length; i++) + foreach (IntervalVar var in taskStructures[i].Intervals) + intervalEnds.Add(var.SafeEndExpr(-1).Var()); + + /* Objective: minimize the makespan (maximum end times of all tasks) */ + makespan = solver.MakeMax(intervalEnds.ToArray()).Var(); + objective = solver.MakeMinimize(makespan, 1); + } + + private void Search() + { + int seed = 2; // This is a good seed to show the crash + + /* Assigning first tools */ + DecisionBuilder myToolAssignmentPhase = new RandomSelectToolHeuristic(this, seed); + + /* Ranking of the tools */ + DecisionBuilder sequencingPhase = solver.MakePhase(allToolSequences, Solver.SEQUENCE_DEFAULT); + + /* Then fixing time of tasks as early as possible */ + DecisionBuilder timingPhase = solver.MakePhase(makespan, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); + + /* Overall phase */ + DecisionBuilder mainPhase = solver.Compose(myToolAssignmentPhase, sequencingPhase, timingPhase); + + /* Logging */ + const int logFrequency = 1000000; + SearchMonitor searchLog = solver.MakeSearchLog(logFrequency, objective); + + /* Restarts */ + SearchMonitor searchRestart = solver.MakeLubyRestart(100); + + /* Search Limit in ms */ + SearchLimit limit = solver.MakeTimeLimit(180 * 1000); + + /* Collecting best solution */ + SolutionCollector collector = solver.MakeLastSolutionCollector(); + collector.AddObjective(makespan); + + // collector.Add( pile.ToArray() ); + solver.NewSearch(mainPhase, searchLog, searchRestart, objective, limit); + while (solver.NextSolution()) + { + Console.WriteLine("MAKESPAN: " + makespan.Value()); + } + } + + public void Solve() + { + Init(); + Model(); + Search(); + } +} + +public class Issue33Test +{ + public static void FactorySchedulingTest() + { + FactoryScheduling scheduling = new FactoryScheduling(new SmallSyntheticData().FetchData()); + scheduling.Solve(); + } + static void Main() + { + FactorySchedulingTest(); + } +} diff --git a/ortools/dotnet/Google.OrTools.sln b/ortools/dotnet/Google.OrTools.sln index c2ef81d8af4..d906c3e4eca 100644 --- a/ortools/dotnet/Google.OrTools.sln +++ b/ortools/dotnet/Google.OrTools.sln @@ -1,37 +1,37 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.OrTools.runtime.linux-x64", "Google.OrTools.runtime.linux-x64\Google.OrTools.runtime.linux-x64.csproj", "{FC646C34-8541-427D-B9F6-1247798F4574}" -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.OrTools.runtime.osx-x64", "Google.OrTools.runtime.osx-x64\Google.OrTools.runtime.osx-x64.csproj", "{FC646C34-8541-427D-B9F6-1247798F4574}" -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.OrTools.runtime.win-x64", "Google.OrTools.runtime.win-x64\Google.OrTools.runtime.win-x64.csproj", "{FC646C34-8541-427D-B9F6-1247798F4574}" -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.OrTools", "Google.OrTools\Google.OrTools.csproj", "{FC646C34-8541-427D-B9F6-1247798F4574}" -EndProject -Global -GlobalSection(SolutionConfigurationPlatforms) = preSolution -Debug|Any CPU = Debug|Any CPU -Debug|x64 = Debug|x64 -Debug|x86 = Debug|x86 -Release|Any CPU = Release|Any CPU -Release|x64 = Release|x64 -Release|x86 = Release|x86 -EndGlobalSection -GlobalSection(SolutionProperties) = preSolution -HideSolutionNode = FALSE -EndGlobalSection -GlobalSection(ProjectConfigurationPlatforms) = postSolution -{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|Any CPU.ActiveCfg = Debug|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|Any CPU.Build.0 = Debug|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|x64.ActiveCfg = Debug|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|x64.Build.0 = Debug|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|x86.ActiveCfg = Debug|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|x86.Build.0 = Debug|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Release|Any CPU.ActiveCfg = Release|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Release|Any CPU.Build.0 = Release|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Release|x64.ActiveCfg = Release|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Release|x64.Build.0 = Release|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Release|x86.ActiveCfg = Release|Any CPU -{FC646C34-8541-427D-B9F6-1247798F4574}.Release|x86.Build.0 = Release|Any CPU -EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.OrTools.runtime.linux-x64", "Google.OrTools.runtime.linux-x64\Google.OrTools.runtime.linux-x64.csproj", "{FC646C34-8541-427D-B9F6-1247798F4574}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.OrTools.runtime.osx-x64", "Google.OrTools.runtime.osx-x64\Google.OrTools.runtime.osx-x64.csproj", "{FC646C34-8541-427D-B9F6-1247798F4574}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.OrTools.runtime.win-x64", "Google.OrTools.runtime.win-x64\Google.OrTools.runtime.win-x64.csproj", "{FC646C34-8541-427D-B9F6-1247798F4574}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.OrTools", "Google.OrTools\Google.OrTools.csproj", "{FC646C34-8541-427D-B9F6-1247798F4574}" +EndProject +Global +GlobalSection(SolutionConfigurationPlatforms) = preSolution +Debug|Any CPU = Debug|Any CPU +Debug|x64 = Debug|x64 +Debug|x86 = Debug|x86 +Release|Any CPU = Release|Any CPU +Release|x64 = Release|x64 +Release|x86 = Release|x86 +EndGlobalSection +GlobalSection(SolutionProperties) = preSolution +HideSolutionNode = FALSE +EndGlobalSection +GlobalSection(ProjectConfigurationPlatforms) = postSolution +{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|Any CPU.ActiveCfg = Debug|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|Any CPU.Build.0 = Debug|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|x64.ActiveCfg = Debug|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|x64.Build.0 = Debug|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|x86.ActiveCfg = Debug|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Debug|x86.Build.0 = Debug|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Release|Any CPU.ActiveCfg = Release|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Release|Any CPU.Build.0 = Release|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Release|x64.ActiveCfg = Release|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Release|x64.Build.0 = Release|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Release|x86.ActiveCfg = Release|Any CPU +{FC646C34-8541-427D-B9F6-1247798F4574}.Release|x86.Build.0 = Release|Any CPU +EndGlobalSection +EndGlobal diff --git a/ortools/routing/parsers/testdata/pdtsp_prob10b.txt b/ortools/routing/parsers/testdata/pdtsp_prob10b.txt index d1194cb91c2..69abc76207d 100644 --- a/ortools/routing/parsers/testdata/pdtsp_prob10b.txt +++ b/ortools/routing/parsers/testdata/pdtsp_prob10b.txt @@ -1,23 +1,23 @@ -21 -1 682 266 -2 129 265 0 12 -3 298 495 0 13 -4 720 160 0 14 -5 93 10 0 15 -6 891 782 0 16 -7 888 533 0 17 -8 414 290 0 18 -9 61 22 0 19 -10 485 352 0 20 -11 817 619 0 21 -12 669 775 1 2 -13 628 117 1 3 -14 178 31 1 4 -15 733 97 1 5 -16 985 320 1 6 -17 319 0 1 7 -18 545 283 1 8 -19 331 664 1 9 -20 598 785 1 10 -21 245 810 1 11 --999 +21 +1 682 266 +2 129 265 0 12 +3 298 495 0 13 +4 720 160 0 14 +5 93 10 0 15 +6 891 782 0 16 +7 888 533 0 17 +8 414 290 0 18 +9 61 22 0 19 +10 485 352 0 20 +11 817 619 0 21 +12 669 775 1 2 +13 628 117 1 3 +14 178 31 1 4 +15 733 97 1 5 +16 985 320 1 6 +17 319 0 1 7 +18 545 283 1 8 +19 331 664 1 9 +20 598 785 1 10 +21 245 810 1 11 +-999