diff --git a/.github/workflows/cmake_system_deps.yml b/.github/workflows/cmake_system_deps.yml index 2172af0d61..a9b6e4dee8 100644 --- a/.github/workflows/cmake_system_deps.yml +++ b/.github/workflows/cmake_system_deps.yml @@ -36,5 +36,6 @@ jobs: - name: Test Install run: make --directory=cmake ${DISTRO}_${LANG}_install_test -# TODO(mizux): Add macOS + brew job -# TODO(mizux): Add Windows + choco/vcpkg job +# TODO(user): Add macOS + brew job +# TODO(user): Add Windows + choco/vcpkg job + diff --git a/cmake/python.cmake b/cmake/python.cmake index d91789c2b0..46224d4467 100644 --- a/cmake/python.cmake +++ b/cmake/python.cmake @@ -389,6 +389,7 @@ if(BUILD_TESTING) ${PROJECT_SOURCE_DIR}/ortools/init/python/version_test.py.in ${PROJECT_BINARY_DIR}/python/version_test.py @ONLY) + # run the tests within the virtualenv add_test(NAME python_init_version_test COMMAND ${VENV_Python3_EXECUTABLE} ${PROJECT_BINARY_DIR}/python/version_test.py) diff --git a/cmake/samples/dotnet/CPSample.cs b/cmake/samples/dotnet/CPSample.cs index acc5ca18f2..2adff51a46 100644 --- a/cmake/samples/dotnet/CPSample.cs +++ b/cmake/samples/dotnet/CPSample.cs @@ -1,3 +1,16 @@ +// Copyright 2010-2022 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 Xunit; diff --git a/cmake/samples/dotnet/LPSample.cs b/cmake/samples/dotnet/LPSample.cs index 9de0277377..dd7910dec4 100644 --- a/cmake/samples/dotnet/LPSample.cs +++ b/cmake/samples/dotnet/LPSample.cs @@ -1,3 +1,16 @@ +// Copyright 2010-2022 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 Xunit; diff --git a/cmake/samples/dotnet/RoutingSample.cs b/cmake/samples/dotnet/RoutingSample.cs index edd1bed20c..ce2cd8413a 100644 --- a/cmake/samples/dotnet/RoutingSample.cs +++ b/cmake/samples/dotnet/RoutingSample.cs @@ -1,3 +1,16 @@ +// Copyright 2010-2022 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 Xunit; diff --git a/cmake/samples/dotnet/SATSample.cs b/cmake/samples/dotnet/SATSample.cs index 122df519a7..0cf6fcf076 100644 --- a/cmake/samples/dotnet/SATSample.cs +++ b/cmake/samples/dotnet/SATSample.cs @@ -1,3 +1,16 @@ +// Copyright 2010-2022 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 Xunit; diff --git a/cmake/samples/java/src/main/java/com/google/ortools/App.java b/cmake/samples/java/src/main/java/com/google/ortools/App.java index b3fa4d82b2..64fe48d720 100644 --- a/cmake/samples/java/src/main/java/com/google/ortools/App.java +++ b/cmake/samples/java/src/main/java/com/google/ortools/App.java @@ -1,3 +1,16 @@ +// Copyright 2010-2022 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. + package com.google.ortools; /** Hello world! */ diff --git a/cmake/samples/java/src/test/java/com/google/ortools/AppTest.java b/cmake/samples/java/src/test/java/com/google/ortools/AppTest.java index b14e396326..61dcde5b9e 100644 --- a/cmake/samples/java/src/test/java/com/google/ortools/AppTest.java +++ b/cmake/samples/java/src/test/java/com/google/ortools/AppTest.java @@ -1,3 +1,16 @@ +// Copyright 2010-2022 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. + package com.google.ortools; import junit.framework.Test; diff --git a/cmake/samples/python/sample.py b/cmake/samples/python/sample.py index 3e425b047f..063b5a8aee 100644 --- a/cmake/samples/python/sample.py +++ b/cmake/samples/python/sample.py @@ -1,3 +1,17 @@ +#!/usr/bin/env python3 +# Copyright 2010-2022 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. + """Sample to test or-tools installation.""" import ortools # from ortools.algorithms import pywrapknapsack_solver diff --git a/makefiles/docs/generate_image.sh b/makefiles/docs/generate_image.sh index 5bd21128a3..f1edd7a072 100755 --- a/makefiles/docs/generate_image.sh +++ b/makefiles/docs/generate_image.sh @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -#/usr/bin/env bash set -ex rm -f *.svg diff --git a/ortools/base/BUILD.bazel b/ortools/base/BUILD.bazel index a56d400caa..281a0a5205 100644 --- a/ortools/base/BUILD.bazel +++ b/ortools/base/BUILD.bazel @@ -202,6 +202,7 @@ cc_library( hdrs = [ "file.h", "helpers.h", + "options.h", ], deps = [ ":base", diff --git a/ortools/base/options.h b/ortools/base/options.h new file mode 100644 index 0000000000..1aa40990c4 --- /dev/null +++ b/ortools/base/options.h @@ -0,0 +1,19 @@ +// Copyright 2010-2022 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. + +#ifndef OR_TOOLS_BASE_OPTIONS_H_ +#define OR_TOOLS_BASE_OPTIONS_H_ + +#include "ortools/base/file.h" + +#endif // OR_TOOLS_BASE_OPTIONS_H_ diff --git a/ortools/constraint_solver/docs/CP.md b/ortools/constraint_solver/docs/CP.md index 5b0e90e542..430415d217 100644 --- a/ortools/constraint_solver/docs/CP.md +++ b/ortools/constraint_solver/docs/CP.md @@ -68,49 +68,50 @@ int main(int argc, char** argv) { ### Python code samples ```python +#!/usr/bin/env python3 """Simple Constraint optimization example.""" from ortools.constraint_solver import pywrapcp def main(): - """Entry point of the program.""" - # Instantiate the solver. - solver = pywrapcp.Solver('CPSimple') + """Entry point of the program.""" + # Instantiate the solver. + solver = pywrapcp.Solver('CPSimple') - # Create the variables. - num_vals = 3 - x = solver.IntVar(0, num_vals - 1, 'x') - y = solver.IntVar(0, num_vals - 1, 'y') - z = solver.IntVar(0, num_vals - 1, 'z') + # Create the variables. + num_vals = 3 + x = solver.IntVar(0, num_vals - 1, 'x') + y = solver.IntVar(0, num_vals - 1, 'y') + z = solver.IntVar(0, num_vals - 1, 'z') - # Constraint 0: x != y. - solver.Add(x != y) - print('Number of constraints: ', solver.Constraints()) + # Constraint 0: x != y. + solver.Add(x != y) + print('Number of constraints: ', solver.Constraints()) - # Solve the problem. - decision_builder = solver.Phase([x, y, z], solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MIN_VALUE) + # Solve the problem. + decision_builder = solver.Phase([x, y, z], solver.CHOOSE_FIRST_UNBOUND, + solver.ASSIGN_MIN_VALUE) - # Print solution on console. - count = 0 - solver.NewSearch(decision_builder) - while solver.NextSolution(): - count += 1 - solution = 'Solution {}:\n'.format(count) - for var in [x, y, z]: - solution += ' {} = {}'.format(var.Name(), var.Value()) - print(solution) - solver.EndSearch() - print('Number of solutions found: ', count) + # Print solution on console. + count = 0 + solver.NewSearch(decision_builder) + while solver.NextSolution(): + count += 1 + solution = 'Solution {}:\n'.format(count) + for var in [x, y, z]: + solution += ' {} = {}'.format(var.Name(), var.Value()) + print(solution) + solver.EndSearch() + print('Number of solutions found: ', count) - print('Advanced usage:') - print('Problem solved in ', solver.WallTime(), 'ms') - print('Memory usage: ', pywrapcp.Solver.MemoryUsage(), 'bytes') + print('Advanced usage:') + print('Problem solved in ', solver.WallTime(), 'ms') + print('Memory usage: ', pywrapcp.Solver.MemoryUsage(), 'bytes') if __name__ == '__main__': - main() + main() ``` ### Java code samples @@ -141,39 +142,32 @@ public class SimpleCpProgram { final IntVar z = solver.makeIntVar(0, numVals - 1, "z"); // Constraint 0: x != y.. - solver.addConstraint(solver.makeAllDifferent(new IntVar[]{x, y})); + solver.addConstraint(solver.makeAllDifferent(new IntVar[] {x, y})); logger.info("Number of constraints: " + solver.constraints()); // Solve the problem. final DecisionBuilder db = solver.makePhase( - new IntVar[]{x, y, z}, - Solver.CHOOSE_FIRST_UNBOUND, - Solver.ASSIGN_MIN_VALUE); + new IntVar[] {x, y, z}, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); // Print solution on console. int count = 0; solver.newSearch(db); while (solver.nextSolution()) { ++count; - logger.info(String.format("Solution: %d\n x=%d y=%d z=%d" - , count - , x.value() - , y.value() - , z.value())); + logger.info( + String.format("Solution: %d\n x=%d y=%d z=%d", count, x.value(), y.value(), z.value())); } solver.endSearch(); logger.info("Number of solutions found: " + solver.solutions()); - logger.info(String.format( - "Advanced usage:\nProblem solved in %d ms\nMemory usage: %d bytes" - , solver.wallTime(), Solver.memoryUsage())); + logger.info(String.format("Advanced usage:\nProblem solved in %d ms\nMemory usage: %d bytes", + solver.wallTime(), Solver.memoryUsage())); } } ``` ### .Net code samples - ```cs using System; using Google.OrTools.ConstraintSolver; @@ -181,38 +175,41 @@ using Google.OrTools.ConstraintSolver; /// /// This is a simple CP program. /// -public class SimpleCpProgram { - public static void Main(String[] args) { - // Instantiate the solver. - Solver solver = new Solver("CpSimple"); +public class SimpleCpProgram +{ + public static void Main(String[] args) + { + // Instantiate the solver. + Solver solver = new Solver("CpSimple"); - // Create the variables. - const long numVals = 3; - IntVar x = solver.MakeIntVar(0, numVals - 1, "x"); - IntVar y = solver.MakeIntVar(0, numVals - 1, "y"); - IntVar z = solver.MakeIntVar(0, numVals - 1, "z"); + // Create the variables. + const long numVals = 3; + IntVar x = solver.MakeIntVar(0, numVals - 1, "x"); + IntVar y = solver.MakeIntVar(0, numVals - 1, "y"); + IntVar z = solver.MakeIntVar(0, numVals - 1, "z"); - // Constraint 0: x != y.. - solver.Add(solver.MakeAllDifferent(new IntVar[] { x, y })); - Console.WriteLine($"Number of constraints: {solver.Constraints()}"); + // Constraint 0: x != y.. + solver.Add(solver.MakeAllDifferent(new IntVar[] { x, y })); + Console.WriteLine($"Number of constraints: {solver.Constraints()}"); - // Solve the problem. - DecisionBuilder db = solver.MakePhase(new IntVar[] { x, y, z }, Solver.CHOOSE_FIRST_UNBOUND, - Solver.ASSIGN_MIN_VALUE); + // Solve the problem. + DecisionBuilder db = + solver.MakePhase(new IntVar[] { x, y, z }, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); - // Print solution on console. - int count = 0; - solver.NewSearch(db); - while (solver.NextSolution()) { - ++count; - Console.WriteLine($"Solution: {count}\n x={x.Value()} y={y.Value()} z={z.Value()}"); + // Print solution on console. + int count = 0; + solver.NewSearch(db); + while (solver.NextSolution()) + { + ++count; + Console.WriteLine($"Solution: {count}\n x={x.Value()} y={y.Value()} z={z.Value()}"); + } + solver.EndSearch(); + Console.WriteLine($"Number of solutions found: {solver.Solutions()}"); + + Console.WriteLine("Advanced usage:"); + Console.WriteLine($"Problem solved in {solver.WallTime()}ms"); + Console.WriteLine($"Memory usage: {Solver.MemoryUsage()}bytes"); } - solver.EndSearch(); - Console.WriteLine($"Number of solutions found: {solver.Solutions()}"); - - Console.WriteLine("Advanced usage:"); - Console.WriteLine($"Problem solved in {solver.WallTime()}ms"); - Console.WriteLine($"Memory usage: {Solver.MemoryUsage()}bytes"); - } } ``` diff --git a/ortools/constraint_solver/docs/ROUTING.md b/ortools/constraint_solver/docs/ROUTING.md index 7bb360c2f0..fb7758c6d2 100644 --- a/ortools/constraint_solver/docs/ROUTING.md +++ b/ortools/constraint_solver/docs/ROUTING.md @@ -89,6 +89,7 @@ int main(int argc, char** argv) { ### Python code samples ```python +#!/usr/bin/env python3 """Vehicle Routing example.""" from ortools.constraint_solver import routing_enums_pb2 @@ -96,59 +97,57 @@ from ortools.constraint_solver import pywrapcp def main(): - """Entry point of the program.""" - # Instantiate the data problem. - num_locations = 5 - num_vehicles = 1 - depot = 0 + """Entry point of the program.""" + # Instantiate the data problem. + num_locations = 5 + num_vehicles = 1 + depot = 0 - # Create the routing index manager. - manager = pywrapcp.RoutingIndexManager( - num_locations, - num_vehicles, - depot) + # Create the routing index manager. + manager = pywrapcp.RoutingIndexManager(num_locations, num_vehicles, depot) - # Create Routing Model. - routing = pywrapcp.RoutingModel(manager) + # Create Routing Model. + routing = pywrapcp.RoutingModel(manager) - # Create and register a transit callback. - def distance_callback(from_index, to_index): - """Returns the absolute difference between the two nodes.""" - # Convert from routing variable Index to user NodeIndex. - from_node = int(manager.IndexToNode(from_index)) - to_node = int(manager.IndexToNode(to_index)) - return abs(to_node - from_node) - transit_callback_index = routing.RegisterTransitCallback(distance_callback) + # Create and register a transit callback. + def distance_callback(from_index, to_index): + """Returns the absolute difference between the two nodes.""" + # Convert from routing variable Index to user NodeIndex. + from_node = int(manager.IndexToNode(from_index)) + to_node = int(manager.IndexToNode(to_index)) + return abs(to_node - from_node) - # Define cost of each arc. - routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index) + transit_callback_index = routing.RegisterTransitCallback(distance_callback) - # Setting first solution heuristic. - search_parameters = pywrapcp.DefaultRoutingSearchParameters() - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member + # Define cost of each arc. + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index) - # Solve the problem. - assignment = routing.SolveWithParameters(search_parameters) + # Setting first solution heuristic. + search_parameters = pywrapcp.DefaultRoutingSearchParameters() + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member - # Print solution on console. - print('Objective: {}'.format(assignment.ObjectiveValue())) - index = routing.Start(0) - plan_output = 'Route for vehicle 0:\n' - route_distance = 0 - while not routing.IsEnd(index): - plan_output += '{} -> '.format(manager.IndexToNode(index)) - previous_index = index - index = assignment.Value(routing.NextVar(index)) - route_distance += routing.GetArcCostForVehicle(previous_index, index, 0) - plan_output += '{}\n'.format(manager.IndexToNode(index)) - plan_output += 'Distance of the route: {}m\n'.format(route_distance) - print(plan_output) + # Solve the problem. + assignment = routing.SolveWithParameters(search_parameters) + + # Print solution on console. + print('Objective: {}'.format(assignment.ObjectiveValue())) + index = routing.Start(0) + plan_output = 'Route for vehicle 0:\n' + route_distance = 0 + while not routing.IsEnd(index): + plan_output += '{} -> '.format(manager.IndexToNode(index)) + previous_index = index + index = assignment.Value(routing.NextVar(index)) + route_distance += routing.GetArcCostForVehicle(previous_index, index, 0) + plan_output += '{}\n'.format(manager.IndexToNode(index)) + plan_output += 'Distance of the route: {}m\n'.format(route_distance) + print(plan_output) if __name__ == '__main__': - main() + main() ``` ### Java code samples @@ -158,18 +157,17 @@ package com.google.ortools.constraintsolver.samples; import static java.lang.Math.abs; import com.google.ortools.Loader; -import com.google.ortools.constraintsolver.FirstSolutionStrategy; -import com.google.ortools.constraintsolver.RoutingSearchParameters; import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; import com.google.ortools.constraintsolver.RoutingIndexManager; import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; import com.google.ortools.constraintsolver.main; import java.util.logging.Logger; /** Minimal Routing example to showcase calling the solver.*/ public class SimpleRoutingProgram { - private static final Logger logger = - Logger.getLogger(SimpleRoutingProgram.class.getName()); + private static final Logger logger = Logger.getLogger(SimpleRoutingProgram.class.getName()); public static void main(String[] args) throws Exception { Loader.loadNativeLibraries(); @@ -185,8 +183,8 @@ public class SimpleRoutingProgram { RoutingModel routing = new RoutingModel(manager); // Create and register a transit callback. - final int transitCallbackIndex = routing.registerTransitCallback( - (long fromIndex, long toIndex) -> { + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { // Convert from routing variable Index to user NodeIndex. int fromNode = manager.indexToNode(fromIndex); int toNode = manager.indexToNode(toIndex); @@ -236,53 +234,58 @@ using Google.OrTools.ConstraintSolver; /// /// This is a sample using the routing library .Net wrapper. /// -public class SimpleRoutingProgram { - public static void Main(String[] args) { - // Instantiate the data problem. - const int numLocation = 5; - const int numVehicles = 1; - const int depot = 0; +public class SimpleRoutingProgram +{ + public static void Main(String[] args) + { + // Instantiate the data problem. + const int numLocation = 5; + const int numVehicles = 1; + const int depot = 0; - // Create Routing Index Manager - RoutingIndexManager manager = new RoutingIndexManager(numLocation, numVehicles, depot); + // Create Routing Index Manager + RoutingIndexManager manager = new RoutingIndexManager(numLocation, numVehicles, depot); - // Create Routing Model. - RoutingModel routing = new RoutingModel(manager); + // Create Routing Model. + RoutingModel routing = new RoutingModel(manager); - // Create and register a transit callback. - int transitCallbackIndex = routing.RegisterTransitCallback((long fromIndex, long toIndex) => { - // Convert from routing variable Index to distance matrix NodeIndex. - var fromNode = manager.IndexToNode(fromIndex); - var toNode = manager.IndexToNode(toIndex); - return Math.Abs(toNode - fromNode); - }); + // Create and register a transit callback. + int transitCallbackIndex = routing.RegisterTransitCallback((long fromIndex, long toIndex) => + { + // Convert from routing variable Index to + // distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return Math.Abs(toNode - fromNode); + }); - // Define cost of each arc. - routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // Define cost of each arc. + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); - // Setting first solution heuristic. - RoutingSearchParameters searchParameters = - operations_research_constraint_solver.DefaultRoutingSearchParameters(); - searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + // Setting first solution heuristic. + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; - // Solve the problem. - Assignment solution = routing.SolveWithParameters(searchParameters); + // Solve the problem. + Assignment solution = routing.SolveWithParameters(searchParameters); - // Print solution on console. - Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); - // Inspect solution. - long index = routing.Start(0); - Console.WriteLine("Route for Vehicle 0:"); - long route_distance = 0; - while (routing.IsEnd(index) == false) { - Console.Write("{0} -> ", manager.IndexToNode((int)index)); - long previousIndex = index; - index = solution.Value(routing.NextVar(index)); - route_distance += routing.GetArcCostForVehicle(previousIndex, index, 0); + // Print solution on console. + Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); + // Inspect solution. + long index = routing.Start(0); + Console.WriteLine("Route for Vehicle 0:"); + long route_distance = 0; + while (routing.IsEnd(index) == false) + { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + long previousIndex = index; + index = solution.Value(routing.NextVar(index)); + route_distance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode(index)); + Console.WriteLine("Distance of the route: {0}m", route_distance); } - Console.WriteLine("{0}", manager.IndexToNode(index)); - Console.WriteLine("Distance of the route: {0}m", route_distance); - } } ``` diff --git a/ortools/constraint_solver/docs/routing_svg.py b/ortools/constraint_solver/docs/routing_svg.py index d5fb0e15b6..f9b6fcf8a0 100755 --- a/ortools/constraint_solver/docs/routing_svg.py +++ b/ortools/constraint_solver/docs/routing_svg.py @@ -10,7 +10,6 @@ # 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. - """Generate SVG for a Routing problem.""" # [START import] @@ -22,883 +21,919 @@ from ortools.constraint_solver import routing_enums_pb2 # [START data_model] class DataModel(object): # pylint: disable=too-many-instance-attributes - """Stores the data for the problem.""" + """Stores the data for the problem.""" - def __init__(self, args): - # Locations in block units - locations = [ - (4, 4), # depot - (2, 0), (8, 0), # locations to visit - (0, 1), (1, 1), - (5, 2), (7, 2), - (3, 3), (6, 3), - (5, 5), (8, 5), - (1, 6), (2, 6), - (3, 7), (6, 7), - (0, 8), (7, 8), - ] - # Convert locations in meters using a city block dimension of 114m x 80m. - self._locations = [(l[0] * 114, l[1] * 80) for l in locations] - self._distance_matrix = [ - [ - 0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, - 468, 776, 662 - ], - [ - 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, - 1016, 868, 1210 - ], - [ - 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, - 1130, 788, 1552, 754 - ], - [ - 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, - 1164, 560, 1358 - ], - [ - 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, - 1050, 674, 1244 - ], - [ - 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, - 514, 1050, 708 - ], - [ - 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, - 514, 1278, 480 - ], - [ - 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, - 662, 742, 856 - ], - [ - 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, - 320, 1084, 514 - ], - [ - 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, - 274, 810, 468 - ], - [ - 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, - 730, 388, 1152, 354 - ], - [ - 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, - 308, 650, 274, 844 - ], - [ - 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, - 536, 388, 730 - ], - [ - 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, - 342, 422, 536 - ], - [ - 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, - 342, 0, 764, 194 - ], - [ - 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, - 388, 422, 764, 0, 798 - ], - [ - 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, - 536, 194, 798, 0 - ], - ] - self._time_matrix = [ - [0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7], - [6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14], - [9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9], - [8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16], - [7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14], - [3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8], - [6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5], - [2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10], - [3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6], - [2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5], - [6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4], - [6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10], - [4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8], - [4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6], - [5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2], - [9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9], - [7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0], - ] - self._time_windows = [ - (0, 5), # depot - (7, 12), # 1 - (10, 15), # 2 - (5, 14), # 3 - (5, 13), # 4 - (0, 5), # 5 - (5, 10), # 6 - (0, 10), # 7 - (5, 10), # 8 - (0, 5), # 9 - (10, 16), # 10 - (10, 15), # 11 - (0, 5), # 12 - (5, 10), # 13 - (7, 12), # 14 - (10, 15), # 15 - (5, 15), # 16 - ] - if args['drop_nodes']: - self._demands = [0, 1, 1, 3, 6, 3, 6, 8, 8, 1, 2, 1, 2, 6, 6, 8, 8] - else: - self._demands = [0, 1, 1, 2, 4, 2, 4, 8, 8, 1, 2, 1, 2, 4, 4, 8, 8] - self._pickups_deliveries = [ - [1, 6], - [2, 10], - [4, 3], - [5, 9], - [7, 8], - [15, 11], - [13, 12], - [16, 14], - ] + def __init__(self, args): + # Locations in block units + locations = [ + (4, 4), # depot + (2, 0), + (8, 0), # locations to visit + (0, 1), + (1, 1), + (5, 2), + (7, 2), + (3, 3), + (6, 3), + (5, 5), + (8, 5), + (1, 6), + (2, 6), + (3, 7), + (6, 7), + (0, 8), + (7, 8), + ] + # Convert locations in meters using a city block dimension of 114m x 80m. + self._locations = [(l[0] * 114, l[1] * 80) for l in locations] + self._distance_matrix = [ + [ + 0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, + 354, 468, 776, 662 + ], + [ + 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, + 674, 1016, 868, 1210 + ], + [ + 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, + 1164, 1130, 788, 1552, 754 + ], + [ + 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, + 822, 1164, 560, 1358 + ], + [ + 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, + 708, 1050, 674, 1244 + ], + [ + 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, + 628, 514, 1050, 708 + ], + [ + 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, + 856, 514, 1278, 480 + ], + [ + 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, + 320, 662, 742, 856 + ], + [ + 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, + 662, 320, 1084, 514 + ], + [ + 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, + 388, 274, 810, 468 + ], + [ + 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, + 764, 730, 388, 1152, 354 + ], + [ + 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, + 114, 308, 650, 274, 844 + ], + [ + 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, + 194, 536, 388, 730 + ], + [ + 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, + 194, 0, 342, 422, 536 + ], + [ + 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, + 536, 342, 0, 764, 194 + ], + [ + 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, + 274, 388, 422, 764, 0, 798 + ], + [ + 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, + 730, 536, 194, 798, 0 + ], + ] + self._time_matrix = [ + [0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7], + [6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14], + [9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9], + [8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16], + [7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14], + [3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8], + [6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5], + [2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10], + [3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6], + [2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5], + [6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4], + [6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10], + [4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8], + [4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6], + [5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2], + [9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9], + [7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0], + ] + self._time_windows = [ + (0, 5), # depot + (7, 12), # 1 + (10, 15), # 2 + (5, 14), # 3 + (5, 13), # 4 + (0, 5), # 5 + (5, 10), # 6 + (0, 10), # 7 + (5, 10), # 8 + (0, 5), # 9 + (10, 16), # 10 + (10, 15), # 11 + (0, 5), # 12 + (5, 10), # 13 + (7, 12), # 14 + (10, 15), # 15 + (5, 15), # 16 + ] + if args['drop_nodes']: + self._demands = [0, 1, 1, 3, 6, 3, 6, 8, 8, 1, 2, 1, 2, 6, 6, 8, 8] + else: + self._demands = [0, 1, 1, 2, 4, 2, 4, 8, 8, 1, 2, 1, 2, 4, 4, 8, 8] + self._pickups_deliveries = [ + [1, 6], + [2, 10], + [4, 3], + [5, 9], + [7, 8], + [15, 11], + [13, 12], + [16, 14], + ] - if args['tsp']: - self._num_vehicles = 1 - else: - self._num_vehicles = 4 - self._vehicle_capacities = [15, 15, 15, 15] + if args['tsp']: + self._num_vehicles = 1 + else: + self._num_vehicles = 4 + self._vehicle_capacities = [15, 15, 15, 15] - if args['resources']: - self._vehicle_load_time = 5 - self._vehicle_unload_time = 5 + if args['resources']: + self._vehicle_load_time = 5 + self._vehicle_unload_time = 5 - self._depot = 0 - self._depot_capacity = 2 - self._starts = [1, 2, 15, 16] - self._ends = [0, 0, 0, 0] + self._depot = 0 + self._depot_capacity = 2 + self._starts = [1, 2, 15, 16] + self._ends = [0, 0, 0, 0] - @property - def locations(self): - """Gets the locations.""" - return self._locations + @property + def locations(self): + """Gets the locations.""" + return self._locations - @property - def distance_matrix(self): - """Gets the distance matrix.""" - return self._distance_matrix + @property + def distance_matrix(self): + """Gets the distance matrix.""" + return self._distance_matrix - @property - def time_matrix(self): - """Gets the time matrix.""" - return self._time_matrix + @property + def time_matrix(self): + """Gets the time matrix.""" + return self._time_matrix - @property - def time_windows(self): - """Gets the time windows.""" - return self._time_windows + @property + def time_windows(self): + """Gets the time windows.""" + return self._time_windows - @property - def demands(self): - """Gets the locations demands.""" - return self._demands + @property + def demands(self): + """Gets the locations demands.""" + return self._demands - @property - def pickups_deliveries(self): - """Gets the pickups deliveries.""" - return self._pickups_deliveries + @property + def pickups_deliveries(self): + """Gets the pickups deliveries.""" + return self._pickups_deliveries - @property - def num_vehicles(self): - """Gets the number of vehicles.""" - return self._num_vehicles + @property + def num_vehicles(self): + """Gets the number of vehicles.""" + return self._num_vehicles - @property - def vehicle_capacities(self): - """Gets the capacity of each vehicles.""" - return self._vehicle_capacities + @property + def vehicle_capacities(self): + """Gets the capacity of each vehicles.""" + return self._vehicle_capacities - @property - def vehicle_load_time(self): - """Gets the load time of each vehicles.""" - return self._vehicle_load_time + @property + def vehicle_load_time(self): + """Gets the load time of each vehicles.""" + return self._vehicle_load_time - @property - def vehicle_unload_time(self): - """Gets the unload time of each vehicles.""" - return self._vehicle_unload_time + @property + def vehicle_unload_time(self): + """Gets the unload time of each vehicles.""" + return self._vehicle_unload_time - @property - def depot_capacity(self): - """Gets the depot capacity.""" - return self._depot_capacity + @property + def depot_capacity(self): + """Gets the depot capacity.""" + return self._depot_capacity - @property - def depot(self): - """Gets the depot node index.""" - return self._depot + @property + def depot(self): + """Gets the depot node index.""" + return self._depot - @property - def starts(self): - """Gets the start nodes indices.""" - return self._starts + @property + def starts(self): + """Gets the start nodes indices.""" + return self._starts - @property - def ends(self): - """Gets the end nodes indices.""" - return self._ends + @property + def ends(self): + """Gets the end nodes indices.""" + return self._ends - # [END data_model] + # [END data_model] ########### # Printer # ########### class GoogleColorPalette(object): - """Google color codes palette.""" + """Google color codes palette.""" - def __init__(self): - """Initialize Google ColorPalette.""" - self._colors = [('blue', r'#4285F4'), ('red', r'#EA4335'), - ('yellow', r'#FBBC05'), ('green', r'#34A853'), - ('black', r'#101010'), ('white', r'#FFFFFF')] + def __init__(self): + """Initialize Google ColorPalette.""" + self._colors = [('blue', r'#4285F4'), ('red', r'#EA4335'), + ('yellow', r'#FBBC05'), ('green', r'#34A853'), + ('black', r'#101010'), ('white', r'#FFFFFF')] - def __getitem__(self, key): - """Gets color name from idx.""" - return self._colors[key][0] + def __getitem__(self, key): + """Gets color name from idx.""" + return self._colors[key][0] - def __len__(self): - """Gets the number of colors.""" - return len(self._colors) + def __len__(self): + """Gets the number of colors.""" + return len(self._colors) - @property - def colors(self): - """Gets the colors list.""" - return self._colors + @property + def colors(self): + """Gets the colors list.""" + return self._colors - def name(self, idx): - """Return color name from idx.""" - return self._colors[idx][0] + def name(self, idx): + """Return color name from idx.""" + return self._colors[idx][0] - def value(self, idx): - """Return color value from idx.""" - return self._colors[idx][1] + def value(self, idx): + """Return color value from idx.""" + return self._colors[idx][1] - def value_from_name(self, name): - """Return color value from name.""" - return dict(self._colors)[name] + def value_from_name(self, name): + """Return color value from name.""" + return dict(self._colors)[name] class SVG(object): - """SVG draw primitives.""" + """SVG draw primitives.""" - @staticmethod - def header(size, margin): - """Writes header.""" - print(r''.format( - width=size[0] + 2 * margin, - height=size[1] + 2 * margin, - margin=margin)) + @staticmethod + def header(size, margin): + """Writes header.""" + print(r''.format( + width=size[0] + 2 * margin, + height=size[1] + 2 * margin, + margin=margin)) - @staticmethod - def definitions(colors): - """Writes definitions.""" - print(r'') - print(r'') - for color in colors: - print( - r' '.format(colorname=color[0])) - print( - r' ' - .format(color=color[1])) - print(r' ') - print(r'') + @staticmethod + def definitions(colors): + """Writes definitions.""" + print(r'') + print(r'') + for color in colors: + print( + r' '.format(colorname=color[0])) + print( + r' ' + .format(color=color[1])) + print(r' ') + print(r'') - @staticmethod - def footer(): - """Writes svg footer.""" - print(r'') + @staticmethod + def footer(): + """Writes svg footer.""" + print(r'') - @staticmethod - def draw_line(position_1, position_2, size, fg_color): - """Draws a line.""" - line_style = (r'style="stroke-width:{sz};stroke:{fg};fill:none"').format( - sz=size, fg=fg_color) - print(r''.format( - x1=position_1[0], - y1=position_1[1], - x2=position_2[0], - y2=position_2[1], - style=line_style)) + @staticmethod + def draw_line(position_1, position_2, size, fg_color): + """Draws a line.""" + line_style = ( + r'style="stroke-width:{sz};stroke:{fg};fill:none"').format( + sz=size, fg=fg_color) + print( + r''.format( + x1=position_1[0], + y1=position_1[1], + x2=position_2[0], + y2=position_2[1], + style=line_style)) - @staticmethod - def draw_polyline(position_1, position_2, size, fg_color, colorname): - """Draws a line with arrow maker in the middle.""" - polyline_style = (r'style="stroke-width:{sz};stroke:{fg};fill:none;' - 'marker-mid:url(#arrow_{colorname})"').format( - sz=size, fg=fg_color, colorname=colorname) - print(r''.format( - x1=position_1[0], - y1=position_1[1], - x2=(position_1[0] + position_2[0]) / 2, - y2=(position_1[1] + position_2[1]) / 2, - x3=position_2[0], - y3=position_2[1], - style=polyline_style)) + @staticmethod + def draw_polyline(position_1, position_2, size, fg_color, colorname): + """Draws a line with arrow maker in the middle.""" + polyline_style = (r'style="stroke-width:{sz};stroke:{fg};fill:none;' + 'marker-mid:url(#arrow_{colorname})"').format( + sz=size, fg=fg_color, colorname=colorname) + print(r''. + format(x1=position_1[0], + y1=position_1[1], + x2=(position_1[0] + position_2[0]) / 2, + y2=(position_1[1] + position_2[1]) / 2, + x3=position_2[0], + y3=position_2[1], + style=polyline_style)) - @staticmethod - def draw_circle(position, radius, size, fg_color, bg_color='white'): - """Print a circle.""" - circle_style = (r'style="stroke-width:{sz};stroke:{fg};fill:{bg}"').format( - sz=size, fg=fg_color, bg=bg_color) - print(r''.format( - cx=position[0], cy=position[1], r=radius, style=circle_style)) + @staticmethod + def draw_circle(position, radius, size, fg_color, bg_color='white'): + """Print a circle.""" + circle_style = ( + r'style="stroke-width:{sz};stroke:{fg};fill:{bg}"').format( + sz=size, fg=fg_color, bg=bg_color) + print(r''.format( + cx=position[0], cy=position[1], r=radius, style=circle_style)) - @staticmethod - def draw_text(text, position, size, fg_color='none', bg_color='black'): - """Print a middle centred text.""" - text_style = (r'style="text-anchor:middle;font-weight:bold;' - 'font-size:{sz};stroke:{fg};fill:{bg}"').format( - sz=size, fg=fg_color, bg=bg_color) - print(r'{txt}'.format( - x=position[0], y=position[1], dy=size / 3, style=text_style, txt=text)) + @staticmethod + def draw_text(text, position, size, fg_color='none', bg_color='black'): + """Print a middle centred text.""" + text_style = (r'style="text-anchor:middle;font-weight:bold;' + 'font-size:{sz};stroke:{fg};fill:{bg}"').format( + sz=size, fg=fg_color, bg=bg_color) + print(r'{txt}'.format( + x=position[0], + y=position[1], + dy=size / 3, + style=text_style, + txt=text)) class SVGPrinter(object): # pylint: disable=too-many-instance-attributes - """Generate Problem as svg file to stdout.""" + """Generate Problem as svg file to stdout.""" - # pylint: disable=too-many-arguments - def __init__(self, args, data, manager=None, routing=None, assignment=None): - """Initializes the printer.""" - self._args = args - self._data = data - self._manager = manager - self._routing = routing - self._assignment = assignment - # Design variables - self._color_palette = GoogleColorPalette() - self._svg = SVG() - # City block size 114mx80m - self._radius = min(114, 80) / 3 - self._stroke_width = self._radius / 4 + # pylint: disable=too-many-arguments + def __init__(self, + args, + data, + manager=None, + routing=None, + assignment=None): + """Initializes the printer.""" + self._args = args + self._data = data + self._manager = manager + self._routing = routing + self._assignment = assignment + # Design variables + self._color_palette = GoogleColorPalette() + self._svg = SVG() + # City block size 114mx80m + self._radius = min(114, 80) / 3 + self._stroke_width = self._radius / 4 - @property - def data(self): - """Gets the Data Model.""" - return self._data + @property + def data(self): + """Gets the Data Model.""" + return self._data - @property - def manager(self): - """Gets the RoutingIndexManager.""" - return self._manager + @property + def manager(self): + """Gets the RoutingIndexManager.""" + return self._manager - @property - def routing(self): - """Gets the Routing solver.""" - return self._routing + @property + def routing(self): + """Gets the Routing solver.""" + return self._routing - @property - def assignment(self): - """Gets the assignment.""" - return self._assignment + @property + def assignment(self): + """Gets the assignment.""" + return self._assignment - @property - def color_palette(self): - """Gets the color palette.""" - return self._color_palette + @property + def color_palette(self): + """Gets the color palette.""" + return self._color_palette - @property - def svg(self): - """Gets the svg.""" - return self._svg + @property + def svg(self): + """Gets the svg.""" + return self._svg - def draw_grid(self): - """Draws the city grid.""" - print(r'') - color = '#969696' - # Horizontal streets - for i in range(9): - p_1 = [0, i * 80] - p_2 = [8 * 114, p_1[1]] - self._svg.draw_line(p_1, p_2, 2, color) - # Vertical streets - for i in range(9): - p_1 = [i * 114, 0] - p_2 = [p_1[0], 8 * 80] - self._svg.draw_line(p_1, p_2, 2, color) + def draw_grid(self): + """Draws the city grid.""" + print(r'') + color = '#969696' + # Horizontal streets + for i in range(9): + p_1 = [0, i * 80] + p_2 = [8 * 114, p_1[1]] + self._svg.draw_line(p_1, p_2, 2, color) + # Vertical streets + for i in range(9): + p_1 = [i * 114, 0] + p_2 = [p_1[0], 8 * 80] + self._svg.draw_line(p_1, p_2, 2, color) - def draw_depot(self): - """Draws the depot.""" - print(r'') - color = self._color_palette.value_from_name('black') - loc = self._data.locations[self._data.depot] - self._svg.draw_circle(loc, self._radius, self._stroke_width, color, 'white') - self._svg.draw_text(self._data.depot, loc, self._radius, 'none', color) - - def draw_depots(self): - """Draws the depot.""" - print(r'') - # print starts - for vehicle_idx, start in enumerate(self._data.starts): - del vehicle_idx - color = self._color_palette.value_from_name('black') - # color = self._color_palette.value(vehicle_idx) - loc = self._data.locations[start] - self._svg.draw_circle(loc, self._radius, self._stroke_width, color, - 'white') - self._svg.draw_text(start, loc, self._radius, 'none', color) - # print end - color = self._color_palette.value_from_name('black') - loc = self._data.locations[0] - self._svg.draw_circle(loc, self._radius, self._stroke_width, color, 'white') - self._svg.draw_text(0, loc, self._radius, 'none', color) - - def draw_locations(self): - """Draws all the locations but the depot.""" - print(r'') - color = self._color_palette.value_from_name('blue') - if not self._args['starts_ends']: - for idx, loc in enumerate(self._data.locations): - if idx == self._data.depot: - continue + def draw_depot(self): + """Draws the depot.""" + print(r'') + color = self._color_palette.value_from_name('black') + loc = self._data.locations[self._data.depot] self._svg.draw_circle(loc, self._radius, self._stroke_width, color, 'white') - self._svg.draw_text(idx, loc, self._radius, 'none', color) - else: - for idx, loc in enumerate(self._data.locations): - if idx in self._data.starts + self._data.ends: - continue + self._svg.draw_text(self._data.depot, loc, self._radius, 'none', color) + + def draw_depots(self): + """Draws the depot.""" + print(r'') + # print starts + for vehicle_idx, start in enumerate(self._data.starts): + del vehicle_idx + color = self._color_palette.value_from_name('black') + # color = self._color_palette.value(vehicle_idx) + loc = self._data.locations[start] + self._svg.draw_circle(loc, self._radius, self._stroke_width, color, + 'white') + self._svg.draw_text(start, loc, self._radius, 'none', color) + # print end + color = self._color_palette.value_from_name('black') + loc = self._data.locations[0] self._svg.draw_circle(loc, self._radius, self._stroke_width, color, 'white') - self._svg.draw_text(idx, loc, self._radius, 'none', color) + self._svg.draw_text(0, loc, self._radius, 'none', color) - def draw_demands(self): - """Draws all the demands.""" - print(r'') - for idx, loc in enumerate(self._data.locations): - if idx == self._data.depot: - continue - demand = self._data.demands[idx] - position = [ - x + y for x, y in zip(loc, [self._radius * 1.2, self._radius * 1.1]) - ] - color = self._color_palette.value_from_name('red') - # color = self._color_palette.value(int(math.log(demand, 2))) - self._svg.draw_text(demand, position, self._radius, 'none', color) + def draw_locations(self): + """Draws all the locations but the depot.""" + print(r'') + color = self._color_palette.value_from_name('blue') + if not self._args['starts_ends']: + for idx, loc in enumerate(self._data.locations): + if idx == self._data.depot: + continue + self._svg.draw_circle(loc, self._radius, self._stroke_width, + color, 'white') + self._svg.draw_text(idx, loc, self._radius, 'none', color) + else: + for idx, loc in enumerate(self._data.locations): + if idx in self._data.starts + self._data.ends: + continue + self._svg.draw_circle(loc, self._radius, self._stroke_width, + color, 'white') + self._svg.draw_text(idx, loc, self._radius, 'none', color) - def draw_pickups_deliveries(self): - """Draws all pickups deliveries.""" - print(r'') - colorname = 'red' - color = self._color_palette.value_from_name(colorname) - for pickup_delivery in self._data.pickups_deliveries: - self._svg.draw_polyline(self._data.locations[pickup_delivery[0]], - self._data.locations[pickup_delivery[1]], - self._stroke_width, color, colorname) + def draw_demands(self): + """Draws all the demands.""" + print(r'') + for idx, loc in enumerate(self._data.locations): + if idx == self._data.depot: + continue + demand = self._data.demands[idx] + position = [ + x + y + for x, y in zip(loc, [self._radius * 1.2, self._radius * 1.1]) + ] + color = self._color_palette.value_from_name('red') + # color = self._color_palette.value(int(math.log(demand, 2))) + self._svg.draw_text(demand, position, self._radius, 'none', color) - def draw_time_windows(self): - """Draws all the time windows.""" - print(r'') - for idx, loc in enumerate(self._data.locations): - if idx == self._data.depot: - continue - time_window = self._data.time_windows[idx] - position = [ - x + y for x, y in zip(loc, [self._radius * 0, -self._radius * 1.6]) - ] - color = self._color_palette.value_from_name('red') - self._svg.draw_text( - '[{t1},{t2}]'.format(t1=time_window[0], t2=time_window[1]), position, - self._radius * 0.75, 'white', color) + def draw_pickups_deliveries(self): + """Draws all pickups deliveries.""" + print(r'') + colorname = 'red' + color = self._color_palette.value_from_name(colorname) + for pickup_delivery in self._data.pickups_deliveries: + self._svg.draw_polyline(self._data.locations[pickup_delivery[0]], + self._data.locations[pickup_delivery[1]], + self._stroke_width, color, colorname) + + def draw_time_windows(self): + """Draws all the time windows.""" + print(r'') + for idx, loc in enumerate(self._data.locations): + if idx == self._data.depot: + continue + time_window = self._data.time_windows[idx] + position = [ + x + y + for x, y in zip(loc, [self._radius * 0, -self._radius * 1.6]) + ] + color = self._color_palette.value_from_name('red') + self._svg.draw_text( + '[{t1},{t2}]'.format(t1=time_window[0], t2=time_window[1]), + position, self._radius * 0.75, 'white', color) ############## ## ROUTES ## ############## - def draw_drop_nodes(self): - """Draws the dropped nodes.""" - print(r'') - if self._assignment is None: - print('') - # Display dropped nodes. - dropped_nodes = [] - for node in range(self._routing.Size()): - if self._routing.IsStart(node) or self._routing.IsEnd(node): - continue - if self._assignment.Value(self._routing.NextVar(node)) == node: - dropped_nodes.append(self._manager.IndexToNode(node)) - color = self._color_palette.value_from_name('black') - for node_idx in dropped_nodes: - loc = self._data.locations[node_idx] - self._svg.draw_circle(loc, self._radius, self._stroke_width, color, - 'white') - self._svg.draw_text(node_idx, loc, self._radius, 'none', color) + def draw_drop_nodes(self): + """Draws the dropped nodes.""" + print(r'') + if self._assignment is None: + print('') + # Display dropped nodes. + dropped_nodes = [] + for node in range(self._routing.Size()): + if self._routing.IsStart(node) or self._routing.IsEnd(node): + continue + if self._assignment.Value(self._routing.NextVar(node)) == node: + dropped_nodes.append(self._manager.IndexToNode(node)) + color = self._color_palette.value_from_name('black') + for node_idx in dropped_nodes: + loc = self._data.locations[node_idx] + self._svg.draw_circle(loc, self._radius, self._stroke_width, color, + 'white') + self._svg.draw_text(node_idx, loc, self._radius, 'none', color) - def routes(self): - """Creates the route list from the assignment.""" - if self._assignment is None: - print('') - return [] - routes = [] - for vehicle_id in range(self._data.num_vehicles): - index = self._routing.Start(vehicle_id) - route = [] - while not self._routing.IsEnd(index): - node_index = self._manager.IndexToNode(index) - route.append(node_index) - index = self._assignment.Value(self._routing.NextVar(index)) - node_index = self._manager.IndexToNode(index) - route.append(node_index) - routes.append(route) - return routes + def routes(self): + """Creates the route list from the assignment.""" + if self._assignment is None: + print('') + return [] + routes = [] + for vehicle_id in range(self._data.num_vehicles): + index = self._routing.Start(vehicle_id) + route = [] + while not self._routing.IsEnd(index): + node_index = self._manager.IndexToNode(index) + route.append(node_index) + index = self._assignment.Value(self._routing.NextVar(index)) + node_index = self._manager.IndexToNode(index) + route.append(node_index) + routes.append(route) + return routes - def draw_route(self, route, color, colorname): - """Draws a Route.""" - # First print route - previous_loc_idx = None - for loc_idx in route: - if previous_loc_idx is not None and previous_loc_idx != loc_idx: - self._svg.draw_polyline(self._data.locations[previous_loc_idx], - self._data.locations[loc_idx], - self._stroke_width, color, colorname) - previous_loc_idx = loc_idx - # Then print location along the route - for loc_idx in route: - if loc_idx != self._data.depot: - loc = self._data.locations[loc_idx] - self._svg.draw_circle(loc, self._radius, self._stroke_width, color, - 'white') - self._svg.draw_text(loc_idx, loc, self._radius, 'none', color) + def draw_route(self, route, color, colorname): + """Draws a Route.""" + # First print route + previous_loc_idx = None + for loc_idx in route: + if previous_loc_idx is not None and previous_loc_idx != loc_idx: + self._svg.draw_polyline(self._data.locations[previous_loc_idx], + self._data.locations[loc_idx], + self._stroke_width, color, colorname) + previous_loc_idx = loc_idx + # Then print location along the route + for loc_idx in route: + if loc_idx != self._data.depot: + loc = self._data.locations[loc_idx] + self._svg.draw_circle(loc, self._radius, self._stroke_width, + color, 'white') + self._svg.draw_text(loc_idx, loc, self._radius, 'none', color) - def draw_routes(self): - """Draws the routes.""" - print(r'') - for route_idx, route in enumerate(self.routes()): - print(r''.format(idx=route_idx)) - color = self._color_palette.value(route_idx) - colorname = self._color_palette.name(route_idx) - self.draw_route(route, color, colorname) + def draw_routes(self): + """Draws the routes.""" + print(r'') + for route_idx, route in enumerate(self.routes()): + print(r''.format(idx=route_idx)) + color = self._color_palette.value(route_idx) + colorname = self._color_palette.name(route_idx) + self.draw_route(route, color, colorname) - def tw_routes(self): - """Creates the route time window list from the assignment.""" - if self._assignment is None: - print('') - return [] - time_dimension = self._routing.GetDimensionOrDie('Time') - loc_routes = [] - tw_routes = [] - for vehicle_id in range(self._data.num_vehicles): - index = self._routing.Start(vehicle_id) - # index = self._assignment.Value(self._routing.NextVar(index)) - loc_route = [] - tw_route = [] - while True: - node_index = self._manager.IndexToNode(index) - loc_route.append(node_index) - time_var = time_dimension.CumulVar(index) - t_min = self._assignment.Min(time_var) - t_max = self._assignment.Max(time_var) - tw_route.append((t_min, t_max)) - if self._routing.IsEnd(index): - break - index = self._assignment.Value(self._routing.NextVar(index)) - loc_routes.append(loc_route) - tw_routes.append(tw_route) - return zip(loc_routes, tw_routes) + def tw_routes(self): + """Creates the route time window list from the assignment.""" + if self._assignment is None: + print('') + return [] + time_dimension = self._routing.GetDimensionOrDie('Time') + loc_routes = [] + tw_routes = [] + for vehicle_id in range(self._data.num_vehicles): + index = self._routing.Start(vehicle_id) + # index = self._assignment.Value(self._routing.NextVar(index)) + loc_route = [] + tw_route = [] + while True: + node_index = self._manager.IndexToNode(index) + loc_route.append(node_index) + time_var = time_dimension.CumulVar(index) + t_min = self._assignment.Min(time_var) + t_max = self._assignment.Max(time_var) + tw_route.append((t_min, t_max)) + if self._routing.IsEnd(index): + break + index = self._assignment.Value(self._routing.NextVar(index)) + loc_routes.append(loc_route) + tw_routes.append(tw_route) + return zip(loc_routes, tw_routes) - def draw_tw_route(self, route_idx, locations, tw_route, color): - """Draws the time windows for a Route.""" - is_start = -1 - for loc_idx, time_window in zip(locations, tw_route): - loc = self._data.locations[loc_idx] - if loc_idx == 0: # special case for depot - position = [ - x + y for x, y in - zip(loc, - [self._radius * is_start, self._radius * (1.8 + route_idx)]) - ] - is_start = 1 - else: - position = [ - x + y for x, y in zip(loc, [self._radius * 0, self._radius * 1.8]) - ] - self._svg.draw_text('[{t_min}]'.format(t_min=time_window[0]), position, - self._radius * 0.75, 'white', color) + def draw_tw_route(self, route_idx, locations, tw_route, color): + """Draws the time windows for a Route.""" + is_start = -1 + for loc_idx, time_window in zip(locations, tw_route): + loc = self._data.locations[loc_idx] + if loc_idx == 0: # special case for depot + position = [ + x + y for x, y in zip(loc, [ + self._radius * is_start, self._radius * + (1.8 + route_idx) + ]) + ] + is_start = 1 + else: + position = [ + x + y for x, y in zip( + loc, [self._radius * 0, self._radius * 1.8]) + ] + self._svg.draw_text('[{t_min}]'.format(t_min=time_window[0]), + position, self._radius * 0.75, 'white', color) - def draw_tw_routes(self): - """Draws the time window routes.""" - print(r'') - for route_idx, loc_tw in enumerate(self.tw_routes()): - print(r''.format(route_idx)) - color = self._color_palette.value(route_idx) - self.draw_tw_route(route_idx, loc_tw[0], loc_tw[1], color) + def draw_tw_routes(self): + """Draws the time window routes.""" + print(r'') + for route_idx, loc_tw in enumerate(self.tw_routes()): + print(r''.format(route_idx)) + color = self._color_palette.value(route_idx) + self.draw_tw_route(route_idx, loc_tw[0], loc_tw[1], color) - def print_to_console(self): - """Prints a full svg document on stdout.""" - margin = self._radius * 2 + 2 - size = [8 * 114, 8 * 80] - self._svg.header(size, margin) - self._svg.definitions(self._color_palette.colors) - self.draw_grid() - if not self._args['solution']: - if self._args['pickup_delivery']: - self.draw_pickups_deliveries() - self.draw_locations() - else: - self.draw_routes() - self.draw_drop_nodes() - if self._args['starts_ends']: - self.draw_depots() - else: - self.draw_depot() - if self._args['capacity']: - self.draw_demands() - if self._args['drop_nodes']: - self.draw_demands() - if self._args['time_windows'] or self._args['resources']: - self.draw_time_windows() - if ((self._args['time_windows'] or self._args['resources']) and - self._args['solution']): - self.draw_tw_routes() - self._svg.footer() + def print_to_console(self): + """Prints a full svg document on stdout.""" + margin = self._radius * 2 + 2 + size = [8 * 114, 8 * 80] + self._svg.header(size, margin) + self._svg.definitions(self._color_palette.colors) + self.draw_grid() + if not self._args['solution']: + if self._args['pickup_delivery']: + self.draw_pickups_deliveries() + self.draw_locations() + else: + self.draw_routes() + self.draw_drop_nodes() + if self._args['starts_ends']: + self.draw_depots() + else: + self.draw_depot() + if self._args['capacity']: + self.draw_demands() + if self._args['drop_nodes']: + self.draw_demands() + if self._args['time_windows'] or self._args['resources']: + self.draw_time_windows() + if ((self._args['time_windows'] or self._args['resources']) + and self._args['solution']): + self.draw_tw_routes() + self._svg.footer() ######## # Main # ######## def main(): # pylint: disable=too-many-locals,too-many-branches - """Entry point of the program.""" - parser = argparse.ArgumentParser(description='Output VRP as svg image.') - parser.add_argument( - '-tsp', '--tsp', action='store_true', help='use 1 vehicle') - parser.add_argument( - '-vrp', '--vrp', action='store_true', help='use 4 vehicle') - parser.add_argument( - '-gs', - '--global-span', - action='store_true', - help='use global span constraints') - parser.add_argument( - '-c', '--capacity', action='store_true', help='use capacity constraints') - parser.add_argument( - '-r', - '--resources', - action='store_true', - help='use resources constraints') - parser.add_argument( - '-dn', - '--drop-nodes', - action='store_true', - help='allow drop nodes (disjuntion constraints)') - parser.add_argument( - '-tw', - '--time-windows', - action='store_true', - help='use time-window constraints') - parser.add_argument( - '-se', - '--starts-ends', - action='store_true', - help='use multiple starts & ends') - parser.add_argument( - '-pd', - '--pickup-delivery', - action='store_true', - help='use pickup & delivery constraints') - parser.add_argument( - '-fifo', - '--fifo', - action='store_true', - help='use pickup & delivery FIFO Policy') - parser.add_argument( - '-lifo', - '--lifo', - action='store_true', - help='use pickup & delivery LIFO Policy') - parser.add_argument( - '-s', '--solution', action='store_true', help='print solution') - args = vars(parser.parse_args()) + """Entry point of the program.""" + parser = argparse.ArgumentParser(description='Output VRP as svg image.') + parser.add_argument('-tsp', + '--tsp', + action='store_true', + help='use 1 vehicle') + parser.add_argument('-vrp', + '--vrp', + action='store_true', + help='use 4 vehicle') + parser.add_argument('-gs', + '--global-span', + action='store_true', + help='use global span constraints') + parser.add_argument('-c', + '--capacity', + action='store_true', + help='use capacity constraints') + parser.add_argument('-r', + '--resources', + action='store_true', + help='use resources constraints') + parser.add_argument('-dn', + '--drop-nodes', + action='store_true', + help='allow drop nodes (disjuntion constraints)') + parser.add_argument('-tw', + '--time-windows', + action='store_true', + help='use time-window constraints') + parser.add_argument('-se', + '--starts-ends', + action='store_true', + help='use multiple starts & ends') + parser.add_argument('-pd', + '--pickup-delivery', + action='store_true', + help='use pickup & delivery constraints') + parser.add_argument('-fifo', + '--fifo', + action='store_true', + help='use pickup & delivery FIFO Policy') + parser.add_argument('-lifo', + '--lifo', + action='store_true', + help='use pickup & delivery LIFO Policy') + parser.add_argument('-s', + '--solution', + action='store_true', + help='print solution') + args = vars(parser.parse_args()) - # Instantiate the data problem. - # [START data] - data = DataModel(args) - # [END data] + # Instantiate the data problem. + # [START data] + data = DataModel(args) + # [END data] - if not args['solution']: - # Print svg on cout - printer = SVGPrinter(args, data) + if not args['solution']: + # Print svg on cout + printer = SVGPrinter(args, data) + printer.print_to_console() + return 0 + + # Create the routing index manager. + # [START index_manager] + if args['starts_ends']: + manager = pywrapcp.RoutingIndexManager(len(data.locations), + data.num_vehicles, data.starts, + data.ends) + else: + manager = pywrapcp.RoutingIndexManager(len(data.locations), + data.num_vehicles, data.depot) + # [END index_manager] + + # Create Routing Model. + # [START routing_model] + routing = pywrapcp.RoutingModel(manager) + + # [END routing_model] + + # Register distance callback + def distance_callback(from_index, to_index): + """Returns the manhattan distance between the two nodes.""" + # Convert from routing variable Index to distance matrix NodeIndex. + from_node = manager.IndexToNode(from_index) + to_node = manager.IndexToNode(to_index) + return data.distance_matrix[from_node][to_node] + + distance_callback_index = routing.RegisterTransitCallback( + distance_callback) + + # Register time callback + def time_callback(from_index, to_index): + """Returns the manhattan distance travel time between the two nodes.""" + # Convert from routing variable Index to distance matrix NodeIndex. + from_node = manager.IndexToNode(from_index) + to_node = manager.IndexToNode(to_index) + return data.time_matrix[from_node][to_node] + + time_callback_index = routing.RegisterTransitCallback(time_callback) + + # Register demands callback + def demand_callback(from_index): + """Returns the demand of the node.""" + # Convert from routing variable Index to demands NodeIndex. + from_node = manager.IndexToNode(from_index) + return data.demands[from_node] + + demand_callback_index = routing.RegisterUnaryTransitCallback( + demand_callback) + + if args['time_windows'] or args['resources']: + routing.SetArcCostEvaluatorOfAllVehicles(time_callback_index) + else: + routing.SetArcCostEvaluatorOfAllVehicles(distance_callback_index) + + if args['global_span'] or args['pickup_delivery']: + dimension_name = 'Distance' + routing.AddDimension(distance_callback_index, 0, 3000, True, + dimension_name) + distance_dimension = routing.GetDimensionOrDie(dimension_name) + distance_dimension.SetGlobalSpanCostCoefficient(100) + + if args['capacity'] or args['drop_nodes']: + routing.AddDimensionWithVehicleCapacity(demand_callback_index, 0, + data.vehicle_capacities, True, + 'Capacity') + + if args['drop_nodes']: + # Allow to drop nodes. + penalty = 1000 + for node in range(1, len(data.locations)): + routing.AddDisjunction([manager.NodeToIndex(node)], penalty) + + if args['pickup_delivery']: + dimension_name = 'Distance' + routing.AddDimension(distance_callback_index, 0, 3000, True, + dimension_name) + distance_dimension = routing.GetDimensionOrDie(dimension_name) + distance_dimension.SetGlobalSpanCostCoefficient(100) + for request in data.pickups_deliveries: + pickup_index = manager.NodeToIndex(request[0]) + delivery_index = manager.NodeToIndex(request[1]) + routing.AddPickupAndDelivery(pickup_index, delivery_index) + routing.solver().Add( + routing.VehicleVar(pickup_index) == routing.VehicleVar( + delivery_index)) + routing.solver().Add( + distance_dimension.CumulVar(pickup_index) <= + distance_dimension.CumulVar(delivery_index)) + if args['fifo']: + routing.SetPickupAndDeliveryPolicyOfAllVehicles( + pywrapcp.RoutingModel.PICKUP_AND_DELIVERY_FIFO) + if args['lifo']: + routing.SetPickupAndDeliveryPolicyOfAllVehicles( + pywrapcp.RoutingModel.PICKUP_AND_DELIVERY_LIFO) + + if args['starts_ends']: + dimension_name = 'Distance' + routing.AddDimension(distance_callback_index, 0, 2000, True, + dimension_name) + distance_dimension = routing.GetDimensionOrDie(dimension_name) + distance_dimension.SetGlobalSpanCostCoefficient(100) + + time = 'Time' + if args['time_windows'] or args['resources']: + routing.AddDimension(time_callback_index, 30, 30, False, time) + time_dimension = routing.GetDimensionOrDie(time) + # Add time window constraints for each location except depot and 'copy' the + # slack var in the solution object (aka Assignment) to print it. + for location_idx, time_window in enumerate(data.time_windows): + if location_idx == 0: + continue + index = manager.NodeToIndex(location_idx) + time_dimension.CumulVar(index).SetRange(time_window[0], + time_window[1]) + routing.AddToAssignment(time_dimension.SlackVar(index)) + # Add time window constraints for each vehicle start node and 'copy' the + # slack var in the solution object (aka Assignment) to print it. + for vehicle_id in range(data.num_vehicles): + index = routing.Start(vehicle_id) + time_window = data.time_windows[0] + time_dimension.CumulVar(index).SetRange(time_window[0], + time_window[1]) + routing.AddToAssignment(time_dimension.SlackVar(index)) + + # Instantiate route start and end times to produce feasible times. + for vehicle_id in range(data.num_vehicles): + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.End(vehicle_id))) + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.Start(vehicle_id))) + + if args['resources']: + # Add resource constraints at the depot. + time_dimension = routing.GetDimensionOrDie(time) + solver = routing.solver() + intervals = [] + for i in range(data.num_vehicles): + # Add loading time at start of routes + intervals.append( + solver.FixedDurationIntervalVar( + time_dimension.CumulVar(routing.Start(i)), + data.vehicle_load_time, 'depot_interval')) + # Add unloading time at end of routes. + intervals.append( + solver.FixedDurationIntervalVar( + time_dimension.CumulVar(routing.End(i)), + data.vehicle_unload_time, 'depot_interval ')) + + depot_usage = [1 for i in range(data.num_vehicles * 2)] + solver.AddConstraint( + solver.Cumulative(intervals, depot_usage, data.depot_capacity, + 'depot')) + + # Setting first solution heuristic (cheapest addition). + search_parameters = pywrapcp.DefaultRoutingSearchParameters() + # pylint: disable=no-member + if not args['pickup_delivery']: + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) + else: + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION + ) + + search_parameters.local_search_metaheuristic = ( + routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) + search_parameters.time_limit.FromSeconds(2) + + # Solve the problem. + assignment = routing.SolveWithParameters(search_parameters) + # Print the solution. + printer = SVGPrinter(args, data, manager, routing, assignment) printer.print_to_console() return 0 - # Create the routing index manager. - # [START index_manager] - if args['starts_ends']: - manager = pywrapcp.RoutingIndexManager( - len(data.locations), data.num_vehicles, data.starts, data.ends) - else: - manager = pywrapcp.RoutingIndexManager( - len(data.locations), data.num_vehicles, data.depot) - # [END index_manager] - - # Create Routing Model. - # [START routing_model] - routing = pywrapcp.RoutingModel(manager) - - # [END routing_model] - - # Register distance callback - def distance_callback(from_index, to_index): - """Returns the manhattan distance between the two nodes.""" - # Convert from routing variable Index to distance matrix NodeIndex. - from_node = manager.IndexToNode(from_index) - to_node = manager.IndexToNode(to_index) - return data.distance_matrix[from_node][to_node] - - distance_callback_index = routing.RegisterTransitCallback(distance_callback) - - # Register time callback - def time_callback(from_index, to_index): - """Returns the manhattan distance travel time between the two nodes.""" - # Convert from routing variable Index to distance matrix NodeIndex. - from_node = manager.IndexToNode(from_index) - to_node = manager.IndexToNode(to_index) - return data.time_matrix[from_node][to_node] - - time_callback_index = routing.RegisterTransitCallback(time_callback) - - # Register demands callback - def demand_callback(from_index): - """Returns the demand of the node.""" - # Convert from routing variable Index to demands NodeIndex. - from_node = manager.IndexToNode(from_index) - return data.demands[from_node] - - demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback) - - if args['time_windows'] or args['resources']: - routing.SetArcCostEvaluatorOfAllVehicles(time_callback_index) - else: - routing.SetArcCostEvaluatorOfAllVehicles(distance_callback_index) - - if args['global_span'] or args['pickup_delivery']: - dimension_name = 'Distance' - routing.AddDimension(distance_callback_index, 0, 3000, True, dimension_name) - distance_dimension = routing.GetDimensionOrDie(dimension_name) - distance_dimension.SetGlobalSpanCostCoefficient(100) - - if args['capacity'] or args['drop_nodes']: - routing.AddDimensionWithVehicleCapacity(demand_callback_index, 0, - data.vehicle_capacities, True, - 'Capacity') - - if args['drop_nodes']: - # Allow to drop nodes. - penalty = 1000 - for node in range(1, len(data.locations)): - routing.AddDisjunction([manager.NodeToIndex(node)], penalty) - - if args['pickup_delivery']: - dimension_name = 'Distance' - routing.AddDimension(distance_callback_index, 0, 3000, True, dimension_name) - distance_dimension = routing.GetDimensionOrDie(dimension_name) - distance_dimension.SetGlobalSpanCostCoefficient(100) - for request in data.pickups_deliveries: - pickup_index = manager.NodeToIndex(request[0]) - delivery_index = manager.NodeToIndex(request[1]) - routing.AddPickupAndDelivery(pickup_index, delivery_index) - routing.solver().Add( - routing.VehicleVar(pickup_index) == routing.VehicleVar( - delivery_index)) - routing.solver().Add( - distance_dimension.CumulVar(pickup_index) <= - distance_dimension.CumulVar(delivery_index)) - if args['fifo']: - routing.SetPickupAndDeliveryPolicyOfAllVehicles( - pywrapcp.RoutingModel.PICKUP_AND_DELIVERY_FIFO) - if args['lifo']: - routing.SetPickupAndDeliveryPolicyOfAllVehicles( - pywrapcp.RoutingModel.PICKUP_AND_DELIVERY_LIFO) - - if args['starts_ends']: - dimension_name = 'Distance' - routing.AddDimension(distance_callback_index, 0, 2000, True, dimension_name) - distance_dimension = routing.GetDimensionOrDie(dimension_name) - distance_dimension.SetGlobalSpanCostCoefficient(100) - - time = 'Time' - if args['time_windows'] or args['resources']: - routing.AddDimension(time_callback_index, 30, 30, False, time) - time_dimension = routing.GetDimensionOrDie(time) - # Add time window constraints for each location except depot and 'copy' the - # slack var in the solution object (aka Assignment) to print it. - for location_idx, time_window in enumerate(data.time_windows): - if location_idx == 0: - continue - index = manager.NodeToIndex(location_idx) - time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1]) - routing.AddToAssignment(time_dimension.SlackVar(index)) - # Add time window constraints for each vehicle start node and 'copy' the - # slack var in the solution object (aka Assignment) to print it. - for vehicle_id in range(data.num_vehicles): - index = routing.Start(vehicle_id) - time_window = data.time_windows[0] - time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1]) - routing.AddToAssignment(time_dimension.SlackVar(index)) - - # Instantiate route start and end times to produce feasible times. - for vehicle_id in range(data.num_vehicles): - routing.AddVariableMinimizedByFinalizer( - time_dimension.CumulVar(routing.End(vehicle_id))) - routing.AddVariableMinimizedByFinalizer( - time_dimension.CumulVar(routing.Start(vehicle_id))) - - if args['resources']: - # Add resource constraints at the depot. - time_dimension = routing.GetDimensionOrDie(time) - solver = routing.solver() - intervals = [] - for i in range(data.num_vehicles): - # Add loading time at start of routes - intervals.append( - solver.FixedDurationIntervalVar( - time_dimension.CumulVar(routing.Start(i)), data.vehicle_load_time, - 'depot_interval')) - # Add unloading time at end of routes. - intervals.append( - solver.FixedDurationIntervalVar( - time_dimension.CumulVar(routing.End(i)), data.vehicle_unload_time, - 'depot_interval ')) - - depot_usage = [1 for i in range(data.num_vehicles * 2)] - solver.AddConstraint( - solver.Cumulative(intervals, depot_usage, data.depot_capacity, 'depot')) - - # Setting first solution heuristic (cheapest addition). - search_parameters = pywrapcp.DefaultRoutingSearchParameters() - # pylint: disable=no-member - if not args['pickup_delivery']: - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) - else: - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION) - - search_parameters.local_search_metaheuristic = ( - routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) - search_parameters.time_limit.FromSeconds(2) - - # Solve the problem. - assignment = routing.SolveWithParameters(search_parameters) - # Print the solution. - printer = SVGPrinter(args, data, manager, routing, assignment) - printer.print_to_console() - return 0 - if __name__ == '__main__': - main() + main() diff --git a/ortools/linear_solver/linear_solver_natural_api.py b/ortools/linear_solver/linear_solver_natural_api.py index 1d949ab5a9..bee3cbc236 100644 --- a/ortools/linear_solver/linear_solver_natural_api.py +++ b/ortools/linear_solver/linear_solver_natural_api.py @@ -13,7 +13,7 @@ """Patch to the python wrapper of ../linear_solver.h providing an algebraic API. -This is directly imported, and use exclusively in ./linear_solver.swig. See that +This is directly imported, and use exclusively in ./linear_solver.i. See that file. For examples leveraging the code defined here, see ./pywraplp_test.py and ../../../python/linear_programming.py. diff --git a/ortools/linear_solver/proto_solver/highs_proto_solver.cc b/ortools/linear_solver/proto_solver/highs_proto_solver.cc index 4f74ce7ff2..e3d5a40053 100644 --- a/ortools/linear_solver/proto_solver/highs_proto_solver.cc +++ b/ortools/linear_solver/proto_solver/highs_proto_solver.cc @@ -29,8 +29,7 @@ namespace operations_research { -absl::StatusOr HighsSolveProto(MPModelRequest request, - bool solve_as_a_mip) { +absl::StatusOr HighsSolveProto(MPModelRequest request) { return absl::UnimplementedError("Highs support is not yet implemented"); } diff --git a/ortools/linear_solver/proto_solver/highs_proto_solver.h b/ortools/linear_solver/proto_solver/highs_proto_solver.h index 67d7684d29..226dc13ed8 100644 --- a/ortools/linear_solver/proto_solver/highs_proto_solver.h +++ b/ortools/linear_solver/proto_solver/highs_proto_solver.h @@ -23,8 +23,7 @@ namespace operations_research { // Solve the input MIP model with the HIGHS solver. -absl::StatusOr HighsSolveProto(MPModelRequest request, - bool solve_as_a_mip = true); +absl::StatusOr HighsSolveProto(MPModelRequest request); } // namespace operations_research diff --git a/ortools/linear_solver/python/pywraplp_test.py b/ortools/linear_solver/python/pywraplp_test.py index d098d9e3a8..c6d9491602 100755 --- a/ortools/linear_solver/python/pywraplp_test.py +++ b/ortools/linear_solver/python/pywraplp_test.py @@ -10,7 +10,7 @@ # 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. -"""Simple unit tests for python/linear_solver.swig. Not exhaustive.""" +"""Simple unit tests for python/linear_solver.i. Not exhaustive.""" import unittest from ortools.linear_solver import linear_solver_pb2 diff --git a/ortools/lp_data/model_reader.cc b/ortools/lp_data/model_reader.cc index c1096452a5..f7ddfd0e0b 100644 --- a/ortools/lp_data/model_reader.cc +++ b/ortools/lp_data/model_reader.cc @@ -17,6 +17,7 @@ #include "ortools/base/file.h" #include "ortools/base/helpers.h" +#include "ortools/base/options.h" #include "ortools/linear_solver/linear_solver.pb.h" #include "ortools/lp_data/proto_utils.h" #include "ortools/util/file_util.h" diff --git a/ortools/pdlp/quadratic_program_io.cc b/ortools/pdlp/quadratic_program_io.cc index cab6ec5057..69ccbe93ba 100644 --- a/ortools/pdlp/quadratic_program_io.cc +++ b/ortools/pdlp/quadratic_program_io.cc @@ -34,6 +34,7 @@ #include "ortools/base/helpers.h" #include "ortools/base/logging.h" #include "ortools/base/mathutil.h" +#include "ortools/base/options.h" #include "ortools/base/status_macros.h" #include "ortools/linear_solver/linear_solver.pb.h" #include "ortools/linear_solver/model_exporter.h" diff --git a/ortools/port/file.cc b/ortools/port/file.cc index 5932649bb3..fa854ec047 100644 --- a/ortools/port/file.cc +++ b/ortools/port/file.cc @@ -27,6 +27,7 @@ #include "absl/time/clock.h" #include "ortools/base/file.h" #include "ortools/base/helpers.h" +#include "ortools/base/options.h" #endif // !defined(__PORTABLE_PLATFORM__) namespace operations_research { diff --git a/ortools/sat/cp_model_expand.cc b/ortools/sat/cp_model_expand.cc index 8628f54914..fbad10f058 100644 --- a/ortools/sat/cp_model_expand.cc +++ b/ortools/sat/cp_model_expand.cc @@ -1712,7 +1712,7 @@ void ExpandSomeLinearOfSizeTwo(ConstraintProto* ct, PresolveContext* context) { DCHECK(context->DomainContains(var2, value2)) << "value2 = " << value2; DCHECK_EQ(coeff1 * value1 + coeff2 * value2, infeasible_reachable_values.FixedValue()); - // TODO(user, fdid): Presolve if one or two variables are Boolean. + // TODO(user): Presolve if one or two variables are Boolean. if (!context->HasVarValueEncoding(var1, value1, nullptr) || size1 == 2) { return; } diff --git a/ortools/sat/cp_model_presolve.cc b/ortools/sat/cp_model_presolve.cc index cef7ada116..a1b6016d26 100644 --- a/ortools/sat/cp_model_presolve.cc +++ b/ortools/sat/cp_model_presolve.cc @@ -2387,7 +2387,7 @@ bool CpModelPresolver::PresolveDiophantine(ConstraintProto* ct) { return false; } } - // TODO(user, demonet): Make sure the newly generated linear constraint + // TODO(user): Make sure the newly generated linear constraint // satisfy our no-overflow precondition on the min/max activity. // We should check that the model still satisfy conditions in @@ -8950,7 +8950,7 @@ bool ModelCopy::ImportAndSimplifyConstraints( ConstraintProto* new_ct = context_->working_model->add_constraints(); *new_ct = ct; if (ignore_names) { - // TODO(user, fdid): find a better way than copy then clear_name()? + // TODO(user): find a better way than copy then clear_name()? new_ct->clear_name(); } } diff --git a/ortools/sat/cp_model_solver.cc b/ortools/sat/cp_model_solver.cc index 45a434667a..3600fc85bf 100644 --- a/ortools/sat/cp_model_solver.cc +++ b/ortools/sat/cp_model_solver.cc @@ -33,6 +33,7 @@ #include "google/protobuf/text_format.h" #include "ortools/base/file.h" #include "ortools/base/helpers.h" +#include "ortools/base/options.h" #endif // __PORTABLE_PLATFORM__ #include "absl/base/thread_annotations.h" #include "absl/container/btree_map.h" @@ -2422,7 +2423,7 @@ class FeasibilityPumpSolver : public SubSolver { deterministic_time_since_last_synchronize_ = 0.0; } - // TODO(user, fdid): Display feasibility pump statistics. + // TODO(user): Display feasibility pump statistics. private: SharedClasses* shared_; diff --git a/ortools/sat/docs/README.md b/ortools/sat/docs/README.md index a9207042a4..90e348e84d 100644 --- a/ortools/sat/docs/README.md +++ b/ortools/sat/docs/README.md @@ -43,34 +43,35 @@ The Python interface to the CP-SAT solver is implemented using two classes. access the solution found by the solve. ```python +#!/usr/bin/env python3 """Simple solve.""" from ortools.sat.python import cp_model def SimpleSatProgram(): - """Minimal CP-SAT example to showcase calling the solver.""" - # Creates the model. - model = cp_model.CpModel() + """Minimal CP-SAT example to showcase calling the solver.""" + # Creates the model. + model = cp_model.CpModel() - # Creates the variables. - num_vals = 3 - x = model.NewIntVar(0, num_vals - 1, 'x') - y = model.NewIntVar(0, num_vals - 1, 'y') - z = model.NewIntVar(0, num_vals - 1, 'z') + # Creates the variables. + num_vals = 3 + x = model.NewIntVar(0, num_vals - 1, 'x') + y = model.NewIntVar(0, num_vals - 1, 'y') + z = model.NewIntVar(0, num_vals - 1, 'z') - # Creates the constraints. - model.Add(x != y) + # Creates the constraints. + model.Add(x != y) - # Creates a solver and solves the model. - solver = cp_model.CpSolver() - status = solver.Solve(model) + # Creates a solver and solves the model. + solver = cp_model.CpSolver() + status = solver.Solve(model) - if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE: - print('x = %i' % solver.Value(x)) - print('y = %i' % solver.Value(y)) - print('z = %i' % solver.Value(z)) - else: - print('No solution found.') + if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE: + print('x = %i' % solver.Value(x)) + print('y = %i' % solver.Value(y)) + print('z = %i' % solver.Value(z)) + else: + print('No solution found.') SimpleSatProgram() @@ -139,9 +140,9 @@ The Java code implements the same interface as the Python code, with a ```java package com.google.ortools.sat.samples; import com.google.ortools.Loader; -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.IntVar; /** Minimal CP-SAT example to showcase calling the solver. */ diff --git a/ortools/sat/docs/boolean_logic.md b/ortools/sat/docs/boolean_logic.md index d4a6fc9925..93bd844ad3 100644 --- a/ortools/sat/docs/boolean_logic.md +++ b/ortools/sat/docs/boolean_logic.md @@ -47,18 +47,17 @@ negation of `x`. ### Python code ```python -"""Code sample to demonstrate Boolean variable and literals.""" - +#!/usr/bin/env python3 from ortools.sat.python import cp_model def LiteralSampleSat(): - model = cp_model.CpModel() - x = model.NewBoolVar('x') - not_x = x.Not() - print(x) - print(not_x) + model = cp_model.CpModel() + x = model.NewBoolVar('x') + not_x = x.Not() + print(x) + print(not_x) LiteralSampleSat() @@ -98,10 +97,10 @@ int main() { ```java package com.google.ortools.sat.samples; +import com.google.ortools.Loader; import com.google.ortools.sat.BoolVar; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.Literal; -import com.google.ortools.Loader; /** Code sample to demonstrate Boolean variable and literals. */ public class LiteralSampleSat { @@ -147,19 +146,18 @@ constraints. For instance, we can add a constraint Or(x, not(y)). ### Python code ```python -"""Code sample to demonstrates a simple Boolean constraint.""" - +#!/usr/bin/env python3 from ortools.sat.python import cp_model def BoolOrSampleSat(): - model = cp_model.CpModel() + model = cp_model.CpModel() - x = model.NewBoolVar('x') - y = model.NewBoolVar('y') + x = model.NewBoolVar('x') + y = model.NewBoolVar('y') - model.AddBoolOr([x, y.Not()]) + model.AddBoolOr([x, y.Not()]) BoolOrSampleSat() @@ -256,29 +254,29 @@ then is written as Or(not b, x) and Or(not b, not y). ### Python code ```python -"""Simple model with a reified constraint.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model def ReifiedSampleSat(): - """Showcase creating a reified constraint.""" - model = cp_model.CpModel() + """Showcase creating a reified constraint.""" + model = cp_model.CpModel() - x = model.NewBoolVar('x') - y = model.NewBoolVar('y') - b = model.NewBoolVar('b') + x = model.NewBoolVar('x') + y = model.NewBoolVar('y') + b = model.NewBoolVar('b') - # First version using a half-reified bool and. - model.AddBoolAnd(x, y.Not()).OnlyEnforceIf(b) + # First version using a half-reified bool and. + model.AddBoolAnd(x, y.Not()).OnlyEnforceIf(b) - # Second version using implications. - model.AddImplication(b, x) - model.AddImplication(b, y.Not()) + # Second version using implications. + model.AddImplication(b, x) + model.AddImplication(b, y.Not()) - # Third version using bool or. - model.AddBoolOr(b.Not(), x) - model.AddBoolOr(b.Not(), y.Not()) + # Third version using bool or. + model.AddBoolOr(b.Not(), x) + model.AddBoolOr(b.Not(), y.Not()) ReifiedSampleSat() @@ -418,34 +416,33 @@ code samples output this truth table: ### Python code ```python -"""Code sample that encodes the product of two Boolean variables.""" - +#!/usr/bin/env python3 from ortools.sat.python import cp_model def BooleanProductSampleSat(): - """Encoding of the product of two Boolean variables. + """Encoding of the product of two Boolean variables. p == x * y, which is the same as p <=> x and y """ - model = cp_model.CpModel() - x = model.NewBoolVar('x') - y = model.NewBoolVar('y') - p = model.NewBoolVar('p') + model = cp_model.CpModel() + x = model.NewBoolVar('x') + y = model.NewBoolVar('y') + p = model.NewBoolVar('p') - # x and y implies p, rewrite as not(x and y) or p - model.AddBoolOr(x.Not(), y.Not(), p) + # x and y implies p, rewrite as not(x and y) or p + model.AddBoolOr(x.Not(), y.Not(), p) - # p implies x and y, expanded into two implication - model.AddImplication(p, x) - model.AddImplication(p, y) + # p implies x and y, expanded into two implication + model.AddImplication(p, x) + model.AddImplication(p, y) - # Create a solver and solve. - solver = cp_model.CpSolver() - solution_printer = cp_model.VarArraySolutionPrinter([x, y, p]) - solver.parameters.enumerate_all_solutions = True - solver.Solve(model, solution_printer) + # Create a solver and solve. + solver = cp_model.CpSolver() + solution_printer = cp_model.VarArraySolutionPrinter([x, y, p]) + solver.parameters.enumerate_all_solutions = True + solver.Solve(model, solution_printer) BooleanProductSampleSat() diff --git a/ortools/sat/docs/channeling.md b/ortools/sat/docs/channeling.md index e90b6ea89b..108d66de65 100644 --- a/ortools/sat/docs/channeling.md +++ b/ortools/sat/docs/channeling.md @@ -46,68 +46,67 @@ These are implemented using the `OnlyEnforceIf` method as shown below. ### Python code ```python -"""Link integer constraints together.""" - +#!/usr/bin/env python3 from ortools.sat.python import cp_model class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback): - """Print intermediate solutions.""" + """Print intermediate solutions.""" - def __init__(self, variables): - cp_model.CpSolverSolutionCallback.__init__(self) - self.__variables = variables - self.__solution_count = 0 + def __init__(self, variables): + cp_model.CpSolverSolutionCallback.__init__(self) + self.__variables = variables + self.__solution_count = 0 - def on_solution_callback(self): - self.__solution_count += 1 - for v in self.__variables: - print('%s=%i' % (v, self.Value(v)), end=' ') - print() + def on_solution_callback(self): + self.__solution_count += 1 + for v in self.__variables: + print('%s=%i' % (v, self.Value(v)), end=' ') + print() - def solution_count(self): - return self.__solution_count + def solution_count(self): + return self.__solution_count def ChannelingSampleSat(): - """Demonstrates how to link integer constraints together.""" + """Demonstrates how to link integer constraints together.""" - # Create the CP-SAT model. - model = cp_model.CpModel() + # Create the CP-SAT model. + model = cp_model.CpModel() - # Declare our two primary variables. - x = model.NewIntVar(0, 10, 'x') - y = model.NewIntVar(0, 10, 'y') + # Declare our two primary variables. + x = model.NewIntVar(0, 10, 'x') + y = model.NewIntVar(0, 10, 'y') - # Declare our intermediate boolean variable. - b = model.NewBoolVar('b') + # Declare our intermediate boolean variable. + b = model.NewBoolVar('b') - # Implement b == (x >= 5). - model.Add(x >= 5).OnlyEnforceIf(b) - model.Add(x < 5).OnlyEnforceIf(b.Not()) + # Implement b == (x >= 5). + model.Add(x >= 5).OnlyEnforceIf(b) + model.Add(x < 5).OnlyEnforceIf(b.Not()) - # Create our two half-reified constraints. - # First, b implies (y == 10 - x). - model.Add(y == 10 - x).OnlyEnforceIf(b) - # Second, not(b) implies y == 0. - model.Add(y == 0).OnlyEnforceIf(b.Not()) + # Create our two half-reified constraints. + # First, b implies (y == 10 - x). + model.Add(y == 10 - x).OnlyEnforceIf(b) + # Second, not(b) implies y == 0. + model.Add(y == 0).OnlyEnforceIf(b.Not()) - # Search for x values in increasing order. - model.AddDecisionStrategy([x], cp_model.CHOOSE_FIRST, - cp_model.SELECT_MIN_VALUE) + # Search for x values in increasing order. + model.AddDecisionStrategy([x], cp_model.CHOOSE_FIRST, + cp_model.SELECT_MIN_VALUE) - # Create a solver and solve with a fixed search. - solver = cp_model.CpSolver() + # Create a solver and solve with a fixed search. + solver = cp_model.CpSolver() - # Force the solver to follow the decision strategy exactly. - solver.parameters.search_branching = cp_model.FIXED_SEARCH - # Enumerate all solutions. - solver.parameters.enumerate_all_solutions = True + # Force the solver to follow the decision strategy exactly. + solver.parameters.search_branching = cp_model.FIXED_SEARCH + # Enumerate all solutions. + solver.parameters.enumerate_all_solutions = True - # Search and print out all solutions. - solution_printer = VarArraySolutionPrinter([x, y, b]) - solver.Solve(model, solution_printer) + # Search and print out all solutions. + solution_printer = VarArraySolutionPrinter([x, y, b]) + solver.Solve(model, solution_printer) ChannelingSampleSat() @@ -118,8 +117,8 @@ ChannelingSampleSat() ```cpp #include -#include "ortools/base/logging.h" #include "absl/types/span.h" +#include "ortools/base/logging.h" #include "ortools/sat/cp_model.h" #include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_solver.h" @@ -184,14 +183,14 @@ int main() { package com.google.ortools.sat.samples; import com.google.ortools.Loader; -import com.google.ortools.sat.DecisionStrategyProto; -import com.google.ortools.sat.SatParameters; import com.google.ortools.sat.BoolVar; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.DecisionStrategyProto; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.LinearExpr; +import com.google.ortools.sat.SatParameters; /** Link integer constraints together. */ public class ChannelingSampleSat { @@ -217,8 +216,7 @@ public class ChannelingSampleSat { model.addEquality(vars[1], 0).onlyEnforceIf(b.not()); // Search for x values in increasing order. - model.addDecisionStrategy( - new IntVar[] {vars[0]}, + model.addDecisionStrategy(new IntVar[] {vars[0]}, DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST, DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE); @@ -231,24 +229,22 @@ public class ChannelingSampleSat { solver.getParameters().setEnumerateAllSolutions(true); // Solve the problem with the printer callback. - solver.solve( - model, - new CpSolverSolutionCallback() { - public CpSolverSolutionCallback init(IntVar[] variables) { - variableArray = variables; - return this; - } + solver.solve(model, new CpSolverSolutionCallback() { + public CpSolverSolutionCallback init(IntVar[] variables) { + variableArray = variables; + return this; + } - @Override - public void onSolutionCallback() { - for (IntVar v : variableArray) { - System.out.printf("%s=%d ", v.getName(), value(v)); - } - System.out.println(); - } + @Override + public void onSolutionCallback() { + for (IntVar v : variableArray) { + System.out.printf("%s=%d ", v.getName(), value(v)); + } + System.out.println(); + } - private IntVar[] variableArray; - }.init(new IntVar[] {vars[0], vars[1], b})); + private IntVar[] variableArray; + }.init(new IntVar[] {vars[0], vars[1], b})); } } ``` @@ -358,70 +354,68 @@ variables together: ### Python code ```python -"""Solves a binpacking problem using the CP-SAT solver.""" - +#!/usr/bin/env python3 from ortools.sat.python import cp_model - def BinpackingProblemSat(): - """Solves a bin-packing problem using the CP-SAT solver.""" - # Data. - bin_capacity = 100 - slack_capacity = 20 - num_bins = 5 - all_bins = range(num_bins) + """Solves a bin-packing problem using the CP-SAT solver.""" + # Data. + bin_capacity = 100 + slack_capacity = 20 + num_bins = 5 + all_bins = range(num_bins) - items = [(20, 6), (15, 6), (30, 4), (45, 3)] - num_items = len(items) - all_items = range(num_items) + items = [(20, 6), (15, 6), (30, 4), (45, 3)] + num_items = len(items) + all_items = range(num_items) - # Model. - model = cp_model.CpModel() + # Model. + model = cp_model.CpModel() - # Main variables. - x = {} - for i in all_items: - num_copies = items[i][1] + # Main variables. + x = {} + for i in all_items: + num_copies = items[i][1] + for b in all_bins: + x[(i, b)] = model.NewIntVar(0, num_copies, 'x_%i_%i' % (i, b)) + + # Load variables. + load = [model.NewIntVar(0, bin_capacity, 'load_%i' % b) for b in all_bins] + + # Slack variables. + slacks = [model.NewBoolVar('slack_%i' % b) for b in all_bins] + + # Links load and x. for b in all_bins: - x[(i, b)] = model.NewIntVar(0, num_copies, 'x_%i_%i' % (i, b)) + model.Add(load[b] == sum(x[(i, b)] * items[i][0] for i in all_items)) - # Load variables. - load = [model.NewIntVar(0, bin_capacity, 'load_%i' % b) for b in all_bins] + # Place all items. + for i in all_items: + model.Add(sum(x[(i, b)] for b in all_bins) == items[i][1]) - # Slack variables. - slacks = [model.NewBoolVar('slack_%i' % b) for b in all_bins] + # Links load and slack through an equivalence relation. + safe_capacity = bin_capacity - slack_capacity + for b in all_bins: + # slack[b] => load[b] <= safe_capacity. + model.Add(load[b] <= safe_capacity).OnlyEnforceIf(slacks[b]) + # not(slack[b]) => load[b] > safe_capacity. + model.Add(load[b] > safe_capacity).OnlyEnforceIf(slacks[b].Not()) - # Links load and x. - for b in all_bins: - model.Add(load[b] == sum(x[(i, b)] * items[i][0] for i in all_items)) + # Maximize sum of slacks. + model.Maximize(sum(slacks)) - # Place all items. - for i in all_items: - model.Add(sum(x[(i, b)] for b in all_bins) == items[i][1]) - - # Links load and slack through an equivalence relation. - safe_capacity = bin_capacity - slack_capacity - for b in all_bins: - # slack[b] => load[b] <= safe_capacity. - model.Add(load[b] <= safe_capacity).OnlyEnforceIf(slacks[b]) - # not(slack[b]) => load[b] > safe_capacity. - model.Add(load[b] > safe_capacity).OnlyEnforceIf(slacks[b].Not()) - - # Maximize sum of slacks. - model.Maximize(sum(slacks)) - - # Solves and prints out the solution. - solver = cp_model.CpSolver() - status = solver.Solve(model) - print('Solve status: %s' % solver.StatusName(status)) - if status == cp_model.OPTIMAL: - print('Optimal objective value: %i' % solver.ObjectiveValue()) - print('Statistics') - print(' - conflicts : %i' % solver.NumConflicts()) - print(' - branches : %i' % solver.NumBranches()) - print(' - wall time : %f s' % solver.WallTime()) + # Solves and prints out the solution. + solver = cp_model.CpSolver() + status = solver.Solve(model) + print('Solve status: %s' % solver.StatusName(status)) + if status == cp_model.OPTIMAL: + print('Optimal objective value: %i' % solver.ObjectiveValue()) + print('Statistics') + print(' - conflicts : %i' % solver.NumConflicts()) + print(' - branches : %i' % solver.NumBranches()) + print(' - wall time : %f s' % solver.WallTime()) BinpackingProblemSat() @@ -524,9 +518,9 @@ int main() { package com.google.ortools.sat.samples; import com.google.ortools.Loader; -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.LinearExpr; import com.google.ortools.sat.LinearExprBuilder; diff --git a/ortools/sat/docs/integer_arithmetic.md b/ortools/sat/docs/integer_arithmetic.md index 3491ac4ab2..c4078fa41d 100644 --- a/ortools/sat/docs/integer_arithmetic.md +++ b/ortools/sat/docs/integer_arithmetic.md @@ -148,29 +148,30 @@ rabbits and pheasants are there? ### Python code ```python -"""Rabbits and Pheasants quizz.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model def RabbitsAndPheasantsSat(): - """Solves the rabbits + pheasants problem.""" - model = cp_model.CpModel() + """Solves the rabbits + pheasants problem.""" + model = cp_model.CpModel() - r = model.NewIntVar(0, 100, 'r') - p = model.NewIntVar(0, 100, 'p') + r = model.NewIntVar(0, 100, 'r') + p = model.NewIntVar(0, 100, 'p') - # 20 heads. - model.Add(r + p == 20) - # 56 legs. - model.Add(4 * r + 2 * p == 56) + # 20 heads. + model.Add(r + p == 20) + # 56 legs. + model.Add(4 * r + 2 * p == 56) - # Solves and prints out the solution. - solver = cp_model.CpSolver() - status = solver.Solve(model) + # Solves and prints out the solution. + solver = cp_model.CpSolver() + status = solver.Solve(model) - if status == cp_model.OPTIMAL: - print('%i rabbits and %i pheasants' % (solver.Value(r), solver.Value(p))) + if status == cp_model.OPTIMAL: + print('%i rabbits and %i pheasants' % + (solver.Value(r), solver.Value(p))) RabbitsAndPheasantsSat() @@ -226,9 +227,9 @@ int main() { package com.google.ortools.sat.samples; import com.google.ortools.Loader; -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.LinearExpr; @@ -290,7 +291,6 @@ public class RabbitsAndPheasantsSat } } } - ``` ## Earliness-Tardiness cost function. @@ -330,82 +330,81 @@ The following samples output: ### Python code ```python -"""Encodes an convex piecewise linear function.""" - +#!/usr/bin/env python3 from ortools.sat.python import cp_model class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback): - """Print intermediate solutions.""" + """Print intermediate solutions.""" - def __init__(self, variables): - cp_model.CpSolverSolutionCallback.__init__(self) - self.__variables = variables - self.__solution_count = 0 + def __init__(self, variables): + cp_model.CpSolverSolutionCallback.__init__(self) + self.__variables = variables + self.__solution_count = 0 - def on_solution_callback(self): - self.__solution_count += 1 - for v in self.__variables: - print('%s=%i' % (v, self.Value(v)), end=' ') - print() + def on_solution_callback(self): + self.__solution_count += 1 + for v in self.__variables: + print('%s=%i' % (v, self.Value(v)), end=' ') + print() - def solution_count(self): - return self.__solution_count + def solution_count(self): + return self.__solution_count def earliness_tardiness_cost_sample_sat(): - """Encode the piecewise linear expression.""" + """Encode the piecewise linear expression.""" - earliness_date = 5 # ed. - earliness_cost = 8 - lateness_date = 15 # ld. - lateness_cost = 12 + earliness_date = 5 # ed. + earliness_cost = 8 + lateness_date = 15 # ld. + lateness_cost = 12 - # Model. - model = cp_model.CpModel() + # Model. + model = cp_model.CpModel() - # Declare our primary variable. - x = model.NewIntVar(0, 20, 'x') + # Declare our primary variable. + x = model.NewIntVar(0, 20, 'x') - # Create the expression variable and implement the piecewise linear function. - # - # \ / - # \______/ - # ed ld - # - large_constant = 1000 - expr = model.NewIntVar(0, large_constant, 'expr') + # Create the expression variable and implement the piecewise linear function. + # + # \ / + # \______/ + # ed ld + # + large_constant = 1000 + expr = model.NewIntVar(0, large_constant, 'expr') - # First segment. - s1 = model.NewIntVar(-large_constant, large_constant, 's1') - model.Add(s1 == earliness_cost * (earliness_date - x)) + # First segment. + s1 = model.NewIntVar(-large_constant, large_constant, 's1') + model.Add(s1 == earliness_cost * (earliness_date - x)) - # Second segment. - s2 = 0 + # Second segment. + s2 = 0 - # Third segment. - s3 = model.NewIntVar(-large_constant, large_constant, 's3') - model.Add(s3 == lateness_cost * (x - lateness_date)) + # Third segment. + s3 = model.NewIntVar(-large_constant, large_constant, 's3') + model.Add(s3 == lateness_cost * (x - lateness_date)) - # Link together expr and x through s1, s2, and s3. - model.AddMaxEquality(expr, [s1, s2, s3]) + # Link together expr and x through s1, s2, and s3. + model.AddMaxEquality(expr, [s1, s2, s3]) - # Search for x values in increasing order. - model.AddDecisionStrategy([x], cp_model.CHOOSE_FIRST, - cp_model.SELECT_MIN_VALUE) + # Search for x values in increasing order. + model.AddDecisionStrategy([x], cp_model.CHOOSE_FIRST, + cp_model.SELECT_MIN_VALUE) - # Create a solver and solve with a fixed search. - solver = cp_model.CpSolver() + # Create a solver and solve with a fixed search. + solver = cp_model.CpSolver() - # Force the solver to follow the decision strategy exactly. - solver.parameters.search_branching = cp_model.FIXED_SEARCH - # Enumerate all solutions. - solver.parameters.enumerate_all_solutions = True + # Force the solver to follow the decision strategy exactly. + solver.parameters.search_branching = cp_model.FIXED_SEARCH + # Enumerate all solutions. + solver.parameters.enumerate_all_solutions = True - # Search and print out all solutions. - solution_printer = VarArraySolutionPrinter([x, expr]) - solver.Solve(model, solution_printer) + # Search and print out all solutions. + solution_printer = VarArraySolutionPrinter([x, expr]) + solver.Solve(model, solution_printer) earliness_tardiness_cost_sample_sat() @@ -418,8 +417,8 @@ earliness_tardiness_cost_sample_sat() #include -#include "ortools/base/logging.h" #include "absl/types/span.h" +#include "ortools/base/logging.h" #include "ortools/sat/cp_model.h" #include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_solver.h" @@ -487,13 +486,13 @@ int main() { package com.google.ortools.sat.samples; import com.google.ortools.Loader; -import com.google.ortools.sat.DecisionStrategyProto; -import com.google.ortools.sat.SatParameters; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.DecisionStrategyProto; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.LinearExpr; +import com.google.ortools.sat.SatParameters; /** Encode the piecewise linear expression. */ public class EarlinessTardinessCostSampleSat { @@ -523,20 +522,19 @@ public class EarlinessTardinessCostSampleSat { // First segment: y == earlinessCost * (earlinessDate - x). // Second segment: y = 0 // Third segment: y == latenessCost * (x - latenessDate). - model.addMaxEquality( - expr, - new LinearExpr[] { - LinearExpr.newBuilder() - .addTerm(x, -earlinessCost) - .add(earlinessCost * earlinessDate) - .build(), - LinearExpr.constant(0), - LinearExpr.newBuilder().addTerm(x, latenessCost).add(-latenessCost * latenessDate).build() - }); + model.addMaxEquality(expr, + new LinearExpr[] {LinearExpr.newBuilder() + .addTerm(x, -earlinessCost) + .add(earlinessCost * earlinessDate) + .build(), + LinearExpr.constant(0), + LinearExpr.newBuilder() + .addTerm(x, latenessCost) + .add(-latenessCost * latenessDate) + .build()}); // Search for x values in increasing order. - model.addDecisionStrategy( - new IntVar[] {x}, + model.addDecisionStrategy(new IntVar[] {x}, DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST, DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE); @@ -549,24 +547,22 @@ public class EarlinessTardinessCostSampleSat { solver.getParameters().setEnumerateAllSolutions(true); // Solve the problem with the printer callback. - solver.solve( - model, - new CpSolverSolutionCallback() { - public CpSolverSolutionCallback init(IntVar[] variables) { - variableArray = variables; - return this; - } + solver.solve(model, new CpSolverSolutionCallback() { + public CpSolverSolutionCallback init(IntVar[] variables) { + variableArray = variables; + return this; + } - @Override - public void onSolutionCallback() { - for (IntVar v : variableArray) { - System.out.printf("%s=%d ", v.getName(), value(v)); - } - System.out.println(); - } + @Override + public void onSolutionCallback() { + for (IntVar v : variableArray) { + System.out.printf("%s=%d ", v.getName(), value(v)); + } + System.out.println(); + } - private IntVar[] variableArray; - }.init(new IntVar[] {x, expr})); + private IntVar[] variableArray; + }.init(new IntVar[] {x, expr})); } } ``` @@ -676,85 +672,85 @@ The following samples output: ### Python code ```python -"""Implements a step function.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback): - """Print intermediate solutions.""" + """Print intermediate solutions.""" - def __init__(self, variables): - cp_model.CpSolverSolutionCallback.__init__(self) - self.__variables = variables - self.__solution_count = 0 + def __init__(self, variables): + cp_model.CpSolverSolutionCallback.__init__(self) + self.__variables = variables + self.__solution_count = 0 - def on_solution_callback(self): - self.__solution_count += 1 - for v in self.__variables: - print('%s=%i' % (v, self.Value(v)), end=' ') - print() + def on_solution_callback(self): + self.__solution_count += 1 + for v in self.__variables: + print('%s=%i' % (v, self.Value(v)), end=' ') + print() - def solution_count(self): - return self.__solution_count + def solution_count(self): + return self.__solution_count def step_function_sample_sat(): - """Encode the step function.""" + """Encode the step function.""" - # Model. - model = cp_model.CpModel() + # Model. + model = cp_model.CpModel() - # Declare our primary variable. - x = model.NewIntVar(0, 20, 'x') + # Declare our primary variable. + x = model.NewIntVar(0, 20, 'x') - # Create the expression variable and implement the step function - # Note it is not defined for x == 2. - # - # - 3 - # -- -- --------- 2 - # 1 - # -- --- 0 - # 0 ================ 20 - # - expr = model.NewIntVar(0, 3, 'expr') + # Create the expression variable and implement the step function + # Note it is not defined for x == 2. + # + # - 3 + # -- -- --------- 2 + # 1 + # -- --- 0 + # 0 ================ 20 + # + expr = model.NewIntVar(0, 3, 'expr') - # expr == 0 on [5, 6] U [8, 10] - b0 = model.NewBoolVar('b0') - model.AddLinearExpressionInDomain( - x, cp_model.Domain.FromIntervals([(5, 6), (8, 10)])).OnlyEnforceIf(b0) - model.Add(expr == 0).OnlyEnforceIf(b0) + # expr == 0 on [5, 6] U [8, 10] + b0 = model.NewBoolVar('b0') + model.AddLinearExpressionInDomain( + x, cp_model.Domain.FromIntervals([(5, 6), (8, 10)])).OnlyEnforceIf(b0) + model.Add(expr == 0).OnlyEnforceIf(b0) - # expr == 2 on [0, 1] U [3, 4] U [11, 20] - b2 = model.NewBoolVar('b2') - model.AddLinearExpressionInDomain( - x, cp_model.Domain.FromIntervals([(0, 1), (3, 4), - (11, 20)])).OnlyEnforceIf(b2) - model.Add(expr == 2).OnlyEnforceIf(b2) + # expr == 2 on [0, 1] U [3, 4] U [11, 20] + b2 = model.NewBoolVar('b2') + model.AddLinearExpressionInDomain( + x, cp_model.Domain.FromIntervals([(0, 1), (3, 4), + (11, 20)])).OnlyEnforceIf(b2) + model.Add(expr == 2).OnlyEnforceIf(b2) - # expr == 3 when x == 7 - b3 = model.NewBoolVar('b3') - model.Add(x == 7).OnlyEnforceIf(b3) - model.Add(expr == 3).OnlyEnforceIf(b3) + # expr == 3 when x == 7 + b3 = model.NewBoolVar('b3') + model.Add(x == 7).OnlyEnforceIf(b3) + model.Add(expr == 3).OnlyEnforceIf(b3) - # At least one bi is true. (we could use an exactly one constraint). - model.AddBoolOr(b0, b2, b3) + # At least one bi is true. (we could use an exactly one constraint). + model.AddBoolOr(b0, b2, b3) - # Search for x values in increasing order. - model.AddDecisionStrategy([x], cp_model.CHOOSE_FIRST, - cp_model.SELECT_MIN_VALUE) + # Search for x values in increasing order. + model.AddDecisionStrategy([x], cp_model.CHOOSE_FIRST, + cp_model.SELECT_MIN_VALUE) - # Create a solver and solve with a fixed search. - solver = cp_model.CpSolver() + # Create a solver and solve with a fixed search. + solver = cp_model.CpSolver() - # Force the solver to follow the decision strategy exactly. - solver.parameters.search_branching = cp_model.FIXED_SEARCH - # Enumerate all solutions. - solver.parameters.enumerate_all_solutions = True + # Force the solver to follow the decision strategy exactly. + solver.parameters.search_branching = cp_model.FIXED_SEARCH + # Enumerate all solutions. + solver.parameters.enumerate_all_solutions = True - # Search and print out all solutions. - solution_printer = VarArraySolutionPrinter([x, expr]) - solver.Solve(model, solution_printer) + # Search and print out all solutions. + solution_printer = VarArraySolutionPrinter([x, expr]) + solver.Solve(model, solution_printer) step_function_sample_sat() @@ -767,9 +763,9 @@ step_function_sample_sat() #include +#include "absl/types/span.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "absl/types/span.h" #include "ortools/sat/cp_model.h" #include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_solver.h" @@ -852,13 +848,13 @@ int main() { package com.google.ortools.sat.samples; import com.google.ortools.Loader; -import com.google.ortools.sat.DecisionStrategyProto; -import com.google.ortools.sat.SatParameters; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.DecisionStrategyProto; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.Literal; +import com.google.ortools.sat.SatParameters; import com.google.ortools.util.Domain; /** Link integer constraints together. */ @@ -884,8 +880,7 @@ public class StepFunctionSampleSat { // expr == 0 on [5, 6] U [8, 10] Literal b0 = model.newBoolVar("b0"); - model - .addLinearExpressionInDomain(x, Domain.fromValues(new long[] {5, 6, 8, 9, 10})) + model.addLinearExpressionInDomain(x, Domain.fromValues(new long[] {5, 6, 8, 9, 10})) .onlyEnforceIf(b0); model.addEquality(expr, 0).onlyEnforceIf(b0); @@ -906,8 +901,7 @@ public class StepFunctionSampleSat { model.addBoolOr(new Literal[] {b0, b2, b3}); // Search for x values in increasing order. - model.addDecisionStrategy( - new IntVar[] {x}, + model.addDecisionStrategy(new IntVar[] {x}, DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST, DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE); @@ -920,24 +914,22 @@ public class StepFunctionSampleSat { solver.getParameters().setEnumerateAllSolutions(true); // Solve the problem with the printer callback. - solver.solve( - model, - new CpSolverSolutionCallback() { - public CpSolverSolutionCallback init(IntVar[] variables) { - variableArray = variables; - return this; - } + solver.solve(model, new CpSolverSolutionCallback() { + public CpSolverSolutionCallback init(IntVar[] variables) { + variableArray = variables; + return this; + } - @Override - public void onSolutionCallback() { - for (IntVar v : variableArray) { - System.out.printf("%s=%d ", v.getName(), value(v)); - } - System.out.println(); - } + @Override + public void onSolutionCallback() { + for (IntVar v : variableArray) { + System.out.printf("%s=%d ", v.getName(), value(v)); + } + System.out.println(); + } - private IntVar[] variableArray; - }.init(new IntVar[] {x, expr})); + private IntVar[] variableArray; + }.init(new IntVar[] {x, expr})); } } ``` diff --git a/ortools/sat/docs/model.md b/ortools/sat/docs/model.md index 98b9da2697..8c781dfc79 100644 --- a/ortools/sat/docs/model.md +++ b/ortools/sat/docs/model.md @@ -89,38 +89,38 @@ Some remarks: ### Python code ```python -"""Code sample that solves a model using solution hinting.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model def SolutionHintingSampleSat(): - """Showcases solution hinting.""" - # Creates the model. - model = cp_model.CpModel() + """Showcases solution hinting.""" + # Creates the model. + model = cp_model.CpModel() - # Creates the variables. - num_vals = 3 - x = model.NewIntVar(0, num_vals - 1, 'x') - y = model.NewIntVar(0, num_vals - 1, 'y') - z = model.NewIntVar(0, num_vals - 1, 'z') + # Creates the variables. + num_vals = 3 + x = model.NewIntVar(0, num_vals - 1, 'x') + y = model.NewIntVar(0, num_vals - 1, 'y') + z = model.NewIntVar(0, num_vals - 1, 'z') - # Creates the constraints. - model.Add(x != y) + # Creates the constraints. + model.Add(x != y) - model.Maximize(x + 2 * y + 3 * z) + model.Maximize(x + 2 * y + 3 * z) - # Solution hinting: x <- 1, y <- 2 - model.AddHint(x, 1) - model.AddHint(y, 2) + # Solution hinting: x <- 1, y <- 2 + model.AddHint(x, 1) + model.AddHint(y, 2) - # Creates a solver and solves. - solver = cp_model.CpSolver() - solution_printer = cp_model.VarArrayAndObjectiveSolutionPrinter([x, y, z]) - status = solver.Solve(model, solution_printer) + # Creates a solver and solves. + solver = cp_model.CpSolver() + solution_printer = cp_model.VarArrayAndObjectiveSolutionPrinter([x, y, z]) + status = solver.Solve(model, solution_printer) - print('Status = %s' % solver.StatusName(status)) - print('Number of solutions found: %i' % solution_printer.solution_count()) + print('Status = %s' % solver.StatusName(status)) + print('Number of solutions found: %i' % solution_printer.solution_count()) SolutionHintingSampleSat() @@ -321,48 +321,48 @@ illustrated in the following examples. ### Python code ```python -"""Showcases deep copying of a model.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model def CopyModelSat(): - """Showcases printing intermediate solutions found during search.""" - # Creates the model. - model = cp_model.CpModel() + """Showcases printing intermediate solutions found during search.""" + # Creates the model. + model = cp_model.CpModel() - # Creates the variables. - num_vals = 3 - x = model.NewIntVar(0, num_vals - 1, 'x') - y = model.NewIntVar(0, num_vals - 1, 'y') - z = model.NewIntVar(0, num_vals - 1, 'z') + # Creates the variables. + num_vals = 3 + x = model.NewIntVar(0, num_vals - 1, 'x') + y = model.NewIntVar(0, num_vals - 1, 'y') + z = model.NewIntVar(0, num_vals - 1, 'z') - # Creates the constraints. - model.Add(x != y) + # Creates the constraints. + model.Add(x != y) - model.Maximize(x + 2 * y + 3 * z) + model.Maximize(x + 2 * y + 3 * z) - # Creates a solver and solves. - solver = cp_model.CpSolver() - status = solver.Solve(model) + # Creates a solver and solves. + solver = cp_model.CpSolver() + status = solver.Solve(model) - if status == cp_model.OPTIMAL: - print('Optimal value of the original model: {}'.format( - solver.ObjectiveValue())) + if status == cp_model.OPTIMAL: + print('Optimal value of the original model: {}'.format( + solver.ObjectiveValue())) - # Copy the model. - copy = cp_model.CpModel() - copy.CopyFrom(model) + # Copy the model. + copy = cp_model.CpModel() + copy.CopyFrom(model) - copy_x = copy.GetIntVarFromProtoIndex(x.Index()) - copy_y = copy.GetIntVarFromProtoIndex(y.Index()) + copy_x = copy.GetIntVarFromProtoIndex(x.Index()) + copy_y = copy.GetIntVarFromProtoIndex(y.Index()) - copy.Add(copy_x + copy_y <= 1) - status = solver.Solve(copy) + copy.Add(copy_x + copy_y <= 1) + status = solver.Solve(copy) - if status == cp_model.OPTIMAL: - print('Optimal value of the modified model: {}'.format( - solver.ObjectiveValue())) + if status == cp_model.OPTIMAL: + print('Optimal value of the modified model: {}'.format( + solver.ObjectiveValue())) CopyModelSat() diff --git a/ortools/sat/docs/scheduling.md b/ortools/sat/docs/scheduling.md index 3679f7309d..661310873d 100644 --- a/ortools/sat/docs/scheduling.md +++ b/ortools/sat/docs/scheduling.md @@ -64,35 +64,34 @@ Creating these intervals is illustrated in the following code snippets. ### Python code ```python -"""Code sample to demonstrates how to build an interval.""" - +#!/usr/bin/env python3 from ortools.sat.python import cp_model def IntervalSampleSat(): - """Showcases how to build interval variables.""" - model = cp_model.CpModel() - horizon = 100 + """Showcases how to build interval variables.""" + model = cp_model.CpModel() + horizon = 100 - # An interval can be created from three affine expressions. - start_var = model.NewIntVar(0, horizon, 'start') - duration = 10 # Python cp/sat code accept integer variables or constants. - end_var = model.NewIntVar(0, horizon, 'end') - interval_var = model.NewIntervalVar(start_var, duration, end_var + 2, - 'interval') + # An interval can be created from three affine expressions. + start_var = model.NewIntVar(0, horizon, 'start') + duration = 10 # Python cp/sat code accept integer variables or constants. + end_var = model.NewIntVar(0, horizon, 'end') + interval_var = model.NewIntervalVar(start_var, duration, end_var + 2, + 'interval') - print(f'interval = {repr(interval_var)}') + print(f'interval = {repr(interval_var)}') - # If the size is fixed, a simpler version uses the start expression and the - # size. - fixed_size_interval_var = model.NewFixedSizeIntervalVar( - start_var, 10, 'fixed_size_interval_var') - print(f'fixed_size_interval_var = {repr(fixed_size_interval_var)}') + # If the size is fixed, a simpler version uses the start expression and the + # size. + fixed_size_interval_var = model.NewFixedSizeIntervalVar( + start_var, 10, 'fixed_size_interval_var') + print(f'fixed_size_interval_var = {repr(fixed_size_interval_var)}') - # A fixed interval can be created using the same API. - fixed_interval = model.NewFixedSizeIntervalVar(5, 10, 'fixed_interval') - print(f'fixed_interval = {repr(fixed_interval)}') + # A fixed interval can be created using the same API. + fixed_interval = model.NewFixedSizeIntervalVar(5, 10, 'fixed_interval') + print(f'fixed_interval = {repr(fixed_interval)}') IntervalSampleSat() @@ -177,12 +176,8 @@ public class IntervalSampleSat { // An interval can be created from three affine expressions. IntVar startVar = model.newIntVar(0, horizon, "start"); IntVar endVar = model.newIntVar(0, horizon, "end"); - IntervalVar intervalVar = - model.newIntervalVar( - startVar, - LinearExpr.constant(10), - LinearExpr.newBuilder().add(endVar).add(2), - "interval"); + IntervalVar intervalVar = model.newIntervalVar( + startVar, LinearExpr.constant(10), LinearExpr.newBuilder().add(endVar).add(2), "interval"); System.out.println(intervalVar); // If the size is fixed, a simpler version uses the start expression and the size. @@ -237,36 +232,37 @@ understand these presence literals, and correctly ignore inactive intervals. ### Python code ```python -"""Code sample to demonstrates how to build an optional interval.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model def OptionalIntervalSampleSat(): - """Showcases how to build optional interval variables.""" - model = cp_model.CpModel() - horizon = 100 + """Showcases how to build optional interval variables.""" + model = cp_model.CpModel() + horizon = 100 - # An interval can be created from three affine expressions. - start_var = model.NewIntVar(0, horizon, 'start') - duration = 10 # Python cp/sat code accept integer variables or constants. - end_var = model.NewIntVar(0, horizon, 'end') - presence_var = model.NewBoolVar('presence') - interval_var = model.NewOptionalIntervalVar(start_var, duration, end_var + 2, - presence_var, 'interval') + # An interval can be created from three affine expressions. + start_var = model.NewIntVar(0, horizon, 'start') + duration = 10 # Python cp/sat code accept integer variables or constants. + end_var = model.NewIntVar(0, horizon, 'end') + presence_var = model.NewBoolVar('presence') + interval_var = model.NewOptionalIntervalVar(start_var, duration, + end_var + 2, presence_var, + 'interval') - print(f'interval = {repr(interval_var)}') + print(f'interval = {repr(interval_var)}') - # If the size is fixed, a simpler version uses the start expression and the - # size. - fixed_size_interval_var = model.NewOptionalFixedSizeIntervalVar( - start_var, 10, presence_var, 'fixed_size_interval_var') - print(f'fixed_size_interval_var = {repr(fixed_size_interval_var)}') + # If the size is fixed, a simpler version uses the start expression and the + # size. + fixed_size_interval_var = model.NewOptionalFixedSizeIntervalVar( + start_var, 10, presence_var, 'fixed_size_interval_var') + print(f'fixed_size_interval_var = {repr(fixed_size_interval_var)}') - # A fixed interval can be created using the same API. - fixed_interval = model.NewOptionalFixedSizeIntervalVar( - 5, 10, presence_var, 'fixed_interval') - print(f'fixed_interval = {repr(fixed_interval)}') + # A fixed interval can be created using the same API. + fixed_interval = model.NewOptionalFixedSizeIntervalVar( + 5, 10, presence_var, 'fixed_interval') + print(f'fixed_interval = {repr(fixed_interval)}') OptionalIntervalSampleSat() @@ -350,13 +346,8 @@ public class OptionalIntervalSampleSat { IntVar startVar = model.newIntVar(0, horizon, "start"); IntVar endVar = model.newIntVar(0, horizon, "end"); Literal presence = model.newBoolVar("presence"); - IntervalVar intervalVar = - model.newOptionalIntervalVar( - startVar, - LinearExpr.constant(10), - LinearExpr.newBuilder().add(endVar).add(2), - presence, - "interval"); + IntervalVar intervalVar = model.newOptionalIntervalVar(startVar, LinearExpr.constant(10), + LinearExpr.newBuilder().add(endVar).add(2), presence, "interval"); System.out.println(intervalVar); // If the size is fixed, a simpler version uses the start expression, the size and the literal. @@ -416,58 +407,59 @@ weekends, making the final day as early as possible. ### Python code ```python -"""Code sample to demonstrate how to build a NoOverlap constraint.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model def NoOverlapSampleSat(): - """No overlap sample with fixed activities.""" - model = cp_model.CpModel() - horizon = 21 # 3 weeks. + """No overlap sample with fixed activities.""" + model = cp_model.CpModel() + horizon = 21 # 3 weeks. - # Task 0, duration 2. - start_0 = model.NewIntVar(0, horizon, 'start_0') - duration_0 = 2 # Python cp/sat code accepts integer variables or constants. - end_0 = model.NewIntVar(0, horizon, 'end_0') - task_0 = model.NewIntervalVar(start_0, duration_0, end_0, 'task_0') - # Task 1, duration 4. - start_1 = model.NewIntVar(0, horizon, 'start_1') - duration_1 = 4 # Python cp/sat code accepts integer variables or constants. - end_1 = model.NewIntVar(0, horizon, 'end_1') - task_1 = model.NewIntervalVar(start_1, duration_1, end_1, 'task_1') + # Task 0, duration 2. + start_0 = model.NewIntVar(0, horizon, 'start_0') + duration_0 = 2 # Python cp/sat code accepts integer variables or constants. + end_0 = model.NewIntVar(0, horizon, 'end_0') + task_0 = model.NewIntervalVar(start_0, duration_0, end_0, 'task_0') + # Task 1, duration 4. + start_1 = model.NewIntVar(0, horizon, 'start_1') + duration_1 = 4 # Python cp/sat code accepts integer variables or constants. + end_1 = model.NewIntVar(0, horizon, 'end_1') + task_1 = model.NewIntervalVar(start_1, duration_1, end_1, 'task_1') - # Task 2, duration 3. - start_2 = model.NewIntVar(0, horizon, 'start_2') - duration_2 = 3 # Python cp/sat code accepts integer variables or constants. - end_2 = model.NewIntVar(0, horizon, 'end_2') - task_2 = model.NewIntervalVar(start_2, duration_2, end_2, 'task_2') + # Task 2, duration 3. + start_2 = model.NewIntVar(0, horizon, 'start_2') + duration_2 = 3 # Python cp/sat code accepts integer variables or constants. + end_2 = model.NewIntVar(0, horizon, 'end_2') + task_2 = model.NewIntervalVar(start_2, duration_2, end_2, 'task_2') - # Weekends. - weekend_0 = model.NewIntervalVar(5, 2, 7, 'weekend_0') - weekend_1 = model.NewIntervalVar(12, 2, 14, 'weekend_1') - weekend_2 = model.NewIntervalVar(19, 2, 21, 'weekend_2') + # Weekends. + weekend_0 = model.NewIntervalVar(5, 2, 7, 'weekend_0') + weekend_1 = model.NewIntervalVar(12, 2, 14, 'weekend_1') + weekend_2 = model.NewIntervalVar(19, 2, 21, 'weekend_2') - # No Overlap constraint. - model.AddNoOverlap([task_0, task_1, task_2, weekend_0, weekend_1, weekend_2]) + # No Overlap constraint. + model.AddNoOverlap( + [task_0, task_1, task_2, weekend_0, weekend_1, weekend_2]) - # Makespan objective. - obj = model.NewIntVar(0, horizon, 'makespan') - model.AddMaxEquality(obj, [end_0, end_1, end_2]) - model.Minimize(obj) + # Makespan objective. + obj = model.NewIntVar(0, horizon, 'makespan') + model.AddMaxEquality(obj, [end_0, end_1, end_2]) + model.Minimize(obj) - # Solve model. - solver = cp_model.CpSolver() - status = solver.Solve(model) + # Solve model. + solver = cp_model.CpSolver() + status = solver.Solve(model) - if status == cp_model.OPTIMAL: - # Print out makespan and the start times for all tasks. - print('Optimal Schedule Length: %i' % solver.ObjectiveValue()) - print('Task 0 starts at %i' % solver.Value(start_0)) - print('Task 1 starts at %i' % solver.Value(start_1)) - print('Task 2 starts at %i' % solver.Value(start_2)) - else: - print('Solver exited with nonoptimal status: %i' % status) + if status == cp_model.OPTIMAL: + # Print out makespan and the start times for all tasks. + print('Optimal Schedule Length: %i' % solver.ObjectiveValue()) + print('Task 0 starts at %i' % solver.Value(start_0)) + print('Task 1 starts at %i' % solver.Value(start_1)) + print('Task 2 starts at %i' % solver.Value(start_2)) + else: + print('Solver exited with nonoptimal status: %i' % status) NoOverlapSampleSat() @@ -480,8 +472,8 @@ NoOverlapSampleSat() #include -#include "ortools/base/logging.h" #include "absl/types/span.h" +#include "ortools/base/logging.h" #include "ortools/sat/cp_model.h" #include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_solver.h" @@ -563,9 +555,9 @@ int main() { package com.google.ortools.sat.samples; import com.google.ortools.Loader; -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.IntervalVar; import com.google.ortools.sat.LinearExpr; @@ -608,13 +600,10 @@ public class NoOverlapSampleSat { // Makespan objective. IntVar obj = model.newIntVar(0, horizon, "makespan"); - model.addMaxEquality( - obj, - new LinearExpr[] { - LinearExpr.newBuilder().add(start0).add(duration0).build(), - LinearExpr.newBuilder().add(start1).add(duration1).build(), - LinearExpr.newBuilder().add(start2).add(duration2).build() - }); + model.addMaxEquality(obj, + new LinearExpr[] {LinearExpr.newBuilder().add(start0).add(duration0).build(), + LinearExpr.newBuilder().add(start1).add(duration1).build(), + LinearExpr.newBuilder().add(start2).add(duration2).build()}); model.minimize(obj); // Creates a solver and solves the model. @@ -718,14 +707,13 @@ number of other intervals that precede it. ### Python code ```python -"""Code sample to demonstrates how to rank intervals.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model - def RankTasks(model, starts, presences, ranks): - """This method adds constraints and variables to links tasks and ranks. + """This method adds constraints and variables to links tasks and ranks. This method assumes that all starts are disjoint, meaning that all tasks have a strictly positive duration, and they appear in the same NoOverlap @@ -738,118 +726,124 @@ def RankTasks(model, starts, presences, ranks): ranks: The array of rank variables of all tasks. """ - num_tasks = len(starts) - all_tasks = range(num_tasks) + num_tasks = len(starts) + all_tasks = range(num_tasks) - # Creates precedence variables between pairs of intervals. - precedences = {} - for i in all_tasks: - for j in all_tasks: - if i == j: - precedences[(i, j)] = presences[i] - else: - prec = model.NewBoolVar('%i before %i' % (i, j)) - precedences[(i, j)] = prec - model.Add(starts[i] < starts[j]).OnlyEnforceIf(prec) + # Creates precedence variables between pairs of intervals. + precedences = {} + for i in all_tasks: + for j in all_tasks: + if i == j: + precedences[(i, j)] = presences[i] + else: + prec = model.NewBoolVar('%i before %i' % (i, j)) + precedences[(i, j)] = prec + model.Add(starts[i] < starts[j]).OnlyEnforceIf(prec) - # Treats optional intervals. - for i in range(num_tasks - 1): - for j in range(i + 1, num_tasks): - tmp_array = [precedences[(i, j)], precedences[(j, i)]] - if not cp_model.ObjectIsATrueLiteral(presences[i]): - tmp_array.append(presences[i].Not()) - # Makes sure that if i is not performed, all precedences are false. - model.AddImplication(presences[i].Not(), precedences[(i, j)].Not()) - model.AddImplication(presences[i].Not(), precedences[(j, i)].Not()) - if not cp_model.ObjectIsATrueLiteral(presences[j]): - tmp_array.append(presences[j].Not()) - # Makes sure that if j is not performed, all precedences are false. - model.AddImplication(presences[j].Not(), precedences[(i, j)].Not()) - model.AddImplication(presences[j].Not(), precedences[(j, i)].Not()) - # The following bool_or will enforce that for any two intervals: - # i precedes j or j precedes i or at least one interval is not - # performed. - model.AddBoolOr(tmp_array) - # Redundant constraint: it propagates early that at most one precedence - # is true. - model.AddImplication(precedences[(i, j)], precedences[(j, i)].Not()) - model.AddImplication(precedences[(j, i)], precedences[(i, j)].Not()) + # Treats optional intervals. + for i in range(num_tasks - 1): + for j in range(i + 1, num_tasks): + tmp_array = [precedences[(i, j)], precedences[(j, i)]] + if not cp_model.ObjectIsATrueLiteral(presences[i]): + tmp_array.append(presences[i].Not()) + # Makes sure that if i is not performed, all precedences are false. + model.AddImplication(presences[i].Not(), + precedences[(i, j)].Not()) + model.AddImplication(presences[i].Not(), + precedences[(j, i)].Not()) + if not cp_model.ObjectIsATrueLiteral(presences[j]): + tmp_array.append(presences[j].Not()) + # Makes sure that if j is not performed, all precedences are false. + model.AddImplication(presences[j].Not(), + precedences[(i, j)].Not()) + model.AddImplication(presences[j].Not(), + precedences[(j, i)].Not()) + # The following bool_or will enforce that for any two intervals: + # i precedes j or j precedes i or at least one interval is not + # performed. + model.AddBoolOr(tmp_array) + # Redundant constraint: it propagates early that at most one precedence + # is true. + model.AddImplication(precedences[(i, j)], precedences[(j, i)].Not()) + model.AddImplication(precedences[(j, i)], precedences[(i, j)].Not()) - # Links precedences and ranks. - for i in all_tasks: - model.Add(ranks[i] == sum(precedences[(j, i)] for j in all_tasks) - 1) + # Links precedences and ranks. + for i in all_tasks: + model.Add(ranks[i] == sum(precedences[(j, i)] for j in all_tasks) - 1) def RankingSampleSat(): - """Ranks tasks in a NoOverlap constraint.""" + """Ranks tasks in a NoOverlap constraint.""" - model = cp_model.CpModel() - horizon = 100 - num_tasks = 4 - all_tasks = range(num_tasks) + model = cp_model.CpModel() + horizon = 100 + num_tasks = 4 + all_tasks = range(num_tasks) - starts = [] - ends = [] - intervals = [] - presences = [] - ranks = [] + starts = [] + ends = [] + intervals = [] + presences = [] + ranks = [] - # Creates intervals, half of them are optional. - for t in all_tasks: - start = model.NewIntVar(0, horizon, 'start_%i' % t) - duration = t + 1 - end = model.NewIntVar(0, horizon, 'end_%i' % t) - if t < num_tasks // 2: - interval = model.NewIntervalVar(start, duration, end, 'interval_%i' % t) - presence = True - else: - presence = model.NewBoolVar('presence_%i' % t) - interval = model.NewOptionalIntervalVar(start, duration, end, presence, - 'o_interval_%i' % t) - starts.append(start) - ends.append(end) - intervals.append(interval) - presences.append(presence) - - # Ranks = -1 if and only if the tasks is not performed. - ranks.append(model.NewIntVar(-1, num_tasks - 1, 'rank_%i' % t)) - - # Adds NoOverlap constraint. - model.AddNoOverlap(intervals) - - # Adds ranking constraint. - RankTasks(model, starts, presences, ranks) - - # Adds a constraint on ranks. - model.Add(ranks[0] < ranks[1]) - - # Creates makespan variable. - makespan = model.NewIntVar(0, horizon, 'makespan') - for t in all_tasks: - model.Add(ends[t] <= makespan).OnlyEnforceIf(presences[t]) - - # Minimizes makespan - fixed gain per tasks performed. - # As the fixed cost is less that the duration of the last interval, - # the solver will not perform the last interval. - model.Minimize(2 * makespan - 7 * sum(presences[t] for t in all_tasks)) - - # Solves the model model. - solver = cp_model.CpSolver() - status = solver.Solve(model) - - if status == cp_model.OPTIMAL: - # Prints out the makespan and the start times and ranks of all tasks. - print('Optimal cost: %i' % solver.ObjectiveValue()) - print('Makespan: %i' % solver.Value(makespan)) + # Creates intervals, half of them are optional. for t in all_tasks: - if solver.Value(presences[t]): - print('Task %i starts at %i with rank %i' % (t, solver.Value(starts[t]), - solver.Value(ranks[t]))) - else: - print('Task %i in not performed and ranked at %i' % - (t, solver.Value(ranks[t]))) - else: - print('Solver exited with nonoptimal status: %i' % status) + start = model.NewIntVar(0, horizon, 'start_%i' % t) + duration = t + 1 + end = model.NewIntVar(0, horizon, 'end_%i' % t) + if t < num_tasks // 2: + interval = model.NewIntervalVar(start, duration, end, + 'interval_%i' % t) + presence = True + else: + presence = model.NewBoolVar('presence_%i' % t) + interval = model.NewOptionalIntervalVar(start, duration, end, + presence, + 'o_interval_%i' % t) + starts.append(start) + ends.append(end) + intervals.append(interval) + presences.append(presence) + + # Ranks = -1 if and only if the tasks is not performed. + ranks.append(model.NewIntVar(-1, num_tasks - 1, 'rank_%i' % t)) + + # Adds NoOverlap constraint. + model.AddNoOverlap(intervals) + + # Adds ranking constraint. + RankTasks(model, starts, presences, ranks) + + # Adds a constraint on ranks. + model.Add(ranks[0] < ranks[1]) + + # Creates makespan variable. + makespan = model.NewIntVar(0, horizon, 'makespan') + for t in all_tasks: + model.Add(ends[t] <= makespan).OnlyEnforceIf(presences[t]) + + # Minimizes makespan - fixed gain per tasks performed. + # As the fixed cost is less that the duration of the last interval, + # the solver will not perform the last interval. + model.Minimize(2 * makespan - 7 * sum(presences[t] for t in all_tasks)) + + # Solves the model model. + solver = cp_model.CpSolver() + status = solver.Solve(model) + + if status == cp_model.OPTIMAL: + # Prints out the makespan and the start times and ranks of all tasks. + print('Optimal cost: %i' % solver.ObjectiveValue()) + print('Makespan: %i' % solver.Value(makespan)) + for t in all_tasks: + if solver.Value(presences[t]): + print('Task %i starts at %i with rank %i' % + (t, solver.Value(starts[t]), solver.Value(ranks[t]))) + else: + print('Task %i in not performed and ranked at %i' % + (t, solver.Value(ranks[t]))) + else: + print('Solver exited with nonoptimal status: %i' % status) RankingSampleSat() @@ -863,8 +857,8 @@ RankingSampleSat() #include -#include "ortools/base/logging.h" #include "absl/types/span.h" +#include "ortools/base/logging.h" #include "ortools/sat/cp_model.h" #include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_solver.h" @@ -1015,10 +1009,10 @@ int main() { package com.google.ortools.sat.samples; import com.google.ortools.Loader; -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.BoolVar; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.IntervalVar; import com.google.ortools.sat.LinearExpr; @@ -1114,15 +1108,13 @@ public class RankingSampleSat { int duration = t + 1; ends[t] = model.newIntVar(0, horizon, "end_" + t); if (t < numTasks / 2) { - intervals[t] = - model.newIntervalVar( - starts[t], LinearExpr.constant(duration), ends[t], "interval_" + t); + intervals[t] = model.newIntervalVar( + starts[t], LinearExpr.constant(duration), ends[t], "interval_" + t); presences[t] = trueLiteral; } else { presences[t] = model.newBoolVar("presence_" + t); - intervals[t] = - model.newOptionalIntervalVar( - starts[t], LinearExpr.constant(duration), ends[t], presences[t], "o_interval_" + t); + intervals[t] = model.newOptionalIntervalVar( + starts[t], LinearExpr.constant(duration), ends[t], presences[t], "o_interval_" + t); } // The rank will be -1 iff the task is not performed. @@ -1166,9 +1158,8 @@ public class RankingSampleSat { System.out.println("Makespan: " + solver.value(makespan)); for (int t = 0; t < numTasks; ++t) { if (solver.booleanValue(presences[t])) { - System.out.printf( - "Task %d starts at %d with rank %d%n", - t, solver.value(starts[t]), solver.value(ranks[t])); + System.out.printf("Task %d starts at %d with rank %d%n", t, solver.value(starts[t]), + solver.value(ranks[t])); } else { System.out.printf( "Task %d in not performed and ranked at %d%n", t, solver.value(ranks[t])); @@ -1366,71 +1357,71 @@ The following code displays: ### Python code ```python -"""Code sample to demonstrate how an interval can span across a break.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback): - """Print intermediate solutions.""" + """Print intermediate solutions.""" - def __init__(self, variables): - cp_model.CpSolverSolutionCallback.__init__(self) - self.__variables = variables - self.__solution_count = 0 + def __init__(self, variables): + cp_model.CpSolverSolutionCallback.__init__(self) + self.__variables = variables + self.__solution_count = 0 - def on_solution_callback(self): - self.__solution_count += 1 - for v in self.__variables: - print('%s=%i' % (v, self.Value(v)), end=' ') - print() + def on_solution_callback(self): + self.__solution_count += 1 + for v in self.__variables: + print('%s=%i' % (v, self.Value(v)), end=' ') + print() - def solution_count(self): - return self.__solution_count + def solution_count(self): + return self.__solution_count def SchedulingWithCalendarSampleSat(): - """Interval spanning across a lunch break.""" - model = cp_model.CpModel() + """Interval spanning across a lunch break.""" + model = cp_model.CpModel() - # The data is the following: - # Work starts at 8h, ends at 18h, with a lunch break between 13h and 14h. - # We need to schedule a task that needs 3 hours of processing time. - # Total duration can be 3 or 4 (if it spans the lunch break). - # - # Because the duration is at least 3 hours, work cannot start after 15h. - # Because of the break, work cannot start at 13h. + # The data is the following: + # Work starts at 8h, ends at 18h, with a lunch break between 13h and 14h. + # We need to schedule a task that needs 3 hours of processing time. + # Total duration can be 3 or 4 (if it spans the lunch break). + # + # Because the duration is at least 3 hours, work cannot start after 15h. + # Because of the break, work cannot start at 13h. - start = model.NewIntVarFromDomain( - cp_model.Domain.FromIntervals([(8, 12), (14, 15)]), 'start') - duration = model.NewIntVar(3, 4, 'duration') - end = model.NewIntVar(8, 18, 'end') - unused_interval = model.NewIntervalVar(start, duration, end, 'interval') + start = model.NewIntVarFromDomain( + cp_model.Domain.FromIntervals([(8, 12), (14, 15)]), 'start') + duration = model.NewIntVar(3, 4, 'duration') + end = model.NewIntVar(8, 18, 'end') + unused_interval = model.NewIntervalVar(start, duration, end, 'interval') - # We have 2 states (spanning across lunch or not) - across = model.NewBoolVar('across') - non_spanning_hours = cp_model.Domain.FromValues([8, 9, 10, 14, 15]) - model.AddLinearExpressionInDomain(start, non_spanning_hours).OnlyEnforceIf( - across.Not()) - model.AddLinearConstraint(start, 11, 12).OnlyEnforceIf(across) - model.Add(duration == 3).OnlyEnforceIf(across.Not()) - model.Add(duration == 4).OnlyEnforceIf(across) + # We have 2 states (spanning across lunch or not) + across = model.NewBoolVar('across') + non_spanning_hours = cp_model.Domain.FromValues([8, 9, 10, 14, 15]) + model.AddLinearExpressionInDomain(start, non_spanning_hours).OnlyEnforceIf( + across.Not()) + model.AddLinearConstraint(start, 11, 12).OnlyEnforceIf(across) + model.Add(duration == 3).OnlyEnforceIf(across.Not()) + model.Add(duration == 4).OnlyEnforceIf(across) - # Search for x values in increasing order. - model.AddDecisionStrategy([start], cp_model.CHOOSE_FIRST, - cp_model.SELECT_MIN_VALUE) + # Search for x values in increasing order. + model.AddDecisionStrategy([start], cp_model.CHOOSE_FIRST, + cp_model.SELECT_MIN_VALUE) - # Create a solver and solve with a fixed search. - solver = cp_model.CpSolver() + # Create a solver and solve with a fixed search. + solver = cp_model.CpSolver() - # Force the solver to follow the decision strategy exactly. - solver.parameters.search_branching = cp_model.FIXED_SEARCH - # Enumerate all solutions. - solver.parameters.enumerate_all_solutions = True + # Force the solver to follow the decision strategy exactly. + solver.parameters.search_branching = cp_model.FIXED_SEARCH + # Enumerate all solutions. + solver.parameters.enumerate_all_solutions = True - # Search and print all solutions. - solution_printer = VarArraySolutionPrinter([start, duration, across]) - solver.Solve(model, solution_printer) + # Search and print all solutions. + solution_printer = VarArraySolutionPrinter([start, duration, across]) + solver.Solve(model, solution_printer) SchedulingWithCalendarSampleSat() @@ -1472,86 +1463,86 @@ start_a=1 start_b=5 a_overlaps_b=0 ### Python code ```python -"""Code sample to demonstrates how to detect if two intervals overlap.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback): - """Print intermediate solutions.""" + """Print intermediate solutions.""" - def __init__(self, variables): - cp_model.CpSolverSolutionCallback.__init__(self) - self.__variables = variables - self.__solution_count = 0 + def __init__(self, variables): + cp_model.CpSolverSolutionCallback.__init__(self) + self.__variables = variables + self.__solution_count = 0 - def on_solution_callback(self): - self.__solution_count += 1 - for v in self.__variables: - print('%s=%i' % (v, self.Value(v)), end=' ') - print() + def on_solution_callback(self): + self.__solution_count += 1 + for v in self.__variables: + print('%s=%i' % (v, self.Value(v)), end=' ') + print() - def solution_count(self): - return self.__solution_count + def solution_count(self): + return self.__solution_count def OverlappingIntervals(): - """Create the overlapping Boolean variables and enumerate all states.""" - model = cp_model.CpModel() + """Create the overlapping Boolean variables and enumerate all states.""" + model = cp_model.CpModel() - horizon = 7 + horizon = 7 - # First interval. - start_var_a = model.NewIntVar(0, horizon, 'start_a') - duration_a = 3 - end_var_a = model.NewIntVar(0, horizon, 'end_a') - unused_interval_var_a = model.NewIntervalVar(start_var_a, duration_a, - end_var_a, 'interval_a') + # First interval. + start_var_a = model.NewIntVar(0, horizon, 'start_a') + duration_a = 3 + end_var_a = model.NewIntVar(0, horizon, 'end_a') + unused_interval_var_a = model.NewIntervalVar(start_var_a, duration_a, + end_var_a, 'interval_a') - # Second interval. - start_var_b = model.NewIntVar(0, horizon, 'start_b') - duration_b = 2 - end_var_b = model.NewIntVar(0, horizon, 'end_b') - unused_interval_var_b = model.NewIntervalVar(start_var_b, duration_b, - end_var_b, 'interval_b') + # Second interval. + start_var_b = model.NewIntVar(0, horizon, 'start_b') + duration_b = 2 + end_var_b = model.NewIntVar(0, horizon, 'end_b') + unused_interval_var_b = model.NewIntervalVar(start_var_b, duration_b, + end_var_b, 'interval_b') - # a_after_b Boolean variable. - a_after_b = model.NewBoolVar('a_after_b') - model.Add(start_var_a >= end_var_b).OnlyEnforceIf(a_after_b) - model.Add(start_var_a < end_var_b).OnlyEnforceIf(a_after_b.Not()) + # a_after_b Boolean variable. + a_after_b = model.NewBoolVar('a_after_b') + model.Add(start_var_a >= end_var_b).OnlyEnforceIf(a_after_b) + model.Add(start_var_a < end_var_b).OnlyEnforceIf(a_after_b.Not()) - # b_after_a Boolean variable. - b_after_a = model.NewBoolVar('b_after_a') - model.Add(start_var_b >= end_var_a).OnlyEnforceIf(b_after_a) - model.Add(start_var_b < end_var_a).OnlyEnforceIf(b_after_a.Not()) + # b_after_a Boolean variable. + b_after_a = model.NewBoolVar('b_after_a') + model.Add(start_var_b >= end_var_a).OnlyEnforceIf(b_after_a) + model.Add(start_var_b < end_var_a).OnlyEnforceIf(b_after_a.Not()) - # Result Boolean variable. - a_overlaps_b = model.NewBoolVar('a_overlaps_b') + # Result Boolean variable. + a_overlaps_b = model.NewBoolVar('a_overlaps_b') - # Option a: using only clauses - model.AddBoolOr(a_after_b, b_after_a, a_overlaps_b) - model.AddImplication(a_after_b, a_overlaps_b.Not()) - model.AddImplication(b_after_a, a_overlaps_b.Not()) + # Option a: using only clauses + model.AddBoolOr(a_after_b, b_after_a, a_overlaps_b) + model.AddImplication(a_after_b, a_overlaps_b.Not()) + model.AddImplication(b_after_a, a_overlaps_b.Not()) - # Option b: using an exactly one constraint. - # model.AddExactlyOne(a_after_b, b_after_a, a_overlaps_b) + # Option b: using an exactly one constraint. + # model.AddExactlyOne(a_after_b, b_after_a, a_overlaps_b) - # Search for start values in increasing order for the two intervals. - model.AddDecisionStrategy([start_var_a, start_var_b], cp_model.CHOOSE_FIRST, - cp_model.SELECT_MIN_VALUE) + # Search for start values in increasing order for the two intervals. + model.AddDecisionStrategy([start_var_a, start_var_b], cp_model.CHOOSE_FIRST, + cp_model.SELECT_MIN_VALUE) - # Create a solver and solve with a fixed search. - solver = cp_model.CpSolver() + # Create a solver and solve with a fixed search. + solver = cp_model.CpSolver() - # Force the solver to follow the decision strategy exactly. - solver.parameters.search_branching = cp_model.FIXED_SEARCH - # Enumerate all solutions. - solver.parameters.enumerate_all_solutions = True + # Force the solver to follow the decision strategy exactly. + solver.parameters.search_branching = cp_model.FIXED_SEARCH + # Enumerate all solutions. + solver.parameters.enumerate_all_solutions = True - # Search and print out all solutions. - solution_printer = VarArraySolutionPrinter( - [start_var_a, start_var_b, a_overlaps_b]) - solver.Solve(model, solution_printer) + # Search and print out all solutions. + solution_printer = VarArraySolutionPrinter( + [start_var_a, start_var_b, a_overlaps_b]) + solver.Solve(model, solution_printer) OverlappingIntervals() diff --git a/ortools/sat/docs/solver.md b/ortools/sat/docs/solver.md index b17bc85c1c..36e50b376d 100644 --- a/ortools/sat/docs/solver.md +++ b/ortools/sat/docs/solver.md @@ -40,35 +40,36 @@ solver. The most useful one is the time limit. ### Specifying the time limit in Python ```python +#!/usr/bin/env python3 """Solves a problem with a time limit.""" from ortools.sat.python import cp_model def SolveWithTimeLimitSampleSat(): - """Minimal CP-SAT example to showcase calling the solver.""" - # Creates the model. - model = cp_model.CpModel() - # Creates the variables. - num_vals = 3 - x = model.NewIntVar(0, num_vals - 1, 'x') - y = model.NewIntVar(0, num_vals - 1, 'y') - z = model.NewIntVar(0, num_vals - 1, 'z') - # Adds an all-different constraint. - model.Add(x != y) + """Minimal CP-SAT example to showcase calling the solver.""" + # Creates the model. + model = cp_model.CpModel() + # Creates the variables. + num_vals = 3 + x = model.NewIntVar(0, num_vals - 1, 'x') + y = model.NewIntVar(0, num_vals - 1, 'y') + z = model.NewIntVar(0, num_vals - 1, 'z') + # Adds an all-different constraint. + model.Add(x != y) - # Creates a solver and solves the model. - solver = cp_model.CpSolver() + # Creates a solver and solves the model. + solver = cp_model.CpSolver() - # Sets a time limit of 10 seconds. - solver.parameters.max_time_in_seconds = 10.0 + # Sets a time limit of 10 seconds. + solver.parameters.max_time_in_seconds = 10.0 - status = solver.Solve(model) + status = solver.Solve(model) - if status == cp_model.OPTIMAL: - print('x = %i' % solver.Value(x)) - print('y = %i' % solver.Value(y)) - print('z = %i' % solver.Value(z)) + if status == cp_model.OPTIMAL: + print('x = %i' % solver.Value(x)) + print('y = %i' % solver.Value(y)) + print('z = %i' % solver.Value(z)) SolveWithTimeLimitSampleSat() @@ -135,9 +136,9 @@ int main() { package com.google.ortools.sat.samples; import com.google.ortools.Loader; -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.IntVar; /** Solves a problem with a time limit. */ @@ -210,7 +211,6 @@ public class SolveWithTimeLimitSampleSat } } } - ``` ## Printing intermediate solutions @@ -223,55 +223,55 @@ The exact implementation depends on the target language. ### Python code ```python -"""Solves an optimization problem and displays all intermediate solutions.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model # You need to subclass the cp_model.CpSolverSolutionCallback class. class VarArrayAndObjectiveSolutionPrinter(cp_model.CpSolverSolutionCallback): - """Print intermediate solutions.""" + """Print intermediate solutions.""" - def __init__(self, variables): - cp_model.CpSolverSolutionCallback.__init__(self) - self.__variables = variables - self.__solution_count = 0 + def __init__(self, variables): + cp_model.CpSolverSolutionCallback.__init__(self) + self.__variables = variables + self.__solution_count = 0 - def on_solution_callback(self): - print('Solution %i' % self.__solution_count) - print(' objective value = %i' % self.ObjectiveValue()) - for v in self.__variables: - print(' %s = %i' % (v, self.Value(v)), end=' ') - print() - self.__solution_count += 1 + def on_solution_callback(self): + print('Solution %i' % self.__solution_count) + print(' objective value = %i' % self.ObjectiveValue()) + for v in self.__variables: + print(' %s = %i' % (v, self.Value(v)), end=' ') + print() + self.__solution_count += 1 - def solution_count(self): - return self.__solution_count + def solution_count(self): + return self.__solution_count def SolveAndPrintIntermediateSolutionsSampleSat(): - """Showcases printing intermediate solutions found during search.""" - # Creates the model. - model = cp_model.CpModel() + """Showcases printing intermediate solutions found during search.""" + # Creates the model. + model = cp_model.CpModel() - # Creates the variables. - num_vals = 3 - x = model.NewIntVar(0, num_vals - 1, 'x') - y = model.NewIntVar(0, num_vals - 1, 'y') - z = model.NewIntVar(0, num_vals - 1, 'z') + # Creates the variables. + num_vals = 3 + x = model.NewIntVar(0, num_vals - 1, 'x') + y = model.NewIntVar(0, num_vals - 1, 'y') + z = model.NewIntVar(0, num_vals - 1, 'z') - # Creates the constraints. - model.Add(x != y) + # Creates the constraints. + model.Add(x != y) - model.Maximize(x + 2 * y + 3 * z) + model.Maximize(x + 2 * y + 3 * z) - # Creates a solver and solves. - solver = cp_model.CpSolver() - solution_printer = VarArrayAndObjectiveSolutionPrinter([x, y, z]) - status = solver.Solve(model, solution_printer) + # Creates a solver and solves. + solver = cp_model.CpSolver() + solution_printer = VarArrayAndObjectiveSolutionPrinter([x, y, z]) + status = solver.Solve(model, solution_printer) - print('Status = %s' % solver.StatusName(status)) - print('Number of solutions found: %i' % solution_printer.solution_count()) + print('Status = %s' % solver.StatusName(status)) + print('Number of solutions found: %i' % solution_printer.solution_count()) SolveAndPrintIntermediateSolutionsSampleSat() @@ -483,53 +483,53 @@ To search for all solutions, use the Solve() method after setting the correct parameter. ```python -"""Code sample that solves a model and displays all solutions.""" +#!/usr/bin/env python3 from ortools.sat.python import cp_model class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback): - """Print intermediate solutions.""" + """Print intermediate solutions.""" - def __init__(self, variables): - cp_model.CpSolverSolutionCallback.__init__(self) - self.__variables = variables - self.__solution_count = 0 + def __init__(self, variables): + cp_model.CpSolverSolutionCallback.__init__(self) + self.__variables = variables + self.__solution_count = 0 - def on_solution_callback(self): - self.__solution_count += 1 - for v in self.__variables: - print('%s=%i' % (v, self.Value(v)), end=' ') - print() + def on_solution_callback(self): + self.__solution_count += 1 + for v in self.__variables: + print('%s=%i' % (v, self.Value(v)), end=' ') + print() - def solution_count(self): - return self.__solution_count + def solution_count(self): + return self.__solution_count def SearchForAllSolutionsSampleSat(): - """Showcases calling the solver to search for all solutions.""" - # Creates the model. - model = cp_model.CpModel() + """Showcases calling the solver to search for all solutions.""" + # Creates the model. + model = cp_model.CpModel() - # Creates the variables. - num_vals = 3 - x = model.NewIntVar(0, num_vals - 1, 'x') - y = model.NewIntVar(0, num_vals - 1, 'y') - z = model.NewIntVar(0, num_vals - 1, 'z') + # Creates the variables. + num_vals = 3 + x = model.NewIntVar(0, num_vals - 1, 'x') + y = model.NewIntVar(0, num_vals - 1, 'y') + z = model.NewIntVar(0, num_vals - 1, 'z') - # Create the constraints. - model.Add(x != y) + # Create the constraints. + model.Add(x != y) - # Create a solver and solve. - solver = cp_model.CpSolver() - solution_printer = VarArraySolutionPrinter([x, y, z]) - # Enumerate all solutions. - solver.parameters.enumerate_all_solutions = True - # Solve. - status = solver.Solve(model, solution_printer) + # Create a solver and solve. + solver = cp_model.CpSolver() + solution_printer = VarArraySolutionPrinter([x, y, z]) + # Enumerate all solutions. + solver.parameters.enumerate_all_solutions = True + # Solve. + status = solver.Solve(model, solution_printer) - print('Status = %s' % solver.StatusName(status)) - print('Number of solutions found: %i' % solution_printer.solution_count()) + print('Status = %s' % solver.StatusName(status)) + print('Number of solutions found: %i' % solution_printer.solution_count()) SearchForAllSolutionsSampleSat() @@ -738,53 +738,54 @@ You can stop the search by calling StopSearch() inside of CpSolverSolutionCallback.OnSolutionCallback(). ```python +#!/usr/bin/env python3 """Code sample that solves a model and displays a small number of solutions.""" from ortools.sat.python import cp_model class VarArraySolutionPrinterWithLimit(cp_model.CpSolverSolutionCallback): - """Print intermediate solutions.""" + """Print intermediate solutions.""" - def __init__(self, variables, limit): - cp_model.CpSolverSolutionCallback.__init__(self) - self.__variables = variables - self.__solution_count = 0 - self.__solution_limit = limit + def __init__(self, variables, limit): + cp_model.CpSolverSolutionCallback.__init__(self) + self.__variables = variables + self.__solution_count = 0 + self.__solution_limit = limit - def on_solution_callback(self): - self.__solution_count += 1 - for v in self.__variables: - print('%s=%i' % (v, self.Value(v)), end=' ') - print() - if self.__solution_count >= self.__solution_limit: - print('Stop search after %i solutions' % self.__solution_limit) - self.StopSearch() + def on_solution_callback(self): + self.__solution_count += 1 + for v in self.__variables: + print('%s=%i' % (v, self.Value(v)), end=' ') + print() + if self.__solution_count >= self.__solution_limit: + print('Stop search after %i solutions' % self.__solution_limit) + self.StopSearch() - def solution_count(self): - return self.__solution_count + def solution_count(self): + return self.__solution_count def StopAfterNSolutionsSampleSat(): - """Showcases calling the solver to search for small number of solutions.""" - # Creates the model. - model = cp_model.CpModel() - # Creates the variables. - num_vals = 3 - x = model.NewIntVar(0, num_vals - 1, 'x') - y = model.NewIntVar(0, num_vals - 1, 'y') - z = model.NewIntVar(0, num_vals - 1, 'z') + """Showcases calling the solver to search for small number of solutions.""" + # Creates the model. + model = cp_model.CpModel() + # Creates the variables. + num_vals = 3 + x = model.NewIntVar(0, num_vals - 1, 'x') + y = model.NewIntVar(0, num_vals - 1, 'y') + z = model.NewIntVar(0, num_vals - 1, 'z') - # Create a solver and solve. - solver = cp_model.CpSolver() - solution_printer = VarArraySolutionPrinterWithLimit([x, y, z], 5) - # Enumerate all solutions. - solver.parameters.enumerate_all_solutions = True - # Solve. - status = solver.Solve(model, solution_printer) - print('Status = %s' % solver.StatusName(status)) - print('Number of solutions found: %i' % solution_printer.solution_count()) - assert solution_printer.solution_count() == 5 + # Create a solver and solve. + solver = cp_model.CpSolver() + solution_printer = VarArraySolutionPrinterWithLimit([x, y, z], 5) + # Enumerate all solutions. + solver.parameters.enumerate_all_solutions = True + # Solve. + status = solver.Solve(model, solution_printer) + print('Status = %s' % solver.StatusName(status)) + print('Number of solutions found: %i' % solution_printer.solution_count()) + assert solution_printer.solution_count() == 5 StopAfterNSolutionsSampleSat() diff --git a/ortools/sat/drat_writer.cc b/ortools/sat/drat_writer.cc index 25a4e76453..1d35261b11 100644 --- a/ortools/sat/drat_writer.cc +++ b/ortools/sat/drat_writer.cc @@ -19,6 +19,7 @@ #if !defined(__PORTABLE_PLATFORM__) #include "ortools/base/file.h" #include "ortools/base/helpers.h" +#include "ortools/base/options.h" #endif // !__PORTABLE_PLATFORM__ #include "absl/status/status.h" #include "absl/strings/str_format.h" diff --git a/ortools/sat/sat_parameters.proto b/ortools/sat/sat_parameters.proto index 586fb58572..b2ba2482f7 100644 --- a/ortools/sat/sat_parameters.proto +++ b/ortools/sat/sat_parameters.proto @@ -317,7 +317,7 @@ message SatParameters { // Maximum number of conflicts allowed to solve a problem. // - // TODO(user,user): Maybe change the way the conflict limit is enforced? + // TODO(user): Maybe change the way the conflict limit is enforced? // currently it is enforced on each independent internal SAT solve, rather // than on the overall number of conflicts across all solves. So in the // context of an optimization problem, this is not really usable directly by a diff --git a/ortools/sat/synchronization.cc b/ortools/sat/synchronization.cc index e5b472db50..74c0ee9bd1 100644 --- a/ortools/sat/synchronization.cc +++ b/ortools/sat/synchronization.cc @@ -29,6 +29,7 @@ #include "ortools/base/timer.h" #if !defined(__PORTABLE_PLATFORM__) #include "ortools/base/helpers.h" +#include "ortools/base/options.h" #endif // __PORTABLE_PLATFORM__ #include "absl/container/btree_map.h" #include "absl/container/flat_hash_map.h" diff --git a/ortools/sat/timetable_edgefinding.cc b/ortools/sat/timetable_edgefinding.cc index fe6bb40306..11eda56ac3 100644 --- a/ortools/sat/timetable_edgefinding.cc +++ b/ortools/sat/timetable_edgefinding.cc @@ -298,7 +298,7 @@ bool TimeTableEdgeFinding::TimeTableEdgeFindingPass() { // Enough energy to schedule max_task at its minimum start time? // - // TODO(user, fdid): In case of alternatives, for each fixed + // TODO(user): In case of alternatives, for each fixed // size/demand pair, we can compute a new_start and use the min of them. if (extra_energy_required_by_max_task <= available_energy) { // If the test below is true, we know the max_task cannot fully