diff --git a/.appveyor.yml b/.appveyor.yml index 2521e2dac96..6939854b96f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -8,38 +8,24 @@ environment: matrix: # Makefile - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - VS: 2017 BUILDER: make LANGUAGE: cc - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - VS: 2017 BUILDER: make LANGUAGE: python - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - VS: 2017 BUILDER: make LANGUAGE: java - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - VS: 2017 BUILDER: make LANGUAGE: dotnet - - - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - VS: 2015 - BUILDER: make - LANGUAGE: cc # CMake - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - VS: 2017 BUILDER: cmake CMAKE_GENERATOR: "Visual Studio 15 2017 Win64" - - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - VS: 2015 - BUILDER: cmake - CMAKE_GENERATOR: "Visual Studio 14 2015 Win64" matrix: fast_finish: false @@ -47,9 +33,7 @@ matrix: before_build: - git config --global user.email "ci@appveyor.com" - git config --global user.name "CI" - - if "%VS%"=="2017" call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" - - if "%VS%"=="2015" call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 - - if "%VS%"=="2015" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 + - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" - tools\win\which.exe csc.exe - tools\win\which.exe fsc.exe - tools\win\which.exe dotnet.exe diff --git a/cmake/cpp.cmake b/cmake/cpp.cmake index 89da5025164..0e04e55df73 100644 --- a/cmake/cpp.cmake +++ b/cmake/cpp.cmake @@ -76,16 +76,26 @@ target_include_directories(${PROJECT_NAME} INTERFACE $ ) target_link_libraries(${PROJECT_NAME} PUBLIC + ZLIB::ZLIB + absl::base + absl::container + absl::hash + absl::memory + absl::meta + absl::str_format + absl::strings + absl::synchronization + absl::types gflags::gflags glog::glog - ZLIB::ZLIB protobuf::libprotobuf + protobuf::libprotobuf Cbc::CbcSolver Cbc::OsiCbc Cbc::ClpSolver Cbc::OsiClp Threads::Threads) if(WIN32) - target_link_libraries(${PROJECT_NAME} PUBLIC psapi.lib ws2_32.lib) - target_compile_definitions(${PROJECT_NAME} PUBLIC __WIN32__) + target_link_libraries(${PROJECT_NAME} PUBLIC psapi.lib ws2_32.lib) +target_compile_definitions(${PROJECT_NAME} PUBLIC __WIN32__) endif() target_compile_definitions(${PROJECT_NAME} - PUBLIC USE_BOP USE_GLOP USE_CBC USE_CLP) + PUBLIC USE_BOP USE_GLOP USE_CBC USE_CLP) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) diff --git a/cmake/external/CMakeLists.txt b/cmake/external/CMakeLists.txt index 96da6d43d7c..00c88e52497 100644 --- a/cmake/external/CMakeLists.txt +++ b/cmake/external/CMakeLists.txt @@ -163,6 +163,35 @@ if(NOT TARGET protobuf::libprotobuf OR NOT TARGET protobuf::protoc) message(STATUS "Adding CMake Subproject: Protobuf...DONE") endif() +############## +## ABSEIL ## +############## +if(NOT TARGET absl::base) + message(STATUS "Target absl::base not found.") + message(STATUS "Adding CMake Subproject: Abseil...") + # Download and unpack abseil at configure time + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/abseil.CMakeLists.txt + ${CMAKE_BINARY_DIR}/abseil-cpp-download/CMakeLists.txt) + execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/abseil-cpp-download) + if(result) + message(FATAL_ERROR "CMake step for abseil failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/abseil-cpp-download) + if(result) + message(FATAL_ERROR "Build step for abseil failed: ${result}") + endif() + + add_subdirectory( + ${CMAKE_BINARY_DIR}/abseil-cpp-src + ${CMAKE_BINARY_DIR}/abseil-cpp-build) + message(STATUS "Adding CMake Subproject: Abseil...DONE") +endif() + ########### ## CBC ## ########### diff --git a/cmake/external/abseil.CMakeLists.txt b/cmake/external/abseil.CMakeLists.txt new file mode 100644 index 00000000000..dd5be9d3c8d --- /dev/null +++ b/cmake/external/abseil.CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.5) + +# simplify variable expansion +cmake_policy(SET CMP0053 NEW) +cmake_policy(SET CMP0010 NEW) + +project(abseil-cpp-download NONE) + +include(ExternalProject) +ExternalProject_Add(abseil-cpp_project + GIT_REPOSITORY https://github.com/abseil/abseil-cpp + GIT_TAG "45221cc" + SOURCE_DIR "${CMAKE_BINARY_DIR}/abseil-cpp-src" + BINARY_DIR "${CMAKE_BINARY_DIR}/abseil-cpp-build" + UPDATE_COMMAND "" + PATCH_COMMAND git apply "${PROJECT_SOURCE_DIR}/patches/abseil-cpp-master.patch" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + LOG_DOWNLOAD ON +) diff --git a/examples/contrib/AllDifferentExcept0.java b/examples/contrib/AllDifferentExcept0.java index 3f06a4f08bc..6aaf33a071a 100644 --- a/examples/contrib/AllDifferentExcept0.java +++ b/examples/contrib/AllDifferentExcept0.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class AllDifferentExcept0 { @@ -34,25 +33,19 @@ public class AllDifferentExcept0 { public static void alldifferent_except_0(Solver solver, IntVar[] a) { int n = a.length; - for(int i = 0; i < n; i++) { - for(int j = 0; j < i; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { IntVar bi = solver.makeIsDifferentCstVar(a[i], 0); IntVar bj = solver.makeIsDifferentCstVar(a[j], 0); IntVar bij = solver.makeIsDifferentCstVar(a[i], a[j]); - solver.addConstraint( - solver.makeLessOrEqual( - solver.makeProd(bi, bj).var(), bij)); + solver.addConstraint(solver.makeLessOrEqual(solver.makeProd(bi, bj).var(), bij)); } } } - /** - * - * Implements a (decomposition) of global constraint - * alldifferent_except_0. - * See http://www.hakank.org/google_or_tools/circuit.py - * + * Implements a (decomposition) of global constraint alldifferent_except_0. See + * http://www.hakank.org/google_or_tools/circuit.py */ private static void solve() { @@ -75,9 +68,8 @@ private static void solve() { // we also require at least 2 0's IntVar[] z_tmp = solver.makeBoolVarArray(n, "z_tmp"); - for(int i = 0; i < n; i++) { - solver.addConstraint( - solver.makeIsEqualCstCt(x[i], 0, z_tmp[i])); + for (int i = 0; i < n; i++) { + solver.addConstraint(solver.makeIsEqualCstCt(x[i], 0, z_tmp[i])); } IntVar z = solver.makeSum(z_tmp).var(); @@ -86,9 +78,7 @@ private static void solve() { // // search // - DecisionBuilder db = solver.makePhase(x, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db); // @@ -96,7 +86,7 @@ private static void solve() { // while (solver.nextSolution()) { System.out.print("x: "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(x[i].value() + " "); } System.out.println(" z: " + z.value()); @@ -109,7 +99,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/AllInterval.java b/examples/contrib/AllInterval.java index 2a0e9ffdede..444fb9b1829 100644 --- a/examples/contrib/AllInterval.java +++ b/examples/contrib/AllInterval.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class AllInterval { @@ -24,18 +23,13 @@ public class AllInterval { System.loadLibrary("jniortools"); } - /** - * - * Implements the all interval problem. - * See http://www.hakank.org/google_or_tools/all_interval.py - * + * Implements the all interval problem. See http://www.hakank.org/google_or_tools/all_interval.py */ private static void solve(int n) { Solver solver = new Solver("AllInterval"); - // // variables // @@ -48,24 +42,20 @@ private static void solve(int n) { solver.addConstraint(solver.makeAllDifferent(x)); solver.addConstraint(solver.makeAllDifferent(diffs)); - for(int k = 0; k < n - 1; k++) { + for (int k = 0; k < n - 1; k++) { solver.addConstraint( - solver.makeEquality(diffs[k], - solver.makeAbs(solver.makeDifference(x[k + 1], x[k])).var())); + solver.makeEquality( + diffs[k], solver.makeAbs(solver.makeDifference(x[k + 1], x[k])).var())); } - // symmetry breaking solver.addConstraint(solver.makeLess(x[0], x[n - 1])); solver.addConstraint(solver.makeLess(diffs[0], diffs[1])); - // // search // - DecisionBuilder db = solver.makePhase(x, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = solver.makePhase(x, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); @@ -74,16 +64,15 @@ private static void solve(int n) { // while (solver.nextSolution()) { System.out.print("x : "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(x[i].value() + " "); } System.out.print("\ndiffs: "); - for(int i = 0; i < n-1; i++) { + for (int i = 0; i < n - 1; i++) { System.out.print(diffs[i].value() + " "); } System.out.println("\n"); - } solver.endSearch(); @@ -93,7 +82,6 @@ private static void solve(int n) { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/Circuit.java b/examples/contrib/Circuit.java index fac5138e2bb..03659a4ae17 100644 --- a/examples/contrib/Circuit.java +++ b/examples/contrib/Circuit.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class Circuit { @@ -27,11 +26,10 @@ public class Circuit { /** * circuit(solver, x) * - * A decomposition of the global constraint circuit, based - * on some observation of the orbits in an array. + *

A decomposition of the global constraint circuit, based on some observation of the orbits in + * an array. * - * Note: The domain of x must be 0..n-1 (not 1..n) - * since Java is 0-based. + *

Note: The domain of x must be 0..n-1 (not 1..n) since Java is 0-based. */ public static void circuit(Solver solver, IntVar[] x) { @@ -43,28 +41,22 @@ public static void circuit(Solver solver, IntVar[] x) { // put the orbit of x[0] in z[0..n-1] solver.addConstraint(solver.makeEquality(z[0], x[0])); - for(int i = 1; i < n-1; i++) { - solver.addConstraint( - solver.makeEquality(z[i], - solver.makeElement(x, z[i-1]).var())); + for (int i = 1; i < n - 1; i++) { + solver.addConstraint(solver.makeEquality(z[i], solver.makeElement(x, z[i - 1]).var())); } // z may not be 0 for i < n-1 - for(int i = 1; i < n - 1; i++) { + for (int i = 1; i < n - 1; i++) { solver.addConstraint(solver.makeNonEquality(z[i], 0)); } // when i = n-1 it must be 0 solver.addConstraint(solver.makeEquality(z[n - 1], 0)); - } - /** - * - * Implements a (decomposition) of the global constraint circuit. - * See http://www.hakank.org/google_or_tools/circuit.py - * + * Implements a (decomposition) of the global constraint circuit. See + * http://www.hakank.org/google_or_tools/circuit.py */ private static void solve(int n) { @@ -75,7 +67,6 @@ private static void solve(int n) { // IntVar[] x = solver.makeIntVarArray(n, 0, n - 1, "x"); - // // constraints // @@ -84,16 +75,14 @@ private static void solve(int n) { // // search // - DecisionBuilder db = solver.makePhase(x, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db); // // output // while (solver.nextSolution()) { - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(x[i].value() + " "); } System.out.println(); @@ -106,7 +95,6 @@ private static void solve(int n) { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/CoinsGrid.java b/examples/contrib/CoinsGrid.java index 19bc588b2e8..dbe0b709fe3 100644 --- a/examples/contrib/CoinsGrid.java +++ b/examples/contrib/CoinsGrid.java @@ -10,15 +10,13 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; -import com.google.ortools.constraintsolver.Solver; import com.google.ortools.constraintsolver.OptimizeVar; - +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class CoinsGrid { @@ -26,12 +24,7 @@ public class CoinsGrid { System.loadLibrary("jniortools"); } - /** - * - * Solves the Coins Grid problem. - * See http://www.hakank.org/google_or_tools/coins_grid.py - * - */ + /** Solves the Coins Grid problem. See http://www.hakank.org/google_or_tools/coins_grid.py */ private static void solve() { Solver solver = new Solver("CoinsGrid"); @@ -47,8 +40,8 @@ private static void solve() { IntVar[][] x = new IntVar[n][n]; IntVar[] x_flat = new IntVar[n * n]; - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { x[i][j] = solver.makeIntVar(0, 1, "x[" + i + "," + j + "]"); x_flat[i * n + j] = x[i][j]; } @@ -59,10 +52,10 @@ private static void solve() { // // sum row/columns == c - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { IntVar[] row = new IntVar[n]; IntVar[] col = new IntVar[n]; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { row[j] = x[i][j]; col[j] = x[j][i]; } @@ -72,10 +65,9 @@ private static void solve() { // quadratic horizonal distance IntVar[] obj_tmp = new IntVar[n * n]; - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { - obj_tmp[i * n + j] = - solver.makeProd(x[i][j],(i - j) * (i - j)).var(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + obj_tmp[i * n + j] = solver.makeProd(x[i][j], (i - j) * (i - j)).var(); } } IntVar obj_var = solver.makeSum(obj_tmp).var(); @@ -88,9 +80,8 @@ private static void solve() { // // search // - DecisionBuilder db = solver.makePhase(x_flat, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MAX_VALUE); + DecisionBuilder db = + solver.makePhase(x_flat, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MAX_VALUE); solver.newSearch(db, obj); @@ -99,8 +90,8 @@ private static void solve() { // while (solver.nextSolution()) { System.out.println("obj_var: " + obj_var.value()); - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { System.out.print(x[i][j].value() + " "); } System.out.println(); diff --git a/examples/contrib/CoinsGridMIP.java b/examples/contrib/CoinsGridMIP.java index 3b51a421a38..38c82b8ed9c 100755 --- a/examples/contrib/CoinsGridMIP.java +++ b/examples/contrib/CoinsGridMIP.java @@ -45,72 +45,71 @@ import com.google.ortools.linearsolver.*; public class CoinsGridMIP { - static { - System.loadLibrary("jniortools"); - } + static { + System.loadLibrary("jniortools"); + } - private static MPSolver createSolver (String solverType) { - return new MPSolver("MIPDiet", - MPSolver.OptimizationProblemType.valueOf(solverType)); - } + private static MPSolver createSolver(String solverType) { + return new MPSolver("MIPDiet", MPSolver.OptimizationProblemType.valueOf(solverType)); + } - private static void solve(String solverType) { - MPSolver solver = createSolver(solverType); + private static void solve(String solverType) { + MPSolver solver = createSolver(solverType); - /** invariants */ - int n = 31; - int c = 14; + /** invariants */ + int n = 31; + int c = 14; - /** variables */ - MPVariable[][] x = new MPVariable[n][n]; - for (int i = 0; i < n; i ++) { - x[i] = solver.makeBoolVarArray(n); - } + /** variables */ + MPVariable[][] x = new MPVariable[n][n]; + for (int i = 0; i < n; i++) { + x[i] = solver.makeBoolVarArray(n); + } - /** constraints & objective */ - MPConstraint[] constraints = new MPConstraint[2 * n]; - MPObjective obj = solver.objective(); + /** constraints & objective */ + MPConstraint[] constraints = new MPConstraint[2 * n]; + MPObjective obj = solver.objective(); - for (int i = 0; i < n; i ++) { - constraints[2*i] = solver.makeConstraint(c, c); - constraints[2*i + 1] = solver.makeConstraint(c, c); + for (int i = 0; i < n; i++) { + constraints[2 * i] = solver.makeConstraint(c, c); + constraints[2 * i + 1] = solver.makeConstraint(c, c); - for (int j = 0; j < n; j ++) { - constraints[2*i].setCoefficient(x[i][j], 1); - constraints[2*i + 1].setCoefficient(x[j][i], 1); + for (int j = 0; j < n; j++) { + constraints[2 * i].setCoefficient(x[i][j], 1); + constraints[2 * i + 1].setCoefficient(x[j][i], 1); - obj.setCoefficient(x[i][j], (i-j) * (j-i)); - } - } + obj.setCoefficient(x[i][j], (i - j) * (j - i)); + } + } - solver.solve(); + solver.solve(); - for (int i = 0; i < n; i ++) { - for (int j = 0; j < n; j ++) { - System.out.print((int) x[i][j].solutionValue() + " "); - } - System.out.println(); - } - } + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + System.out.print((int) x[i][j].solutionValue() + " "); + } + System.out.println(); + } + } - public static void main(String[] args) { - try { - System.out.println("---- Integer programming example with SCIP (recommended) ----"); - solve("SCIP_MIXED_INTEGER_PROGRAMMING"); - } catch (java.lang.IllegalArgumentException e) { - System.err.println("Bad solver type: " + e); - } - try { - System.out.println("---- Integer programming example with CBC ----"); - solve("CBC_MIXED_INTEGER_PROGRAMMING"); - } catch (java.lang.IllegalArgumentException e) { - System.err.println("Bad solver type: " + e); - } - try { - System.out.println("---- Integer programming example with GLPK ----"); - solve("GLPK_MIXED_INTEGER_PROGRAMMING"); - } catch (java.lang.IllegalArgumentException e) { - System.err.println("Bad solver type: " + e); - } - } + public static void main(String[] args) { + try { + System.out.println("---- Integer programming example with SCIP (recommended) ----"); + solve("SCIP_MIXED_INTEGER_PROGRAMMING"); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + } + try { + System.out.println("---- Integer programming example with CBC ----"); + solve("CBC_MIXED_INTEGER_PROGRAMMING"); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + } + try { + System.out.println("---- Integer programming example with GLPK ----"); + solve("GLPK_MIXED_INTEGER_PROGRAMMING"); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + } + } } diff --git a/examples/contrib/ColoringMIP.java b/examples/contrib/ColoringMIP.java index 80173e7b270..24f88479f59 100755 --- a/examples/contrib/ColoringMIP.java +++ b/examples/contrib/ColoringMIP.java @@ -24,131 +24,130 @@ import com.google.ortools.linearsolver.MPVariable; public class ColoringMIP { - public static class Edge { - public int a, b; - - public Edge(int a, int b) { - this.a = a; - this.b = b; - } - } - - static { - System.loadLibrary("jniortools"); - } - - private static MPSolver createSolver (String solverType) { - return new MPSolver("MIPDiet", - MPSolver.OptimizationProblemType.valueOf(solverType)); - } - - private static void solve(String solverType) { - MPSolver solver = createSolver(solverType); - double infinity = MPSolver.infinity(); - - /** invariants */ - int noCols = 5; // variables number - int noNodes = 11; // constraints number - - Edge[] edges = { - new Edge(1,2), - new Edge(1,4), - new Edge(1,7), - new Edge(1,9), - new Edge(2,3), - new Edge(2,6), - new Edge(2,8), - new Edge(3,5), - new Edge(3,7), - new Edge(3,10), - new Edge(4,5), - new Edge(4,6), - new Edge(4,10), - new Edge(5,8), - new Edge(5,9), - new Edge(6,11), - new Edge(7,11), - new Edge(8,11), - new Edge(9,11), - new Edge(10,11) - }; - - /** variables */ - MPVariable[][] x = new MPVariable[noNodes][noCols]; - for (Integer i = 0; i < noNodes; i ++) { - x[i] = solver.makeBoolVarArray(noCols); - } - - MPVariable[] colUsed = solver.makeBoolVarArray(noCols); - - MPObjective obj = solver.objective(); - for (MPVariable objVar : colUsed) { - obj.setCoefficient(objVar, 1); - } - - /** Bound each vertex to only one color */ - MPConstraint[] constraints = new MPConstraint[noNodes]; - for (int i = 0; i < noNodes; i ++) { - constraints[i] = solver.makeConstraint(1,1); - for (int j = 0; j < noCols; j ++) { - constraints[i].setCoefficient(x[i][j], 1); - } - } - - /** Set adjacent nodes to have different colors */ - MPConstraint[][] adjacencies = new MPConstraint[edges.length][noCols]; - for (int i = 0; i < edges.length; i ++) { - for (int j = 0; j < noCols; j ++) { - adjacencies[i][j] = solver.makeConstraint(-infinity, 0); - adjacencies[i][j].setCoefficient(x[edges[i].a - 1][j], 1); - adjacencies[i][j].setCoefficient(x[edges[i].b - 1][j], 1); - adjacencies[i][j].setCoefficient(colUsed[j], -1); - } - } - - /** Minimize by default */ - final MPSolver.ResultStatus resultStatus = solver.solve(); - - /** printing */ - if (resultStatus != MPSolver.ResultStatus.OPTIMAL) { - System.err.println("The problem does not have an optimal solution!"); - return; - } else { - System.out.print("Colors used: "); - for (MPVariable var : colUsed) { - System.out.print((int) var.solutionValue() + " "); - } - System.out.println("\n"); - - for (int i = 0; i < noNodes; i ++) { - System.out.print("Col of vertex " + i + " : "); - for (int j = 0; j < noCols; j ++) { - if (x[i][j].solutionValue() > 0) { - System.out.println(j); - } - } - } - } - } - - public static void main(String[] args) { - try { - System.out.println("---- Integer programming example with SCIP (recommended) ----"); - solve("SCIP_MIXED_INTEGER_PROGRAMMING"); - } catch (java.lang.IllegalArgumentException e) { - System.err.println("Bad solver type: " + e); - } - try { - System.out.println("---- Integer programming example with CBC ----"); - solve("CBC_MIXED_INTEGER_PROGRAMMING"); - } catch (java.lang.IllegalArgumentException e) { - System.err.println("Bad solver type: " + e); - } - try { - System.out.println("---- Integer programming example with GLPK ----"); - solve("GLPK_MIXED_INTEGER_PROGRAMMING"); - } catch (java.lang.IllegalArgumentException e) { - System.err.println("Bad solver type: " + e); - } - } + public static class Edge { + public int a, b; + + public Edge(int a, int b) { + this.a = a; + this.b = b; + } + } + + static { + System.loadLibrary("jniortools"); + } + + private static MPSolver createSolver(String solverType) { + return new MPSolver("MIPDiet", MPSolver.OptimizationProblemType.valueOf(solverType)); + } + + private static void solve(String solverType) { + MPSolver solver = createSolver(solverType); + double infinity = MPSolver.infinity(); + + /** invariants */ + int noCols = 5; // variables number + int noNodes = 11; // constraints number + + Edge[] edges = { + new Edge(1, 2), + new Edge(1, 4), + new Edge(1, 7), + new Edge(1, 9), + new Edge(2, 3), + new Edge(2, 6), + new Edge(2, 8), + new Edge(3, 5), + new Edge(3, 7), + new Edge(3, 10), + new Edge(4, 5), + new Edge(4, 6), + new Edge(4, 10), + new Edge(5, 8), + new Edge(5, 9), + new Edge(6, 11), + new Edge(7, 11), + new Edge(8, 11), + new Edge(9, 11), + new Edge(10, 11) + }; + + /** variables */ + MPVariable[][] x = new MPVariable[noNodes][noCols]; + for (Integer i = 0; i < noNodes; i++) { + x[i] = solver.makeBoolVarArray(noCols); + } + + MPVariable[] colUsed = solver.makeBoolVarArray(noCols); + + MPObjective obj = solver.objective(); + for (MPVariable objVar : colUsed) { + obj.setCoefficient(objVar, 1); + } + + /** Bound each vertex to only one color */ + MPConstraint[] constraints = new MPConstraint[noNodes]; + for (int i = 0; i < noNodes; i++) { + constraints[i] = solver.makeConstraint(1, 1); + for (int j = 0; j < noCols; j++) { + constraints[i].setCoefficient(x[i][j], 1); + } + } + + /** Set adjacent nodes to have different colors */ + MPConstraint[][] adjacencies = new MPConstraint[edges.length][noCols]; + for (int i = 0; i < edges.length; i++) { + for (int j = 0; j < noCols; j++) { + adjacencies[i][j] = solver.makeConstraint(-infinity, 0); + adjacencies[i][j].setCoefficient(x[edges[i].a - 1][j], 1); + adjacencies[i][j].setCoefficient(x[edges[i].b - 1][j], 1); + adjacencies[i][j].setCoefficient(colUsed[j], -1); + } + } + + /** Minimize by default */ + final MPSolver.ResultStatus resultStatus = solver.solve(); + + /** printing */ + if (resultStatus != MPSolver.ResultStatus.OPTIMAL) { + System.err.println("The problem does not have an optimal solution!"); + return; + } else { + System.out.print("Colors used: "); + for (MPVariable var : colUsed) { + System.out.print((int) var.solutionValue() + " "); + } + System.out.println("\n"); + + for (int i = 0; i < noNodes; i++) { + System.out.print("Col of vertex " + i + " : "); + for (int j = 0; j < noCols; j++) { + if (x[i][j].solutionValue() > 0) { + System.out.println(j); + } + } + } + } + } + + public static void main(String[] args) { + try { + System.out.println("---- Integer programming example with SCIP (recommended) ----"); + solve("SCIP_MIXED_INTEGER_PROGRAMMING"); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + } + try { + System.out.println("---- Integer programming example with CBC ----"); + solve("CBC_MIXED_INTEGER_PROGRAMMING"); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + } + try { + System.out.println("---- Integer programming example with GLPK ----"); + solve("GLPK_MIXED_INTEGER_PROGRAMMING"); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + } + } } diff --git a/examples/contrib/CoveringOpl.java b/examples/contrib/CoveringOpl.java index 524552991b1..92be92eb934 100644 --- a/examples/contrib/CoveringOpl.java +++ b/examples/contrib/CoveringOpl.java @@ -11,14 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; -import com.google.ortools.constraintsolver.Solver; import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class CoveringOpl { @@ -26,12 +25,7 @@ public class CoveringOpl { System.loadLibrary("jniortools"); } - /** - * - * Solves a set covering problem. - * See http://www.hakank.org/google_or_tools/covering_opl.py - * - */ + /** Solves a set covering problem. See http://www.hakank.org/google_or_tools/covering_opl.py */ private static void solve() { Solver solver = new Solver("CoveringOpl"); @@ -44,25 +38,28 @@ private static void solve() { // Which worker is qualified for each task. // Note: This is 1-based and will be made 0-base below. - int[][] qualified = {{ 1, 9, 19, 22, 25, 28, 31 }, - { 2, 12, 15, 19, 21, 23, 27, 29, 30, 31, 32 }, - { 3, 10, 19, 24, 26, 30, 32 }, - { 4, 21, 25, 28, 32 }, - { 5, 11, 16, 22, 23, 27, 31 }, - { 6, 20, 24, 26, 30, 32 }, - { 7, 12, 17, 25, 30, 31 } , - { 8, 17, 20, 22, 23 }, - { 9, 13, 14, 26, 29, 30, 31 }, - { 10, 21, 25, 31, 32 }, - { 14, 15, 18, 23, 24, 27, 30, 32 }, - { 18, 19, 22, 24, 26, 29, 31 }, - { 11, 20, 25, 28, 30, 32 }, - { 16, 19, 23, 31 }, - { 9, 18, 26, 28, 31, 32 }}; - - int[] cost = {1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, - 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9}; - + int[][] qualified = { + {1, 9, 19, 22, 25, 28, 31}, + {2, 12, 15, 19, 21, 23, 27, 29, 30, 31, 32}, + {3, 10, 19, 24, 26, 30, 32}, + {4, 21, 25, 28, 32}, + {5, 11, 16, 22, 23, 27, 31}, + {6, 20, 24, 26, 30, 32}, + {7, 12, 17, 25, 30, 31}, + {8, 17, 20, 22, 23}, + {9, 13, 14, 26, 29, 30, 31}, + {10, 21, 25, 31, 32}, + {14, 15, 18, 23, 24, 27, 30, 32}, + {18, 19, 22, 24, 26, 29, 31}, + {11, 20, 25, 28, 30, 32}, + {16, 19, 23, 31}, + {9, 18, 26, 28, 31, 32} + }; + + int[] cost = { + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9 + }; // // variables @@ -73,28 +70,26 @@ private static void solve() { // // constraints // - for(int j = 0; j < num_tasks; j++) { + for (int j = 0; j < num_tasks; j++) { // Sum the cost for hiring the qualified workers // (also, make 0-base). int len = qualified[j].length; IntVar[] tmp = new IntVar[len]; - for(int c = 0; c < len; c++) { + for (int c = 0; c < len; c++) { tmp[c] = hire[qualified[j][c] - 1]; } IntVar b = solver.makeSum(tmp).var(); solver.addConstraint(solver.makeGreaterOrEqual(b, 1)); } - // Objective: Minimize total cost OptimizeVar objective = solver.makeMinimize(total_cost, 1); // // search // - DecisionBuilder db = solver.makePhase(hire, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = + solver.makePhase(hire, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); solver.newSearch(db, objective); @@ -104,13 +99,12 @@ private static void solve() { while (solver.nextSolution()) { System.out.println("Cost: " + total_cost.value()); System.out.print("Hire: "); - for(int i = 0; i < num_workers; i++) { + for (int i = 0; i < num_workers; i++) { if (hire[i].value() == 1) { System.out.print(i + " "); } } System.out.println("\n"); - } solver.endSearch(); @@ -121,12 +115,10 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { CoveringOpl.solve(); - } } diff --git a/examples/contrib/Crossword.java b/examples/contrib/Crossword.java index f1f77951b79..cdff5b5e282 100644 --- a/examples/contrib/Crossword.java +++ b/examples/contrib/Crossword.java @@ -11,13 +11,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class Crossword { @@ -25,12 +24,7 @@ public class Crossword { System.loadLibrary("jniortools"); } - /** - * - * Solving a simple crossword. - * See http://www.hakank.org/google_or_tools/crossword2.py - * - */ + /** Solving a simple crossword. See http://www.hakank.org/google_or_tools/crossword2.py */ private static void solve() { Solver solver = new Solver("Crossword"); @@ -38,56 +32,79 @@ private static void solve() { // // data // - String[] alpha = {"_","a","b","c","d","e","f", - "g","h","i","j","k","l","m", - "n","o","p","q","r","s","t", - "u","v","w","x","y","z"}; - - int a=1; int b=2; int c=3; int d=4; int e=5; int f=6; - int g=7; int h=8; int i=9; int j=10; int k=11; int l=12; - int m=13; int n=14; int o=15; int p=16; int q=17; int r=18; - int s=19; int t=20; int u=21; int v=22; int w=23; int x=24; - int y=25; int z=26; + String[] alpha = { + "_", "a", "b", "c", "d", "e", "f", + "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", + "u", "v", "w", "x", "y", "z" + }; + + int a = 1; + int b = 2; + int c = 3; + int d = 4; + int e = 5; + int f = 6; + int g = 7; + int h = 8; + int i = 9; + int j = 10; + int k = 11; + int l = 12; + int m = 13; + int n = 14; + int o = 15; + int p = 16; + int q = 17; + int r = 18; + int s = 19; + int t = 20; + int u = 21; + int v = 22; + int w = 23; + int x = 24; + int y = 25; + int z = 26; int num_words = 15; int word_len = 5; - int[][] AA = {{h, o, s, e, s}, // HOSES - {l, a, s, e, r}, // LASER - {s, a, i, l, s}, // SAILS - {s, h, e, e, t}, // SHEET - {s, t, e, e, r}, // STEER - {h, e, e, l, 0}, // HEEL - {h, i, k, e, 0}, // HIKE - {k, e, e, l, 0}, // KEEL - {k, n, o, t, 0}, // KNOT - {l, i, n, e, 0}, // LINE - {a, f, t, 0, 0}, // AFT - {a, l, e, 0, 0}, // ALE - {e, e, l, 0, 0}, // EEL - {l, e, e, 0, 0}, // LEE - {t, i, e, 0, 0}}; // TIE + int[][] AA = { + {h, o, s, e, s}, // HOSES + {l, a, s, e, r}, // LASER + {s, a, i, l, s}, // SAILS + {s, h, e, e, t}, // SHEET + {s, t, e, e, r}, // STEER + {h, e, e, l, 0}, // HEEL + {h, i, k, e, 0}, // HIKE + {k, e, e, l, 0}, // KEEL + {k, n, o, t, 0}, // KNOT + {l, i, n, e, 0}, // LINE + {a, f, t, 0, 0}, // AFT + {a, l, e, 0, 0}, // ALE + {e, e, l, 0, 0}, // EEL + {l, e, e, 0, 0}, // LEE + {t, i, e, 0, 0} + }; // TIE int num_overlapping = 12; - int[][] overlapping = {{0, 2, 1, 0}, // s - {0, 4, 2, 0}, // s - - {3, 1, 1, 2}, // i - {3, 2, 4, 0}, // k - {3, 3, 2, 2}, // e - - {6, 0, 1, 3}, // l - {6, 1, 4, 1}, // e - {6, 2, 2, 3}, // e - - {7, 0, 5, 1}, // l - {7, 2, 1, 4}, // s - {7, 3, 4, 2}, // e - {7, 4, 2, 4}}; // r + int[][] overlapping = { + {0, 2, 1, 0}, // s + {0, 4, 2, 0}, // s + {3, 1, 1, 2}, // i + {3, 2, 4, 0}, // k + {3, 3, 2, 2}, // e + {6, 0, 1, 3}, // l + {6, 1, 4, 1}, // e + {6, 2, 2, 3}, // e + {7, 0, 5, 1}, // l + {7, 2, 1, 4}, // s + {7, 3, 4, 2}, // e + {7, 4, 2, 4} + }; // r int N = 8; - // // variables // @@ -96,8 +113,8 @@ private static void solve() { // for labeling on A and E IntVar[] all = new IntVar[(num_words * word_len) + N]; - for(int I = 0; I < num_words; I++) { - for(int J = 0; J < word_len; J++) { + for (int I = 0; I < num_words; I++) { + for (int J = 0; J < word_len; J++) { A[I][J] = solver.makeIntVar(0, 26, "A[" + I + "," + J + "]"); A_flat[I * word_len + J] = A[I][J]; all[I * word_len + J] = A[I][J]; @@ -105,43 +122,48 @@ private static void solve() { } IntVar[] E = solver.makeIntVarArray(N, 0, num_words, "E"); - for(int I = 0; I < N; I++) { + for (int I = 0; I < N; I++) { all[num_words * word_len + I] = E[I]; } - // // constraints // solver.addConstraint(solver.makeAllDifferent(E)); - for(int I = 0; I < num_words; I++) { - for(int J = 0; J < word_len; J++) { + for (int I = 0; I < num_words; I++) { + for (int J = 0; J < word_len; J++) { solver.addConstraint(solver.makeEquality(A[I][J], AA[I][J])); } } - for(int I = 0; I < num_overlapping; I++) { + for (int I = 0; I < num_overlapping; I++) { solver.addConstraint( solver.makeEquality( - solver.makeElement(A_flat, - solver.makeSum( - solver.makeProd( - E[overlapping[I][0]], word_len).var(), - overlapping[I][1]).var()).var(), - solver.makeElement(A_flat, - solver.makeSum( - solver.makeProd( - E[overlapping[I][2]], word_len).var(), - overlapping[I][3]).var()).var() )); + solver + .makeElement( + A_flat, + solver + .makeSum( + solver.makeProd(E[overlapping[I][0]], word_len).var(), + overlapping[I][1]) + .var()) + .var(), + solver + .makeElement( + A_flat, + solver + .makeSum( + solver.makeProd(E[overlapping[I][2]], word_len).var(), + overlapping[I][3]) + .var()) + .var())); } // // search // - DecisionBuilder db = solver.makePhase(all, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(all, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db); @@ -150,17 +172,16 @@ private static void solve() { // while (solver.nextSolution()) { System.out.println("E:"); - for(int ee = 0; ee < N; ee++) { - int e_val = (int)E[ee].value(); + for (int ee = 0; ee < N; ee++) { + int e_val = (int) E[ee].value(); System.out.print(ee + ": (" + e_val + ") "); - for(int ii = 0; ii < word_len; ii++) { - System.out.print(alpha[(int)A[ee][ii].value()]); + for (int ii = 0; ii < word_len; ii++) { + System.out.print(alpha[(int) A[ee][ii].value()]); } System.out.println(); } System.out.println(); - } solver.endSearch(); @@ -171,12 +192,10 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { Crossword.solve(); - } } diff --git a/examples/contrib/DeBruijn.java b/examples/contrib/DeBruijn.java index b2f987aa789..78f8539d212 100644 --- a/examples/contrib/DeBruijn.java +++ b/examples/contrib/DeBruijn.java @@ -11,13 +11,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class DeBruijn { @@ -25,31 +24,24 @@ public class DeBruijn { System.loadLibrary("jniortools"); } - /** + * toNum(solver, a, num, base) * - * toNum(solver, a, num, base) - * - * channelling between the array a and the number num - * + *

channelling between the array a and the number num */ private static void toNum(Solver solver, IntVar[] a, IntVar num, int base) { int len = a.length; IntVar[] tmp = new IntVar[len]; - for(int i = 0; i < len; i++) { - tmp[i] = solver.makeProd(a[i], (int)Math.pow(base,(len-i-1))).var(); + for (int i = 0; i < len; i++) { + tmp[i] = solver.makeProd(a[i], (int) Math.pow(base, (len - i - 1))).var(); } - solver.addConstraint( - solver.makeEquality(solver.makeSum(tmp).var(), num)); + solver.addConstraint(solver.makeEquality(solver.makeSum(tmp).var(), num)); } - /** - * - * Implements "arbitrary" de Bruijn sequences. - * See http://www.hakank.org/google_or_tools/debruijn_binary.py - * + * Implements "arbitrary" de Bruijn sequences. See + * http://www.hakank.org/google_or_tools/debruijn_binary.py */ private static void solve(int base, int n, int m) { @@ -64,31 +56,28 @@ private static void solve(int base, int n, int m) { // // variables // - IntVar[] x = - solver.makeIntVarArray(m, 0, (int)Math.pow(base, n) - 1, "x"); + IntVar[] x = solver.makeIntVarArray(m, 0, (int) Math.pow(base, n) - 1, "x"); IntVar[][] binary = new IntVar[m][n]; - for(int i = 0; i < m; i++) { - for(int j = 0; j < n; j++) { - binary[i][j] = - solver.makeIntVar(0, base - 1, "binary[" + i + "," + j + "]"); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + binary[i][j] = solver.makeIntVar(0, base - 1, "binary[" + i + "," + j + "]"); } } // this is the de Bruijn sequence - IntVar[] bin_code = - solver.makeIntVarArray(m, 0, base - 1, "bin_code"); + IntVar[] bin_code = solver.makeIntVarArray(m, 0, base - 1, "bin_code"); // occurences of each number in bin_code IntVar[] gcc = solver.makeIntVarArray(base, 0, m, "gcc"); // for the branching IntVar[] all = new IntVar[2 * m + base]; - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { all[i] = x[i]; all[m + i] = bin_code[i]; } - for(int i = 0; i < base; i++) { + for (int i = 0; i < base; i++) { all[2 * m + i] = gcc[i]; } @@ -98,9 +87,9 @@ private static void solve(int base, int n, int m) { solver.addConstraint(solver.makeAllDifferent(x)); // converts x <-> binary - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { IntVar[] t = new IntVar[n]; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { t[j] = binary[i][j]; } toNum(solver, t, x[i], base); @@ -109,31 +98,28 @@ private static void solve(int base, int n, int m) { // the de Bruijn condition: // the first elements in binary[i] is the same as the last // elements in binary[i-1] - for(int i = 1; i < m; i++) { - for(int j = 1; j < n; j++) { - solver.addConstraint( - solver.makeEquality(binary[i - 1][j], binary[i][j - 1])); + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + solver.addConstraint(solver.makeEquality(binary[i - 1][j], binary[i][j - 1])); } } // ... and around the corner - for(int j = 1; j < n; j++) { - solver.addConstraint( - solver.makeEquality(binary[m - 1][j], binary[0][j - 1])); + for (int j = 1; j < n; j++) { + solver.addConstraint(solver.makeEquality(binary[m - 1][j], binary[0][j - 1])); } // converts binary -> bin_code (de Bruijn sequence) - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { solver.addConstraint(solver.makeEquality(bin_code[i], binary[i][0])); } - // extra: ensure that all the numbers in the de Bruijn sequence // (bin_code) has the same occurrences (if check_same_gcc is True // and mathematically possible) solver.addConstraint(solver.makeDistribute(bin_code, gcc)); if (check_same_gcc && m % base == 0) { - for(int i = 1; i < base; i++) { + for (int i = 1; i < base; i++) { solver.addConstraint(solver.makeEquality(gcc[i], gcc[i - 1])); } } @@ -142,13 +128,11 @@ private static void solve(int base, int n, int m) { // the minimum value of x should be first solver.addConstraint(solver.makeEquality(x[0], solver.makeMin(x).var())); - // // search // - DecisionBuilder db = solver.makePhase(all, - solver.CHOOSE_MIN_SIZE_LOWEST_MAX, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = + solver.makePhase(all, solver.CHOOSE_MIN_SIZE_LOWEST_MAX, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); @@ -157,22 +141,21 @@ private static void solve(int base, int n, int m) { // while (solver.nextSolution()) { System.out.print("x: "); - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { System.out.print(x[i].value() + " "); } System.out.print("\nde Bruijn sequence:"); - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { System.out.print(bin_code[i].value() + " "); } System.out.print("\ngcc: "); - for(int i = 0; i < base; i++) { + for (int i = 0; i < base; i++) { System.out.print(gcc[i].value() + " "); } System.out.println("\n"); - // for debugging etc: show the full binary table /* System.out.println("binary:"); @@ -195,7 +178,6 @@ private static void solve(int base, int n, int m) { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { @@ -210,15 +192,14 @@ public static void main(String[] args) throws Exception { if (args.length > 1) { n = Integer.parseInt(args[1]); - m = (int)Math.pow(base, n); + m = (int) Math.pow(base, n); } if (args.length > 2) { int m_max = (int) Math.pow(base, n); m = Integer.parseInt(args[2]); if (m > m_max) { - System.out.println("m(" + m + ") is too large. Set m to " + - m_max + "."); + System.out.println("m(" + m + ") is too large. Set m to " + m_max + "."); m = m_max; } } diff --git a/examples/contrib/Diet.java b/examples/contrib/Diet.java index 62f4396a142..c2af97a3b3c 100644 --- a/examples/contrib/Diet.java +++ b/examples/contrib/Diet.java @@ -11,14 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - +import com.google.ortools.constraintsolver.*; import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; -import com.google.ortools.constraintsolver.*; +import java.io.*; +import java.text.*; +import java.util.*; public class Diet { @@ -26,29 +25,22 @@ public class Diet { System.loadLibrary("jniortools"); } - - /** - * - * Solves the Diet problem. - * See http://www.hakank.org/google_or_tools/diet1.py - * - */ + /** Solves the Diet problem. See http://www.hakank.org/google_or_tools/diet1.py */ private static void solve() { Solver solver = new Solver("Diet"); int n = 4; - int[] price = { 50, 20, 30, 80}; // in cents + int[] price = {50, 20, 30, 80}; // in cents // requirements for each nutrition type - int[] limits = {500, 6, 10, 8}; + int[] limits = {500, 6, 10, 8}; // nutritions for each product - int[] calories = {400, 200, 150, 500}; + int[] calories = {400, 200, 150, 500}; int[] chocolate = {3, 2, 0, 0}; - int[] sugar = {2, 2, 4, 4}; - int[] fat = {2, 4, 1, 5}; - + int[] sugar = {2, 2, 4, 4}; + int[] fat = {2, 4, 1, 5}; // // Variables @@ -60,18 +52,13 @@ private static void solve() { // // Constraints // - solver.addConstraint( - solver.makeScalProdGreaterOrEqual(x, calories, limits[0])); - - solver.addConstraint( - solver.makeScalProdGreaterOrEqual(x,chocolate, limits[1])); + solver.addConstraint(solver.makeScalProdGreaterOrEqual(x, calories, limits[0])); - solver.addConstraint( - solver.makeScalProdGreaterOrEqual(x, sugar, limits[2])); + solver.addConstraint(solver.makeScalProdGreaterOrEqual(x, chocolate, limits[1])); - solver.addConstraint( - solver.makeScalProdGreaterOrEqual(x, fat, limits[3])); + solver.addConstraint(solver.makeScalProdGreaterOrEqual(x, sugar, limits[2])); + solver.addConstraint(solver.makeScalProdGreaterOrEqual(x, fat, limits[3])); // // Objective @@ -81,14 +68,12 @@ private static void solve() { // // Search // - DecisionBuilder db = solver.makePhase(x, - solver.CHOOSE_PATH, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = solver.makePhase(x, solver.CHOOSE_PATH, solver.ASSIGN_MIN_VALUE); solver.newSearch(db, obj); while (solver.nextSolution()) { System.out.println("cost: " + cost.value()); System.out.print("x: "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(x[i].value() + " "); } System.out.println(); @@ -101,7 +86,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/DivisibleBy9Through1.java b/examples/contrib/DivisibleBy9Through1.java index 5086805dbc3..b5c4b111b4d 100644 --- a/examples/contrib/DivisibleBy9Through1.java +++ b/examples/contrib/DivisibleBy9Through1.java @@ -11,13 +11,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class DivisibleBy9Through1 { @@ -25,17 +24,13 @@ public class DivisibleBy9Through1 { System.loadLibrary("jniortools"); } - /** - * * A simple propagator for modulo constraint. * - * This implementation is based on the ECLiPSe version - * mentioned in "A Modulo propagator for ECLiPSE" + *

This implementation is based on the ECLiPSe version mentioned in "A Modulo propagator for + * ECLiPSE" * http://www.hakank.org/constraint_programming_blog/2010/05/a_modulo_propagator_for_eclips.html - * The ECLiPSe Prolog source code: - * http://www.hakank.org/eclipse/modulo_propagator.ecl - * + * The ECLiPSe Prolog source code: http://www.hakank.org/eclipse/modulo_propagator.ecl */ public static void my_mod(Solver solver, IntVar x, IntVar y, IntVar r) { @@ -43,67 +38,52 @@ public static void my_mod(Solver solver, IntVar x, IntVar y, IntVar r) { long ubx = x.max(); long ubx_neg = -ubx; long lbx_neg = -lbx; - int min_x = (int)Math.min(lbx, ubx_neg); - int max_x = (int)Math.max(ubx, lbx_neg); + int min_x = (int) Math.min(lbx, ubx_neg); + int max_x = (int) Math.max(ubx, lbx_neg); IntVar d = solver.makeIntVar(min_x, max_x, "d"); // r >= 0 - solver.addConstraint(solver.makeGreaterOrEqual(r,0)); + solver.addConstraint(solver.makeGreaterOrEqual(r, 0)); // x*r >= 0 - solver.addConstraint( - solver.makeGreaterOrEqual( - solver.makeProd(x,r).var(), 0)); + solver.addConstraint(solver.makeGreaterOrEqual(solver.makeProd(x, r).var(), 0)); // -abs(y) < r - solver.addConstraint( - solver.makeLess( - solver.makeOpposite(solver.makeAbs(y).var()).var(), r)); + solver.addConstraint(solver.makeLess(solver.makeOpposite(solver.makeAbs(y).var()).var(), r)); // r < abs(y) - solver.addConstraint( - solver.makeLess(r, - solver.makeAbs(y).var().var())); + solver.addConstraint(solver.makeLess(r, solver.makeAbs(y).var().var())); // min_x <= d, i.e. d > min_x solver.addConstraint(solver.makeGreater(d, min_x)); - // d <= max_x solver.addConstraint(solver.makeLessOrEqual(d, max_x)); // x == y*d+r - solver.addConstraint(solver.makeEquality(x, - solver.makeSum( - solver.makeProd(y,d).var(),r).var())); - + solver.addConstraint( + solver.makeEquality(x, solver.makeSum(solver.makeProd(y, d).var(), r).var())); } - /** + * toNum(solver, a, num, base) * - * toNum(solver, a, num, base) - * - * channelling between the array a and the number num - * + *

channelling between the array a and the number num */ private static void toNum(Solver solver, IntVar[] a, IntVar num, int base) { int len = a.length; IntVar[] tmp = new IntVar[len]; - for(int i = 0; i < len; i++) { - tmp[i] = solver.makeProd(a[i], (int)Math.pow(base,(len-i-1))).var(); + for (int i = 0; i < len; i++) { + tmp[i] = solver.makeProd(a[i], (int) Math.pow(base, (len - i - 1))).var(); } - solver.addConstraint( - solver.makeEquality(solver.makeSum(tmp).var(), num)); + solver.addConstraint(solver.makeEquality(solver.makeSum(tmp).var(), num)); } /** - * - * Solves the divisible by 9 through 1 problem. - * See http://www.hakank.org/google_or_tools/divisible_by_9_through_1.py - * + * Solves the divisible by 9 through 1 problem. See + * http://www.hakank.org/google_or_tools/divisible_by_9_through_1.py */ private static void solve(int base) { @@ -112,10 +92,10 @@ private static void solve(int base) { // // data // - int m = (int)Math.pow(base,(base-1)) - 1; + int m = (int) Math.pow(base, (base - 1)) - 1; int n = base - 1; - String[] digits_str = {"_","0","1","2","3","4","5","6","7","8","9"}; + String[] digits_str = {"_", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; System.out.println("base: " + base); @@ -129,7 +109,6 @@ private static void solve(int base) { // the numbers. t[0] contains the answe IntVar[] t = solver.makeIntVarArray(n, 0, m, "t"); - // // constraints // @@ -137,10 +116,10 @@ private static void solve(int base) { // Ensure the divisibility of base .. 1 IntVar zero = solver.makeIntConst(0); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { int mm = base - i - 1; IntVar[] tt = new IntVar[mm]; - for(int j = 0; j < mm; j++) { + for (int j = 0; j < mm; j++) { tt[j] = x[j]; } toNum(solver, tt, t[i], base); @@ -148,14 +127,10 @@ private static void solve(int base) { my_mod(solver, t[i], mm_const, zero); } - - // // search // - DecisionBuilder db = solver.makePhase(x, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db); // @@ -163,11 +138,11 @@ private static void solve(int base) { // while (solver.nextSolution()) { System.out.print("x: "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(x[i].value() + " "); } System.out.println("\nt: "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(t[i].value() + " "); } System.out.println(); @@ -175,12 +150,11 @@ private static void solve(int base) { if (base != 10) { System.out.print("Number base 10: " + t[0].value()); System.out.print(" Base " + base + ": "); - for(int i = 0; i < n; i++) { - System.out.print(digits_str[(int)x[i].value() + 1]); + for (int i = 0; i < n; i++) { + System.out.print(digits_str[(int) x[i].value() + 1]); } System.out.println("\n"); } - } solver.endSearch(); @@ -190,7 +164,6 @@ private static void solve(int base) { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/GolombRuler.java b/examples/contrib/GolombRuler.java index ac66701d4fe..8a5a2bfd886 100644 --- a/examples/contrib/GolombRuler.java +++ b/examples/contrib/GolombRuler.java @@ -1,31 +1,25 @@ /** - * Copyright (c) 1999-2011, Ecole des Mines de Nantes - * All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + * Copyright (c) 1999-2011, Ecole des Mines de Nantes All rights reserved. Redistribution and use in + * source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the Ecole des Mines de Nantes nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. + *

* Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * Neither the name of the Ecole des Mines + * de Nantes nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *

THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import com.google.ortools.constraintsolver.DecisionBuilder; -import com.google.ortools.constraintsolver.IntExpr; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.OptimizeVar; import com.google.ortools.constraintsolver.SearchMonitor; @@ -33,8 +27,7 @@ import com.google.ortools.constraintsolver.Solver; /** - * Golomb ruler problem - *
+ * Golomb ruler problem
* * @author Charles Prud'homme * @since 17/03/11 @@ -44,18 +37,11 @@ public class GolombRuler { System.loadLibrary("jniortools"); } - - /** - * Golomb Ruler Problem. - */ + /** Golomb Ruler Problem. */ private static void solve(int m) { Solver solver = new Solver("GR " + m); - IntVar[] ticks = - solver.makeIntVarArray(m, - 0, - ((m < 31) ? (1 << (m + 1)) - 1 : 9999), - "ticks"); + IntVar[] ticks = solver.makeIntVarArray(m, 0, ((m < 31) ? (1 << (m + 1)) - 1 : 9999), "ticks"); solver.addConstraint(solver.makeEquality(ticks[0], 0)); @@ -68,8 +54,7 @@ private static void solve(int m) { for (int k = 0, i = 0; i < m - 1; i++) { for (int j = i + 1; j < m; j++, k++) { diff[k] = solver.makeDifference(ticks[j], ticks[i]).var(); - solver.addConstraint( - solver.makeGreaterOrEqual(diff[k], (j - i) * (j - i + 1) / 2)); + solver.addConstraint(solver.makeGreaterOrEqual(diff[k], (j - i) * (j - i + 1) / 2)); } } @@ -80,10 +65,9 @@ private static void solve(int m) { solver.addConstraint(solver.makeLess(diff[0], diff[diff.length - 1])); } - OptimizeVar opt = solver.makeMinimize(ticks[m - 1], 1); - DecisionBuilder db = solver.makePhase(ticks, - solver.CHOOSE_MIN_SIZE_LOWEST_MIN, - solver.ASSIGN_MIN_VALUE); + OptimizeVar opt = solver.makeMinimize(ticks[m - 1], 1); + DecisionBuilder db = + solver.makePhase(ticks, solver.CHOOSE_MIN_SIZE_LOWEST_MIN, solver.ASSIGN_MIN_VALUE); SolutionCollector collector = solver.makeLastSolutionCollector(); collector.add(ticks); collector.addObjective(ticks[m - 1]); diff --git a/examples/contrib/Issue173.java b/examples/contrib/Issue173.java index 7a1c99d9221..50a4246cc1d 100644 --- a/examples/contrib/Issue173.java +++ b/examples/contrib/Issue173.java @@ -17,11 +17,9 @@ public static void breakit() { } private static void solveLP() { - MPSolver solver = new MPSolver( - "test", - MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); - MPVariable x = solver.makeNumVar(Double.NEGATIVE_INFINITY, - Double.POSITIVE_INFINITY, "x"); + MPSolver solver = + new MPSolver("test", MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); + MPVariable x = solver.makeNumVar(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, "x"); final MPObjective objective = solver.objective(); objective.setMaximization(); diff --git a/examples/contrib/KnapsackMIP.java b/examples/contrib/KnapsackMIP.java index 2c0fc56b207..77d8dc9d88b 100755 --- a/examples/contrib/KnapsackMIP.java +++ b/examples/contrib/KnapsackMIP.java @@ -3,96 +3,95 @@ * 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. - * + * * ************************************************************************ - * - * Each knapsack perceives a different weight for each item. Item values are - * the same across knapsacks. Optimizing constrains the count of each item such + * + * Each knapsack perceives a different weight for each item. Item values are + * the same across knapsacks. Optimizing constrains the count of each item such * that all knapsack capacities are respected, and their values are maximized. - * + * * This model was created by Hakan Kjellerstrand (hakank@gmail.com) - */ + */ import com.google.ortools.linearsolver.*; public class KnapsackMIP { - static { - System.loadLibrary("jniortools"); - } - - private static MPSolver createSolver (String solverType) { - try { - return new MPSolver("MIPDiet", - MPSolver.OptimizationProblemType.valueOf(solverType)); - } catch (java.lang.IllegalArgumentException e) { - System.err.println("Bad solver type: " + e); - return null; - } - } + static { + System.loadLibrary("jniortools"); + } - private static void solve(String solverType) { - MPSolver solver = createSolver(solverType); - - /** variables */ - int itemCount = 12; - int capacityCount = 7; - - int[] capacity = {18209, 7692, 1333, 924, 26638, 61188, 13360}; - int[] value = {96, 76, 56, 11, 86, 10, 66, 86, 83, 12, 9, 81}; - int[][] weights = { - {19, 1, 10, 1, 1, 14, 152, 11, 1, 1, 1, 1}, - {0, 4, 53, 0, 0, 80, 0, 4, 5, 0, 0, 0}, - {4, 660, 3, 0, 30, 0, 3, 0, 4, 90, 0, 0}, - {7, 0, 18, 6, 770, 330, 7, 0, 0, 6, 0, 0}, - {0, 20, 0, 4, 52, 3, 0, 0, 0, 5, 4, 0}, - {0, 0, 40, 70, 4, 63, 0, 0, 60, 0, 4, 0}, - {0, 32, 0, 0, 0, 5, 0, 3, 0, 660, 0, 9} - }; - - int maxCapacity = -1; - for (int c : capacity) { - if (c > maxCapacity) { - maxCapacity = c; - } - } - - MPVariable[] taken = solver.makeIntVarArray(itemCount, 0, maxCapacity); - - /** constraints */ - MPConstraint constraints[] = new MPConstraint[capacityCount]; - for (int i = 0; i < capacityCount; i ++) { - constraints[i] = solver.makeConstraint(0, capacity[i]); - for (int j = 0; j < itemCount; j ++) { - constraints[i].setCoefficient(taken[j], weights[i][j]); - } - } + private static MPSolver createSolver(String solverType) { + try { + return new MPSolver("MIPDiet", MPSolver.OptimizationProblemType.valueOf(solverType)); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + return null; + } + } - /** objective */ - MPObjective obj = solver.objective(); - obj.setMaximization(); - for (int i = 0; i < itemCount; i ++) { - obj.setCoefficient(taken[i], value[i]); - } - - solver.solve(); - - /** printing */ - System.out.println("Max cost: " + obj.value()); - System.out.print("Item quantities: "); - for (MPVariable var : taken) { - System.out.print((int) var.solutionValue() + " "); - } - } - - public static void main(String[] args) { - solve("CBC_MIXED_INTEGER_PROGRAMMING"); - } + private static void solve(String solverType) { + MPSolver solver = createSolver(solverType); + + /** variables */ + int itemCount = 12; + int capacityCount = 7; + + int[] capacity = {18209, 7692, 1333, 924, 26638, 61188, 13360}; + int[] value = {96, 76, 56, 11, 86, 10, 66, 86, 83, 12, 9, 81}; + int[][] weights = { + {19, 1, 10, 1, 1, 14, 152, 11, 1, 1, 1, 1}, + {0, 4, 53, 0, 0, 80, 0, 4, 5, 0, 0, 0}, + {4, 660, 3, 0, 30, 0, 3, 0, 4, 90, 0, 0}, + {7, 0, 18, 6, 770, 330, 7, 0, 0, 6, 0, 0}, + {0, 20, 0, 4, 52, 3, 0, 0, 0, 5, 4, 0}, + {0, 0, 40, 70, 4, 63, 0, 0, 60, 0, 4, 0}, + {0, 32, 0, 0, 0, 5, 0, 3, 0, 660, 0, 9} + }; + + int maxCapacity = -1; + for (int c : capacity) { + if (c > maxCapacity) { + maxCapacity = c; + } + } + + MPVariable[] taken = solver.makeIntVarArray(itemCount, 0, maxCapacity); + + /** constraints */ + MPConstraint constraints[] = new MPConstraint[capacityCount]; + for (int i = 0; i < capacityCount; i++) { + constraints[i] = solver.makeConstraint(0, capacity[i]); + for (int j = 0; j < itemCount; j++) { + constraints[i].setCoefficient(taken[j], weights[i][j]); + } + } + + /** objective */ + MPObjective obj = solver.objective(); + obj.setMaximization(); + for (int i = 0; i < itemCount; i++) { + obj.setCoefficient(taken[i], value[i]); + } + + solver.solve(); + + /** printing */ + System.out.println("Max cost: " + obj.value()); + System.out.print("Item quantities: "); + for (MPVariable var : taken) { + System.out.print((int) var.solutionValue() + " "); + } + } + + public static void main(String[] args) { + solve("CBC_MIXED_INTEGER_PROGRAMMING"); + } } diff --git a/examples/contrib/LeastDiff.java b/examples/contrib/LeastDiff.java index baeba9fe4a4..814f53bed56 100644 --- a/examples/contrib/LeastDiff.java +++ b/examples/contrib/LeastDiff.java @@ -11,11 +11,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +import com.google.ortools.constraintsolver.*; import java.io.*; -import java.util.*; import java.text.*; - -import com.google.ortools.constraintsolver.*; +import java.util.*; public class LeastDiff { @@ -23,13 +22,7 @@ public class LeastDiff { System.loadLibrary("jniortools"); } - - /** - * - * Solves the Least Diff problem. - * See http://www.hakank.org/google_or_tools/least_diff.py - * - */ + /** Solves the Least Diff problem. See http://www.hakank.org/google_or_tools/least_diff.py */ private static void solve() { final int base = 10; @@ -50,15 +43,15 @@ private static void solve() { IntVar i = solver.makeIntVar(0, base - 1, "i"); IntVar j = solver.makeIntVar(0, base - 1, "j"); - IntVar[] all = {a,b,c,d,e,f,g,h,i,j}; + IntVar[] all = {a, b, c, d, e, f, g, h, i, j}; // // Constraints // int[] coeffs = {10000, 1000, 100, 10, 1}; - IntVar x = solver.makeScalProd(new IntVar[]{a,b,c,d,e}, coeffs).var(); + IntVar x = solver.makeScalProd(new IntVar[] {a, b, c, d, e}, coeffs).var(); x.setName("x"); - IntVar y = solver.makeScalProd(new IntVar[]{f,g,h,i,j}, coeffs).var(); + IntVar y = solver.makeScalProd(new IntVar[] {f, g, h, i, j}, coeffs).var(); y.setName("y"); // a > 0 @@ -80,13 +73,10 @@ private static void solve() { // // Search // - DecisionBuilder db = solver.makePhase(all, - solver.CHOOSE_PATH, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = solver.makePhase(all, solver.CHOOSE_PATH, solver.ASSIGN_MIN_VALUE); solver.newSearch(db, obj); while (solver.nextSolution()) { - System.out.println("" + x.value() + " - " + - y.value() + " = " + diff.value()); + System.out.println("" + x.value() + " - " + y.value() + " = " + diff.value()); } solver.endSearch(); @@ -96,7 +86,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/MagicSquare.java b/examples/contrib/MagicSquare.java index 583df93a639..34da9935f63 100644 --- a/examples/contrib/MagicSquare.java +++ b/examples/contrib/MagicSquare.java @@ -11,13 +11,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class MagicSquare { @@ -25,13 +24,7 @@ public class MagicSquare { System.loadLibrary("jniortools"); } - - /** - * - * Solves the Magic Square problem. - * See http://www.hakank.org/google_or_tools/magic_square.py - * - */ + /** Solves the Magic Square problem. See http://www.hakank.org/google_or_tools/magic_square.py */ private static void solve(int n, int num) { Solver solver = new Solver("MagicSquare"); @@ -44,7 +37,7 @@ private static void solve(int n, int num) { IntVar[][] x = new IntVar[n][n]; // for the branching - IntVar[] x_flat = new IntVar[n*n]; + IntVar[] x_flat = new IntVar[n * n]; // // constraints @@ -55,9 +48,9 @@ private static void solve(int n, int num) { IntVar[] diag1 = new IntVar[n]; IntVar[] diag2 = new IntVar[n]; - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { IntVar[] row = new IntVar[n]; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { x[i][j] = solver.makeIntVar(1, n * n, "x[" + i + "," + j + "]"); x_flat[i * n + j] = x[i][j]; row[j] = x[i][j]; @@ -73,9 +66,9 @@ private static void solve(int n, int num) { solver.addConstraint(solver.makeSumEquality(diag2, s)); // sum columns to s - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { IntVar[] col = new IntVar[n]; - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { col[i] = x[i][j]; } solver.addConstraint(solver.makeSumEquality(col, s)); @@ -87,18 +80,16 @@ private static void solve(int n, int num) { // symmetry breaking: upper left is 1 // solver.addConstraint(solver.makeEquality(x[0][0], 1)); - // // Solve // - DecisionBuilder db = solver.makePhase(x_flat, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_CENTER_VALUE); + DecisionBuilder db = + solver.makePhase(x_flat, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_CENTER_VALUE); solver.newSearch(db); int c = 0; while (solver.nextSolution()) { - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { System.out.print(x[i][j].value() + " "); } System.out.println(); @@ -117,7 +108,6 @@ private static void solve(int n, int num) { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/Map.java b/examples/contrib/Map.java index e9c18ce54c0..61aa1ea3b82 100644 --- a/examples/contrib/Map.java +++ b/examples/contrib/Map.java @@ -11,14 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - +import com.google.ortools.constraintsolver.*; import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; -import com.google.ortools.constraintsolver.*; +import java.io.*; +import java.text.*; +import java.util.*; public class Map { @@ -26,13 +25,7 @@ public class Map { System.loadLibrary("jniortools"); } - - /** - * - * Solves a simple map coloring problem. - * See http://www.hakank.org/google_or_tools/map.py - * - */ + /** Solves a simple map coloring problem. See http://www.hakank.org/google_or_tools/map.py */ private static void solve() { Solver solver = new Solver("Map"); @@ -40,12 +33,12 @@ private static void solve() { // // data // - int Belgium = 0; - int Denmark = 1; - int France = 2; - int Germany = 3; + int Belgium = 0; + int Denmark = 1; + int France = 2; + int Germany = 3; int Netherlands = 4; - int Luxembourg = 5; + int Luxembourg = 5; int n = 6; int max_num_colors = 4; @@ -58,24 +51,15 @@ private static void solve() { // // Constraints // - solver.addConstraint(solver.makeNonEquality(color[France], - color[Belgium])); - solver.addConstraint(solver.makeNonEquality(color[France], - color[Luxembourg])); - solver.addConstraint(solver.makeNonEquality(color[France], - color[Germany])); - solver.addConstraint(solver.makeNonEquality(color[Luxembourg], - color[Germany])); - solver.addConstraint(solver.makeNonEquality(color[Luxembourg], - color[Belgium])); - solver.addConstraint(solver.makeNonEquality(color[Belgium], - color[Netherlands])); - solver.addConstraint(solver.makeNonEquality(color[Belgium], - color[Germany])); - solver.addConstraint(solver.makeNonEquality(color[Germany], - color[Netherlands])); - solver.addConstraint(solver.makeNonEquality(color[Germany], - color[Denmark])); + solver.addConstraint(solver.makeNonEquality(color[France], color[Belgium])); + solver.addConstraint(solver.makeNonEquality(color[France], color[Luxembourg])); + solver.addConstraint(solver.makeNonEquality(color[France], color[Germany])); + solver.addConstraint(solver.makeNonEquality(color[Luxembourg], color[Germany])); + solver.addConstraint(solver.makeNonEquality(color[Luxembourg], color[Belgium])); + solver.addConstraint(solver.makeNonEquality(color[Belgium], color[Netherlands])); + solver.addConstraint(solver.makeNonEquality(color[Belgium], color[Germany])); + solver.addConstraint(solver.makeNonEquality(color[Germany], color[Netherlands])); + solver.addConstraint(solver.makeNonEquality(color[Germany], color[Denmark])); // Symmetry breaking solver.addConstraint(solver.makeEquality(color[Belgium], 1)); @@ -83,14 +67,13 @@ private static void solve() { // // Search // - DecisionBuilder db = solver.makePhase(color, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = + solver.makePhase(color, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); while (solver.nextSolution()) { System.out.print("Colors: "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(color[i].value() + " "); } System.out.println(); @@ -103,7 +86,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/Map2.java b/examples/contrib/Map2.java index c747eabd429..d201ede07fc 100644 --- a/examples/contrib/Map2.java +++ b/examples/contrib/Map2.java @@ -11,14 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - +import com.google.ortools.constraintsolver.*; import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; -import com.google.ortools.constraintsolver.*; +import java.io.*; +import java.text.*; +import java.util.*; public class Map2 { @@ -26,12 +25,8 @@ public class Map2 { System.loadLibrary("jniortools"); } - /** - * - * Solves a simple map coloring problem, take II. - * See http://www.hakank.org/google_or_tools/map.py - * + * Solves a simple map coloring problem, take II. See http://www.hakank.org/google_or_tools/map.py */ private static void solve() { @@ -40,26 +35,27 @@ private static void solve() { // // data // - int Belgium = 0; - int Denmark = 1; - int France = 2; - int Germany = 3; + int Belgium = 0; + int Denmark = 1; + int France = 2; + int Germany = 3; int Netherlands = 4; - int Luxembourg = 5; + int Luxembourg = 5; int n = 6; int max_num_colors = 4; - int[][] neighbours = {{France, Belgium}, - {France, Luxembourg}, - {France, Germany}, - {Luxembourg, Germany}, - {Luxembourg, Belgium}, - {Belgium, Netherlands}, - {Belgium, Germany}, - {Germany, Netherlands}, - {Germany, Denmark}}; - + int[][] neighbours = { + {France, Belgium}, + {France, Luxembourg}, + {France, Germany}, + {Luxembourg, Germany}, + {Luxembourg, Belgium}, + {Belgium, Netherlands}, + {Belgium, Germany}, + {Germany, Netherlands}, + {Germany, Denmark} + }; // // Variables @@ -69,27 +65,24 @@ private static void solve() { // // Constraints // - for(int i = 0; i < neighbours.length; i++) { + for (int i = 0; i < neighbours.length; i++) { solver.addConstraint( - solver.makeNonEquality(color[neighbours[i][0]], - color[neighbours[i][1]])); + solver.makeNonEquality(color[neighbours[i][0]], color[neighbours[i][1]])); } // Symmetry breaking solver.addConstraint(solver.makeEquality(color[Belgium], 1)); - // // Search // - DecisionBuilder db = solver.makePhase(color, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = + solver.makePhase(color, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); while (solver.nextSolution()) { System.out.print("Colors: "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(color[i].value() + " "); } System.out.println(); @@ -102,7 +95,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/Minesweeper.java b/examples/contrib/Minesweeper.java index 7ac67780364..b4528436581 100644 --- a/examples/contrib/Minesweeper.java +++ b/examples/contrib/Minesweeper.java @@ -11,14 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - +import com.google.ortools.constraintsolver.*; import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; -import com.google.ortools.constraintsolver.*; +import java.io.*; +import java.text.*; +import java.util.*; public class Minesweeper { @@ -34,27 +33,23 @@ public class Minesweeper { // static int default_r = 8; static int default_c = 8; - static int[][] default_game = {{2, 3, X, 2, 2, X, 2, 1}, - {X, X, 4, X, X, 4, X, 2}, - {X, X, X, X, X, X, 4, X}, - {X, 5, X, 6, X, X, X, 2}, - {2, X, X, X, 5, 5, X, 2}, - {1, 3, 4, X, X, X, 4, X}, - {0, 1, X, 4, X, X, X, 3}, - {0, 1, 2, X, 2, 3, X, 2}}; + static int[][] default_game = { + {2, 3, X, 2, 2, X, 2, 1}, + {X, X, 4, X, X, 4, X, 2}, + {X, X, X, X, X, X, 4, X}, + {X, 5, X, 6, X, X, X, 2}, + {2, X, X, X, 5, 5, X, 2}, + {1, 3, 4, X, X, X, 4, X}, + {0, 1, X, 4, X, X, X, 3}, + {0, 1, 2, X, 2, 3, X, 2} + }; // for the actual problem static int r; static int c; static int[][] game; - - /** - * - * Solves the Minesweeper problems. - * See http://www.hakank.org/google_or_tools/minesweeper.py - * - */ + /** Solves the Minesweeper problems. See http://www.hakank.org/google_or_tools/minesweeper.py */ private static void solve() { Solver solver = new Solver("Minesweeper"); @@ -65,8 +60,8 @@ private static void solve() { // data // System.out.println("Problem:"); - for(int i = 0; i < r; i++) { - for(int j = 0; j < c; j++) { + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { if (game[i][j] > X) { System.out.print(game[i][j] + " "); } else { @@ -77,14 +72,13 @@ private static void solve() { } System.out.println(); - // // Variables // - IntVar[][] mines = new IntVar[r][c]; + IntVar[][] mines = new IntVar[r][c]; IntVar[] mines_flat = new IntVar[r * c]; // for branching - for(int i = 0; i < r; i++) { - for(int j = 0; j < c; j++) { + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { mines[i][j] = solver.makeIntVar(0, 1, "mines[" + i + ", " + j + "]"); mines_flat[i * c + j] = mines[i][j]; } @@ -93,34 +87,28 @@ private static void solve() { // // Constraints // - for(int i = 0; i < r; i++) { - for(int j = 0; j < c; j++) { + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { if (game[i][j] >= 0) { - solver.addConstraint( - solver.makeEquality(mines[i][j], 0)); + solver.addConstraint(solver.makeEquality(mines[i][j], 0)); // this cell is the sum of all its neighbours ArrayList neighbours = new ArrayList(); - for(int a: S) { - for(int b: S) { - if (i + a >= 0 && - j + b >= 0 && - i + a < r && - j + b < c) { + for (int a : S) { + for (int b : S) { + if (i + a >= 0 && j + b >= 0 && i + a < r && j + b < c) { neighbours.add(mines[i + a][j + b]); } } } solver.addConstraint( - solver.makeSumEquality( - neighbours.toArray(new IntVar[1]), game[i][j])); + solver.makeSumEquality(neighbours.toArray(new IntVar[1]), game[i][j])); } if (game[i][j] > X) { // This cell cannot be a mine since it // has some value assigned to it - solver.addConstraint( - solver.makeEquality(mines[i][j], 0)); + solver.addConstraint(solver.makeEquality(mines[i][j], 0)); } } } @@ -128,23 +116,21 @@ private static void solve() { // // Search // - DecisionBuilder db = solver.makePhase(mines_flat, - solver.INT_VAR_SIMPLE, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = + solver.makePhase(mines_flat, solver.INT_VAR_SIMPLE, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); int sol = 0; while (solver.nextSolution()) { sol++; System.out.println("Solution #" + sol + ":"); - for(int i = 0; i < r; i++) { - for(int j = 0; j < c; j++) { + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { System.out.print(mines[i][j].value() + " "); } System.out.println(); } System.out.println(); - } solver.endSearch(); @@ -154,35 +140,16 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } - /** + * Reads a minesweeper file. File format: # a comment which is ignored % a comment which also is + * ignored number of rows number of columns < row number of neighbours lines... > * - * Reads a minesweeper file. - * File format: - * # a comment which is ignored - * % a comment which also is ignored - * number of rows - * number of columns - * < - * row number of neighbours lines... - * > - * - * 0..8 means number of neighbours, "." mean unknown (may be a mine) - * - * Example (from minesweeper0.txt) - * # Problem from Gecode/examples/minesweeper.cc problem 0 - * 6 - * 6 - * ..2.3. - * 2..... - * ..24.3 - * 1.34.. - * .....3 - * .3.3.. + *

0..8 means number of neighbours, "." mean unknown (may be a mine) * + *

Example (from minesweeper0.txt) # Problem from Gecode/examples/minesweeper.cc problem 0 6 6 + * ..2.3. 2..... ..24.3 1.34.. .....3 .3.3.. */ private static void readFile(String file) { @@ -198,7 +165,7 @@ private static void readFile(String file) { str = str.trim(); // ignore comments - if(str.startsWith("#") || str.startsWith("%")) { + if (str.startsWith("#") || str.startsWith("%")) { continue; } @@ -211,18 +178,17 @@ private static void readFile(String file) { } else { // the problem matrix String row[] = str.split(""); - for(int j = 1; j <= c; j++) { + for (int j = 1; j <= c; j++) { String s = row[j]; if (s.equals(".")) { - game[lineCount-2][j-1] = -1; + game[lineCount - 2][j - 1] = -1; } else { - game[lineCount-2][j-1] = Integer.parseInt(s); + game[lineCount - 2][j - 1] = Integer.parseInt(s); } } } lineCount++; - } // end while inr.close(); @@ -230,11 +196,8 @@ private static void readFile(String file) { } catch (IOException e) { System.out.println(e); } - } // end readFile - - public static void main(String[] args) throws Exception { String file = ""; diff --git a/examples/contrib/MultiThreadTest.java b/examples/contrib/MultiThreadTest.java index 41c0eb17a67..a4d491cae80 100644 --- a/examples/contrib/MultiThreadTest.java +++ b/examples/contrib/MultiThreadTest.java @@ -1,126 +1,120 @@ -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.Arrays; - -import com.google.ortools.linearsolver.MPConstraint; -import com.google.ortools.linearsolver.MPObjective; -import com.google.ortools.linearsolver.MPSolver; -import com.google.ortools.linearsolver.MPSolver.OptimizationProblemType; -import com.google.ortools.linearsolver.MPSolver.ResultStatus; -import com.google.ortools.linearsolver.MPVariable; - - -public class MultiThreadTest { - - static { System.loadLibrary("jniortools"); } - - private final static boolean verboseOutput = false; // To enable Cbc logging - - - public static void main(String[] args) throws Exception { - launchProtocol(10, 8, true); - System.out.println("Cbc multi thread test successful"); - return; - } - - public static void launchProtocol(int wholeLoopAttmpts, int threadPoolSize, boolean runInParallel) throws Exception { - - for (int noAttmpt = 0; noAttmpt < wholeLoopAttmpts; noAttmpt++) { - - System.out.println(String.format("Attempt %d", noAttmpt)); - - int maxThreads = threadPoolSize; - - List threadList = new ArrayList(); - - for (int i = 0; i < maxThreads; i++) { - SolverThread thread = new SolverThread(); - threadList.add(thread); - } - - ExecutorService executor = Executors.newFixedThreadPool(maxThreads); - - if (runInParallel) { - System.out.println("Launching thread pool"); - executor.invokeAll(threadList); - for( SolverThread thread : threadList ) { - System.out.println(thread.getStatusSolver().toString()); - } - } else { - - for (SolverThread thread : threadList) { - System.out.println("Launching single thread"); - executor.invokeAll( Arrays.asList(thread) ); - System.out.println(thread.getStatusSolver().toString()); - } - } - - System.out.println("Attempt finalized!"); - executor.shutdown(); - - } - - System.out.println("Now exiting multi thread execution"); - - } - - private static MPSolver makeProblem() { - - MPSolver solver = new MPSolver(UUID.randomUUID().toString(), OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); - - double infinity = MPSolver.infinity(); - - // x1 and x2 are integer non-negative variables. - MPVariable x1 = solver.makeIntVar(0.0, infinity, "x1"); - MPVariable x2 = solver.makeIntVar(0.0, infinity, "x2"); - - // Minimize x1 + 2 * x2. - MPObjective objective = solver.objective(); - objective.setCoefficient(x1, 1); - objective.setCoefficient(x2, 2); - - // 2 * x2 + 3 * x1 >= 17. - MPConstraint ct = solver.makeConstraint(17, infinity); - ct.setCoefficient(x1, 3); - ct.setCoefficient(x2, 2); - - if (verboseOutput) { - solver.enableOutput(); - } - - return solver; - - } - - private final static class SolverThread implements Callable { - - private MPSolver.ResultStatus statusSolver; - - public SolverThread() { - - } - - @Override - public ResultStatus call() throws Exception { - MPSolver solver = makeProblem(); - statusSolver = solver.solve(); - - // Check that the problem has an optimal solution. - if ( MPSolver.ResultStatus.OPTIMAL.equals(statusSolver) ) { - throw new RuntimeException("Non OPTIMAL status after solve."); - } - return statusSolver; - - } - - public MPSolver.ResultStatus getStatusSolver() { - return statusSolver; - } - - } - -} +import com.google.ortools.linearsolver.MPConstraint; +import com.google.ortools.linearsolver.MPObjective; +import com.google.ortools.linearsolver.MPSolver; +import com.google.ortools.linearsolver.MPSolver.OptimizationProblemType; +import com.google.ortools.linearsolver.MPSolver.ResultStatus; +import com.google.ortools.linearsolver.MPVariable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class MultiThreadTest { + + static { + System.loadLibrary("jniortools"); + } + + private static final boolean verboseOutput = false; // To enable Cbc logging + + public static void main(String[] args) throws Exception { + launchProtocol(10, 8, true); + System.out.println("Cbc multi thread test successful"); + return; + } + + public static void launchProtocol(int wholeLoopAttmpts, int threadPoolSize, boolean runInParallel) + throws Exception { + + for (int noAttmpt = 0; noAttmpt < wholeLoopAttmpts; noAttmpt++) { + + System.out.println(String.format("Attempt %d", noAttmpt)); + + int maxThreads = threadPoolSize; + + List threadList = new ArrayList(); + + for (int i = 0; i < maxThreads; i++) { + SolverThread thread = new SolverThread(); + threadList.add(thread); + } + + ExecutorService executor = Executors.newFixedThreadPool(maxThreads); + + if (runInParallel) { + System.out.println("Launching thread pool"); + executor.invokeAll(threadList); + for (SolverThread thread : threadList) { + System.out.println(thread.getStatusSolver().toString()); + } + } else { + + for (SolverThread thread : threadList) { + System.out.println("Launching single thread"); + executor.invokeAll(Arrays.asList(thread)); + System.out.println(thread.getStatusSolver().toString()); + } + } + + System.out.println("Attempt finalized!"); + executor.shutdown(); + } + + System.out.println("Now exiting multi thread execution"); + } + + private static MPSolver makeProblem() { + + MPSolver solver = + new MPSolver( + UUID.randomUUID().toString(), OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); + + double infinity = MPSolver.infinity(); + + // x1 and x2 are integer non-negative variables. + MPVariable x1 = solver.makeIntVar(0.0, infinity, "x1"); + MPVariable x2 = solver.makeIntVar(0.0, infinity, "x2"); + + // Minimize x1 + 2 * x2. + MPObjective objective = solver.objective(); + objective.setCoefficient(x1, 1); + objective.setCoefficient(x2, 2); + + // 2 * x2 + 3 * x1 >= 17. + MPConstraint ct = solver.makeConstraint(17, infinity); + ct.setCoefficient(x1, 3); + ct.setCoefficient(x2, 2); + + if (verboseOutput) { + solver.enableOutput(); + } + + return solver; + } + + private static final class SolverThread implements Callable { + + private MPSolver.ResultStatus statusSolver; + + public SolverThread() {} + + @Override + public ResultStatus call() throws Exception { + MPSolver solver = makeProblem(); + statusSolver = solver.solve(); + + // Check that the problem has an optimal solution. + if (MPSolver.ResultStatus.OPTIMAL.equals(statusSolver)) { + throw new RuntimeException("Non OPTIMAL status after solve."); + } + return statusSolver; + } + + public MPSolver.ResultStatus getStatusSolver() { + return statusSolver; + } + } +} diff --git a/examples/contrib/NQueens.java b/examples/contrib/NQueens.java index 7d7a0f11aa7..ba32674ed6c 100644 --- a/examples/contrib/NQueens.java +++ b/examples/contrib/NQueens.java @@ -11,110 +11,97 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class NQueens { - static { - System.loadLibrary("jniortools"); - } + static { + System.loadLibrary("jniortools"); + } + /** Solves the N Queens problem. See http://www.hakank.org/google_or_tools/nqueens2.py */ + private static void solve(int n, int num, int print) { - /** - * - * Solves the N Queens problem. - * See http://www.hakank.org/google_or_tools/nqueens2.py - * - */ - private static void solve(int n, int num, int print) { - - Solver solver = new Solver("NQueens"); - - System.out.println("n: " + n); - - // - // variables - // - IntVar[] q = solver.makeIntVarArray(n, 0, n-1, "q"); - - // - // constraints - // - solver.addConstraint(solver.makeAllDifferent(q)); - - IntVar b = solver.makeIntVar(1, 1, "b"); - IntVar[] q1 = new IntVar[n]; - IntVar[] q2 = new IntVar[n]; - for(int i = 0; i < n; i++) { - for(int j = 0; j < i; j++) { - // // q[i]+i != q[j]+j - solver.addConstraint( - solver.makeNonEquality( - solver.makeSum(q[i],i).var(), - solver.makeSum(q[j],j).var())); - - // q[i]-i != q[j]-j - solver.addConstraint( - solver.makeNonEquality(solver.makeSum(q[i],-i).var(), - solver.makeSum(q[j],-j).var())); - } - } + Solver solver = new Solver("NQueens"); - // - // Solve - // - DecisionBuilder db = solver.makePhase(q, - solver.CHOOSE_MIN_SIZE_LOWEST_MAX, - solver.ASSIGN_CENTER_VALUE); - solver.newSearch(db); - int c = 0; - while (solver.nextSolution()) { - if (print != 0) { - for(int i = 0; i < n; i++) { - System.out.print(q[i].value() + " "); - } - System.out.println(); - } - c++; - if (num > 0 && c >= num) { - break; - } - } - solver.endSearch(); + System.out.println("n: " + n); - // Statistics - System.out.println(); - System.out.println("Solutions: " + solver.solutions()); - System.out.println("Failures: " + solver.failures()); - System.out.println("Branches: " + solver.branches()); - System.out.println("Wall time: " + solver.wallTime() + "ms"); + // + // variables + // + IntVar[] q = solver.makeIntVarArray(n, 0, n - 1, "q"); - } - - public static void main(String[] args) throws Exception { - int n = 8; - int num = 0; - int print = 1; + // + // constraints + // + solver.addConstraint(solver.makeAllDifferent(q)); - if (args.length > 0) { - n = Integer.parseInt(args[0]); - } + IntVar b = solver.makeIntVar(1, 1, "b"); + IntVar[] q1 = new IntVar[n]; + IntVar[] q2 = new IntVar[n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + // // q[i]+i != q[j]+j + solver.addConstraint( + solver.makeNonEquality(solver.makeSum(q[i], i).var(), solver.makeSum(q[j], j).var())); - if (args.length > 1) { - num = Integer.parseInt(args[1]); - } + // q[i]-i != q[j]-j + solver.addConstraint( + solver.makeNonEquality(solver.makeSum(q[i], -i).var(), solver.makeSum(q[j], -j).var())); + } + } - if (args.length > 2) { - print = Integer.parseInt(args[2]); + // + // Solve + // + DecisionBuilder db = + solver.makePhase(q, solver.CHOOSE_MIN_SIZE_LOWEST_MAX, solver.ASSIGN_CENTER_VALUE); + solver.newSearch(db); + int c = 0; + while (solver.nextSolution()) { + if (print != 0) { + for (int i = 0; i < n; i++) { + System.out.print(q[i].value() + " "); } + System.out.println(); + } + c++; + if (num > 0 && c >= num) { + break; + } + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + int n = 8; + int num = 0; + int print = 1; + + if (args.length > 0) { + n = Integer.parseInt(args[0]); + } + if (args.length > 1) { + num = Integer.parseInt(args[1]); + } - NQueens.solve(n, num, print); + if (args.length > 2) { + print = Integer.parseInt(args[2]); } + + NQueens.solve(n, num, print); + } } diff --git a/examples/contrib/NQueens2.java b/examples/contrib/NQueens2.java index 45c72c0fc94..0a30246ad5e 100644 --- a/examples/contrib/NQueens2.java +++ b/examples/contrib/NQueens2.java @@ -11,13 +11,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class NQueens2 { @@ -25,13 +24,7 @@ public class NQueens2 { System.loadLibrary("jniortools"); } - - /** - * - * Solves the N Queens problem. - * See http://www.hakank.org/google_or_tools/nqueens2.py - * - */ + /** Solves the N Queens problem. See http://www.hakank.org/google_or_tools/nqueens2.py */ private static void solve(int n, int num, int print) { Solver solver = new Solver("NQueens"); @@ -41,7 +34,7 @@ private static void solve(int n, int num, int print) { // // variables // - IntVar[] q = solver.makeIntVarArray(n, 0, n-1, "q"); + IntVar[] q = solver.makeIntVarArray(n, 0, n - 1, "q"); // // constraints @@ -50,7 +43,7 @@ private static void solve(int n, int num, int print) { IntVar[] q1 = new IntVar[n]; IntVar[] q2 = new IntVar[n]; - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { q1[i] = solver.makeSum(q[i], i).var(); q2[i] = solver.makeSum(q[i], -i).var(); } @@ -60,14 +53,13 @@ private static void solve(int n, int num, int print) { // // Solve // - DecisionBuilder db = solver.makePhase(q, - solver.CHOOSE_MIN_SIZE_LOWEST_MAX, - solver.ASSIGN_CENTER_VALUE); + DecisionBuilder db = + solver.makePhase(q, solver.CHOOSE_MIN_SIZE_LOWEST_MAX, solver.ASSIGN_CENTER_VALUE); solver.newSearch(db); int c = 0; while (solver.nextSolution()) { if (print != 0) { - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(q[i].value() + " "); } System.out.println(); @@ -104,7 +96,6 @@ public static void main(String[] args) throws Exception { print = Integer.parseInt(args[2]); } - NQueens2.solve(n, num, print); } } diff --git a/examples/contrib/Partition.java b/examples/contrib/Partition.java index 679f7ab0b13..2fc13be8ed1 100644 --- a/examples/contrib/Partition.java +++ b/examples/contrib/Partition.java @@ -1,37 +1,30 @@ /** - * Copyright (c) 1999-2011, Ecole des Mines de Nantes - * All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + * Copyright (c) 1999-2011, Ecole des Mines de Nantes All rights reserved. Redistribution and use in + * source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the Ecole des Mines de Nantes nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. + *

* Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * Neither the name of the Ecole des Mines + * de Nantes nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *

THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import com.google.ortools.constraintsolver.*; /** - * Partition n numbers into two groups, so that - * - the sum of the first group equals the sum of the second, - * - and the sum of the squares of the first group equals the sum of - * the squares of the second - *
+ * Partition n numbers into two groups, so that - the sum of the first group equals the sum of the + * second, - and the sum of the squares of the first group equals the sum of the squares of the + * second
* * @author Charles Prud'homme * @since 18/03/11 @@ -42,10 +35,7 @@ public class Partition { System.loadLibrary("jniortools"); } - - /** - * Partition Problem. - */ + /** Partition Problem. */ private static void solve(int m) { Solver solver = new Solver("Partition " + m); @@ -87,14 +77,10 @@ private static void solve(int m) { } solver.addConstraint(solver.makeScalProdEquality(sxy, coeffs, 0)); - solver.addConstraint( - solver.makeSumEquality(x, 2 * m * (2 * m + 1) / 4)); - solver.addConstraint( - solver.makeSumEquality(y, 2 * m * (2 * m + 1) / 4)); - solver.addConstraint( - solver.makeSumEquality(sx, 2 * m * (2 * m + 1) * (4 * m + 1) / 12)); - solver.addConstraint( - solver.makeSumEquality(sy, 2 * m * (2 * m + 1) * (4 * m + 1) / 12)); + solver.addConstraint(solver.makeSumEquality(x, 2 * m * (2 * m + 1) / 4)); + solver.addConstraint(solver.makeSumEquality(y, 2 * m * (2 * m + 1) / 4)); + solver.addConstraint(solver.makeSumEquality(sx, 2 * m * (2 * m + 1) * (4 * m + 1) / 12)); + solver.addConstraint(solver.makeSumEquality(sy, 2 * m * (2 * m + 1) * (4 * m + 1) / 12)); DecisionBuilder db = solver.makeDefaultPhase(xy); SolutionCollector collector = solver.makeFirstSolutionCollector(); @@ -108,7 +94,7 @@ private static void solve(int m) { } System.out.printf("\n"); for (int i = 0; i < m; ++i) { - System.out.print("[" + collector.value(0, xy[m+i]) + "] "); + System.out.print("[" + collector.value(0, xy[m + i]) + "] "); } System.out.println(); } @@ -116,6 +102,4 @@ private static void solve(int m) { public static void main(String[] args) throws Exception { Partition.solve(32); } - - } diff --git a/examples/contrib/QuasigroupCompletion.java b/examples/contrib/QuasigroupCompletion.java index 0728ab072a1..c906bca9032 100644 --- a/examples/contrib/QuasigroupCompletion.java +++ b/examples/contrib/QuasigroupCompletion.java @@ -10,14 +10,13 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - +import com.google.ortools.constraintsolver.*; import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; -import com.google.ortools.constraintsolver.*; +import java.io.*; +import java.text.*; +import java.util.*; public class QuasigroupCompletion { @@ -41,21 +40,21 @@ public class QuasigroupCompletion { * 3 2 5 4 1 */ static int default_n = 5; - static int[][] default_problem = {{1, X, X, X, 4}, - {X, 5, X, X, X}, - {4, X, X, 2, X}, - {X, 4, X, X, X}, - {X, X, 5, X, 1}}; - + static int[][] default_problem = { + {1, X, X, X, 4}, + {X, 5, X, X, X}, + {4, X, X, 2, X}, + {X, 4, X, X, X}, + {X, X, 5, X, 1} + }; // for the actual problem static int n; static int[][] problem; - /** - * Solves the Quasigroup Completion problem. - * See http://www.hakank.org/google_or_tools/quasigroup_completion.py + * Solves the Quasigroup Completion problem. See + * http://www.hakank.org/google_or_tools/quasigroup_completion.py */ private static void solve() { @@ -65,22 +64,21 @@ private static void solve() { // data // System.out.println("Problem:"); - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { System.out.print(problem[i][j] + " "); } System.out.println(); } System.out.println(); - // // Variables // - IntVar[][] x = new IntVar[n][n]; + IntVar[][] x = new IntVar[n][n]; IntVar[] x_flat = new IntVar[n * n]; // for branching - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { x[i][j] = solver.makeIntVar(1, n, "x[" + i + "," + j + "]"); x_flat[i * n + j] = x[i][j]; } @@ -89,8 +87,8 @@ private static void solve() { // // Constraints // - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { if (problem[i][j] > X) { solver.addConstraint(solver.makeEquality(x[i][j], problem[i][j])); } @@ -102,46 +100,40 @@ private static void solve() { // // rows - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { IntVar[] row = new IntVar[n]; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { row[j] = x[i][j]; } - solver.addConstraint( - solver.makeAllDifferent(row)); - + solver.addConstraint(solver.makeAllDifferent(row)); } // columns - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { IntVar[] col = new IntVar[n]; - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { col[i] = x[i][j]; } solver.addConstraint(solver.makeAllDifferent(col)); } - // // Search // - DecisionBuilder db = solver.makePhase(x_flat, - solver.INT_VAR_SIMPLE, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = solver.makePhase(x_flat, solver.INT_VAR_SIMPLE, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); int sol = 0; while (solver.nextSolution()) { sol++; System.out.println("Solution #" + sol + ":"); - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { System.out.print(x[i][j].value() + " "); } System.out.println(); } System.out.println(); - } solver.endSearch(); @@ -151,29 +143,15 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } /** - * Reads a Quasigroup completion file. - * File format: - * # a comment which is ignored - * % a comment which also is ignored - * number of rows (n) - * < - * row number of space separated entries - * > - * - * "." or "0" means unknown, integer 1..n means known value + * Reads a Quasigroup completion file. File format: # a comment which is ignored % a comment which + * also is ignored number of rows (n) < row number of space separated entries > * - * Example - * 5 - * 1 . . . 4 - * . 5 . . . - * 4 . . 2 . - * . 4 . . . - * . . 5 . 1 + *

"." or "0" means unknown, integer 1..n means known value * + *

Example 5 1 . . . 4 . 5 . . . 4 . . 2 . . 4 . . . . . 5 . 1 */ private static void readFile(String file) { @@ -189,7 +167,7 @@ private static void readFile(String file) { str = str.trim(); // ignore comments - if(str.startsWith("#") || str.startsWith("%")) { + if (str.startsWith("#") || str.startsWith("%")) { continue; } @@ -200,7 +178,7 @@ private static void readFile(String file) { } else { // the problem matrix String row[] = str.split(" "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { String s = row[i]; if (s.equals(".")) { problem[lineCount - 1][i] = 0; @@ -211,7 +189,6 @@ private static void readFile(String file) { } lineCount++; - } // end while inr.close(); @@ -219,10 +196,8 @@ private static void readFile(String file) { } catch (IOException e) { System.out.println(e); } - } // end readFile - public static void main(String[] args) throws Exception { if (args.length > 0) { diff --git a/examples/contrib/SendMoreMoney.java b/examples/contrib/SendMoreMoney.java index 6b5c10b4b84..d72c0c34f1f 100644 --- a/examples/contrib/SendMoreMoney.java +++ b/examples/contrib/SendMoreMoney.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class SendMoreMoney { @@ -24,12 +23,7 @@ public class SendMoreMoney { System.loadLibrary("jniortools"); } - - /** - * - * Solves the SEND+MORE=MONEY problem. - * - */ + /** Solves the SEND+MORE=MONEY problem. */ private static void solve() { int base = 10; @@ -43,19 +37,30 @@ private static void solve() { IntVar r = solver.makeIntVar(0, base - 1, "r"); IntVar y = solver.makeIntVar(0, base - 1, "y"); - IntVar[] x = {s,e,n,d,m,o,r,y}; + IntVar[] x = {s, e, n, d, m, o, r, y}; - IntVar[] eq = {s,e,n,d, m,o,r,e, m,o,n,e,y}; - int[] coeffs = {1000, 100, 10, 1, // S E N D + - 1000, 100, 10, 1, // M O R E - -10000, -1000, -100, -10, -1 // == M O N E Y + IntVar[] eq = {s, e, n, d, m, o, r, e, m, o, n, e, y}; + int[] coeffs = { + 1000, + 100, + 10, + 1, // S E N D + + 1000, + 100, + 10, + 1, // M O R E + -10000, + -1000, + -100, + -10, + -1 // == M O N E Y }; solver.addConstraint(solver.makeScalProdEquality(eq, coeffs, 0)); // alternative: solver.addConstraint( - solver.makeScalProdEquality(new IntVar[] {s,e,n,d, m,o,r,e, m,o,n,e,y}, - coeffs, 0)); + solver.makeScalProdEquality( + new IntVar[] {s, e, n, d, m, o, r, e, m, o, n, e, y}, coeffs, 0)); // s > 0 solver.addConstraint(solver.makeGreater(s, 0)); @@ -64,12 +69,10 @@ private static void solve() { solver.addConstraint(solver.makeAllDifferent(x)); - DecisionBuilder db = solver.makePhase(x, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db); while (solver.nextSolution()) { - for(int i = 0; i < 8; i++) { + for (int i = 0; i < 8; i++) { System.out.print(x[i].toString() + " "); } System.out.println(); @@ -82,7 +85,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/SendMoreMoney2.java b/examples/contrib/SendMoreMoney2.java index 675a5f15845..75d31dd89a7 100644 --- a/examples/contrib/SendMoreMoney2.java +++ b/examples/contrib/SendMoreMoney2.java @@ -10,11 +10,10 @@ // 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. +import com.google.ortools.constraintsolver.*; import java.io.*; -import java.util.*; import java.text.*; - -import com.google.ortools.constraintsolver.*; +import java.util.*; public class SendMoreMoney2 { @@ -40,19 +39,14 @@ static IntExpr sp(IntVar[] a) { int len = a.length; int c = 1; int[] t = new int[len]; - for(int i = len-1; i >= 0; i--) { + for (int i = len - 1; i >= 0; i--) { t[i] = c; c *= 10; } return sol.makeScalProd(a, t); } - - /** - * - * Solves the SEND+MORE=MONEY problem with different approaches. - * - */ + /** Solves the SEND+MORE=MONEY problem with different approaches. */ private static void solve(int alt) { sol = new Solver("SendMoreMoney"); @@ -88,32 +82,32 @@ private static void solve(int alt) { * */ - if (alt == 0) { // // First, a version approach which is just too noisy. // sol.addConstraint( sol.makeEquality( - sol.makeSum(sol.makeSum( - sol.makeProd(s, 1000), - sol.makeSum(sol.makeProd(e, 100), - sol.makeSum(sol.makeProd(n, 10), - sol.makeProd(d, 1)))), + sol.makeSum( + sol.makeSum( + sol.makeProd(s, 1000), sol.makeSum( - sol.makeProd(m, 1000), - sol.makeSum(sol.makeProd(o, 100), - sol.makeSum(sol.makeProd(r, 10), - sol.makeProd(e, 1)))) - ).var(), - sol.makeSum(sol.makeProd(m, 10000), + sol.makeProd(e, 100), + sol.makeSum(sol.makeProd(n, 10), sol.makeProd(d, 1)))), + sol.makeSum( + sol.makeProd(m, 1000), sol.makeSum( - sol.makeProd(o, 1000), - sol.makeSum( - sol.makeProd(n, 100), - sol.makeSum( - sol.makeProd(e, 10), - sol.makeProd(y, 1))))).var())); + sol.makeProd(o, 100), + sol.makeSum(sol.makeProd(r, 10), sol.makeProd(e, 1))))) + .var(), + sol.makeSum( + sol.makeProd(m, 10000), + sol.makeSum( + sol.makeProd(o, 1000), + sol.makeSum( + sol.makeProd(n, 100), + sol.makeSum(sol.makeProd(e, 10), sol.makeProd(y, 1))))) + .var())); } else if (alt == 1) { @@ -125,8 +119,10 @@ private static void solve(int alt) { // sol.addConstraint( sol.makeEquality( - sol.makeSum(p(s, 1000, p(e, 100, p(n, 10, p(d, 1)))), - p(m, 1000, p(o, 100, p(r, 10, p(e, 1))))).var(), + sol.makeSum( + p(s, 1000, p(e, 100, p(n, 10, p(d, 1)))), + p(m, 1000, p(o, 100, p(r, 10, p(e, 1))))) + .var(), p(m, 10000, p(o, 1000, p(n, 100, p(e, 10, p(y, 1))))).var())); } else if (alt == 2) { @@ -137,41 +133,34 @@ private static void solve(int alt) { sol.addConstraint( sol.makeEquality( sol.makeSum( - sol.makeScalProd(new IntVar[] {s, e, n, d}, - new int[] {1000, 100, 10, 1}), - sol.makeScalProd(new IntVar[] {m, o, r, e}, - new int[] {1000, 100, 10, 1})).var(), - sol.makeScalProd(new IntVar[] {m, o, n, e, y}, - new int[] {10000, 1000, 100, 10, 1}).var())); + sol.makeScalProd(new IntVar[] {s, e, n, d}, new int[] {1000, 100, 10, 1}), + sol.makeScalProd(new IntVar[] {m, o, r, e}, new int[] {1000, 100, 10, 1})) + .var(), + sol.makeScalProd(new IntVar[] {m, o, n, e, y}, new int[] {10000, 1000, 100, 10, 1}) + .var())); } else if (alt == 3) { - // // alternative 3: same approach as 2, with some helper methods // sol.addConstraint( - sol.makeEquality(sol.makeSum(sp(new IntVar[] {s, e, n, d}), - sp(new IntVar[] {m, o, r, e})).var(), - sp(new IntVar[] {m, o, n, e, y}).var())); + sol.makeEquality( + sol.makeSum(sp(new IntVar[] {s, e, n, d}), sp(new IntVar[] {m, o, r, e})).var(), + sp(new IntVar[] {m, o, n, e, y}).var())); } else if (alt == 4) { // // Alternative 4, using explicit variables // - IntExpr send = sol.makeScalProd(new IntVar[] {s, e, n, d}, - new int[] {1000, 100, 10, 1}); - IntExpr more = sol.makeScalProd(new IntVar[] {m, o, r, e}, - new int[] {1000, 100, 10, 1}); - IntExpr money = sol.makeScalProd(new IntVar[] {m, o, n, e, y}, - new int[] {10000, 1000, 100, 10, 1}); - sol.addConstraint( - sol.makeEquality(sol.makeSum(send, more).var(), money.var())); - + IntExpr send = sol.makeScalProd(new IntVar[] {s, e, n, d}, new int[] {1000, 100, 10, 1}); + IntExpr more = sol.makeScalProd(new IntVar[] {m, o, r, e}, new int[] {1000, 100, 10, 1}); + IntExpr money = + sol.makeScalProd(new IntVar[] {m, o, n, e, y}, new int[] {10000, 1000, 100, 10, 1}); + sol.addConstraint(sol.makeEquality(sol.makeSum(send, more).var(), money.var())); } - // s > 0 sol.addConstraint(sol.makeGreater(s, 0)); // m > 0 @@ -182,12 +171,10 @@ private static void solve(int alt) { // // Search // - DecisionBuilder db = sol.makePhase(x, - sol.INT_VAR_DEFAULT, - sol.INT_VALUE_DEFAULT); + DecisionBuilder db = sol.makePhase(x, sol.INT_VAR_DEFAULT, sol.INT_VALUE_DEFAULT); sol.newSearch(db); while (sol.nextSolution()) { - for(int i = 0; i < 8; i++) { + for (int i = 0; i < 8; i++) { System.out.print(x[i].toString() + " "); } System.out.println(); @@ -202,7 +189,6 @@ private static void solve(int alt) { System.out.println("Failures: " + sol.failures()); System.out.println("Branches: " + sol.branches()); System.out.println("Wall time: " + sol.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/SendMostMoney.java b/examples/contrib/SendMostMoney.java index 81f399fa8e5..3b9552e54e7 100644 --- a/examples/contrib/SendMostMoney.java +++ b/examples/contrib/SendMostMoney.java @@ -10,14 +10,13 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - +import com.google.ortools.constraintsolver.*; import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; -import com.google.ortools.constraintsolver.*; +import java.io.*; +import java.text.*; +import java.util.*; public class SendMostMoney { @@ -25,19 +24,14 @@ public class SendMostMoney { System.loadLibrary("jniortools"); } - /** - * - * Solves the SEND+MOST=MONEY problem, where - * we maximize MONEY. - * See http://www.hakank.org/google_or_tools/send_more_money.py - * + * Solves the SEND+MOST=MONEY problem, where we maximize MONEY. See + * http://www.hakank.org/google_or_tools/send_more_money.py */ private static long solve(long MONEY) { Solver solver = new Solver("SendMostMoney"); - // // data // @@ -57,15 +51,28 @@ private static long solve(long MONEY) { IntVar[] x = {s, e, n, d, m, o, t, y}; - IntVar[] eq = {s,e,n,d, m,o,s,t, m,o,n,e,y}; - int[] coeffs = {1000, 100, 10, 1, // S E N D + - 1000, 100, 10, 1, // M O S T - -10000,-1000, -100,-10,-1 // == M O N E Y + IntVar[] eq = {s, e, n, d, m, o, s, t, m, o, n, e, y}; + int[] coeffs = { + 1000, + 100, + 10, + 1, // S E N D + + 1000, + 100, + 10, + 1, // M O S T + -10000, + -1000, + -100, + -10, + -1 // == M O N E Y }; solver.addConstraint(solver.makeScalProdEquality(eq, coeffs, 0)); - IntVar money = solver.makeScalProd(new IntVar[] {m, o, n, e, y}, - new int[] {10000, 1000, 100, 10, 1}).var(); + IntVar money = + solver + .makeScalProd(new IntVar[] {m, o, n, e, y}, new int[] {10000, 1000, 100, 10, 1}) + .var(); // // constraints @@ -86,9 +93,7 @@ private static long solve(long MONEY) { // // search // - DecisionBuilder db = solver.makePhase(x, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MAX_VALUE); + DecisionBuilder db = solver.makePhase(x, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MAX_VALUE); if (MONEY == 0) { // first round: get the optimal value @@ -103,7 +108,7 @@ private static long solve(long MONEY) { while (solver.nextSolution()) { System.out.println("money: " + money.value()); money_ret = money.value(); - for(int i = 0; i < x.length; i++) { + for (int i = 0; i < x.length; i++) { System.out.print(x[i].value() + " "); } System.out.println(); @@ -118,7 +123,6 @@ private static long solve(long MONEY) { System.out.println("Wall time: " + solver.wallTime() + "ms"); return money_ret; - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/Seseman.java b/examples/contrib/Seseman.java index def9037c299..344d07f3474 100644 --- a/examples/contrib/Seseman.java +++ b/examples/contrib/Seseman.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class Seseman { @@ -24,13 +23,7 @@ public class Seseman { System.loadLibrary("jniortools"); } - - /** - * - * Solves the Seseman convent problem. - * See http://www.hakank.org/google_or_tools/seseman.py - * - */ + /** Solves the Seseman convent problem. See http://www.hakank.org/google_or_tools/seseman.py */ private static void solve(int n) { Solver solver = new Solver("Seseman"); @@ -45,8 +38,8 @@ private static void solve(int n) { // IntVar[][] x = new IntVar[n][n]; IntVar[] x_flat = new IntVar[n * n]; - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { x[i][j] = solver.makeIntVar(0, n * n); x_flat[i * n + j] = x[i][j]; } @@ -54,21 +47,20 @@ private static void solve(int n) { IntVar total_sum = solver.makeSum(x_flat).var(); - // // constraints // // zero in all middle cells - for(int i = 1; i < n-1; i++) { - for(int j = 1; j < n-1; j++) { + for (int i = 1; i < n - 1; i++) { + for (int j = 1; j < n - 1; j++) { solver.addConstraint(solver.makeEquality(x[i][j], 0)); } } // all borders must be >= 1 - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { if (i == 0 || j == 0 || i == n - 1 || j == n - 1) { solver.addConstraint(solver.makeGreaterOrEqual(x[i][j], 1)); } @@ -80,38 +72,31 @@ private static void solve(int n) { IntVar[] border2 = new IntVar[n]; IntVar[] border3 = new IntVar[n]; IntVar[] border4 = new IntVar[n]; - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { border1[i] = x[i][0]; border2[i] = x[i][n - 1]; border3[i] = x[0][i]; border4[i] = x[n - 1][i]; } - solver.addConstraint( - solver.makeSumEquality(border1, border_sum)); + solver.addConstraint(solver.makeSumEquality(border1, border_sum)); - solver.addConstraint( - solver.makeSumEquality(border2, border_sum)); + solver.addConstraint(solver.makeSumEquality(border2, border_sum)); - solver.addConstraint( - solver.makeSumEquality(border3, border_sum)); - - solver.addConstraint( - solver.makeSumEquality(border4, border_sum)); + solver.addConstraint(solver.makeSumEquality(border3, border_sum)); + solver.addConstraint(solver.makeSumEquality(border4, border_sum)); // // search // - DecisionBuilder db = solver.makePhase(x_flat, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x_flat, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db); while (solver.nextSolution()) { System.out.println("total_sum: " + total_sum.value()); - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { System.out.print(x[i][j].value() + " "); } System.out.println(); @@ -126,7 +111,6 @@ private static void solve(int n) { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/SetCovering.java b/examples/contrib/SetCovering.java index 5fc2e6e8866..a23840fccfe 100644 --- a/examples/contrib/SetCovering.java +++ b/examples/contrib/SetCovering.java @@ -10,14 +10,13 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; -import com.google.ortools.constraintsolver.Solver; import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class SetCovering { @@ -25,13 +24,7 @@ public class SetCovering { System.loadLibrary("jniortools"); } - - /** - * - * Solves a set covering problem. - * See http://www.hakank.org/google_or_tools/set_covering.py - * - */ + /** Solves a set covering problem. See http://www.hakank.org/google_or_tools/set_covering.py */ private static void solve() { Solver solver = new Solver("SetCovering"); @@ -44,13 +37,14 @@ private static void solve() { int min_distance = 15; int num_cities = 6; - int[][] distance = {{ 0,10,20,30,30,20}, - {10, 0,25,35,20,10}, - {20,25, 0,15,30,20}, - {30,35,15, 0,15,25}, - {30,20,30,15, 0,14}, - {20,10,20,25,14, 0}}; - + int[][] distance = { + {0, 10, 20, 30, 30, 20}, + {10, 0, 25, 35, 20, 10}, + {20, 25, 0, 15, 30, 20}, + {30, 35, 15, 0, 15, 25}, + {30, 20, 30, 15, 0, 14}, + {20, 10, 20, 25, 14, 0} + }; // // variables @@ -58,22 +52,19 @@ private static void solve() { IntVar[] x = solver.makeIntVarArray(num_cities, 0, 1, "x"); IntVar z = solver.makeSum(x).var(); - // // constraints // // ensure that all cities are covered - for(int i = 0; i < num_cities; i++) { + for (int i = 0; i < num_cities; i++) { ArrayList b = new ArrayList(); - for(int j = 0; j < num_cities; j++) { + for (int j = 0; j < num_cities; j++) { if (distance[i][j] <= min_distance) { b.add(x[j]); } } - solver.addConstraint( - solver.makeSumGreaterOrEqual(b.toArray(new IntVar[1]), 1)); - + solver.addConstraint(solver.makeSumGreaterOrEqual(b.toArray(new IntVar[1]), 1)); } // @@ -81,13 +72,10 @@ private static void solve() { // OptimizeVar objective = solver.makeMinimize(z, 1); - // // search // - DecisionBuilder db = solver.makePhase(x, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db, objective); // @@ -96,7 +84,7 @@ private static void solve() { while (solver.nextSolution()) { System.out.println("z: " + z.value()); System.out.print("x: "); - for(int i = 0; i < num_cities; i++) { + for (int i = 0; i < num_cities; i++) { System.out.print(x[i].value() + " "); } System.out.println(); @@ -109,7 +97,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/SetCovering2.java b/examples/contrib/SetCovering2.java index 47362184d63..c2a5b80b01c 100644 --- a/examples/contrib/SetCovering2.java +++ b/examples/contrib/SetCovering2.java @@ -10,14 +10,13 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; -import com.google.ortools.constraintsolver.Solver; import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class SetCovering2 { @@ -25,13 +24,7 @@ public class SetCovering2 { System.loadLibrary("jniortools"); } - - /** - * - * Solves a set covering problem. - * See http://www.hakank.org/google_or_tools/set_covering2.py - * - */ + /** Solves a set covering problem. See http://www.hakank.org/google_or_tools/set_covering2.py */ private static void solve() { Solver solver = new Solver("SetCovering2"); @@ -46,22 +39,24 @@ private static void solve() { // Minimize the number of security telephones in street // corners on a campus. - int n = 8; // maximum number of corners + int n = 8; // maximum number of corners int num_streets = 11; // number of connected streets // corners of each street // Note: 1-based (handled below) - int[][] corner = {{1,2}, - {2,3}, - {4,5}, - {7,8}, - {6,7}, - {2,6}, - {1,6}, - {4,7}, - {2,4}, - {5,8}, - {3,5}}; + int[][] corner = { + {1, 2}, + {2, 3}, + {4, 5}, + {7, 8}, + {6, 7}, + {2, 6}, + {1, 6}, + {4, 7}, + {2, 4}, + {5, 8}, + {3, 5} + }; // // variables @@ -76,12 +71,11 @@ private static void solve() { // // ensure that all cities are covered - for(int i = 0; i < num_streets; i++) { + for (int i = 0; i < num_streets; i++) { IntVar[] b = new IntVar[2]; b[0] = x[corner[i][0] - 1]; b[1] = x[corner[i][1] - 1]; - solver.addConstraint( - solver.makeSumGreaterOrEqual(b, 1)); + solver.addConstraint(solver.makeSumGreaterOrEqual(b, 1)); } // @@ -89,13 +83,10 @@ private static void solve() { // OptimizeVar objective = solver.makeMinimize(z, 1); - // // search // - DecisionBuilder db = solver.makePhase(x, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db, objective); // @@ -104,7 +95,7 @@ private static void solve() { while (solver.nextSolution()) { System.out.println("z: " + z.value()); System.out.print("x: "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(x[i].value() + " "); } System.out.println(); @@ -117,7 +108,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/SetCovering3.java b/examples/contrib/SetCovering3.java index d765a447764..cbb8dd7afae 100644 --- a/examples/contrib/SetCovering3.java +++ b/examples/contrib/SetCovering3.java @@ -10,14 +10,13 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; -import com.google.ortools.constraintsolver.Solver; import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class SetCovering3 { @@ -25,13 +24,7 @@ public class SetCovering3 { System.loadLibrary("jniortools"); } - - /** - * - * Solves a set covering problem. - * See http://www.hakank.org/google_or_tools/set_covering3.py - * - */ + /** Solves a set covering problem. See http://www.hakank.org/google_or_tools/set_covering3.py */ private static void solve() { Solver solver = new Solver("SetCovering3"); @@ -48,12 +41,14 @@ private static void solve() { int num_senators = 10; // which group does a senator belong to? - int[][] belongs = {{1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, // 1 southern - {0, 0, 0, 0, 0, 1, 1, 1, 1, 1}, // 2 northern - {0, 1, 1, 0, 0, 0, 0, 1, 1, 1}, // 3 liberals - {1, 0, 0, 0, 1, 1, 1, 0, 0, 0}, // 4 conservative - {0, 0, 1, 1, 1, 1, 1, 0, 1, 0}, // 5 democrats - {1, 1, 0, 0, 0, 0, 0, 1, 0, 1}}; // 6 republicans + int[][] belongs = { + {1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, // 1 southern + {0, 0, 0, 0, 0, 1, 1, 1, 1, 1}, // 2 northern + {0, 1, 1, 0, 0, 0, 0, 1, 1, 1}, // 3 liberals + {1, 0, 0, 0, 1, 1, 1, 0, 0, 0}, // 4 conservative + {0, 0, 1, 1, 1, 1, 1, 0, 1, 0}, // 5 democrats + {1, 1, 0, 0, 0, 0, 0, 1, 0, 1} + }; // 6 republicans // // variables @@ -67,16 +62,14 @@ private static void solve() { // constraints // - // ensure that each group is covered by at least // one senator - for(int i = 0; i < num_groups; i++) { + for (int i = 0; i < num_groups; i++) { IntVar[] b = new IntVar[num_senators]; - for(int j = 0; j < num_senators; j++) { + for (int j = 0; j < num_senators; j++) { b[j] = solver.makeProd(x[j], belongs[i][j]).var(); } - solver.addConstraint( - solver.makeSumGreaterOrEqual(b, 1)); + solver.addConstraint(solver.makeSumGreaterOrEqual(b, 1)); } // @@ -84,13 +77,10 @@ private static void solve() { // OptimizeVar objective = solver.makeMinimize(z, 1); - // // search // - DecisionBuilder db = solver.makePhase(x, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db, objective); // @@ -99,17 +89,16 @@ private static void solve() { while (solver.nextSolution()) { System.out.println("z: " + z.value()); System.out.print("x: "); - for(int j = 0; j < num_senators; j++) { + for (int j = 0; j < num_senators; j++) { System.out.print(x[j].value() + " "); } System.out.println(); // More details - for(int j = 0; j < num_senators; j++) { + for (int j = 0; j < num_senators; j++) { if (x[j].value() == 1) { - System.out.print("Senator " + (1 + j) + - " belongs to these groups: "); - for(int i = 0; i < num_groups; i++) { + System.out.print("Senator " + (1 + j) + " belongs to these groups: "); + for (int i = 0; i < num_groups; i++) { if (belongs[i][j] == 1) { System.out.print((1 + i) + " "); } @@ -117,8 +106,6 @@ private static void solve() { System.out.println(); } } - - } solver.endSearch(); @@ -128,7 +115,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/SetCovering4.java b/examples/contrib/SetCovering4.java index 3a5c1e2b6f8..a5a2a488f33 100644 --- a/examples/contrib/SetCovering4.java +++ b/examples/contrib/SetCovering4.java @@ -10,14 +10,13 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; -import com.google.ortools.constraintsolver.Solver; import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class SetCovering4 { @@ -25,13 +24,7 @@ public class SetCovering4 { System.loadLibrary("jniortools"); } - - /** - * - * Solves a set covering problem. - * See http://www.hakank.org/google_or_tools/set_covering4.py - * - */ + /** Solves a set covering problem. See http://www.hakank.org/google_or_tools/set_covering4.py */ private static void solve(int set_partition) { Solver solver = new Solver("SetCovering4"); @@ -40,7 +33,6 @@ private static void solve(int set_partition) { // data // - // Set partition and set covering problem from // Example from the Swedish book // Lundgren, Roennqvist, Vaebrand @@ -55,16 +47,17 @@ private static void solve(int set_partition) { // the alternatives, and their objects int[][] a = { // 1 2 3 4 5 6 7 8 the objects - {1,0,0,0,0,1,0,0}, // alternative 1 - {0,1,0,0,0,1,0,1}, // alternative 2 - {1,0,0,1,0,0,1,0}, // alternative 3 - {0,1,1,0,1,0,0,0}, // alternative 4 - {0,1,0,0,1,0,0,0}, // alternative 5 - {0,1,1,0,0,0,0,0}, // alternative 6 - {0,1,1,1,0,0,0,0}, // alternative 7 - {0,0,0,1,1,0,0,1}, // alternative 8 - {0,0,1,0,0,1,0,1}, // alternative 9 - {1,0,0,0,0,1,1,0}}; // alternative 10 + {1, 0, 0, 0, 0, 1, 0, 0}, // alternative 1 + {0, 1, 0, 0, 0, 1, 0, 1}, // alternative 2 + {1, 0, 0, 1, 0, 0, 1, 0}, // alternative 3 + {0, 1, 1, 0, 1, 0, 0, 0}, // alternative 4 + {0, 1, 0, 0, 1, 0, 0, 0}, // alternative 5 + {0, 1, 1, 0, 0, 0, 0, 0}, // alternative 6 + {0, 1, 1, 1, 0, 0, 0, 0}, // alternative 7 + {0, 0, 0, 1, 1, 0, 0, 1}, // alternative 8 + {0, 0, 1, 0, 0, 1, 0, 1}, // alternative 9 + {1, 0, 0, 0, 0, 1, 1, 0} + }; // alternative 10 // // variables @@ -78,19 +71,16 @@ private static void solve(int set_partition) { // constraints // - - for(int j = 0; j < num_objects; j++) { + for (int j = 0; j < num_objects; j++) { IntVar[] b = new IntVar[num_alternatives]; - for(int i = 0; i < num_alternatives; i++) { + for (int i = 0; i < num_alternatives; i++) { b[i] = solver.makeProd(x[i], a[i][j]).var(); } if (set_partition == 1) { - solver.addConstraint( - solver.makeSumGreaterOrEqual(b, 1)); + solver.addConstraint(solver.makeSumGreaterOrEqual(b, 1)); } else { - solver.addConstraint( - solver.makeSumEquality(b, 1)); + solver.addConstraint(solver.makeSumEquality(b, 1)); } } @@ -99,13 +89,10 @@ private static void solve(int set_partition) { // OptimizeVar objective = solver.makeMinimize(z, 1); - // // search // - DecisionBuilder db = solver.makePhase(x, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db, objective); // @@ -114,7 +101,7 @@ private static void solve(int set_partition) { while (solver.nextSolution()) { System.out.println("z: " + z.value()); System.out.print("Selected alternatives: "); - for(int i = 0; i < num_alternatives; i++) { + for (int i = 0; i < num_alternatives; i++) { if (x[i].value() == 1) { System.out.print((1 + i) + " "); } @@ -129,7 +116,6 @@ private static void solve(int set_partition) { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/SetCoveringDeployment.java b/examples/contrib/SetCoveringDeployment.java index a0aae47dc0a..e429e90ef98 100644 --- a/examples/contrib/SetCoveringDeployment.java +++ b/examples/contrib/SetCoveringDeployment.java @@ -10,14 +10,13 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; -import com.google.ortools.constraintsolver.Solver; import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class SetCoveringDeployment { @@ -25,12 +24,9 @@ public class SetCoveringDeployment { System.loadLibrary("jniortools"); } - /** - * - * Solves a set covering deployment problem. - * See http://www.hakank.org/google_or_tools/set_covering_deployment.py - * + * Solves a set covering deployment problem. See + * http://www.hakank.org/google_or_tools/set_covering_deployment.py */ private static void solve() { @@ -41,27 +37,23 @@ private static void solve() { // // From http://mathworld.wolfram.com/SetCoveringDeployment.html - String[] countries = {"Alexandria", - "Asia Minor", - "Britain", - "Byzantium", - "Gaul", - "Iberia", - "Rome", - "Tunis"}; + String[] countries = { + "Alexandria", "Asia Minor", "Britain", "Byzantium", "Gaul", "Iberia", "Rome", "Tunis" + }; int n = countries.length; // the incidence matrix (neighbours) - int[][] mat = {{0, 1, 0, 1, 0, 0, 1, 1}, - {1, 0, 0, 1, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 1, 0, 0}, - {1, 1, 0, 0, 0, 0, 1, 0}, - {0, 0, 1, 0, 0, 1, 1, 0}, - {0, 0, 1, 0, 1, 0, 1, 1}, - {1, 0, 0, 1, 1, 1, 0, 1}, - {1, 0, 0, 0, 0, 1, 1, 0}}; - + int[][] mat = { + {0, 1, 0, 1, 0, 0, 1, 1}, + {1, 0, 0, 1, 0, 0, 0, 0}, + {0, 0, 0, 0, 1, 1, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 0}, + {0, 0, 1, 0, 0, 1, 1, 0}, + {0, 0, 1, 0, 1, 0, 1, 1}, + {1, 0, 0, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 1, 1, 0} + }; // // variables @@ -74,8 +66,7 @@ private static void solve() { IntVar[] y = solver.makeIntVarArray(n, 0, 1, "y"); // total number of armies - IntVar num_armies = solver.makeSum(solver.makeSum(x), - solver.makeSum(y)).var(); + IntVar num_armies = solver.makeSum(solver.makeSum(x), solver.makeSum(y)).var(); // // constraints @@ -87,7 +78,7 @@ private static void solve() { // Or rather: Is there a backup, there // must be an an army // - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { solver.addConstraint(solver.makeGreaterOrEqual(x[i], y[i])); } @@ -95,18 +86,17 @@ private static void solve() { // Constraint 2: There should always be an backup // army near every city // - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { ArrayList count_neighbours = new ArrayList(); - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { if (mat[i][j] == 1) { count_neighbours.add(y[j]); } } solver.addConstraint( solver.makeGreaterOrEqual( - solver.makeSum(x[i], - solver.makeSum( - count_neighbours.toArray(new IntVar[1])).var()), 1)); + solver.makeSum(x[i], solver.makeSum(count_neighbours.toArray(new IntVar[1])).var()), + 1)); } // @@ -114,13 +104,10 @@ private static void solve() { // OptimizeVar objective = solver.makeMinimize(num_armies, 1); - // // search // - DecisionBuilder db = solver.makePhase(x, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db, objective); // @@ -128,7 +115,7 @@ private static void solve() { // while (solver.nextSolution()) { System.out.println("num_armies: " + num_armies.value()); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { if (x[i].value() == 1) { System.out.print("Army: " + countries[i] + " "); } @@ -136,7 +123,6 @@ private static void solve() { System.out.println("Reserve army: " + countries[i]); } } - } solver.endSearch(); @@ -146,12 +132,10 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { SetCoveringDeployment.solve(); - } } diff --git a/examples/contrib/SimpleRoutingTest.java b/examples/contrib/SimpleRoutingTest.java index 9fe6f0db33c..d882540701e 100644 --- a/examples/contrib/SimpleRoutingTest.java +++ b/examples/contrib/SimpleRoutingTest.java @@ -1,89 +1,115 @@ -import java.util.ArrayList; - -import com.google.ortools.constraintsolver.Assignment; -import com.google.ortools.constraintsolver.NodeEvaluator2; -import com.google.ortools.constraintsolver.RoutingModel; -import com.google.ortools.constraintsolver.FirstSolutionStrategy; -import com.google.ortools.constraintsolver.RoutingSearchParameters; - -public class SimpleRoutingTest { - - //Static Add Library - static { System.loadLibrary("jniortools"); } - - private ArrayList globalRes; - private long globalResCost; - private int[][] costMatrix; - - public ArrayList getGlobalRes() {return globalRes;} - public void setGlobalRes(ArrayList globalRes) {this.globalRes = globalRes;} - public long getGlobalResCost() {return globalResCost;} - public void setGlobalResCost(int globalResCost) {this.globalResCost = globalResCost;} - public int[][] getCostMatrix() {return costMatrix;} - public void setCostMatrix(int[][] costMatrix) {this.costMatrix = costMatrix;} - - public SimpleRoutingTest(int[][] costMatrix) { - super(); - this.costMatrix = costMatrix; - globalRes = new ArrayList(); - } - - //Node Distance Evaluation - public static class NodeDistance extends NodeEvaluator2 { - private int[][] costMatrix; - - public NodeDistance(int[][] costMatrix) { - this.costMatrix = costMatrix; - } - - @Override - public long run(int firstIndex, int secondIndex) { - return costMatrix[firstIndex][secondIndex]; - } - } - - //Solve Method - public void solve() { - RoutingModel routing = new RoutingModel(costMatrix.length, 1, 0); - RoutingSearchParameters parameters = - RoutingSearchParameters.newBuilder() - .mergeFrom(RoutingModel.defaultSearchParameters()) - .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) - .build(); - NodeDistance distances = new NodeDistance(costMatrix); - routing.setArcCostEvaluatorOfAllVehicles(distances); - - Assignment solution = routing.solve(); - if (solution != null) { - int route_number = 0; - for (long node = routing.start(route_number); !routing.isEnd(node); node = solution.value(routing.nextVar(node))) { - globalRes.add((int) node); - } - } - globalResCost = solution.objectiveValue(); - System.out.println("cost = " + globalResCost); - } - - - public static void main(String[] args) throws Exception { - int[][] values = new int[4][4]; - values[0][0]=0; - values[0][1]=5; - values[0][2]=3; - values[0][3]=6; - values[1][0]=5; - values[1][1]=0; - values[1][2]=8; - values[1][3]=1; - values[2][0]=3; - values[2][1]=8; - values[2][2]=0; - values[2][3]=4; - values[3][0]=6; - values[3][1]=1; - values[3][2]=4; - values[3][3]=0; - SimpleRoutingTest model = new SimpleRoutingTest(values); - model.solve(); - } -} +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.IntIntToLong; +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.ArrayList; + +public class SimpleRoutingTest { + + // Static Add Library + static { + System.loadLibrary("jniortools"); + } + + private ArrayList globalRes; + private long globalResCost; + private int[][] costMatrix; + + public ArrayList getGlobalRes() { + return globalRes; + } + + public void setGlobalRes(ArrayList globalRes) { + this.globalRes = globalRes; + } + + public long getGlobalResCost() { + return globalResCost; + } + + public void setGlobalResCost(int globalResCost) { + this.globalResCost = globalResCost; + } + + public int[][] getCostMatrix() { + return costMatrix; + } + + public void setCostMatrix(int[][] costMatrix) { + this.costMatrix = costMatrix; + } + + public SimpleRoutingTest(int[][] costMatrix) { + super(); + this.costMatrix = costMatrix; + globalRes = new ArrayList(); + } + + // Node Distance Evaluation + public static class NodeDistance extends IntIntToLong { + private int[][] costMatrix; + private RoutingIndexManager indexManager; + + public NodeDistance(RoutingIndexManager manager, int[][] costMatrix) { + this.costMatrix = costMatrix; + this.indexManager = manager; + } + + @Override + public long run(int firstIndex, int secondIndex) { + final int firstNode = indexManager.indexToNode(firstIndex); + final int secondNode = indexManager.indexToNode(secondIndex); + return costMatrix[firstNode][secondNode]; + } + } + + // Solve Method + public void solve() { + RoutingIndexManager manager = new RoutingIndexManager(costMatrix.length, 1, 0); + RoutingModel routing = new RoutingModel(manager); + RoutingSearchParameters parameters = + RoutingSearchParameters.newBuilder() + .mergeFrom(main.defaultRoutingSearchParameters()) + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + NodeDistance distances = new NodeDistance(manager, costMatrix); + routing.setArcCostEvaluatorOfAllVehicles(routing.registerTransitCallback(distances)); + + Assignment solution = routing.solve(); + if (solution != null) { + int route_number = 0; + for (long node = routing.start(route_number); + !routing.isEnd(node); + node = solution.value(routing.nextVar(node))) { + globalRes.add((int) node); + } + } + globalResCost = solution.objectiveValue(); + System.out.println("cost = " + globalResCost); + } + + public static void main(String[] args) throws Exception { + int[][] values = new int[4][4]; + values[0][0] = 0; + values[0][1] = 5; + values[0][2] = 3; + values[0][3] = 6; + values[1][0] = 5; + values[1][1] = 0; + values[1][2] = 8; + values[1][3] = 1; + values[2][0] = 3; + values[2][1] = 8; + values[2][2] = 0; + values[2][3] = 4; + values[3][0] = 6; + values[3][1] = 1; + values[3][2] = 4; + values[3][3] = 0; + SimpleRoutingTest model = new SimpleRoutingTest(values); + model.solve(); + } +} diff --git a/examples/contrib/StableMarriage.java b/examples/contrib/StableMarriage.java index 3afbdcf97d5..d9aede3765a 100644 --- a/examples/contrib/StableMarriage.java +++ b/examples/contrib/StableMarriage.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class StableMarriage { @@ -25,10 +24,8 @@ public class StableMarriage { } /** - * - * Solves some stable marriage problems. - * See http://www.hakank.org/google_or_tools/stable_marriage.py - * + * Solves some stable marriage problems. See + * http://www.hakank.org/google_or_tools/stable_marriage.py */ private static void solve(long[][][] ranks, String problem_name) { @@ -40,9 +37,8 @@ private static void solve(long[][][] ranks, String problem_name) { System.out.println("\n#####################"); System.out.println("Problem: " + problem_name); - long[][] rankWomen = ranks[0]; - long[][] rankMen = ranks[1]; + long[][] rankMen = ranks[1]; int n = rankWomen.length; @@ -57,61 +53,50 @@ private static void solve(long[][][] ranks, String problem_name) { // (the comments are the Comet code) // forall(m in Men) // cp.post(husband[wife[m]] == m); - for(int m = 0; m < n; m++) { - solver.addConstraint( - solver.makeEquality(solver.makeElement(husband, wife[m]), m)); + for (int m = 0; m < n; m++) { + solver.addConstraint(solver.makeEquality(solver.makeElement(husband, wife[m]), m)); } // forall(w in Women) // cp.post(wife[husband[w]] == w); - for(int w = 0; w < n; w++) { - solver.addConstraint( - solver.makeEquality(solver.makeElement(wife, husband[w]), w)); + for (int w = 0; w < n; w++) { + solver.addConstraint(solver.makeEquality(solver.makeElement(wife, husband[w]), w)); } - // forall(m in Men, o in Women) // cp.post(rankMen[m,o] < rankMen[m, wife[m]] => // rankWomen[o,husband[o]] < rankWomen[o,m]); - for(int m = 0; m < n; m++) { - for(int o = 0; o < n; o++) { - IntVar b1 = solver.makeIsGreaterCstVar( - solver.makeElement(rankMen[m], wife[m]).var(), - rankMen[m][o]); - - IntVar b2 = solver.makeIsLessCstVar( - solver.makeElement(rankWomen[o], husband[o]).var(), - rankWomen[o][m]); - solver.addConstraint( - solver.makeLessOrEqual( - solver.makeDifference(b1, b2), 0)); + for (int m = 0; m < n; m++) { + for (int o = 0; o < n; o++) { + IntVar b1 = + solver.makeIsGreaterCstVar( + solver.makeElement(rankMen[m], wife[m]).var(), rankMen[m][o]); + + IntVar b2 = + solver.makeIsLessCstVar( + solver.makeElement(rankWomen[o], husband[o]).var(), rankWomen[o][m]); + solver.addConstraint(solver.makeLessOrEqual(solver.makeDifference(b1, b2), 0)); } } // forall(w in Women, o in Men) // cp.post(rankWomen[w,o] < rankWomen[w,husband[w]] => // rankMen[o,wife[o]] < rankMen[o,w]); - for(int w = 0; w < n; w++) { - for(int o = 0; o < n; o++) { - IntVar b1 = solver.makeIsGreaterCstVar( - solver.makeElement(rankWomen[w], husband[w]).var(), - rankWomen[w][o]); - IntVar b2 = solver.makeIsLessCstVar( - solver.makeElement(rankMen[o], wife[o]).var(), - rankMen[o][w]); - solver.addConstraint( - solver.makeLessOrEqual( - solver.makeDifference(b1, b2), 0)); - } + for (int w = 0; w < n; w++) { + for (int o = 0; o < n; o++) { + IntVar b1 = + solver.makeIsGreaterCstVar( + solver.makeElement(rankWomen[w], husband[w]).var(), rankWomen[w][o]); + IntVar b2 = + solver.makeIsLessCstVar(solver.makeElement(rankMen[o], wife[o]).var(), rankMen[o][w]); + solver.addConstraint(solver.makeLessOrEqual(solver.makeDifference(b1, b2), 0)); } - + } // // search // - DecisionBuilder db = solver.makePhase(wife, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(wife, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db); @@ -120,15 +105,14 @@ private static void solve(long[][][] ranks, String problem_name) { // while (solver.nextSolution()) { System.out.print("wife : "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(wife[i].value() + " "); } System.out.print("\nhusband: "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(husband[i].value() + " "); } System.out.println("\n"); - } solver.endSearch(); @@ -139,7 +123,6 @@ private static void solve(long[][][] ranks, String problem_name) { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { @@ -149,47 +132,55 @@ public static void main(String[] args) throws Exception { // long[][][] van_hentenryck = { // rankWomen - {{1, 2, 4, 3, 5}, - {3, 5, 1, 2, 4}, - {5, 4, 2, 1, 3}, - {1, 3, 5, 4, 2}, - {4, 2, 3, 5, 1}}, - - // rankMen - {{5, 1, 2, 4, 3}, - {4, 1, 3, 2, 5}, - {5, 3, 2, 4, 1}, - {1, 5, 4, 3, 2}, - {4, 3, 2, 1, 5}} - }; + { + {1, 2, 4, 3, 5}, + {3, 5, 1, 2, 4}, + {5, 4, 2, 1, 3}, + {1, 3, 5, 4, 2}, + {4, 2, 3, 5, 1} + }, + // rankMen + { + {5, 1, 2, 4, 3}, + {4, 1, 3, 2, 5}, + {5, 3, 2, 4, 1}, + {1, 5, 4, 3, 2}, + {4, 3, 2, 1, 5} + } + }; // // Data from MathWorld // http://mathworld.wolfram.com/StableMarriageProblem.html // - long[][][] mathworld = { + long[][][] mathworld = { // rankWomen - {{3, 1, 5, 2, 8, 7, 6, 9, 4}, - {9, 4, 8, 1, 7, 6, 3, 2, 5}, - {3, 1, 8, 9, 5, 4, 2, 6, 7}, - {8, 7, 5, 3, 2, 6, 4, 9, 1}, - {6, 9, 2, 5, 1, 4, 7, 3, 8}, - {2, 4, 5, 1, 6, 8, 3, 9, 7}, - {9, 3, 8, 2, 7, 5, 4, 6, 1}, - {6, 3, 2, 1, 8, 4, 5, 9, 7}, - {8, 2, 6, 4, 9, 1, 3, 7, 5}}, + { + {3, 1, 5, 2, 8, 7, 6, 9, 4}, + {9, 4, 8, 1, 7, 6, 3, 2, 5}, + {3, 1, 8, 9, 5, 4, 2, 6, 7}, + {8, 7, 5, 3, 2, 6, 4, 9, 1}, + {6, 9, 2, 5, 1, 4, 7, 3, 8}, + {2, 4, 5, 1, 6, 8, 3, 9, 7}, + {9, 3, 8, 2, 7, 5, 4, 6, 1}, + {6, 3, 2, 1, 8, 4, 5, 9, 7}, + {8, 2, 6, 4, 9, 1, 3, 7, 5} + }, // rankMen - {{7, 3, 8, 9, 6, 4, 2, 1, 5}, - {5, 4, 8, 3, 1, 2, 6, 7, 9}, - {4, 8, 3, 9, 7, 5, 6, 1, 2}, - {9, 7, 4, 2, 5, 8, 3, 1, 6}, - {2, 6, 4, 9, 8, 7, 5, 1, 3}, - {2, 7, 8, 6, 5, 3, 4, 1, 9}, - {1, 6, 2, 3, 8, 5, 4, 9, 7}, - {5, 6, 9, 1, 2, 8, 4, 3, 7}, - {6, 1, 4, 7, 5, 8, 3, 9, 2}}}; + { + {7, 3, 8, 9, 6, 4, 2, 1, 5}, + {5, 4, 8, 3, 1, 2, 6, 7, 9}, + {4, 8, 3, 9, 7, 5, 6, 1, 2}, + {9, 7, 4, 2, 5, 8, 3, 1, 6}, + {2, 6, 4, 9, 8, 7, 5, 1, 3}, + {2, 7, 8, 6, 5, 3, 4, 1, 9}, + {1, 6, 2, 3, 8, 5, 4, 9, 7}, + {5, 6, 9, 1, 2, 8, 4, 3, 7}, + {6, 1, 4, 7, 5, 8, 3, 9, 2} + } + }; // // Data from @@ -197,17 +188,21 @@ public static void main(String[] args) throws Exception { // long[][][] problem3 = { // rankWomen - {{1,2,3,4}, - {4,3,2,1}, - {1,2,3,4}, - {3,4,1,2}}, + { + {1, 2, 3, 4}, + {4, 3, 2, 1}, + {1, 2, 3, 4}, + {3, 4, 1, 2} + }, // rankMen" - {{1,2,3,4}, - {2,1,3,4}, - {1,4,3,2}, - {4,3,1,2}}}; - + { + {1, 2, 3, 4}, + {2, 1, 3, 4}, + {1, 4, 3, 2}, + {4, 3, 1, 2} + } + }; // // Data from @@ -216,27 +211,29 @@ public static void main(String[] args) throws Exception { // long[][][] problem4 = { // rankWomen - {{1,5,4,6,2,3}, - {4,1,5,2,6,3}, - {6,4,2,1,5,3}, - {1,5,2,4,3,6}, - {4,2,1,5,6,3}, - {2,6,3,5,1,4}}, + { + {1, 5, 4, 6, 2, 3}, + {4, 1, 5, 2, 6, 3}, + {6, 4, 2, 1, 5, 3}, + {1, 5, 2, 4, 3, 6}, + {4, 2, 1, 5, 6, 3}, + {2, 6, 3, 5, 1, 4} + }, // rankMen - {{1,4,2,5,6,3}, - {3,4,6,1,5,2}, - {1,6,4,2,3,5}, - {6,5,3,4,2,1}, - {3,1,2,4,5,6}, - {2,3,1,6,5,4}}}; - + { + {1, 4, 2, 5, 6, 3}, + {3, 4, 6, 1, 5, 2}, + {1, 6, 4, 2, 3, 5}, + {6, 5, 3, 4, 2, 1}, + {3, 1, 2, 4, 5, 6}, + {2, 3, 1, 6, 5, 4} + } + }; StableMarriage.solve(van_hentenryck, "Van Hentenryck"); StableMarriage.solve(mathworld, "MathWorld"); StableMarriage.solve(problem3, "Problem 3"); StableMarriage.solve(problem4, "Problem 4"); - - } } diff --git a/examples/contrib/StiglerMIP.java b/examples/contrib/StiglerMIP.java index 35e9be2781b..914a9ab3738 100755 --- a/examples/contrib/StiglerMIP.java +++ b/examples/contrib/StiglerMIP.java @@ -18,283 +18,285 @@ * * Java version by Darian Sastre (darian.sastre@minimaxlabs.com) */ -import java.math.RoundingMode; -import java.text.DecimalFormat; - import com.google.ortools.linearsolver.MPConstraint; import com.google.ortools.linearsolver.MPObjective; import com.google.ortools.linearsolver.MPSolver; import com.google.ortools.linearsolver.MPVariable; +import java.math.RoundingMode; +import java.text.DecimalFormat; public class StiglerMIP { - static { - System.loadLibrary("jniortools"); - } - - private static MPSolver createSolver (String solverType) { - return new MPSolver("MIPDiet", - MPSolver.OptimizationProblemType.valueOf(solverType)); - } + static { + System.loadLibrary("jniortools"); + } - private static void solve(String solverType) { - MPSolver solver = createSolver(solverType); - double infinity = MPSolver.infinity(); + private static MPSolver createSolver(String solverType) { + return new MPSolver("MIPDiet", MPSolver.OptimizationProblemType.valueOf(solverType)); + } - /** invariants */ - double days = 365.25; - int nutrientsCount = 9; - int commoditiesCount = 77; + private static void solve(String solverType) { + MPSolver solver = createSolver(solverType); + double infinity = MPSolver.infinity(); - String[] nutrients = { - "calories", // Calories, unit = 1000 - "protein", // Protein, unit = grams - "calcium", // Calcium, unit = grams - "iron", // Iron, unit = milligrams - "vitaminA", // Vitamin A, unit = 1000 International Units - "thiamine", // Thiamine, Vit. B1, unit = milligrams - "riboflavin", // Riboflavin, Vit. B2, unit = milligrams - "niacin", // Niacin (Nicotinic Acid), unit = milligrams - "ascorbicAcid" // Ascorbic Acid, Vit. C, unit = milligrams - }; + /** invariants */ + double days = 365.25; + int nutrientsCount = 9; + int commoditiesCount = 77; - String[] commodities = { - "Wheat Flour (Enriched), 10 lb.", - "Macaroni, 1 lb.", - "Wheat Cereal (Enriched), 28 oz.", - "Corn Flakes, 8 oz.", - "Corn Meal, 1 lb.", - "Hominy Grits, 24 oz.", - "Rice, 1 lb.", - "Rolled Oats, 1 lb.", - "White Bread (Enriched), 1 lb.", - "Whole Wheat Bread, 1 lb.", - "Rye Bread, 1 lb.", - "Pound Cake, 1 lb.", - "Soda Crackers, 1 lb.", - "Milk, 1 qt.", - "Evaporated Milk (can), 14.5 oz.", - "Butter, 1 lb.", - "Oleomargarine, 1 lb.", - "Eggs, 1 doz.", - "Cheese (Cheddar), 1 lb.", - "Cream, 1/2 pt.", - "Peanut Butter, 1 lb.", - "Mayonnaise, 1/2 pt.", - "Crisco, 1 lb.", - "Lard, 1 lb.", - "Sirloin Steak, 1 lb.", - "Round Steak, 1 lb.", - "Rib Roast, 1 lb.", - "Chuck Roast, 1 lb.", - "Plate, 1 lb.", - "Liver (Beef), 1 lb.", - "Leg of Lamb, 1 lb.", - "Lamb Chops (Rib), 1 lb.", - "Pork Chops, 1 lb.", - "Pork Loin Roast, 1 lb.", - "Bacon, 1 lb.", - "Ham - smoked, 1 lb.", - "Salt Pork, 1 lb.", - "Roasting Chicken, 1 lb.", - "Veal Cutlets, 1 lb.", - "Salmon, Pink (can), 16 oz.", - "Apples, 1 lb.", - "Bananas, 1 lb.", - "Lemons, 1 doz.", - "Oranges, 1 doz.", - "Green Beans, 1 lb.", - "Cabbage, 1 lb.", - "Carrots, 1 bunch", - "Celery, 1 stalk", - "Lettuce, 1 head", - "Onions, 1 lb.", - "Potatoes, 15 lb.", - "Spinach, 1 lb.", - "Sweet Potatoes, 1 lb.", - "Peaches (can), No. 2 1/2", - "Pears (can), No. 2 1/2,", - "Pineapple (can), No. 2 1/2", - "Asparagus (can), No. 2", - "Grean Beans (can), No. 2", - "Pork and Beans (can), 16 oz.", - "Corn (can), No. 2", - "Peas (can), No. 2", - "Tomatoes (can), No. 2", - "Tomato Soup (can), 10 1/2 oz.", - "Peaches, Dried, 1 lb.", - "Prunes, Dried, 1 lb.", - "Raisins, Dried, 15 oz.", - "Peas, Dried, 1 lb.", - "Lima Beans, Dried, 1 lb.", - "Navy Beans, Dried, 1 lb.", - "Coffee, 1 lb.", - "Tea, 1/4 lb.", - "Cocoa, 8 oz.", - "Chocolate, 8 oz.", - "Sugar, 10 lb.", - "Corn Sirup, 24 oz.", - "Molasses, 18 oz.", - "Strawberry Preserve, 1 lb." - }; + String[] nutrients = { + "calories", // Calories, unit = 1000 + "protein", // Protein, unit = grams + "calcium", // Calcium, unit = grams + "iron", // Iron, unit = milligrams + "vitaminA", // Vitamin A, unit = 1000 International Units + "thiamine", // Thiamine, Vit. B1, unit = milligrams + "riboflavin", // Riboflavin, Vit. B2, unit = milligrams + "niacin", // Niacin (Nicotinic Acid), unit = milligrams + "ascorbicAcid" // Ascorbic Acid, Vit. C, unit = milligrams + }; - // price and weight per unit correspond to the two first columns - double[][] data = { - {36.0, 12600.0, 44.7, 1411.0, 2.0, 365.0, 0.0, 55.4, 33.3, 441.0, 0.0}, - {14.1, 3217.0, 11.6, 418.0, 0.7, 54.0, 0.0, 3.2, 1.9, 68.0, 0.0}, - {24.2, 3280.0, 11.8, 377.0, 14.4, 175.0, 0.0, 14.4, 8.8, 114.0, 0.0}, - {7.1, 3194.0, 11.4, 252.0, 0.1, 56.0, 0.0, 13.5, 2.3, 68.0, 0.0}, - {4.6, 9861.0, 36.0, 897.0, 1.7, 99.0, 30.9, 17.4, 7.9, 106.0, 0.0}, - {8.5, 8005.0, 28.6, 680.0, 0.8, 80.0, 0.0, 10.6, 1.6, 110.0, 0.0}, - {7.5, 6048.0, 21.2, 460.0, 0.6, 41.0, 0.0, 2.0, 4.8, 60.0, 0.0}, - {7.1, 6389.0, 25.3, 907.0, 5.1, 341.0, 0.0, 37.1, 8.9, 64.0, 0.0}, - {7.9, 5742.0, 15.6, 488.0, 2.5, 115.0, 0.0, 13.8, 8.5, 126.0, 0.0}, - {9.1, 4985.0, 12.2, 484.0, 2.7, 125.0, 0.0, 13.9, 6.4, 160.0, 0.0}, - {9.2, 4930.0, 12.4, 439.0, 1.1, 82.0, 0.0, 9.9, 3.0, 66.0, 0.0}, - {24.8, 1829.0, 8.0, 130.0, 0.4, 31.0, 18.9, 2.8, 3.0, 17.0, 0.0}, - {15.1, 3004.0, 12.5, 288.0, 0.5, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {11.0, 8867.0, 6.1, 310.0, 10.5, 18.0, 16.8, 4.0, 16.0, 7.0, 177.0}, - {6.7, 6035.0, 8.4, 422.0, 15.1, 9.0, 26.0, 3.0, 23.5, 11.0, 60.0}, - {20.8, 1473.0, 10.8, 9.0, 0.2, 3.0, 44.2, 0.0, 0.2, 2.0, 0.0}, - {16.1, 2817.0, 20.6, 17.0, 0.6, 6.0, 55.8, 0.2, 0.0, 0.0, 0.0}, - {32.6, 1857.0, 2.9, 238.0, 1.0, 52.0, 18.6, 2.8, 6.5, 1.0, 0.0}, - {24.2, 1874.0, 7.4, 448.0, 16.4, 19.0, 28.1, 0.8, 10.3, 4.0, 0.0}, - {14.1, 1689.0, 3.5, 49.0, 1.7, 3.0, 16.9, 0.6, 2.5, 0.0, 17.0}, - {17.9, 2534.0, 15.7, 661.0, 1.0, 48.0, 0.0, 9.6, 8.1, 471.0, 0.0}, - {16.7, 1198.0, 8.6, 18.0, 0.2, 8.0, 2.7, 0.4, 0.5, 0.0, 0.0}, - {20.3, 2234.0, 20.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {9.8, 4628.0, 41.7, 0.0, 0.0, 0.0, 0.2, 0.0, 0.5, 5.0, 0.0}, - {39.6, 1145.0, 2.9, 166.0, 0.1, 34.0, 0.2, 2.1, 2.9, 69.0, 0.0}, - {36.4, 1246.0, 2.2, 214.0, 0.1, 32.0, 0.4, 2.5, 2.4, 87.0, 0.0}, - {29.2, 1553.0, 3.4, 213.0, 0.1, 33.0, 0.0, 0.0, 2.0, 0.0, 0.0}, - {22.6, 2007.0, 3.6, 309.0, 0.2, 46.0, 0.4, 1.0, 4.0, 120.0, 0.0}, - {14.6, 3107.0, 8.5, 404.0, 0.2, 62.0, 0.0, 0.9, 0.0, 0.0, 0.0}, - {26.8, 1692.0, 2.2, 333.0, 0.2, 139.0, 169.2, 6.4, 50.8, 316.0, 525.0}, - {27.6, 1643.0, 3.1, 245.0, 0.1, 20.0, 0.0, 2.8, 3.0, 86.0, 0.0}, - {36.6, 1239.0, 3.3, 140.0, 0.1, 15.0, 0.0, 1.7, 2.7, 54.0, 0.0}, - {30.7, 1477.0, 3.5, 196.0, 0.2, 80.0, 0.0, 17.4, 2.7, 60.0, 0.0}, - {24.2, 1874.0, 4.4, 249.0, 0.3, 37.0, 0.0, 18.2, 3.6, 79.0, 0.0}, - {25.6, 1772.0, 10.4, 152.0, 0.2, 23.0, 0.0, 1.8, 1.8, 71.0, 0.0}, - {27.4, 1655.0, 6.7, 212.0, 0.2, 31.0, 0.0, 9.9, 3.3, 50.0, 0.0}, - {16.0, 2835.0, 18.8, 164.0, 0.1, 26.0, 0.0, 1.4, 1.8, 0.0, 0.0}, - {30.3, 1497.0, 1.8, 184.0, 0.1, 30.0, 0.1, 0.9, 1.8, 68.0, 46.0}, - {42.3, 1072.0, 1.7, 156.0, 0.1, 24.0, 0.0, 1.4, 2.4, 57.0, 0.0}, - {13.0, 3489.0, 5.8, 705.0, 6.8, 45.0, 3.5, 1.0, 4.9, 209.0, 0.0}, - {4.4, 9072.0, 5.8, 27.0, 0.5, 36.0, 7.3, 3.6, 2.7, 5.0, 544.0}, - {6.1, 4982.0, 4.9, 60.0, 0.4, 30.0, 17.4, 2.5, 3.5, 28.0, 498.0}, - {26.0, 2380.0, 1.0, 21.0, 0.5, 14.0, 0.0, 0.5, 0.0, 4.0, 952.0}, - {30.9, 4439.0, 2.2, 40.0, 1.1, 18.0, 11.1, 3.6, 1.3, 10.0, 1993.0}, - {7.1, 5750.0, 2.4, 138.0, 3.7, 80.0, 69.0, 4.3, 5.8, 37.0, 862.0}, - {3.7, 8949.0, 2.6, 125.0, 4.0, 36.0, 7.2, 9.0, 4.5, 26.0, 5369.0}, - {4.7, 6080.0, 2.7, 73.0, 2.8, 43.0, 188.5, 6.1, 4.3, 89.0, 608.0}, - {7.3, 3915.0, 0.9, 51.0, 3.0, 23.0, 0.9, 1.4, 1.4, 9.0, 313.0}, - {8.2, 2247.0, 0.4, 27.0, 1.1, 22.0, 112.4, 1.8, 3.4, 11.0, 449.0}, - {3.6, 11844.0, 5.8, 166.0, 3.8, 59.0, 16.6, 4.7, 5.9, 21.0, 1184.0}, - {34.0, 16810.0, 14.3, 336.0, 1.8, 118.0, 6.7, 29.4, 7.1, 198.0, 2522.0}, - {8.1, 4592.0, 1.1, 106.0, 0.0, 138.0, 918.4, 5.7, 13.8, 33.0, 2755.0}, - {5.1, 7649.0, 9.6, 138.0, 2.7, 54.0, 290.7, 8.4, 5.4, 83.0, 1912.0}, - {16.8, 4894.0, 3.7, 20.0, 0.4, 10.0, 21.5, 0.5, 1.0, 31.0, 196.0}, - {20.4, 4030.0, 3.0, 8.0, 0.3, 8.0, 0.8, 0.8, 0.8, 5.0, 81.0}, - {21.3, 3993.0, 2.4, 16.0, 0.4, 8.0, 2.0, 2.8, 0.8, 7.0, 399.0}, - {27.7, 1945.0, 0.4, 33.0, 0.3, 12.0, 16.3, 1.4, 2.1, 17.0, 272.0}, - {10.0, 5386.0, 1.0, 54.0, 2.0, 65.0, 53.9, 1.6, 4.3, 32.0, 431.0}, - {7.1, 6389.0, 7.5, 364.0, 4.0, 134.0, 3.5, 8.3, 7.7, 56.0, 0.0}, - {10.4, 5452.0, 5.2, 136.0, 0.2, 16.0, 12.0, 1.6, 2.7, 42.0, 218.0}, - {13.8, 4109.0, 2.3, 136.0, 0.6, 45.0, 34.9, 4.9, 2.5, 37.0, 370.0}, - {8.6, 6263.0, 1.3, 63.0, 0.7, 38.0, 53.2, 3.4, 2.5, 36.0, 1253.0}, - {7.6, 3917.0, 1.6, 71.0, 0.6, 43.0, 57.9, 3.5, 2.4, 67.0, 862.0}, - {15.7, 2889.0, 8.5, 87.0, 1.7, 173.0, 86.8, 1.2, 4.3, 55.0, 57.0}, - {9.0, 4284.0, 12.8, 99.0, 2.5, 154.0, 85.7, 3.9, 4.3, 65.0, 257.0}, - {9.4, 4524.0, 13.5, 104.0, 2.5, 136.0, 4.5, 6.3, 1.4, 24.0, 136.0}, - {7.9, 5742.0, 20.0, 1367.0, 4.2, 345.0, 2.9, 28.7, 18.4, 162.0, 0.0}, - {8.9, 5097.0, 17.4, 1055.0, 3.7, 459.0, 5.1, 26.9, 38.2, 93.0, 0.0}, - {5.9, 7688.0, 26.9, 1691.0, 11.4, 792.0, 0.0, 38.4, 24.6, 217.0, 0.0}, - {22.4, 2025.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 5.1, 50.0, 0.0}, - {17.4, 652.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.3, 42.0, 0.0}, - {8.6, 2637.0, 8.7, 237.0, 3.0, 72.0, 0.0, 2.0, 11.9, 40.0, 0.0}, - {16.2, 1400.0, 8.0, 77.0, 1.3, 39.0, 0.0, 0.9, 3.4, 14.0, 0.0}, - {51.7, 8773.0, 34.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {13.7, 4996.0, 14.7, 0.0, 0.5, 74.0, 0.0, 0.0, 0.0, 5.0, 0.0}, - {13.6, 3752.0, 9.0, 0.0, 10.3, 244.0, 0.0, 1.9, 7.5, 146.0, 0.0}, - {20.5, 2213.0, 6.4, 11.0, 0.4, 7.0, 0.2, 0.2, 0.4, 3.0, 0.0} - }; + String[] commodities = { + "Wheat Flour (Enriched), 10 lb.", + "Macaroni, 1 lb.", + "Wheat Cereal (Enriched), 28 oz.", + "Corn Flakes, 8 oz.", + "Corn Meal, 1 lb.", + "Hominy Grits, 24 oz.", + "Rice, 1 lb.", + "Rolled Oats, 1 lb.", + "White Bread (Enriched), 1 lb.", + "Whole Wheat Bread, 1 lb.", + "Rye Bread, 1 lb.", + "Pound Cake, 1 lb.", + "Soda Crackers, 1 lb.", + "Milk, 1 qt.", + "Evaporated Milk (can), 14.5 oz.", + "Butter, 1 lb.", + "Oleomargarine, 1 lb.", + "Eggs, 1 doz.", + "Cheese (Cheddar), 1 lb.", + "Cream, 1/2 pt.", + "Peanut Butter, 1 lb.", + "Mayonnaise, 1/2 pt.", + "Crisco, 1 lb.", + "Lard, 1 lb.", + "Sirloin Steak, 1 lb.", + "Round Steak, 1 lb.", + "Rib Roast, 1 lb.", + "Chuck Roast, 1 lb.", + "Plate, 1 lb.", + "Liver (Beef), 1 lb.", + "Leg of Lamb, 1 lb.", + "Lamb Chops (Rib), 1 lb.", + "Pork Chops, 1 lb.", + "Pork Loin Roast, 1 lb.", + "Bacon, 1 lb.", + "Ham - smoked, 1 lb.", + "Salt Pork, 1 lb.", + "Roasting Chicken, 1 lb.", + "Veal Cutlets, 1 lb.", + "Salmon, Pink (can), 16 oz.", + "Apples, 1 lb.", + "Bananas, 1 lb.", + "Lemons, 1 doz.", + "Oranges, 1 doz.", + "Green Beans, 1 lb.", + "Cabbage, 1 lb.", + "Carrots, 1 bunch", + "Celery, 1 stalk", + "Lettuce, 1 head", + "Onions, 1 lb.", + "Potatoes, 15 lb.", + "Spinach, 1 lb.", + "Sweet Potatoes, 1 lb.", + "Peaches (can), No. 2 1/2", + "Pears (can), No. 2 1/2,", + "Pineapple (can), No. 2 1/2", + "Asparagus (can), No. 2", + "Grean Beans (can), No. 2", + "Pork and Beans (can), 16 oz.", + "Corn (can), No. 2", + "Peas (can), No. 2", + "Tomatoes (can), No. 2", + "Tomato Soup (can), 10 1/2 oz.", + "Peaches, Dried, 1 lb.", + "Prunes, Dried, 1 lb.", + "Raisins, Dried, 15 oz.", + "Peas, Dried, 1 lb.", + "Lima Beans, Dried, 1 lb.", + "Navy Beans, Dried, 1 lb.", + "Coffee, 1 lb.", + "Tea, 1/4 lb.", + "Cocoa, 8 oz.", + "Chocolate, 8 oz.", + "Sugar, 10 lb.", + "Corn Sirup, 24 oz.", + "Molasses, 18 oz.", + "Strawberry Preserve, 1 lb." + }; - // recommended daily nutritional allowance - double[] allowance = {3.0, 70.0, 0.8, 12.0, 5.0, 1.8, 2.7, 18.0, 75.0}; + // price and weight per unit correspond to the two first columns + double[][] data = { + {36.0, 12600.0, 44.7, 1411.0, 2.0, 365.0, 0.0, 55.4, 33.3, 441.0, 0.0}, + {14.1, 3217.0, 11.6, 418.0, 0.7, 54.0, 0.0, 3.2, 1.9, 68.0, 0.0}, + {24.2, 3280.0, 11.8, 377.0, 14.4, 175.0, 0.0, 14.4, 8.8, 114.0, 0.0}, + {7.1, 3194.0, 11.4, 252.0, 0.1, 56.0, 0.0, 13.5, 2.3, 68.0, 0.0}, + {4.6, 9861.0, 36.0, 897.0, 1.7, 99.0, 30.9, 17.4, 7.9, 106.0, 0.0}, + {8.5, 8005.0, 28.6, 680.0, 0.8, 80.0, 0.0, 10.6, 1.6, 110.0, 0.0}, + {7.5, 6048.0, 21.2, 460.0, 0.6, 41.0, 0.0, 2.0, 4.8, 60.0, 0.0}, + {7.1, 6389.0, 25.3, 907.0, 5.1, 341.0, 0.0, 37.1, 8.9, 64.0, 0.0}, + {7.9, 5742.0, 15.6, 488.0, 2.5, 115.0, 0.0, 13.8, 8.5, 126.0, 0.0}, + {9.1, 4985.0, 12.2, 484.0, 2.7, 125.0, 0.0, 13.9, 6.4, 160.0, 0.0}, + {9.2, 4930.0, 12.4, 439.0, 1.1, 82.0, 0.0, 9.9, 3.0, 66.0, 0.0}, + {24.8, 1829.0, 8.0, 130.0, 0.4, 31.0, 18.9, 2.8, 3.0, 17.0, 0.0}, + {15.1, 3004.0, 12.5, 288.0, 0.5, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {11.0, 8867.0, 6.1, 310.0, 10.5, 18.0, 16.8, 4.0, 16.0, 7.0, 177.0}, + {6.7, 6035.0, 8.4, 422.0, 15.1, 9.0, 26.0, 3.0, 23.5, 11.0, 60.0}, + {20.8, 1473.0, 10.8, 9.0, 0.2, 3.0, 44.2, 0.0, 0.2, 2.0, 0.0}, + {16.1, 2817.0, 20.6, 17.0, 0.6, 6.0, 55.8, 0.2, 0.0, 0.0, 0.0}, + {32.6, 1857.0, 2.9, 238.0, 1.0, 52.0, 18.6, 2.8, 6.5, 1.0, 0.0}, + {24.2, 1874.0, 7.4, 448.0, 16.4, 19.0, 28.1, 0.8, 10.3, 4.0, 0.0}, + {14.1, 1689.0, 3.5, 49.0, 1.7, 3.0, 16.9, 0.6, 2.5, 0.0, 17.0}, + {17.9, 2534.0, 15.7, 661.0, 1.0, 48.0, 0.0, 9.6, 8.1, 471.0, 0.0}, + {16.7, 1198.0, 8.6, 18.0, 0.2, 8.0, 2.7, 0.4, 0.5, 0.0, 0.0}, + {20.3, 2234.0, 20.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {9.8, 4628.0, 41.7, 0.0, 0.0, 0.0, 0.2, 0.0, 0.5, 5.0, 0.0}, + {39.6, 1145.0, 2.9, 166.0, 0.1, 34.0, 0.2, 2.1, 2.9, 69.0, 0.0}, + {36.4, 1246.0, 2.2, 214.0, 0.1, 32.0, 0.4, 2.5, 2.4, 87.0, 0.0}, + {29.2, 1553.0, 3.4, 213.0, 0.1, 33.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {22.6, 2007.0, 3.6, 309.0, 0.2, 46.0, 0.4, 1.0, 4.0, 120.0, 0.0}, + {14.6, 3107.0, 8.5, 404.0, 0.2, 62.0, 0.0, 0.9, 0.0, 0.0, 0.0}, + {26.8, 1692.0, 2.2, 333.0, 0.2, 139.0, 169.2, 6.4, 50.8, 316.0, 525.0}, + {27.6, 1643.0, 3.1, 245.0, 0.1, 20.0, 0.0, 2.8, 3.0, 86.0, 0.0}, + {36.6, 1239.0, 3.3, 140.0, 0.1, 15.0, 0.0, 1.7, 2.7, 54.0, 0.0}, + {30.7, 1477.0, 3.5, 196.0, 0.2, 80.0, 0.0, 17.4, 2.7, 60.0, 0.0}, + {24.2, 1874.0, 4.4, 249.0, 0.3, 37.0, 0.0, 18.2, 3.6, 79.0, 0.0}, + {25.6, 1772.0, 10.4, 152.0, 0.2, 23.0, 0.0, 1.8, 1.8, 71.0, 0.0}, + {27.4, 1655.0, 6.7, 212.0, 0.2, 31.0, 0.0, 9.9, 3.3, 50.0, 0.0}, + {16.0, 2835.0, 18.8, 164.0, 0.1, 26.0, 0.0, 1.4, 1.8, 0.0, 0.0}, + {30.3, 1497.0, 1.8, 184.0, 0.1, 30.0, 0.1, 0.9, 1.8, 68.0, 46.0}, + {42.3, 1072.0, 1.7, 156.0, 0.1, 24.0, 0.0, 1.4, 2.4, 57.0, 0.0}, + {13.0, 3489.0, 5.8, 705.0, 6.8, 45.0, 3.5, 1.0, 4.9, 209.0, 0.0}, + {4.4, 9072.0, 5.8, 27.0, 0.5, 36.0, 7.3, 3.6, 2.7, 5.0, 544.0}, + {6.1, 4982.0, 4.9, 60.0, 0.4, 30.0, 17.4, 2.5, 3.5, 28.0, 498.0}, + {26.0, 2380.0, 1.0, 21.0, 0.5, 14.0, 0.0, 0.5, 0.0, 4.0, 952.0}, + {30.9, 4439.0, 2.2, 40.0, 1.1, 18.0, 11.1, 3.6, 1.3, 10.0, 1993.0}, + {7.1, 5750.0, 2.4, 138.0, 3.7, 80.0, 69.0, 4.3, 5.8, 37.0, 862.0}, + {3.7, 8949.0, 2.6, 125.0, 4.0, 36.0, 7.2, 9.0, 4.5, 26.0, 5369.0}, + {4.7, 6080.0, 2.7, 73.0, 2.8, 43.0, 188.5, 6.1, 4.3, 89.0, 608.0}, + {7.3, 3915.0, 0.9, 51.0, 3.0, 23.0, 0.9, 1.4, 1.4, 9.0, 313.0}, + {8.2, 2247.0, 0.4, 27.0, 1.1, 22.0, 112.4, 1.8, 3.4, 11.0, 449.0}, + {3.6, 11844.0, 5.8, 166.0, 3.8, 59.0, 16.6, 4.7, 5.9, 21.0, 1184.0}, + {34.0, 16810.0, 14.3, 336.0, 1.8, 118.0, 6.7, 29.4, 7.1, 198.0, 2522.0}, + {8.1, 4592.0, 1.1, 106.0, 0.0, 138.0, 918.4, 5.7, 13.8, 33.0, 2755.0}, + {5.1, 7649.0, 9.6, 138.0, 2.7, 54.0, 290.7, 8.4, 5.4, 83.0, 1912.0}, + {16.8, 4894.0, 3.7, 20.0, 0.4, 10.0, 21.5, 0.5, 1.0, 31.0, 196.0}, + {20.4, 4030.0, 3.0, 8.0, 0.3, 8.0, 0.8, 0.8, 0.8, 5.0, 81.0}, + {21.3, 3993.0, 2.4, 16.0, 0.4, 8.0, 2.0, 2.8, 0.8, 7.0, 399.0}, + {27.7, 1945.0, 0.4, 33.0, 0.3, 12.0, 16.3, 1.4, 2.1, 17.0, 272.0}, + {10.0, 5386.0, 1.0, 54.0, 2.0, 65.0, 53.9, 1.6, 4.3, 32.0, 431.0}, + {7.1, 6389.0, 7.5, 364.0, 4.0, 134.0, 3.5, 8.3, 7.7, 56.0, 0.0}, + {10.4, 5452.0, 5.2, 136.0, 0.2, 16.0, 12.0, 1.6, 2.7, 42.0, 218.0}, + {13.8, 4109.0, 2.3, 136.0, 0.6, 45.0, 34.9, 4.9, 2.5, 37.0, 370.0}, + {8.6, 6263.0, 1.3, 63.0, 0.7, 38.0, 53.2, 3.4, 2.5, 36.0, 1253.0}, + {7.6, 3917.0, 1.6, 71.0, 0.6, 43.0, 57.9, 3.5, 2.4, 67.0, 862.0}, + {15.7, 2889.0, 8.5, 87.0, 1.7, 173.0, 86.8, 1.2, 4.3, 55.0, 57.0}, + {9.0, 4284.0, 12.8, 99.0, 2.5, 154.0, 85.7, 3.9, 4.3, 65.0, 257.0}, + {9.4, 4524.0, 13.5, 104.0, 2.5, 136.0, 4.5, 6.3, 1.4, 24.0, 136.0}, + {7.9, 5742.0, 20.0, 1367.0, 4.2, 345.0, 2.9, 28.7, 18.4, 162.0, 0.0}, + {8.9, 5097.0, 17.4, 1055.0, 3.7, 459.0, 5.1, 26.9, 38.2, 93.0, 0.0}, + {5.9, 7688.0, 26.9, 1691.0, 11.4, 792.0, 0.0, 38.4, 24.6, 217.0, 0.0}, + {22.4, 2025.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 5.1, 50.0, 0.0}, + {17.4, 652.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.3, 42.0, 0.0}, + {8.6, 2637.0, 8.7, 237.0, 3.0, 72.0, 0.0, 2.0, 11.9, 40.0, 0.0}, + {16.2, 1400.0, 8.0, 77.0, 1.3, 39.0, 0.0, 0.9, 3.4, 14.0, 0.0}, + {51.7, 8773.0, 34.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {13.7, 4996.0, 14.7, 0.0, 0.5, 74.0, 0.0, 0.0, 0.0, 5.0, 0.0}, + {13.6, 3752.0, 9.0, 0.0, 10.3, 244.0, 0.0, 1.9, 7.5, 146.0, 0.0}, + {20.5, 2213.0, 6.4, 11.0, 0.4, 7.0, 0.2, 0.2, 0.4, 3.0, 0.0} + }; + // recommended daily nutritional allowance + double[] allowance = {3.0, 70.0, 0.8, 12.0, 5.0, 1.8, 2.7, 18.0, 75.0}; - /** variables */ - MPVariable[] x = solver.makeNumVarArray(commoditiesCount, 0, 1000); - MPVariable[] xCost = solver.makeNumVarArray(commoditiesCount, 0, 1000); - MPVariable[] quant = solver.makeNumVarArray(commoditiesCount, 0, 1000); + /** variables */ + MPVariable[] x = solver.makeNumVarArray(commoditiesCount, 0, 1000); + MPVariable[] xCost = solver.makeNumVarArray(commoditiesCount, 0, 1000); + MPVariable[] quant = solver.makeNumVarArray(commoditiesCount, 0, 1000); - MPVariable totalCost = solver.makeNumVar(0, 1000, "total_cost"); + MPVariable totalCost = solver.makeNumVar(0, 1000, "total_cost"); - /** constraints & objective */ - MPObjective obj = solver.objective(); + /** constraints & objective */ + MPObjective obj = solver.objective(); - MPConstraint[] costConstraint = new MPConstraint[2 * commoditiesCount]; - MPConstraint[] quantConstraint = new MPConstraint[2 * commoditiesCount]; + MPConstraint[] costConstraint = new MPConstraint[2 * commoditiesCount]; + MPConstraint[] quantConstraint = new MPConstraint[2 * commoditiesCount]; - MPConstraint totalCostConstraint = solver.makeConstraint(0, 0); + MPConstraint totalCostConstraint = solver.makeConstraint(0, 0); - for (int i = 0; i < commoditiesCount; i ++) { - totalCostConstraint.setCoefficient(x[i], days); + for (int i = 0; i < commoditiesCount; i++) { + totalCostConstraint.setCoefficient(x[i], days); - costConstraint[i] = solver.makeConstraint(0, 0); - costConstraint[i].setCoefficient(x[i], days); - costConstraint[i].setCoefficient(xCost[i], -1); + costConstraint[i] = solver.makeConstraint(0, 0); + costConstraint[i].setCoefficient(x[i], days); + costConstraint[i].setCoefficient(xCost[i], -1); - quantConstraint[i] = solver.makeConstraint(0, 0); - quantConstraint[i].setCoefficient(x[i], days * 100 / data[i][0]); - quantConstraint[i].setCoefficient(quant[i], -1); + quantConstraint[i] = solver.makeConstraint(0, 0); + quantConstraint[i].setCoefficient(x[i], days * 100 / data[i][0]); + quantConstraint[i].setCoefficient(quant[i], -1); - obj.setCoefficient(x[i], 1); - } - totalCostConstraint.setCoefficient(totalCost, -1); + obj.setCoefficient(x[i], 1); + } + totalCostConstraint.setCoefficient(totalCost, -1); - MPConstraint[] nutrientConstraint = new MPConstraint[nutrientsCount]; + MPConstraint[] nutrientConstraint = new MPConstraint[nutrientsCount]; - for (int i = 0; i < nutrientsCount; i ++) { - nutrientConstraint[i] = solver.makeConstraint(allowance[i], infinity); - for (int j = 0; j < commoditiesCount; j ++) { - nutrientConstraint[i].setCoefficient(x[j], data[j][i+2]); - } - } + for (int i = 0; i < nutrientsCount; i++) { + nutrientConstraint[i] = solver.makeConstraint(allowance[i], infinity); + for (int j = 0; j < commoditiesCount; j++) { + nutrientConstraint[i].setCoefficient(x[j], data[j][i + 2]); + } + } - solver.solve(); + solver.solve(); - /** printing */ - DecimalFormat df = new DecimalFormat("#.##"); - df.setRoundingMode(RoundingMode.CEILING); + /** printing */ + DecimalFormat df = new DecimalFormat("#.##"); + df.setRoundingMode(RoundingMode.CEILING); - System.out.println("Min cost: " + df.format(obj.value())); - System.out.println("Total cost: " + df.format(totalCost.solutionValue())); + System.out.println("Min cost: " + df.format(obj.value())); + System.out.println("Total cost: " + df.format(totalCost.solutionValue())); - for (int i = 0; i < commoditiesCount; i ++) { - if (x[i].solutionValue() > 0) { - System.out.println(commodities[i] + ": " + df.format(xCost[i].solutionValue()) + " " + df.format(quant[i].solutionValue())); - } - } - } + for (int i = 0; i < commoditiesCount; i++) { + if (x[i].solutionValue() > 0) { + System.out.println( + commodities[i] + + ": " + + df.format(xCost[i].solutionValue()) + + " " + + df.format(quant[i].solutionValue())); + } + } + } - public static void main(String[] args) { - try { - System.out.println("---- Integer programming example with SCIP (recommended) ----"); - solve("SCIP_MIXED_INTEGER_PROGRAMMING"); - } catch (java.lang.IllegalArgumentException e) { - System.err.println("Bad solver type: " + e); - } - try { - System.out.println("---- Integer programming example with CBC ----"); - solve("CBC_MIXED_INTEGER_PROGRAMMING"); - } catch (java.lang.IllegalArgumentException e) { - System.err.println("Bad solver type: " + e); - } - try { - System.out.println("---- Integer programming example with GLPK ----"); - solve("GLPK_MIXED_INTEGER_PROGRAMMING"); - } catch (java.lang.IllegalArgumentException e) { - System.err.println("Bad solver type: " + e); - } - } + public static void main(String[] args) { + try { + System.out.println("---- Integer programming example with SCIP (recommended) ----"); + solve("SCIP_MIXED_INTEGER_PROGRAMMING"); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + } + try { + System.out.println("---- Integer programming example with CBC ----"); + solve("CBC_MIXED_INTEGER_PROGRAMMING"); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + } + try { + System.out.println("---- Integer programming example with GLPK ----"); + solve("GLPK_MIXED_INTEGER_PROGRAMMING"); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + } + } } diff --git a/examples/contrib/Strimko2.java b/examples/contrib/Strimko2.java index 47e4c135f48..032371be637 100644 --- a/examples/contrib/Strimko2.java +++ b/examples/contrib/Strimko2.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class Strimko2 { @@ -24,13 +23,7 @@ public class Strimko2 { System.loadLibrary("jniortools"); } - - /** - * - * Solves a Strimko problem. - * See http://www.hakank.org/google_or_tools/strimko2.py - * - */ + /** Solves a Strimko problem. See http://www.hakank.org/google_or_tools/strimko2.py */ private static void solve() { Solver solver = new Solver("Strimko2"); @@ -38,39 +31,41 @@ private static void solve() { // // data // - int[][] streams = {{1,1,2,2,2,2,2}, - {1,1,2,3,3,3,2}, - {1,4,1,3,3,5,5}, - {4,4,3,1,3,5,5}, - {4,6,6,6,7,7,5}, - {6,4,6,4,5,5,7}, - {6,6,4,7,7,7,7}}; + int[][] streams = { + {1, 1, 2, 2, 2, 2, 2}, + {1, 1, 2, 3, 3, 3, 2}, + {1, 4, 1, 3, 3, 5, 5}, + {4, 4, 3, 1, 3, 5, 5}, + {4, 6, 6, 6, 7, 7, 5}, + {6, 4, 6, 4, 5, 5, 7}, + {6, 6, 4, 7, 7, 7, 7} + }; // Note: This is 1-based - int[][] placed = {{2,1,1}, - {2,3,7}, - {2,5,6}, - {2,7,4}, - {3,2,7}, - {3,6,1}, - {4,1,4}, - {4,7,5}, - {5,2,2}, - {5,6,6}}; + int[][] placed = { + {2, 1, 1}, + {2, 3, 7}, + {2, 5, 6}, + {2, 7, 4}, + {3, 2, 7}, + {3, 6, 1}, + {4, 1, 4}, + {4, 7, 5}, + {5, 2, 2}, + {5, 6, 6} + }; int n = streams.length; int num_placed = placed.length; - - // // variables // IntVar[][] x = new IntVar[n][n]; IntVar[] x_flat = new IntVar[n * n]; - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { x[i][j] = solver.makeIntVar(1, n, "x[" + i + "," + j + "]"); x_flat[i * n + j] = x[i][j]; } @@ -81,11 +76,11 @@ private static void solve() { // // all rows and columns must be unique, i.e. a Latin Square - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { IntVar[] row = new IntVar[n]; IntVar[] col = new IntVar[n]; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { row[j] = x[i][j]; col[j] = x[j][i]; } @@ -94,50 +89,43 @@ private static void solve() { solver.addConstraint(solver.makeAllDifferent(col)); } - // streams - for(int s = 1; s <= n; s++) { + for (int s = 1; s <= n; s++) { ArrayList tmp = new ArrayList(); - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { if (streams[i][j] == s) { tmp.add(x[i][j]); } } } - solver.addConstraint( - solver.makeAllDifferent(tmp.toArray(new IntVar[1]))); + solver.addConstraint(solver.makeAllDifferent(tmp.toArray(new IntVar[1]))); } - // placed - for(int i = 0; i < num_placed; i++) { + for (int i = 0; i < num_placed; i++) { // note: also adjust to 0-based solver.addConstraint( - solver.makeEquality(x[placed[i][0] - 1][placed[i][1] - 1], - placed[i][2])); + solver.makeEquality(x[placed[i][0] - 1][placed[i][1] - 1], placed[i][2])); } // // search // - DecisionBuilder db = solver.makePhase(x_flat, - solver.INT_VAR_DEFAULT, - solver.INT_VALUE_DEFAULT); + DecisionBuilder db = solver.makePhase(x_flat, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); solver.newSearch(db); // // output // while (solver.nextSolution()) { - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { System.out.print(x[i][j].value() + " "); } System.out.println(); } System.out.println(); - } solver.endSearch(); @@ -147,12 +135,10 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { Strimko2.solve(); - } } diff --git a/examples/contrib/Sudoku.java b/examples/contrib/Sudoku.java index a63be9f9e21..2ccf0fb73b3 100644 --- a/examples/contrib/Sudoku.java +++ b/examples/contrib/Sudoku.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class Sudoku { @@ -24,12 +23,7 @@ public class Sudoku { System.loadLibrary("jniortools"); } - - /** - * - * Solves a Sudoku problem. - * - */ + /** Solves a Sudoku problem. */ private static void solve() { Solver solver = new Solver("Sudoku"); @@ -38,15 +32,18 @@ private static void solve() { int n = cell_size * cell_size; // 0 marks an unknown value - int[][] initial_grid = new int[][] {{0, 6, 0, 0, 5, 0, 0, 2, 0}, - {0, 0, 0, 3, 0, 0, 0, 9, 0}, - {7, 0, 0, 6, 0, 0, 0, 1, 0}, - {0, 0, 6, 0, 3, 0, 4, 0, 0}, - {0, 0, 4, 0, 7, 0, 1, 0, 0}, - {0, 0, 5, 0, 9, 0, 8, 0, 0}, - {0, 4, 0, 0, 0, 1, 0, 0, 6}, - {0, 3, 0, 0, 0, 8, 0, 0, 0}, - {0, 2, 0, 0, 4, 0, 0, 5, 0}}; + int[][] initial_grid = + new int[][] { + {0, 6, 0, 0, 5, 0, 0, 2, 0}, + {0, 0, 0, 3, 0, 0, 0, 9, 0}, + {7, 0, 0, 6, 0, 0, 0, 1, 0}, + {0, 0, 6, 0, 3, 0, 4, 0, 0}, + {0, 0, 4, 0, 7, 0, 1, 0, 0}, + {0, 0, 5, 0, 9, 0, 8, 0, 0}, + {0, 4, 0, 0, 0, 1, 0, 0, 6}, + {0, 3, 0, 0, 0, 8, 0, 0, 0}, + {0, 2, 0, 0, 4, 0, 0, 5, 0} + }; // // variables @@ -54,9 +51,9 @@ private static void solve() { IntVar[][] grid = new IntVar[n][n]; IntVar[] grid_flat = new IntVar[n * n]; - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { - grid[i][j] = solver.makeIntVar(1, 9, "grid[" + i +"," + j + "]"); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + grid[i][j] = solver.makeIntVar(1, 9, "grid[" + i + "," + j + "]"); grid_flat[i * n + j] = grid[i][j]; } } @@ -66,12 +63,11 @@ private static void solve() { // // init and rows - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { IntVar[] row = new IntVar[n]; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { if (initial_grid[i][j] > 0) { - solver.addConstraint( - solver.makeEquality(grid[i][j], initial_grid[i][j])); + solver.addConstraint(solver.makeEquality(grid[i][j], initial_grid[i][j])); } row[j] = grid[i][j]; } @@ -79,22 +75,21 @@ private static void solve() { } // columns - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { IntVar[] col = new IntVar[n]; - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { col[i] = grid[i][j]; } solver.addConstraint(solver.makeAllDifferent(col)); } // cells - for(int i = 0; i < cell_size; i++) { - for(int j = 0; j < cell_size; j++) { + for (int i = 0; i < cell_size; i++) { + for (int j = 0; j < cell_size; j++) { IntVar[] cell = new IntVar[n]; - for(int di = 0; di < cell_size; di++) { - for(int dj = 0; dj < cell_size; dj++) { - cell[di * cell_size + dj] = - grid[i * cell_size + di][j * cell_size + dj]; + for (int di = 0; di < cell_size; di++) { + for (int dj = 0; dj < cell_size; dj++) { + cell[di * cell_size + dj] = grid[i * cell_size + di][j * cell_size + dj]; } } solver.addConstraint(solver.makeAllDifferent(cell)); @@ -104,15 +99,14 @@ private static void solve() { // // Search // - DecisionBuilder db = solver.makePhase(grid_flat, - solver.INT_VAR_SIMPLE, - solver.INT_VALUE_SIMPLE); + DecisionBuilder db = + solver.makePhase(grid_flat, solver.INT_VAR_SIMPLE, solver.INT_VALUE_SIMPLE); solver.newSearch(db); while (solver.nextSolution()) { - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { System.out.print(grid[i][j].value() + " "); } System.out.println(); @@ -127,7 +121,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/SurvoPuzzle.java b/examples/contrib/SurvoPuzzle.java index 09de8452574..69af936e423 100644 --- a/examples/contrib/SurvoPuzzle.java +++ b/examples/contrib/SurvoPuzzle.java @@ -10,14 +10,13 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - +import com.google.ortools.constraintsolver.*; import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; -import com.google.ortools.constraintsolver.*; +import java.io.*; +import java.text.*; +import java.util.*; public class SurvoPuzzle { @@ -32,10 +31,11 @@ public class SurvoPuzzle { static int default_c = 4; static int[] default_rowsums = {30, 18, 30}; static int[] default_colsums = {27, 16, 10, 25}; - static int[][] default_game = {{0, 6, 0, 0}, - {8, 0, 0, 0}, - {0, 0, 3, 0}}; - + static int[][] default_game = { + {0, 6, 0, 0}, + {8, 0, 0, 0}, + {0, 0, 3, 0} + }; // for the actual problem static int r; @@ -44,13 +44,7 @@ public class SurvoPuzzle { static int[] colsums; static int[][] game; - - /** - * - * Solves the Survo puzzle problem. - * See http://www.hakank.org/google_or_tools/survo_puzzle.py - * - */ + /** Solves the Survo puzzle problem. See http://www.hakank.org/google_or_tools/survo_puzzle.py */ private static void solve() { Solver solver = new Solver("Survopuzzle"); @@ -59,22 +53,21 @@ private static void solve() { // data // System.out.println("Problem:"); - for(int i = 0; i < r; i++) { - for(int j = 0; j < c; j++) { + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { System.out.print(game[i][j] + " "); } System.out.println(); } System.out.println(); - // // Variables // - IntVar[][] x = new IntVar[r][c]; + IntVar[][] x = new IntVar[r][c]; IntVar[] x_flat = new IntVar[r * c]; // for branching - for(int i = 0; i < r; i++) { - for(int j = 0; j < c; j++) { + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { x[i][j] = solver.makeIntVar(1, r * c, "x[" + i + "," + j + "]"); x_flat[i * c + j] = x[i][j]; } @@ -83,11 +76,10 @@ private static void solve() { // // Constraints // - for(int i = 0; i < r; i++) { - for(int j = 0; j < c; j++) { + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { if (game[i][j] > 0) { - solver.addConstraint( - solver.makeEquality(x[i][j], game[i][j])); + solver.addConstraint(solver.makeEquality(x[i][j], game[i][j])); } } } @@ -97,39 +89,34 @@ private static void solve() { // // calculate rowsums and colsums // - for(int i = 0; i < r; i++) { + for (int i = 0; i < r; i++) { IntVar[] row = new IntVar[c]; - for(int j = 0; j < c; j++) { + for (int j = 0; j < c; j++) { row[j] = x[i][j]; } - solver.addConstraint( - solver.makeEquality(solver.makeSum(row).var(), rowsums[i])); + solver.addConstraint(solver.makeEquality(solver.makeSum(row).var(), rowsums[i])); } - for(int j = 0; j < c; j++) { + for (int j = 0; j < c; j++) { IntVar[] col = new IntVar[r]; - for(int i = 0; i < r; i++) { + for (int i = 0; i < r; i++) { col[i] = x[i][j]; } - solver.addConstraint( - solver.makeEquality(solver.makeSum(col).var(), colsums[j])); + solver.addConstraint(solver.makeEquality(solver.makeSum(col).var(), colsums[j])); } - // // Search // - DecisionBuilder db = solver.makePhase(x_flat, - solver.INT_VAR_SIMPLE, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = solver.makePhase(x_flat, solver.INT_VAR_SIMPLE, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); int sol = 0; while (solver.nextSolution()) { sol++; System.out.println("Solution #" + sol + ":"); - for(int i = 0; i < r; i++) { - for(int j = 0; j < c; j++) { + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { System.out.print(x[i][j].value() + " "); } System.out.println(); @@ -145,30 +132,14 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } /** - * * readFile() * - * Reads a Survo puzzle in the following format - * r - * c - * rowsums - * olsums - * data - * ... - * - * Example: - * 3 - * 4 - * 30,18,30 - * 27,16,10,25 - * 0,6,0,0 - * 8,0,0,0 - * 0,0,3,0 + *

Reads a Survo puzzle in the following format r c rowsums olsums data ... * + *

Example: 3 4 30,18,30 27,16,10,25 0,6,0,0 8,0,0,0 0,0,3,0 */ private static void readFile(String file) { @@ -187,12 +158,12 @@ private static void readFile(String file) { String[] rowsums_str = inr.readLine().split(",\\s*"); String[] colsums_str = inr.readLine().split(",\\s*"); System.out.println("rowsums:"); - for(int i = 0; i < r; i++) { + for (int i = 0; i < r; i++) { System.out.print(rowsums_str[i] + " "); rowsums[i] = Integer.parseInt(rowsums_str[i]); } System.out.println("\ncolsums:"); - for(int j = 0; j < c; j++) { + for (int j = 0; j < c; j++) { System.out.print(colsums_str[j] + " "); colsums[j] = Integer.parseInt(colsums_str[j]); } @@ -207,17 +178,16 @@ private static void readFile(String file) { // ignore comments // starting with either # or % - if(str.startsWith("#") || str.startsWith("%")) { + if (str.startsWith("#") || str.startsWith("%")) { continue; } String this_row[] = str.split(",\\s*"); - for(int j = 0; j < this_row.length; j++) { + for (int j = 0; j < this_row.length; j++) { game[line_count][j] = Integer.parseInt(this_row[j]); } line_count++; - } // end while inr.close(); @@ -225,10 +195,8 @@ private static void readFile(String file) { } catch (IOException e) { System.out.println(e); } - } // end readFile - public static void main(String[] args) throws Exception { if (args.length > 0) { @@ -243,6 +211,5 @@ public static void main(String[] args) throws Exception { } SurvoPuzzle.solve(); - } } diff --git a/examples/contrib/ToNum.java b/examples/contrib/ToNum.java index 2e07c4e6429..037c0655131 100644 --- a/examples/contrib/ToNum.java +++ b/examples/contrib/ToNum.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class ToNum { @@ -24,31 +23,24 @@ public class ToNum { System.loadLibrary("jniortools"); } - /** + * toNum(solver, a, num, base) * - * toNum(solver, a, num, base) - * - * channelling between the array a and the number num - * + *

channelling between the array a and the number num */ private static void toNum(Solver solver, IntVar[] a, IntVar num, int base) { int len = a.length; IntVar[] tmp = new IntVar[len]; - for(int i = 0; i < len; i++) { - tmp[i] = solver.makeProd(a[i], (int)Math.pow(base,(len-i-1))).var(); + for (int i = 0; i < len; i++) { + tmp[i] = solver.makeProd(a[i], (int) Math.pow(base, (len - i - 1))).var(); } - solver.addConstraint( - solver.makeEquality(solver.makeSum(tmp).var(), num)); + solver.addConstraint(solver.makeEquality(solver.makeSum(tmp).var(), num)); } - /** - * - * Implements toNum: channeling between a number and an array. - * See http://www.hakank.org/google_or_tools/toNum.py - * + * Implements toNum: channeling between a number and an array. See + * http://www.hakank.org/google_or_tools/toNum.py */ private static void solve() { @@ -57,34 +49,27 @@ private static void solve() { int n = 5; int base = 10; - // // variables // - IntVar[] x = solver.makeIntVarArray(n, 0, base - 1, "x"); - IntVar num = solver.makeIntVar(0, (int)Math.pow(base, n) - 1 , "num"); - - + IntVar[] x = solver.makeIntVarArray(n, 0, base - 1, "x"); + IntVar num = solver.makeIntVar(0, (int) Math.pow(base, n) - 1, "num"); // // constraints // solver.addConstraint(solver.makeAllDifferent(x)); - toNum(solver, x, num, base); // extra constraint (just for fun): // second digit should be 7 // solver.addConstraint(solver.makeEquality(x[1], 7)); - // // search // - DecisionBuilder db = solver.makePhase(x, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = solver.makePhase(x, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); @@ -93,7 +78,7 @@ private static void solve() { // while (solver.nextSolution()) { System.out.print("num: " + num.value() + ": "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(x[i].value() + " "); } System.out.println(); @@ -106,7 +91,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/WhoKilledAgatha.java b/examples/contrib/WhoKilledAgatha.java index 83543e11eba..d4b191dad5e 100644 --- a/examples/contrib/WhoKilledAgatha.java +++ b/examples/contrib/WhoKilledAgatha.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class WhoKilledAgatha { @@ -24,12 +23,9 @@ public class WhoKilledAgatha { System.loadLibrary("jniortools"); } - /** - * - * Implements the Who killed Agatha problem. - * See http://www.hakank.org/google_or_tools/who_killed_agatha.py - * + * Implements the Who killed Agatha problem. See + * http://www.hakank.org/google_or_tools/who_killed_agatha.py */ private static void solve() { @@ -55,8 +51,8 @@ private static void solve() { IntVar[][] hates = new IntVar[n][n]; IntVar[] hates_flat = new IntVar[n * n]; - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { hates[i][j] = solver.makeIntVar(0, 1, "hates[" + i + "," + j + "]"); hates_flat[i * n + j] = hates[i][j]; all[i * n + j] = hates[i][j]; @@ -65,8 +61,8 @@ private static void solve() { IntVar[][] richer = new IntVar[n][n]; IntVar[] richer_flat = new IntVar[n * n]; - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { richer[i][j] = solver.makeIntVar(0, 1, "richer[" + i + "," + j + "]"); richer_flat[i * n + j] = richer[i][j]; all[(n * n) + (i * n + j)] = richer[i][j]; @@ -85,32 +81,34 @@ private static void solve() { // hates_flat[the_killer * n + the_victim] == 1 solver.addConstraint( solver.makeEquality( - solver.makeElement( - hates_flat, - solver.makeSum( - solver.makeProd(the_killer, n).var(), - the_victim).var()).var(), 1)); + solver + .makeElement( + hates_flat, + solver.makeSum(solver.makeProd(the_killer, n).var(), the_victim).var()) + .var(), + 1)); // richer[the_killer, the_victim] == 0 solver.addConstraint( solver.makeEquality( - solver.makeElement( - richer_flat, - solver.makeSum( - solver.makeProd(the_killer, n).var(), - the_victim).var()).var(), 0)); + solver + .makeElement( + richer_flat, + solver.makeSum(solver.makeProd(the_killer, n).var(), the_victim).var()) + .var(), + 0)); // define the concept of richer: // no one is richer than him-/herself... - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { solver.addConstraint(solver.makeEquality(richer[i][i], 0)); } // (contd...) if i is richer than j then j is not richer than i // if (i != j) => // ((richer[i,j] = 1) <=> (richer[j,i] = 0)) - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { if (i != j) { IntVar bi = solver.makeIsEqualCstVar(richer[i][j], 1); IntVar bj = solver.makeIsEqualCstVar(richer[j][i], 0); @@ -122,12 +120,10 @@ private static void solve() { // Charles hates no one that Agatha hates. // forall i in 0..2: // (hates[agatha, i] = 1) => (hates[charles, i] = 0) - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { IntVar b1a = solver.makeIsEqualCstVar(hates[agatha][i], 1); IntVar b1b = solver.makeIsEqualCstVar(hates[charles][i], 0); - solver.addConstraint( - solver.makeLessOrEqual( - solver.makeDifference(b1a, b1b).var(), 0)); + solver.addConstraint(solver.makeLessOrEqual(solver.makeDifference(b1a, b1b).var(), 0)); } // Agatha hates everybody except the butler. @@ -138,47 +134,40 @@ private static void solve() { // The butler hates everyone not richer than Aunt Agatha. // forall i in 0..2: // (richer[i, agatha] = 0) => (hates[butler, i] = 1) - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { IntVar b2a = solver.makeIsEqualCstVar(richer[i][agatha], 0); IntVar b2b = solver.makeIsEqualCstVar(hates[butler][i], 1); - solver.addConstraint( - solver.makeLessOrEqual( - solver.makeDifference(b2a, b2b).var(), 0)); + solver.addConstraint(solver.makeLessOrEqual(solver.makeDifference(b2a, b2b).var(), 0)); } // The butler hates everyone whom Agatha hates. // forall i : 0..2: // (hates[agatha, i] = 1) => (hates[butler, i] = 1) - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { IntVar b3a = solver.makeIsEqualCstVar(hates[agatha][i], 1); IntVar b3b = solver.makeIsEqualCstVar(hates[butler][i], 1); - solver.addConstraint( - solver.makeLessOrEqual( - solver.makeDifference(b3a, b3b).var(), 0)); + solver.addConstraint(solver.makeLessOrEqual(solver.makeDifference(b3a, b3b).var(), 0)); } // Noone hates everyone. // forall i in 0..2: // (sum j in 0..2: hates[i,j]) <= 2 - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { IntVar[] tmp = new IntVar[n]; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { tmp[j] = hates[i][j]; } - solver.addConstraint( - solver.makeLessOrEqual(solver.makeSum(tmp).var(), 2)); + solver.addConstraint(solver.makeLessOrEqual(solver.makeSum(tmp).var(), 2)); } - // Who killed Agatha? solver.addConstraint(solver.makeEquality(the_victim, agatha)); // // search // - DecisionBuilder db = solver.makePhase(all, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = + solver.makePhase(all, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); @@ -186,7 +175,7 @@ private static void solve() { // output // while (solver.nextSolution()) { - System.out.println("the_killer: " + names[(int)the_killer.value()]); + System.out.println("the_killer: " + names[(int) the_killer.value()]); } solver.endSearch(); @@ -196,7 +185,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/Xkcd.java b/examples/contrib/Xkcd.java index 90509db95f2..b31a56b8b7d 100644 --- a/examples/contrib/Xkcd.java +++ b/examples/contrib/Xkcd.java @@ -10,14 +10,13 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - +import com.google.ortools.constraintsolver.*; import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; -import com.google.ortools.constraintsolver.*; +import java.io.*; +import java.text.*; +import java.util.*; public class Xkcd { @@ -25,20 +24,14 @@ public class Xkcd { System.loadLibrary("jniortools"); } - - /** - * - * Solves the xkcd problem. - * See http://www.hakank.org/google_or_tools/xkcd.py - * - */ + /** Solves the xkcd problem. See http://www.hakank.org/google_or_tools/xkcd.py */ private static void solve() { Solver solver = new Solver("Xkcd"); int n = 6; // for price and total: multiplied by 100 to be able to use integers - int[]price = {215, 275, 335, 355, 420, 580}; + int[] price = {215, 275, 335, 355, 420, 580}; int total = 1505; // @@ -49,20 +42,17 @@ private static void solve() { // // Constraints // - solver.addConstraint( - solver.makeEquality(solver.makeScalProd(x, price).var(), total)); + solver.addConstraint(solver.makeEquality(solver.makeScalProd(x, price).var(), total)); // // Search // - DecisionBuilder db = solver.makePhase(x, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = solver.makePhase(x, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); while (solver.nextSolution()) { System.out.print("x: "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(x[i].value() + " "); } System.out.println(); @@ -75,7 +65,6 @@ private static void solve() { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/YoungTableaux.java b/examples/contrib/YoungTableaux.java index ae921a9c235..d963e5ef9a9 100644 --- a/examples/contrib/YoungTableaux.java +++ b/examples/contrib/YoungTableaux.java @@ -10,13 +10,12 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; public class YoungTableaux { @@ -24,13 +23,9 @@ public class YoungTableaux { System.loadLibrary("jniortools"); } - - /** - * - * Implements Young tableaux and partitions. - * See http://www.hakank.org/google_or_tools/young_tableuax.py - * + * Implements Young tableaux and partitions. See + * http://www.hakank.org/google_or_tools/young_tableuax.py */ private static void solve(int n) { @@ -41,11 +36,11 @@ private static void solve(int n) { // // variables // - IntVar[][] x = new IntVar[n][n]; + IntVar[][] x = new IntVar[n][n]; IntVar[] x_flat = new IntVar[n * n]; - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { x[i][j] = solver.makeIntVar(1, n + 1, "x[" + i + "," + j + "]"); x_flat[i * n + j] = x[i][j]; } @@ -59,52 +54,46 @@ private static void solve(int n) { // // 1..n is used exactly once - for(int i = 1; i <= n; i++) { + for (int i = 1; i <= n; i++) { solver.addConstraint(solver.makeCount(x_flat, i, 1)); } solver.addConstraint(solver.makeEquality(x[0][0], 1)); // row wise - for(int i = 0; i < n; i++) { - for(int j = 1; j < n; j++) { - solver.addConstraint( - solver.makeGreaterOrEqual(x[i][j], x[i][j - 1])); + for (int i = 0; i < n; i++) { + for (int j = 1; j < n; j++) { + solver.addConstraint(solver.makeGreaterOrEqual(x[i][j], x[i][j - 1])); } } // column wise - for(int j = 0; j < n; j++) { - for(int i = 1; i < n; i++) { - solver.addConstraint( - solver.makeGreaterOrEqual(x[i][j], x[i - 1][j])); + for (int j = 0; j < n; j++) { + for (int i = 1; i < n; i++) { + solver.addConstraint(solver.makeGreaterOrEqual(x[i][j], x[i - 1][j])); } } // calculate the structure (i.e. the partition) - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { IntVar[] b = new IntVar[n]; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { b[j] = solver.makeIsLessOrEqualCstVar(x[i][j], n); } - solver.addConstraint( - solver.makeEquality(p[i], solver.makeSum(b).var())); + solver.addConstraint(solver.makeEquality(p[i], solver.makeSum(b).var())); } - solver.addConstraint( - solver.makeEquality(solver.makeSum(p).var(), n)); + solver.addConstraint(solver.makeEquality(solver.makeSum(p).var(), n)); - for(int i = 1; i < n; i++) { + for (int i = 1; i < n; i++) { solver.addConstraint(solver.makeGreaterOrEqual(p[i - 1], p[i])); } - // // search // - DecisionBuilder db = solver.makePhase(x_flat, - solver.CHOOSE_FIRST_UNBOUND, - solver.ASSIGN_MIN_VALUE); + DecisionBuilder db = + solver.makePhase(x_flat, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); solver.newSearch(db); @@ -113,13 +102,13 @@ private static void solve(int n) { // while (solver.nextSolution()) { System.out.print("p: "); - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { System.out.print(p[i].value() + " "); } System.out.println("\nx:"); - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { long val = x[i][j].value(); if (val <= n) { System.out.print(val + " "); @@ -130,7 +119,6 @@ private static void solve(int n) { } } System.out.println(); - } solver.endSearch(); @@ -140,7 +128,6 @@ private static void solve(int n) { System.out.println("Failures: " + solver.failures()); System.out.println("Branches: " + solver.branches()); System.out.println("Wall time: " + solver.wallTime() + "ms"); - } public static void main(String[] args) throws Exception { diff --git a/examples/contrib/bacp.py b/examples/contrib/bacp.py index 6c000302d89..71fec5c782d 100644 --- a/examples/contrib/bacp.py +++ b/examples/contrib/bacp.py @@ -1,13 +1,13 @@ # Copyright 2010 Pierre Schaus pschaus@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/broken_weights.py b/examples/contrib/broken_weights.py index 367b43373b6..05bf69bce9e 100644 --- a/examples/contrib/broken_weights.py +++ b/examples/contrib/broken_weights.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/combinatorial_auction2.py b/examples/contrib/combinatorial_auction2.py index bf0ddb18908..95ba31745d6 100644 --- a/examples/contrib/combinatorial_auction2.py +++ b/examples/contrib/combinatorial_auction2.py @@ -11,9 +11,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. -""" - - Combinatorial auction in Google CP Solver. +"""Combinatorial auction in Google CP Solver. This is a more general model for the combinatorial example in the Numberjack Tutorial, pages 9 and 24 (slides 19/175 and diff --git a/examples/contrib/debruijn_binary.py b/examples/contrib/debruijn_binary.py index 0039d466f8f..4ea26f9fa88 100644 --- a/examples/contrib/debruijn_binary.py +++ b/examples/contrib/debruijn_binary.py @@ -50,8 +50,8 @@ def toNum(solver, t, s, base): tlen = len(t) - solver.Add(s == solver.Sum([(base**(tlen - i - 1)) * t[i] - for i in range(tlen)])) + solver.Add( + s == solver.Sum([(base**(tlen - i - 1)) * t[i] for i in range(tlen)])) def main(base=2, n=3, m=8): diff --git a/examples/contrib/divisible_by_9_through_1.py b/examples/contrib/divisible_by_9_through_1.py index f17472e3c7d..b0ef46d7059 100644 --- a/examples/contrib/divisible_by_9_through_1.py +++ b/examples/contrib/divisible_by_9_through_1.py @@ -100,8 +100,8 @@ def my_mod(solver, x, y, r): # def toNum(solver, t, s, base): tlen = len(t) - solver.Add(s == solver.Sum([(base**(tlen - i - 1)) * t[i] - for i in range(tlen)])) + solver.Add( + s == solver.Sum([(base**(tlen - i - 1)) * t[i] for i in range(tlen)])) def main(base=10): diff --git a/examples/contrib/dudeney.py b/examples/contrib/dudeney.py index d7a9e168c59..ef9abfc4755 100644 --- a/examples/contrib/dudeney.py +++ b/examples/contrib/dudeney.py @@ -1,12 +1,12 @@ # Copyright 2010 Pierre Schaus (pschaus@gmail.com) -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/einav_puzzle.py b/examples/contrib/einav_puzzle.py index 07218e1c863..6d5ed2c0a7b 100644 --- a/examples/contrib/einav_puzzle.py +++ b/examples/contrib/einav_puzzle.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. @@ -87,33 +87,33 @@ def main(): # Full problem rows = 27 cols = 9 - data = [[33, 30, 10, -6, 18, -7, -11, 23, - -6], [16, -19, 9, -26, -8, -19, -8, -21, - -14], [17, 12, -14, 31, -30, 13, -13, 19, - 16], [-6, -11, 1, 17, -12, -4, -7, 14, -21], - [18, -31, 34, -22, 17, -19, 20, 24, - 6], [33, -18, 17, -15, 31, -5, 3, - 27, -3], [-18, -20, -18, 31, 6, 4, -2, -12, - 24], [27, 14, 4, -29, -3, 5, -29, 8, - -12], [-15, -7, -23, 23, -9, -8, 6, 8, -12], - [33, -23, -19, -4, -8, -7, 11, -12, - 31], [-20, 19, -15, -30, 11, 32, 7, 14, - -5], [-23, 18, -32, -2, -31, -7, 8, 24, - 16], [32, -4, -10, -14, -6, -1, 0, 23, - 23], [25, 0, -23, 22, 12, 28, -27, 15, 4], - [-30, -13, -16, -3, -3, -32, -3, 27, - -31], [22, 1, 26, 4, -2, -13, - 26, 17, 14], [-9, -18, 3, -20, -27, -32, -11, 27, - 13], [-17, 33, -7, 19, -32, 13, -31, -2, -24], - [-31, 27, -31, -29, 15, 2, 29, -15, - 33], [-18, -23, 15, 28, 0, 30, -4, 12, - -32], [-3, 34, 27, -25, -18, 26, 1, 34, - 26], [-21, -31, -10, -13, -30, -17, -12, -26, - 31], [23, -31, -19, 21, -17, -10, 2, -23, 23], [ - -3, 6, 0, -3, -32, 0, -10, -25, 14 - ], [-19, 9, 14, -27, 20, 15, -5, -27, 18], [ - 11, -6, 24, 7, -17, 26, 20, -31, -25 - ], [-25, 4, -16, 30, 33, 23, -4, -4, 23]] + data = [[33, 30, 10, -6, 18, -7, -11, 23, -6], + [16, -19, 9, -26, -8, -19, -8, -21, -14], + [17, 12, -14, 31, -30, 13, -13, 19, 16], + [-6, -11, 1, 17, -12, -4, -7, 14, -21], + [18, -31, 34, -22, 17, -19, 20, 24, 6], + [33, -18, 17, -15, 31, -5, 3, 27, -3], + [-18, -20, -18, 31, 6, 4, -2, -12, 24], + [27, 14, 4, -29, -3, 5, -29, 8, -12], + [-15, -7, -23, 23, -9, -8, 6, 8, -12], + [33, -23, -19, -4, -8, -7, 11, -12, 31], + [-20, 19, -15, -30, 11, 32, 7, 14, -5], + [-23, 18, -32, -2, -31, -7, 8, 24, 16], + [32, -4, -10, -14, -6, -1, 0, 23, 23], + [25, 0, -23, 22, 12, 28, -27, 15, 4], + [-30, -13, -16, -3, -3, -32, -3, 27, -31], + [22, 1, 26, 4, -2, -13, 26, 17, 14], + [-9, -18, 3, -20, -27, -32, -11, 27, 13], + [-17, 33, -7, 19, -32, 13, -31, -2, -24], + [-31, 27, -31, -29, 15, 2, 29, -15, 33], + [-18, -23, 15, 28, 0, 30, -4, 12, -32], + [-3, 34, 27, -25, -18, 26, 1, 34, 26], + [-21, -31, -10, -13, -30, -17, -12, -26, 31], + [23, -31, -19, 21, -17, -10, 2, -23, 23], + [-3, 6, 0, -3, -32, 0, -10, -25, 14], + [-19, 9, 14, -27, 20, 15, -5, -27, 18], + [11, -6, 24, 7, -17, 26, 20, -31, -25], + [-25, 4, -16, 30, 33, 23, -4, -4, 23]] # # variables diff --git a/examples/contrib/einav_puzzle2.py b/examples/contrib/einav_puzzle2.py index 5ee2c7a09e8..0d737616af3 100644 --- a/examples/contrib/einav_puzzle2.py +++ b/examples/contrib/einav_puzzle2.py @@ -88,33 +88,33 @@ def main(): # Full problem rows = 27 cols = 9 - data = [[33, 30, 10, -6, 18, -7, -11, 23, - -6], [16, -19, 9, -26, -8, -19, -8, -21, - -14], [17, 12, -14, 31, -30, 13, -13, 19, - 16], [-6, -11, 1, 17, -12, -4, -7, 14, -21], - [18, -31, 34, -22, 17, -19, 20, 24, - 6], [33, -18, 17, -15, 31, -5, 3, - 27, -3], [-18, -20, -18, 31, 6, 4, -2, -12, - 24], [27, 14, 4, -29, -3, 5, -29, 8, - -12], [-15, -7, -23, 23, -9, -8, 6, 8, -12], - [33, -23, -19, -4, -8, -7, 11, -12, - 31], [-20, 19, -15, -30, 11, 32, 7, 14, - -5], [-23, 18, -32, -2, -31, -7, 8, 24, - 16], [32, -4, -10, -14, -6, -1, 0, 23, - 23], [25, 0, -23, 22, 12, 28, -27, 15, 4], - [-30, -13, -16, -3, -3, -32, -3, 27, - -31], [22, 1, 26, 4, -2, -13, - 26, 17, 14], [-9, -18, 3, -20, -27, -32, -11, 27, - 13], [-17, 33, -7, 19, -32, 13, -31, -2, -24], - [-31, 27, -31, -29, 15, 2, 29, -15, - 33], [-18, -23, 15, 28, 0, 30, -4, 12, - -32], [-3, 34, 27, -25, -18, 26, 1, 34, - 26], [-21, -31, -10, -13, -30, -17, -12, -26, - 31], [23, -31, -19, 21, -17, -10, 2, -23, 23], [ - -3, 6, 0, -3, -32, 0, -10, -25, 14 - ], [-19, 9, 14, -27, 20, 15, -5, -27, 18], [ - 11, -6, 24, 7, -17, 26, 20, -31, -25 - ], [-25, 4, -16, 30, 33, 23, -4, -4, 23]] + data = [[33, 30, 10, -6, 18, -7, -11, 23, -6], + [16, -19, 9, -26, -8, -19, -8, -21, -14], + [17, 12, -14, 31, -30, 13, -13, 19, 16], + [-6, -11, 1, 17, -12, -4, -7, 14, -21], + [18, -31, 34, -22, 17, -19, 20, 24, 6], + [33, -18, 17, -15, 31, -5, 3, 27, -3], + [-18, -20, -18, 31, 6, 4, -2, -12, 24], + [27, 14, 4, -29, -3, 5, -29, 8, -12], + [-15, -7, -23, 23, -9, -8, 6, 8, -12], + [33, -23, -19, -4, -8, -7, 11, -12, 31], + [-20, 19, -15, -30, 11, 32, 7, 14, -5], + [-23, 18, -32, -2, -31, -7, 8, 24, 16], + [32, -4, -10, -14, -6, -1, 0, 23, 23], + [25, 0, -23, 22, 12, 28, -27, 15, 4], + [-30, -13, -16, -3, -3, -32, -3, 27, -31], + [22, 1, 26, 4, -2, -13, 26, 17, 14], + [-9, -18, 3, -20, -27, -32, -11, 27, 13], + [-17, 33, -7, 19, -32, 13, -31, -2, -24], + [-31, 27, -31, -29, 15, 2, 29, -15, 33], + [-18, -23, 15, 28, 0, 30, -4, 12, -32], + [-3, 34, 27, -25, -18, 26, 1, 34, 26], + [-21, -31, -10, -13, -30, -17, -12, -26, 31], + [23, -31, -19, 21, -17, -10, 2, -23, 23], + [-3, 6, 0, -3, -32, 0, -10, -25, 14], + [-19, 9, 14, -27, 20, 15, -5, -27, 18], + [11, -6, 24, 7, -17, 26, 20, -31, -25], + [-25, 4, -16, 30, 33, 23, -4, -4, 23]] # # variables diff --git a/examples/contrib/fill_a_pix.py b/examples/contrib/fill_a_pix.py index 558089abff8..5fbde3e4c52 100644 --- a/examples/contrib/fill_a_pix.py +++ b/examples/contrib/fill_a_pix.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. @@ -58,14 +58,13 @@ # http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/rules default_n = 10 X = -1 -default_puzzle = [[X, X, X, X, X, X, X, X, 0, X], [ - X, 8, 8, X, 2, X, 0, X, X, X -], [5, X, 8, X, X, X, X, X, X, - X], [X, X, X, X, X, 2, X, X, X, - 2], [1, X, X, X, 4, 5, 6, X, X, X], [X, 0, X, X, X, 7, 9, X, X, 6], [ - X, X, X, 6, X, X, 9, X, X, 6 - ], [X, X, 6, 6, 8, 7, 8, 7, X, 5], [X, 4, X, 6, 6, 6, X, 6, X, 4], - [X, X, X, X, X, X, 3, X, X, X]] +default_puzzle = [ + [X, X, X, X, X, X, X, X, 0, X], [X, 8, 8, X, 2, X, 0, X, X, X], + [5, X, 8, X, X, X, X, X, X, X], [X, X, X, X, X, 2, X, X, X, 2], + [1, X, X, X, 4, 5, 6, X, X, X], [X, 0, X, X, X, 7, 9, X, X, 6], + [X, X, X, 6, X, X, 9, X, X, 6], [X, X, 6, 6, 8, 7, 8, 7, X, 5], + [X, 4, X, 6, 6, 6, X, 6, X, 4], [X, X, X, X, X, X, 3, X, X, X] +] def main(puzzle='', n=''): diff --git a/examples/contrib/hidato.py b/examples/contrib/hidato.py index 6010d89d79e..742431ab310 100644 --- a/examples/contrib/hidato.py +++ b/examples/contrib/hidato.py @@ -51,22 +51,13 @@ def main(r, c): # data # Simple problem if r == 3 and c == 3: - puzzle = [ - [6,0,9], - [0,2,8], - [1,0,0] - ] + puzzle = [[6, 0, 9], [0, 2, 8], [1, 0, 0]] if r == 7 and c == 7: - puzzle = [ - [0,44,41, 0, 0, 0, 0], - [0,43, 0,28,29, 0, 0], - [0, 1, 0, 0, 0,33, 0], - [0, 2,25, 4,34, 0,36], - [49,16, 0,23, 0, 0, 0], - [0,19, 0, 0,12, 7, 0], - [0, 0, 0,14, 0, 0, 0] - ] + puzzle = [[0, 44, 41, 0, 0, 0, 0], [0, 43, 0, 28, 29, 0, 0], + [0, 1, 0, 0, 0, 33, 0], [0, 2, 25, 4, 34, 0, 36], + [49, 16, 0, 23, 0, 0, 0], [0, 19, 0, 0, 12, 7, 0], + [0, 0, 0, 14, 0, 0, 0]] # Problems from the book: # Gyora Bededek: "Hidato: 2000 Pure Logic Puzzles" @@ -84,38 +75,26 @@ def main(r, c): # Problem 2 (Practice) if r == 5 and c == 5: - puzzle = [ - [0, 0, 0, 0, 14], - [0, 18, 12, 0, 0], - [0, 0, 17, 4, 5], - [0, 0, 7, 0, 0], - [9, 8, 25, 1, 0], - ] + puzzle = [ + [0, 0, 0, 0, 14], + [0, 18, 12, 0, 0], + [0, 0, 17, 4, 5], + [0, 0, 7, 0, 0], + [9, 8, 25, 1, 0], + ] # Problem 3 (Beginner) if r == 6 and c == 6: - puzzle = [ - [ 0, 26,0, 0, 0,18], - [ 0, 0,27, 0, 0,19], - [31,23, 0, 0,14, 0], - [ 0,33, 8, 0,15, 1], - [ 0, 0, 0, 5, 0, 0], - [35,36, 0,10, 0, 0] - ]; + puzzle = [[0, 26, 0, 0, 0, 18], [0, 0, 27, 0, 0, 19], [31, 23, 0, 0, 14, 0], + [0, 33, 8, 0, 15, 1], [0, 0, 0, 5, 0, 0], [35, 36, 0, 10, 0, 0]] # Problem 15 (Intermediate) # Note: This takes very long time to solve... if r == 8 and c == 8: - puzzle = [ - [64, 0, 0, 0, 0, 0, 0, 0], - [ 1,63, 0,59,15,57,53, 0], - [ 0, 4, 0,14, 0, 0, 0, 0], - [ 3, 0,11, 0,20,19, 0,50], - [ 0, 0, 0, 0,22, 0,48,40], - [ 9, 0, 0,32,23, 0, 0,41], - [27, 0, 0, 0,36, 0,46, 0], - [28,30, 0,35, 0, 0, 0, 0] - ] + puzzle = [[64, 0, 0, 0, 0, 0, 0, 0], [1, 63, 0, 59, 15, 57, 53, 0], + [0, 4, 0, 14, 0, 0, 0, 0], [3, 0, 11, 0, 20, 19, 0, 50], + [0, 0, 0, 0, 22, 0, 48, 40], [9, 0, 0, 32, 23, 0, 0, 41], + [27, 0, 0, 0, 36, 0, 46, 0], [28, 30, 0, 35, 0, 0, 0, 0]] print_game(puzzle, r, c) diff --git a/examples/contrib/kakuro.py b/examples/contrib/kakuro.py index 574b6d410f0..e87cc2bfa85 100644 --- a/examples/contrib/kakuro.py +++ b/examples/contrib/kakuro.py @@ -92,22 +92,19 @@ def main(): # segments # [sum, [segments]] # Note: 1-based - problem = [[16, [1, 1], [1, 2]], [24, [1, 5], [1, 6], [1, 7]], [ - 17, [2, 1], [2, 2] - ], [29, [2, 4], [2, 5], [2, 6], - [2, 7]], [35, [3, 1], [3, 2], [3, 3], [3, 4], - [3, 5]], [7, [4, 2], [4, 3]], [8, [4, 5], [4, 6]], [ - 16, [5, 3], [5, 4], [5, 5], [5, 6], [5, 7] - ], [21, [6, 1], [6, 2], [6, 3], - [6, 4]], [5, [6, 6], [6, 7]], [6, [7, 1], [7, 2], [7, 3]], - [3, [7, 6], [7, 7]], [23, [1, 1], [2, 1], [3, 1]], [ - 30, [1, 2], [2, 2], [3, 2], [4, 2] - ], [27, [1, 5], [2, 5], [3, 5], [4, 5], [5, 5]], - [12, [1, 6], [2, 6]], [16, [1, 7], [2, 7]], [17, [2, 4], [3, 4]], [ - 15, [3, 3], [4, 3], [5, 3], [6, 3], [7, 3] - ], [12, [4, 6], [5, 6], [6, 6], [7, 6]], [7, [5, 4], [6, 4]], [ - 7, [5, 7], [6, 7], [7, 7] - ], [11, [6, 1], [7, 1]], [10, [6, 2], [7, 2]]] + problem = [[16, [1, 1], [1, 2]], [24, [1, 5], [1, 6], [1, 7]], + [17, [2, 1], [2, 2]], [29, [2, 4], [2, 5], [2, 6], [2, 7]], + [35, [3, 1], [3, 2], [3, 3], [3, 4], [3, 5]], [7, [4, 2], [4, 3]], + [8, [4, 5], [4, 6]], [16, [5, 3], [5, 4], [5, 5], [5, 6], [5, 7]], + [21, [6, 1], [6, 2], [6, 3], [6, 4]], [5, [6, 6], [6, 7]], + [6, [7, 1], [7, 2], [7, 3]], [3, [7, 6], [7, 7]], + [23, [1, 1], [2, 1], [3, 1]], [30, [1, 2], [2, 2], [3, 2], [4, 2]], + [27, [1, 5], [2, 5], [3, 5], [4, 5], [5, 5]], [12, [1, 6], [2, 6]], + [16, [1, 7], [2, 7]], [17, [2, 4], [3, 4]], + [15, [3, 3], [4, 3], [5, 3], [6, 3], [7, 3]], + [12, [4, 6], [5, 6], [6, 6], [7, 6]], [7, [5, 4], [6, 4]], + [7, [5, 7], [6, 7], [7, 7]], [11, [6, 1], [7, 1]], + [10, [6, 2], [7, 2]]] num_p = len(problem) diff --git a/examples/contrib/kenken2.py b/examples/contrib/kenken2.py index 8ed0b49f3c1..2836e8e5eb7 100644 --- a/examples/contrib/kenken2.py +++ b/examples/contrib/kenken2.py @@ -125,16 +125,14 @@ def main(): # hints # [sum, [segments]] # Note: 1-based - problem = [[11, [[1, 1], [2, 1]]], [2, [[1, 2], [1, - 3]]], [20, [[1, 4], [2, 4]]], - [6, [[1, 5], [1, 6], [2, 6], - [3, 6]]], [3, [[2, 2], [2, 3]]], [3, [[2, 5], [3, 5]]], [ - 240, [[3, 1], [3, 2], [4, 1], [4, 2]] - ], [6, [[3, 3], [3, 4]]], [6, [[4, 3], [5, 3]]], [ - 7, [[4, 4], [5, 4], [5, 5]] - ], [30, [[4, 5], [4, 6]]], [6, [[5, 1], [5, 2]]], - [9, [[5, 6], [6, 6]]], [8, [[6, 1], [6, 2], - [6, 3]]], [2, [[6, 4], [6, 5]]]] + problem = [[11, [[1, 1], [2, 1]]], [2, [[1, 2], [1, 3]]], + [20, [[1, 4], [2, 4]]], [6, [[1, 5], [1, 6], [2, 6], [3, 6]]], + [3, [[2, 2], [2, 3]]], [3, [[2, 5], [3, 5]]], + [240, [[3, 1], [3, 2], [4, 1], [4, 2]]], [6, [[3, 3], [3, 4]]], + [6, [[4, 3], [5, 3]]], [7, [[4, 4], [5, 4], [5, 5]]], + [30, [[4, 5], [4, 6]]], [6, [[5, 1], [5, 2]]], + [9, [[5, 6], [6, 6]]], [8, [[6, 1], [6, 2], [6, 3]]], + [2, [[6, 4], [6, 5]]]] num_p = len(problem) diff --git a/examples/contrib/killer_sudoku.py b/examples/contrib/killer_sudoku.py index c60b891a780..d8ce8c02f57 100644 --- a/examples/contrib/killer_sudoku.py +++ b/examples/contrib/killer_sudoku.py @@ -107,23 +107,19 @@ def main(): # Note: 1-based problem = [[3, [[1, 1], [1, 2]]], [15, [[1, 3], [1, 4], [1, 5]]], [22, [[1, 6], [2, 5], [2, 6], [3, 5]]], [4, [[1, 7], [2, 7]]], - [16, [[1, 8], [2, 8]]], [15, [[1, 9], [2, 9], [3, 9], [4, 9]]], [ - 25, [[2, 1], [2, 2], [3, 1], [3, 2]] - ], [17, [[2, 3], [2, 4]]], [9, [[3, 3], [3, 4], [4, 4]]], [ - 8, [[3, 6], [4, 6], [5, 6]] - ], [20, [[3, 7], [3, 8], [4, 7]]], [6, [[4, 1], [5, 1]]], [ - 14, [[4, 2], [4, 3]] - ], [17, [[4, 5], [5, 5], [6, 5]]], [17, [[4, 8], [5, 7], [5, 8]]], - [13, [[5, 2], [5, 3], [6, 2]]], [20, [[5, 4], [6, 4], [7, 4]]], [ - 12, [[5, 9], [6, 9]] - ], [27, [[6, 1], [7, 1], [8, 1], [9, 1]]], [ - 6, [[6, 3], [7, 2], [7, 3]] - ], [20, [[6, 6], [7, 6], [7, 7]]], [6, [[6, 7], [6, 8]]], [ - 10, [[7, 5], [8, 4], [8, 5], [9, 4]] - ], [14, [[7, 8], [7, 9], [8, 8], [8, 9]]], [8, [[8, 2], [9, 2]]], [ - 16, [[8, 3], [9, 3]] - ], [15, [[8, 6], [8, 7]]], [13, [[9, 5], [9, 6], - [9, 7]]], [17, [[9, 8], [9, 9]]]] + [16, [[1, 8], [2, 8]]], [15, [[1, 9], [2, 9], [3, 9], [4, 9]]], + [25, [[2, 1], [2, 2], [3, 1], [3, 2]]], [17, [[2, 3], [2, 4]]], + [9, [[3, 3], [3, 4], [4, 4]]], [8, [[3, 6], [4, 6], [5, 6]]], + [20, [[3, 7], [3, 8], [4, 7]]], [6, [[4, 1], [5, 1]]], + [14, [[4, 2], [4, 3]]], [17, [[4, 5], [5, 5], [6, 5]]], + [17, [[4, 8], [5, 7], [5, 8]]], [13, [[5, 2], [5, 3], [6, 2]]], + [20, [[5, 4], [6, 4], [7, 4]]], [12, [[5, 9], [6, 9]]], + [27, [[6, 1], [7, 1], [8, 1], [9, 1]]], + [6, [[6, 3], [7, 2], [7, 3]]], [20, [[6, 6], [7, 6], [7, 7]]], + [6, [[6, 7], [6, 8]]], [10, [[7, 5], [8, 4], [8, 5], [9, 4]]], + [14, [[7, 8], [7, 9], [8, 8], [8, 9]]], [8, [[8, 2], [9, 2]]], + [16, [[8, 3], [9, 3]]], [15, [[8, 6], [8, 7]]], + [13, [[9, 5], [9, 6], [9, 7]]], [17, [[9, 8], [9, 9]]]] # # variables @@ -174,7 +170,7 @@ def main(): while solver.NextSolution(): for i in range(n): for j in range(n): - print(x[i, j].Value(), end=' ') + print(x[i, j].Value(), end=" ") print() print() diff --git a/examples/contrib/knapsack_mip.py b/examples/contrib/knapsack_mip.py index db3c2005bba..2c5317beae3 100644 --- a/examples/contrib/knapsack_mip.py +++ b/examples/contrib/knapsack_mip.py @@ -51,13 +51,13 @@ def main(sol='CBC'): capacity = [18209, 7692, 1333, 924, 26638, 61188, 13360] value = [96, 76, 56, 11, 86, 10, 66, 86, 83, 12, 9, 81] - use = [[19, 1, 10, 1, 1, 14, 152, 11, 1, 1, 1, - 1], [0, 4, 53, 0, 0, 80, 0, 4, 5, 0, 0, - 0], [4, 660, 3, 0, 30, 0, 3, 0, 4, 90, 0, - 0], [7, 0, 18, 6, 770, 330, 7, 0, 0, 6, 0, 0], - [0, 20, 0, 4, 52, 3, 0, 0, 0, 5, 4, - 0], [0, 0, 40, 70, 4, 63, 0, 0, 60, 0, 4, - 0], [0, 32, 0, 0, 0, 5, 0, 3, 0, 660, 0, 9]] + use = [[19, 1, 10, 1, 1, 14, 152, 11, 1, 1, 1, 1], + [0, 4, 53, 0, 0, 80, 0, 4, 5, 0, 0, 0], + [4, 660, 3, 0, 30, 0, 3, 0, 4, 90, 0, 0], + [7, 0, 18, 6, 770, 330, 7, 0, 0, 6, 0, 0], + [0, 20, 0, 4, 52, 3, 0, 0, 0, 5, 4, 0], + [0, 0, 40, 70, 4, 63, 0, 0, 60, 0, 4, 0], + [0, 32, 0, 0, 0, 5, 0, 3, 0, 660, 0, 9]] max_value = max(capacity) diff --git a/examples/contrib/lectures.py b/examples/contrib/lectures.py index acda6eb81a9..8a74bf2d6ca 100644 --- a/examples/contrib/lectures.py +++ b/examples/contrib/lectures.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/magic_square.py b/examples/contrib/magic_square.py index 8df37afcbf7..6fffc139005 100644 --- a/examples/contrib/magic_square.py +++ b/examples/contrib/magic_square.py @@ -26,7 +26,7 @@ from ortools.constraint_solver import pywrapcp -def main(n=4): +def main(n, limit): # Create the solver. solver = pywrapcp.Solver("n-queens") @@ -91,6 +91,8 @@ def main(n=4): print() num_solutions += 1 + if num_solutions > limit: + break solver.EndSearch() print() @@ -101,7 +103,11 @@ def main(n=4): n = 4 +limit=100 if __name__ == "__main__": if len(sys.argv) > 1: n = int(sys.argv[1]) - main(n) + if len(sys.argv) > 2: + limit = int(sys.argv[2]) + + main(n, limit) diff --git a/examples/contrib/magic_square_mip.py b/examples/contrib/magic_square_mip.py index 80f847695ff..2fbaae02eeb 100644 --- a/examples/contrib/magic_square_mip.py +++ b/examples/contrib/magic_square_mip.py @@ -145,8 +145,8 @@ def main(n=3, sol='CBC', use_output_matrix=0): if use_output_matrix == 1: for i in range_n: for j in range_n: - solver.Add(square[i, j] == solver.Sum([k * x[i, j, k] - for k in range_N])) + solver.Add( + square[i, j] == solver.Sum([k * x[i, j, k] for k in range_N])) # # solution and search diff --git a/examples/contrib/marathon2.py b/examples/contrib/marathon2.py index 253d7bf8aa7..96b6303cebd 100644 --- a/examples/contrib/marathon2.py +++ b/examples/contrib/marathon2.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/max_flow_taha.py b/examples/contrib/max_flow_taha.py index ad39843d4cf..ee30681f858 100644 --- a/examples/contrib/max_flow_taha.py +++ b/examples/contrib/max_flow_taha.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/max_flow_winston1.py b/examples/contrib/max_flow_winston1.py index 55195cf8179..116e49bcd22 100644 --- a/examples/contrib/max_flow_winston1.py +++ b/examples/contrib/max_flow_winston1.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/minesweeper.py b/examples/contrib/minesweeper.py index 61c3b793375..65a3ca3cf52 100644 --- a/examples/contrib/minesweeper.py +++ b/examples/contrib/minesweeper.py @@ -71,9 +71,8 @@ X = -1 default_game = [[2, 3, X, 2, 2, X, 2, 1], [X, X, 4, X, X, 4, X, 2], [X, X, X, X, X, X, 4, X], [X, 5, X, 6, X, X, X, 2], - [2, X, X, X, 5, 5, X, 2], [1, 3, 4, X, X, X, 4, - X], [0, 1, X, 4, X, X, X, - 3], [0, 1, 2, X, 2, 3, X, 2]] + [2, X, X, X, 5, 5, X, 2], [1, 3, 4, X, X, X, 4, X], + [0, 1, X, 4, X, X, X, 3], [0, 1, 2, X, 2, 3, X, 2]] def main(game="", r="", c=""): diff --git a/examples/contrib/mr_smith.py b/examples/contrib/mr_smith.py index ee888802012..cf0bd14c25e 100644 --- a/examples/contrib/mr_smith.py +++ b/examples/contrib/mr_smith.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/nonogram_default_search.py b/examples/contrib/nonogram_default_search.py index c15c87e727a..744cdb2cedd 100644 --- a/examples/contrib/nonogram_default_search.py +++ b/examples/contrib/nonogram_default_search.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com, lperron@google.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. @@ -197,5 +197,5 @@ def main(rows, row_rule_len, row_rules, cols, col_rule_len, col_rules): if __name__ == '__main__': if len(sys.argv) > 1: file = sys.argv[1] - exec (compile(open(file).read(), file, 'exec')) + exec(compile(open(file).read(), file, 'exec')) main(rows, row_rule_len, row_rules, cols, col_rule_len, col_rules) diff --git a/examples/contrib/nonogram_regular.py b/examples/contrib/nonogram_regular.py index f5305908ec0..67f6a8d8272 100644 --- a/examples/contrib/nonogram_regular.py +++ b/examples/contrib/nonogram_regular.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. @@ -134,8 +134,8 @@ def regular(x, Q, S, d, q0, F): solver.Add(x[i] >= 1) solver.Add(x[i] <= S) # Determine a[i+1]: a[i+1] == d2[a[i], x[i]] - solver.Add(a[i + 1] == solver.Element(d2_flatten, ( - (a[i]) * S) + (x[i] - 1))) + solver.Add( + a[i + 1] == solver.Element(d2_flatten, ((a[i]) * S) + (x[i] - 1))) # @@ -323,5 +323,5 @@ def main(rows, row_rule_len, row_rules, cols, col_rule_len, col_rules): if __name__ == '__main__': if len(sys.argv) > 1: file = sys.argv[1] - exec (compile(open(file).read(), file, 'exec')) + exec(compile(open(file).read(), file, 'exec')) main(rows, row_rule_len, row_rules, cols, col_rule_len, col_rules) diff --git a/examples/contrib/nonogram_table.py b/examples/contrib/nonogram_table.py index c7b2ebf7ce0..403abbcd7cc 100644 --- a/examples/contrib/nonogram_table.py +++ b/examples/contrib/nonogram_table.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. @@ -319,5 +319,5 @@ def main(rows, row_rule_len, row_rules, cols, col_rule_len, col_rules): if __name__ == '__main__': if len(sys.argv) > 1: file = sys.argv[1] - exec (compile(open(file).read(), file, 'exec')) + exec(compile(open(file).read(), file, 'exec')) main(rows, row_rule_len, row_rules, cols, col_rule_len, col_rules) diff --git a/examples/contrib/nonogram_table2.py b/examples/contrib/nonogram_table2.py index 1140938ceaa..64e69ed4413 100644 --- a/examples/contrib/nonogram_table2.py +++ b/examples/contrib/nonogram_table2.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. @@ -219,5 +219,5 @@ def main(rows, row_rule_len, row_rules, cols, col_rule_len, col_rules): if __name__ == '__main__': if len(sys.argv) > 1: file = sys.argv[1] - exec (compile(open(file).read(), file, 'exec')) + exec(compile(open(file).read(), file, 'exec')) main(rows, row_rule_len, row_rules, cols, col_rule_len, col_rules) diff --git a/examples/contrib/nurse_rostering.py b/examples/contrib/nurse_rostering.py index d6b64976b67..224d50557bb 100644 --- a/examples/contrib/nurse_rostering.py +++ b/examples/contrib/nurse_rostering.py @@ -98,8 +98,8 @@ def regular(x, Q, S, d, q0, F): solver.Add(x[i] <= S) # Determine a[i+1]: a[i+1] == d2[a[i], x[i]] - solver.Add(a[i + 1] == solver.Element(d2_flatten, ( - (a[i]) * S) + (x[i] - 1))) + solver.Add( + a[i + 1] == solver.Element(d2_flatten, ((a[i]) * S) + (x[i] - 1))) def main(): diff --git a/examples/contrib/nurses_cp.py b/examples/contrib/nurses_cp.py index dc1f659ada4..7ae782615ed 100644 --- a/examples/contrib/nurses_cp.py +++ b/examples/contrib/nurses_cp.py @@ -54,9 +54,8 @@ def main(): for i in range(num_nurses): for j in range(num_shifts): - solver.Add( - works_shift[(i, j)] == - solver.Max([shifts[(i, k)] == j for k in range(num_days)])) + solver.Add(works_shift[( + i, j)] == solver.Max([shifts[(i, k)] == j for k in range(num_days)])) # For each shift (other than 0), at most 2 nurses are assigned to that shift # during the week. diff --git a/examples/contrib/olympic.py b/examples/contrib/olympic.py index 5abb5294a27..f5de69b0078 100644 --- a/examples/contrib/olympic.py +++ b/examples/contrib/olympic.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/organize_day.py b/examples/contrib/organize_day.py index 5f080647d9a..8d2d323d0e9 100644 --- a/examples/contrib/organize_day.py +++ b/examples/contrib/organize_day.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/p_median.py b/examples/contrib/p_median.py index da19a9a91e4..ce33a34c759 100644 --- a/examples/contrib/p_median.py +++ b/examples/contrib/p_median.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/pandigital_numbers.py b/examples/contrib/pandigital_numbers.py index 07e6b902798..7d8b4acd834 100644 --- a/examples/contrib/pandigital_numbers.py +++ b/examples/contrib/pandigital_numbers.py @@ -72,8 +72,8 @@ def toNum(solver, t, s, base): tlen = len(t) - solver.Add(s == solver.Sum([(base**(tlen - i - 1)) * t[i] - for i in range(tlen)])) + solver.Add( + s == solver.Sum([(base**(tlen - i - 1)) * t[i] for i in range(tlen)])) def main(base=10, start=1, len1=1, len2=4): diff --git a/examples/contrib/regular.py b/examples/contrib/regular.py index 24c250d2ffa..ea74802729e 100644 --- a/examples/contrib/regular.py +++ b/examples/contrib/regular.py @@ -105,8 +105,8 @@ def regular(x, Q, S, d, q0, F): solver.Add(x[i] <= S) # Determine a[i+1]: a[i+1] == d2[a[i], x[i]] - solver.Add(a[i + 1] == solver.Element(d2_flatten, ( - (a[i]) * S) + (x[i] - 1))) + solver.Add( + a[i + 1] == solver.Element(d2_flatten, ((a[i]) * S) + (x[i] - 1))) # diff --git a/examples/contrib/rogo2.py b/examples/contrib/rogo2.py index b9710c92869..8874b16ac18 100644 --- a/examples/contrib/rogo2.py +++ b/examples/contrib/rogo2.py @@ -182,9 +182,9 @@ def main(problem, rows, cols, max_steps): W = 0 B = -1 problem = [[2, W, W, W, W, W, W, W, W], [W, 3, W, W, 1, W, W, 2, W], - [W, W, W, W, W, W, B, W, 2], [W, W, 2, B, W, W, W, W, - W], [W, W, W, W, 2, W, W, 1, W]] + [W, W, W, W, W, W, B, W, 2], [W, W, 2, B, W, W, W, W, W], + [W, W, W, W, 2, W, W, 1, W]] if __name__ == "__main__": if len(sys.argv) > 1: - exec (compile(open(sys.argv[1]).read(), sys.argv[1], "exec")) + exec(compile(open(sys.argv[1]).read(), sys.argv[1], "exec")) main(problem, rows, cols, max_steps) diff --git a/examples/contrib/rostering_with_travel.py b/examples/contrib/rostering_with_travel.py index 03056c4ef31..4004e77d991 100644 --- a/examples/contrib/rostering_with_travel.py +++ b/examples/contrib/rostering_with_travel.py @@ -45,8 +45,8 @@ def SolveRosteringWithTravel(): job_performed.append(performed_on_m) # Create an optional copy of interval to be executed on a machine - location0 = model.NewIntVar( - jobs[i][3], jobs[i][3], 'location_%i_on_m%i' % (i, m)) + location0 = model.NewIntVar(jobs[i][3], jobs[i][3], + 'location_%i_on_m%i' % (i, m)) start0 = model.NewIntVar(jobs[i][1], horizon, 'start_%i_on_m%i' % (i, m)) end0 = model.NewIntVar(0, jobs[i][2], 'end_%i_on_m%i' % (i, m)) interval0 = model.NewOptionalIntervalVar( diff --git a/examples/contrib/safe_cracking.py b/examples/contrib/safe_cracking.py index 6f1a4f9a715..7330eda962a 100644 --- a/examples/contrib/safe_cracking.py +++ b/examples/contrib/safe_cracking.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/scheduling_speakers.py b/examples/contrib/scheduling_speakers.py index 652a6690832..4026e72dcab 100644 --- a/examples/contrib/scheduling_speakers.py +++ b/examples/contrib/scheduling_speakers.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/scheduling_with_transitions_sat.py b/examples/contrib/scheduling_with_transitions_sat.py index af4bc21be77..490b9eaa5f7 100644 --- a/examples/contrib/scheduling_with_transitions_sat.py +++ b/examples/contrib/scheduling_with_transitions_sat.py @@ -14,16 +14,16 @@ from ortools.sat.python import cp_model from google.protobuf import text_format - #---------------------------------------------------------------------------- # Command line arguments. PARSER = argparse.ArgumentParser() -PARSER.add_argument('--problem_instance', default=0, type=int, - help='Problem instance.') -PARSER.add_argument('--output_proto', default="", - help='Output file to write the cp_model proto to.') -PARSER.add_argument('--params', default="", - help='Sat solver parameters.') +PARSER.add_argument( + '--problem_instance', default=0, type=int, help='Problem instance.') +PARSER.add_argument( + '--output_proto', + default='', + help='Output file to write the cp_model proto to.') +PARSER.add_argument('--params', default='', help='Sat solver parameters.') #---------------------------------------------------------------------------- @@ -55,8 +55,8 @@ def main(args): small_jobs = [[[(100, 0, 'R6'), (2, 1, 'R6')]], [[(2, 0, 'R3'), (100, 1, 'R3')]], [[(100, 0, 'R1'), (16, 1, 'R1')]], - [[(1, 0, 'R1'), (38, 1, 'R1')]], - [[(14, 0, 'R1'), (10, 1, 'R1')]], + [[(1, 0, 'R1'), (38, 1, 'R1')]], [[(14, 0, 'R1'), (10, 1, + 'R1')]], [[(16, 0, 'R3'), (17, 1, 'R3')]], [[(14, 0, 'R3'), (14, 1, 'R3')]], [[(14, 0, 'R3'), (15, 1, 'R3')]], @@ -64,7 +64,8 @@ def main(args): [[(100, 0, 'R1'), (38, 1, 'R1')]]] large_jobs = [ - [[(-1, 0, 'R1'), (10, 1, 'R1')]], [[(9, 0, 'R3'), (22, 1, 'R3')]], + [[(-1, 0, 'R1'), (10, 1, 'R1')]], [[(9, 0, 'R3'), + (22, 1, 'R3')]], [[(-1, 0, 'R3'), (13, 1, 'R3')]], [[(-1, 0, 'R3'), (38, 1, 'R3')]], [[(-1, 0, 'R3'), (38, 1, 'R3')]], [[(-1, 0, 'R3'), (16, 1, 'R3')]], [[(-1, 0, 'R3'), (11, 1, 'R3')]], [[(-1, 0, 'R3'), (13, 1, 'R3')]], diff --git a/examples/contrib/school_scheduling_sat.py b/examples/contrib/school_scheduling_sat.py index 36ff6f2bd00..4f16cdff263 100644 --- a/examples/contrib/school_scheduling_sat.py +++ b/examples/contrib/school_scheduling_sat.py @@ -70,8 +70,8 @@ def __init__(self, problem): for section in all_sections: course = level * self.num_sections + section for subject in all_subjects: - required_slots = self.problem.curriculum[self.problem.levels[ - level], self.problem.subjects[subject]] + required_slots = self.problem.curriculum[ + self.problem.levels[level], self.problem.subjects[subject]] self.model.Add( sum(self.assignment[course, subject, teacher, slot] for slot in all_slots diff --git a/examples/contrib/secret_santa.py b/examples/contrib/secret_santa.py index 97f2d3b144b..b9a4acd7a5e 100644 --- a/examples/contrib/secret_santa.py +++ b/examples/contrib/secret_santa.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/secret_santa2.py b/examples/contrib/secret_santa2.py index 509d83d1400..df1a52233e2 100644 --- a/examples/contrib/secret_santa2.py +++ b/examples/contrib/secret_santa2.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/set_covering_deployment.py b/examples/contrib/set_covering_deployment.py index 0be91a4ba92..02970279ca0 100644 --- a/examples/contrib/set_covering_deployment.py +++ b/examples/contrib/set_covering_deployment.py @@ -60,9 +60,8 @@ def main(): # the incidence matrix (neighbours) mat = [[0, 1, 0, 1, 0, 0, 1, 1], [1, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 0, 0], [1, 1, 0, 0, 0, 0, 1, 0], - [0, 0, 1, 0, 0, 1, 1, 0], [0, 0, 1, 0, 1, 0, 1, - 1], [1, 0, 0, 1, 1, 1, 0, - 1], [1, 0, 0, 0, 0, 1, 1, 0]] + [0, 0, 1, 0, 0, 1, 1, 0], [0, 0, 1, 0, 1, 0, 1, 1], + [1, 0, 0, 1, 1, 1, 0, 1], [1, 0, 0, 0, 0, 1, 1, 0]] # # declare variables diff --git a/examples/contrib/set_covering_skiena.py b/examples/contrib/set_covering_skiena.py index 1e5d8e9ba87..526570de4f7 100644 --- a/examples/contrib/set_covering_skiena.py +++ b/examples/contrib/set_covering_skiena.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/slitherlink.py b/examples/contrib/slitherlink.py index bbce75214e1..af17f471e2e 100644 --- a/examples/contrib/slitherlink.py +++ b/examples/contrib/slitherlink.py @@ -4,20 +4,16 @@ small = [[3, 2, -1, 3], [-1, -1, -1, 2], [3, -1, -1, -1], [3, -1, 3, 1]] medium = [[-1, 0, -1, 1, -1, -1, 1, -1], [-1, 3, -1, -1, 2, 3, -1, 2], - [-1, -1, 0, -1, -1, -1, -1, 0], [-1, 3, -1, -1, 0, -1, -1, - -1], [-1, -1, -1, 3, -1, -1, 0, -1], - [1, -1, -1, -1, -1, 3, -1, -1], [3, -1, 1, 3, -1, -1, 3, - -1], [-1, 0, -1, -1, 3, -1, 3, -1]] + [-1, -1, 0, -1, -1, -1, -1, 0], [-1, 3, -1, -1, 0, -1, -1, -1], + [-1, -1, -1, 3, -1, -1, 0, -1], [1, -1, -1, -1, -1, 3, -1, -1], + [3, -1, 1, 3, -1, -1, 3, -1], [-1, 0, -1, -1, 3, -1, 3, -1]] big = [[3, -1, -1, -1, 2, -1, 1, -1, 1, 2], [1, -1, 0, -1, 3, -1, 2, 0, -1, -1], - [-1, 3, -1, -1, -1, -1, -1, -1, 3, - -1], [2, 0, -1, 3, -1, 2, 3, -1, -1, - -1], [-1, -1, -1, 1, 1, 1, -1, -1, 3, 3], - [2, 3, -1, -1, 2, 2, 3, -1, -1, - -1], [-1, -1, -1, 1, 2, -1, 2, -1, - 3, 3], [-1, 2, -1, -1, -1, -1, -1, -1, - 2, -1], [-1, -1, 1, 1, -1, 2, -1, 1, - -1, 3], [3, 3, -1, 1, -1, 2, -1, -1, -1, 2]] + [-1, 3, -1, -1, -1, -1, -1, -1, 3, -1], + [2, 0, -1, 3, -1, 2, 3, -1, -1, -1], [-1, -1, -1, 1, 1, 1, -1, -1, 3, 3], + [2, 3, -1, -1, 2, 2, 3, -1, -1, -1], [-1, -1, -1, 1, 2, -1, 2, -1, 3, 3], + [-1, 2, -1, -1, -1, -1, -1, -1, 2, -1], + [-1, -1, 1, 1, -1, 2, -1, 1, -1, 3], [3, 3, -1, 1, -1, 2, -1, -1, -1, 2]] def NeighboringArcs(i, j, h_arcs, v_arcs): diff --git a/examples/contrib/stable_marriage.py b/examples/contrib/stable_marriage.py index c3dfc5c728f..4d45cb921e0 100644 --- a/examples/contrib/stable_marriage.py +++ b/examples/contrib/stable_marriage.py @@ -148,16 +148,15 @@ def main(ranks, problem_name): # mathworld = { "rankWomen": [[3, 1, 5, 2, 8, 7, 6, 9, 4], [9, 4, 8, 1, 7, 6, 3, 2, 5], - [3, 1, 8, 9, 5, 4, 2, 6, 7], [8, 7, 5, 3, 2, 6, 4, 9, 1], [ - 6, 9, 2, 5, 1, 4, 7, 3, 8 - ], [2, 4, 5, 1, 6, 8, 3, 9, 7], [9, 3, 8, 2, 7, 5, 4, 6, 1], - [6, 3, 2, 1, 8, 4, 5, 9, 7], [8, 2, 6, 4, 9, 1, 3, 7, 5]], - "rankMen": [[7, 3, 8, 9, 6, 4, 2, 1, 5], [5, 4, 8, 3, 1, 2, 6, 7, - 9], [4, 8, 3, 9, 7, 5, 6, 1, 2], - [9, 7, 4, 2, 5, 8, 3, 1, 6], [2, 6, 4, 9, 8, 7, 5, 1, - 3], [2, 7, 8, 6, 5, 3, 4, 1, 9], - [1, 6, 2, 3, 8, 5, 4, 9, 7], [5, 6, 9, 1, 2, 8, 4, 3, - 7], [6, 1, 4, 7, 5, 8, 3, 9, 2]] + [3, 1, 8, 9, 5, 4, 2, 6, 7], [8, 7, 5, 3, 2, 6, 4, 9, 1], + [6, 9, 2, 5, 1, 4, 7, 3, 8], [2, 4, 5, 1, 6, 8, 3, 9, 7], + [9, 3, 8, 2, 7, 5, 4, 6, 1], [6, 3, 2, 1, 8, 4, 5, 9, 7], + [8, 2, 6, 4, 9, 1, 3, 7, 5]], + "rankMen": [[7, 3, 8, 9, 6, 4, 2, 1, 5], [5, 4, 8, 3, 1, 2, 6, 7, 9], + [4, 8, 3, 9, 7, 5, 6, 1, 2], [9, 7, 4, 2, 5, 8, 3, 1, 6], + [2, 6, 4, 9, 8, 7, 5, 1, 3], [2, 7, 8, 6, 5, 3, 4, 1, 9], + [1, 6, 2, 3, 8, 5, 4, 9, 7], [5, 6, 9, 1, 2, 8, 4, 3, 7], + [6, 1, 4, 7, 5, 8, 3, 9, 2]] } # diff --git a/examples/contrib/steel.py b/examples/contrib/steel.py index ce65101364a..ebe00dccce0 100644 --- a/examples/contrib/steel.py +++ b/examples/contrib/steel.py @@ -1,13 +1,13 @@ # Copyright 2010 Pierre Schaus pschaus@gmail.com, lperron@google.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/steel_lns.py b/examples/contrib/steel_lns.py index 08061423307..60269e5876d 100644 --- a/examples/contrib/steel_lns.py +++ b/examples/contrib/steel_lns.py @@ -1,13 +1,13 @@ # Copyright 2010 Pierre Schaus pschaus@gmail.com, lperron@google.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. diff --git a/examples/contrib/stigler.py b/examples/contrib/stigler.py index f84c2a1b830..a58c6e2e0f7 100644 --- a/examples/contrib/stigler.py +++ b/examples/contrib/stigler.py @@ -163,136 +163,126 @@ def main(sol="CBC"): "ascorbicAcid" # Ascorbic Acid, Vit. C, unit = milligrams ] - commodities = [["Wheat Flour (Enriched)", "10 lb."], ["Macaroni", "1 lb."], [ - "Wheat Cereal (Enriched)", "28 oz." - ], ["Corn Flakes", "8 oz."], ["Corn Meal", "1 lb."], [ - "Hominy Grits", "24 oz." - ], ["Rice", "1 lb."], ["Rolled Oats", "1 lb."], [ - "White Bread (Enriched)", "1 lb." - ], ["Whole Wheat Bread", "1 lb."], ["Rye Bread", "1 lb."], [ - "Pound Cake", "1 lb." - ], ["Soda Crackers", "1 lb."], ["Milk", "1 qt."], [ - "Evaporated Milk (can)", "14.5 oz." - ], ["Butter", "1 lb."], ["Oleomargarine", "1 lb."], ["Eggs", "1 doz."], [ - "Cheese (Cheddar)", "1 lb." - ], ["Cream", "1/2 pt."], ["Peanut Butter", "1 lb."], [ - "Mayonnaise", "1/2 pt." - ], ["Crisco", "1 lb."], ["Lard", "1 lb."], ["Sirloin Steak", "1 lb."], [ - "Round Steak", "1 lb." - ], ["Rib Roast", "1 lb."], ["Chuck Roast", "1 lb."], ["Plate", "1 lb."], [ - "Liver (Beef)", "1 lb." - ], ["Leg of Lamb", - "1 lb."], ["Lamb Chops (Rib)", "1 lb."], ["Pork Chops", "1 lb."], [ - "Pork Loin Roast", "1 lb." - ], ["Bacon", "1 lb."], ["Ham - smoked", "1 lb."], ["Salt Pork", "1 lb."], - ["Roasting Chicken", "1 lb."], ["Veal Cutlets", "1 lb."], [ - "Salmon, Pink (can)", "16 oz." - ], ["Apples", "1 lb."], ["Bananas", "1 lb."], [ - "Lemons", "1 doz." - ], ["Oranges", "1 doz."], ["Green Beans", "1 lb."], - ["Cabbage", "1 lb."], ["Carrots", "1 bunch"], [ - "Celery", "1 stalk" - ], ["Lettuce", "1 head"], ["Onions", "1 lb."], [ - "Potatoes", "15 lb." - ], ["Spinach", "1 lb."], ["Sweet Potatoes", "1 lb."], [ - "Peaches (can)", "No. 2 1/2" - ], ["Pears (can)", "No. 2 1/2,"], [ - "Pineapple (can)", "No. 2 1/2" - ], ["Asparagus (can)", "No. 2"], [ - "Grean Beans (can)", "No. 2" - ], ["Pork and Beans (can)", "16 oz."], ["Corn (can)", "No. 2"], - ["Peas (can)", "No. 2"], ["Tomatoes (can)", "No. 2"], [ - "Tomato Soup (can)", "10 1/2 oz." - ], ["Peaches, Dried", "1 lb."], ["Prunes, Dried", "1 lb."], [ - "Raisins, Dried", "15 oz." - ], ["Peas, Dried", "1 lb."], ["Lima Beans, Dried", "1 lb."], [ - "Navy Beans, Dried", "1 lb." - ], ["Coffee", "1 lb."], ["Tea", "1/4 lb."], ["Cocoa", "8 oz."], - ["Chocolate", "8 oz."], ["Sugar", "10 lb."], [ - "Corn Sirup", "24 oz." - ], ["Molasses", "18 oz."], ["Strawberry Preserve", "1 lb."]] + commodities = [["Wheat Flour (Enriched)", "10 lb."], ["Macaroni", "1 lb."], + ["Wheat Cereal (Enriched)", + "28 oz."], ["Corn Flakes", "8 oz."], ["Corn Meal", "1 lb."], + ["Hominy Grits", "24 oz."], ["Rice", "1 lb."], + ["Rolled Oats", "1 lb."], ["White Bread (Enriched)", "1 lb."], + ["Whole Wheat Bread", "1 lb."], ["Rye Bread", "1 lb."], + ["Pound Cake", "1 lb."], ["Soda Crackers", "1 lb."], + ["Milk", "1 qt."], ["Evaporated Milk (can)", "14.5 oz."], + ["Butter", "1 lb."], ["Oleomargarine", "1 lb."], + ["Eggs", "1 doz."], ["Cheese (Cheddar)", "1 lb."], + ["Cream", "1/2 pt."], ["Peanut Butter", "1 lb."], + ["Mayonnaise", "1/2 pt."], ["Crisco", "1 lb."], + ["Lard", "1 lb."], ["Sirloin Steak", "1 lb."], + ["Round Steak", "1 lb."], ["Rib Roast", "1 lb."], + ["Chuck Roast", "1 lb."], ["Plate", "1 lb."], + ["Liver (Beef)", "1 lb."], ["Leg of Lamb", "1 lb."], + ["Lamb Chops (Rib)", "1 lb."], ["Pork Chops", "1 lb."], + ["Pork Loin Roast", "1 lb."], ["Bacon", "1 lb."], + ["Ham - smoked", "1 lb."], ["Salt Pork", "1 lb."], + ["Roasting Chicken", "1 lb."], ["Veal Cutlets", "1 lb."], + ["Salmon, Pink (can)", "16 oz."], ["Apples", "1 lb."], + ["Bananas", "1 lb."], ["Lemons", "1 doz."], + ["Oranges", "1 doz."], ["Green Beans", "1 lb."], + ["Cabbage", "1 lb."], ["Carrots", "1 bunch"], + ["Celery", "1 stalk"], ["Lettuce", "1 head"], + ["Onions", "1 lb."], ["Potatoes", "15 lb."], + ["Spinach", "1 lb."], ["Sweet Potatoes", "1 lb."], + ["Peaches (can)", "No. 2 1/2"], ["Pears (can)", "No. 2 1/2,"], + ["Pineapple (can)", "No. 2 1/2"], ["Asparagus (can)", "No. 2"], + ["Grean Beans (can)", "No. 2"], + ["Pork and Beans (can)", "16 oz."], ["Corn (can)", "No. 2"], + ["Peas (can)", "No. 2"], ["Tomatoes (can)", "No. 2"], + ["Tomato Soup (can)", "10 1/2 oz."], + ["Peaches, Dried", "1 lb."], ["Prunes, Dried", "1 lb."], + ["Raisins, Dried", "15 oz."], ["Peas, Dried", "1 lb."], + ["Lima Beans, Dried", "1 lb."], ["Navy Beans, Dried", "1 lb."], + ["Coffee", "1 lb."], ["Tea", "1/4 lb."], ["Cocoa", "8 oz."], + ["Chocolate", "8 oz."], ["Sugar", "10 lb."], + ["Corn Sirup", "24 oz."], ["Molasses", "18 oz."], + ["Strawberry Preserve", "1 lb."]] # price and weight are the two first columns data = [ - [36.0, 12600.0, 44.7, 1411.0, 2.0, 365.0, 0.0, 55.4, 33.3, 441.0, 0.0], [ - 14.1, 3217.0, 11.6, 418.0, 0.7, 54.0, 0.0, 3.2, 1.9, 68.0, 0.0 - ], [24.2, 3280.0, 11.8, 377.0, 14.4, 175.0, 0.0, 14.4, 8.8, 114.0, 0.0], - [7.1, 3194.0, 11.4, 252.0, 0.1, 56.0, 0.0, 13.5, 2.3, 68.0, 0.0], [ - 4.6, 9861.0, 36.0, 897.0, 1.7, 99.0, 30.9, 17.4, 7.9, 106.0, 0.0 - ], [8.5, 8005.0, 28.6, 680.0, 0.8, 80.0, 0.0, 10.6, 1.6, 110.0, 0.0], [ - 7.5, 6048.0, 21.2, 460.0, 0.6, 41.0, 0.0, 2.0, 4.8, 60.0, 0.0 - ], [7.1, 6389.0, 25.3, 907.0, 5.1, 341.0, 0.0, 37.1, 8.9, 64.0, 0.0], [ - 7.9, 5742.0, 15.6, 488.0, 2.5, 115.0, 0.0, 13.8, 8.5, 126.0, 0.0 - ], [9.1, 4985.0, 12.2, 484.0, 2.7, 125.0, 0.0, 13.9, 6.4, 160.0, 0.0], [ - 9.2, 4930.0, 12.4, 439.0, 1.1, 82.0, 0.0, 9.9, 3.0, 66.0, 0.0 - ], [24.8, 1829.0, 8.0, 130.0, 0.4, 31.0, 18.9, 2.8, 3.0, 17.0, 0.0], [ - 15.1, 3004.0, 12.5, 288.0, 0.5, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0 - ], [11.0, 8867.0, 6.1, 310.0, 10.5, 18.0, 16.8, 4.0, 16.0, 7.0, 177.0], [ - 6.7, 6035.0, 8.4, 422.0, 15.1, 9.0, 26.0, 3.0, 23.5, 11.0, 60.0 - ], [20.8, 1473.0, 10.8, 9.0, 0.2, 3.0, 44.2, 0.0, 0.2, 2.0, 0.0], [ - 16.1, 2817.0, 20.6, 17.0, 0.6, 6.0, 55.8, 0.2, 0.0, 0.0, 0.0 - ], [32.6, 1857.0, 2.9, 238.0, 1.0, 52.0, 18.6, 2.8, 6.5, 1.0, 0.0], [ - 24.2, 1874.0, 7.4, 448.0, 16.4, 19.0, 28.1, 0.8, 10.3, 4.0, 0.0 - ], [14.1, 1689.0, 3.5, 49.0, 1.7, 3.0, 16.9, 0.6, 2.5, 0.0, 17.0], [ - 17.9, 2534.0, 15.7, 661.0, 1.0, 48.0, 0.0, 9.6, 8.1, 471.0, 0.0 - ], [16.7, 1198.0, 8.6, 18.0, 0.2, 8.0, 2.7, 0.4, 0.5, 0.0, 0.0], [ - 20.3, 2234.0, 20.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 - ], [9.8, 4628.0, 41.7, 0.0, 0.0, 0.0, 0.2, 0.0, 0.5, 5.0, 0.0], [ - 39.6, 1145.0, 2.9, 166.0, 0.1, 34.0, 0.2, 2.1, 2.9, 69.0, 0.0 - ], [36.4, 1246.0, 2.2, 214.0, 0.1, 32.0, 0.4, 2.5, 2.4, 87.0, 0.0], [ - 29.2, 1553.0, 3.4, 213.0, 0.1, 33.0, 0.0, 0.0, 2.0, 0.0, 0.0 - ], [22.6, 2007.0, 3.6, 309.0, 0.2, 46.0, 0.4, 1.0, 4.0, 120.0, 0.0], [ - 14.6, 3107.0, 8.5, 404.0, 0.2, 62.0, 0.0, 0.9, 0.0, 0.0, 0.0 - ], [26.8, 1692.0, 2.2, 333.0, 0.2, 139.0, 169.2, 6.4, 50.8, 316.0, 525.0], - [27.6, 1643.0, 3.1, 245.0, 0.1, 20.0, 0.0, 2.8, 3.0, 86.0, 0.0], [ - 36.6, 1239.0, 3.3, 140.0, 0.1, 15.0, 0.0, 1.7, 2.7, 54.0, 0.0 - ], [30.7, 1477.0, 3.5, 196.0, 0.2, 80.0, 0.0, 17.4, 2.7, 60.0, 0.0], [ - 24.2, 1874.0, 4.4, 249.0, 0.3, 37.0, 0.0, 18.2, 3.6, 79.0, 0.0 - ], [25.6, 1772.0, 10.4, 152.0, 0.2, 23.0, 0.0, 1.8, 1.8, 71.0, 0.0], [ - 27.4, 1655.0, 6.7, 212.0, 0.2, 31.0, 0.0, 9.9, 3.3, 50.0, 0.0 - ], [16.0, 2835.0, 18.8, 164.0, 0.1, 26.0, 0.0, 1.4, 1.8, 0.0, 0.0], [ - 30.3, 1497.0, 1.8, 184.0, 0.1, 30.0, 0.1, 0.9, 1.8, 68.0, 46.0 - ], [42.3, 1072.0, 1.7, 156.0, 0.1, 24.0, 0.0, 1.4, 2.4, 57.0, 0.0], [ - 13.0, 3489.0, 5.8, 705.0, 6.8, 45.0, 3.5, 1.0, 4.9, 209.0, 0.0 - ], [4.4, 9072.0, 5.8, 27.0, 0.5, 36.0, 7.3, 3.6, 2.7, 5.0, 544.0], [ - 6.1, 4982.0, 4.9, 60.0, 0.4, 30.0, 17.4, 2.5, 3.5, 28.0, 498.0 - ], [26.0, 2380.0, 1.0, 21.0, 0.5, 14.0, 0.0, 0.5, 0.0, 4.0, 952.0], [ - 30.9, 4439.0, 2.2, 40.0, 1.1, 18.0, 11.1, 3.6, 1.3, 10.0, 1993.0 - ], [7.1, 5750.0, 2.4, 138.0, 3.7, 80.0, 69.0, 4.3, 5.8, 37.0, 862.0], [ - 3.7, 8949.0, 2.6, 125.0, 4.0, 36.0, 7.2, 9.0, 4.5, 26.0, 5369.0 - ], [4.7, 6080.0, 2.7, 73.0, 2.8, 43.0, 188.5, 6.1, 4.3, 89.0, 608.0], [ - 7.3, 3915.0, 0.9, 51.0, 3.0, 23.0, 0.9, 1.4, 1.4, 9.0, 313.0 - ], [8.2, 2247.0, 0.4, 27.0, 1.1, 22.0, 112.4, 1.8, 3.4, 11.0, 449.0], [ - 3.6, 11844.0, 5.8, 166.0, 3.8, 59.0, 16.6, 4.7, 5.9, 21.0, 1184.0 - ], [ - 34.0, 16810.0, 14.3, 336.0, 1.8, 118.0, 6.7, 29.4, 7.1, 198.0, 2522.0 - ], [8.1, 4592.0, 1.1, 106.0, 0.0, 138.0, 918.4, 5.7, 13.8, 33.0, - 2755.0], [ - 5.1, 7649.0, 9.6, 138.0, 2.7, 54.0, 290.7, 8.4, 5.4, 83.0, 1912.0 - ], [16.8, 4894.0, 3.7, 20.0, 0.4, 10.0, 21.5, 0.5, 1.0, 31.0, 196.0], - [20.4, 4030.0, 3.0, 8.0, 0.3, 8.0, 0.8, 0.8, 0.8, 5.0, - 81.0], [21.3, 3993.0, 2.4, 16.0, 0.4, 8.0, 2.0, 2.8, 0.8, 7.0, 399.0], [ - 27.7, 1945.0, 0.4, 33.0, 0.3, 12.0, 16.3, 1.4, 2.1, 17.0, 272.0 - ], [10.0, 5386.0, 1.0, 54.0, 2.0, 65.0, 53.9, 1.6, 4.3, 32.0, 431.0], [ - 7.1, 6389.0, 7.5, 364.0, 4.0, 134.0, 3.5, 8.3, 7.7, 56.0, 0.0 - ], [10.4, 5452.0, 5.2, 136.0, 0.2, 16.0, 12.0, 1.6, 2.7, 42.0, 218.0], [ - 13.8, 4109.0, 2.3, 136.0, 0.6, 45.0, 34.9, 4.9, 2.5, 37.0, 370.0 - ], [8.6, 6263.0, 1.3, 63.0, 0.7, 38.0, 53.2, 3.4, 2.5, 36.0, 1253.0], [ - 7.6, 3917.0, 1.6, 71.0, 0.6, 43.0, 57.9, 3.5, 2.4, 67.0, 862.0 - ], [15.7, 2889.0, 8.5, 87.0, 1.7, 173.0, 86.8, 1.2, 4.3, 55.0, 57.0], [ - 9.0, 4284.0, 12.8, 99.0, 2.5, 154.0, 85.7, 3.9, 4.3, 65.0, 257.0 - ], [9.4, 4524.0, 13.5, 104.0, 2.5, 136.0, 4.5, 6.3, 1.4, 24.0, 136.0], [ - 7.9, 5742.0, 20.0, 1367.0, 4.2, 345.0, 2.9, 28.7, 18.4, 162.0, 0.0 - ], [8.9, 5097.0, 17.4, 1055.0, 3.7, 459.0, 5.1, 26.9, 38.2, 93.0, 0.0], [ - 5.9, 7688.0, 26.9, 1691.0, 11.4, 792.0, 0.0, 38.4, 24.6, 217.0, 0.0 - ], [22.4, 2025.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 5.1, 50.0, - 0.0], [17.4, 652.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.3, 42.0, 0.0], [ - 8.6, 2637.0, 8.7, 237.0, 3.0, 72.0, 0.0, 2.0, 11.9, 40.0, 0.0 - ], [16.2, 1400.0, 8.0, 77.0, 1.3, 39.0, 0.0, 0.9, 3.4, 14.0, 0.0], [ - 51.7, 8773.0, 34.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 - ], [13.7, 4996.0, 14.7, 0.0, 0.5, 74.0, 0.0, 0.0, 0.0, 5.0, 0.0], [ - 13.6, 3752.0, 9.0, 0.0, 10.3, 244.0, 0.0, 1.9, 7.5, 146.0, 0.0 - ], [20.5, 2213.0, 6.4, 11.0, 0.4, 7.0, 0.2, 0.2, 0.4, 3.0, 0.0] + [36.0, 12600.0, 44.7, 1411.0, 2.0, 365.0, 0.0, 55.4, 33.3, 441.0, 0.0], + [14.1, 3217.0, 11.6, 418.0, 0.7, 54.0, 0.0, 3.2, 1.9, 68.0, 0.0], + [24.2, 3280.0, 11.8, 377.0, 14.4, 175.0, 0.0, 14.4, 8.8, 114.0, 0.0], + [7.1, 3194.0, 11.4, 252.0, 0.1, 56.0, 0.0, 13.5, 2.3, 68.0, 0.0], + [4.6, 9861.0, 36.0, 897.0, 1.7, 99.0, 30.9, 17.4, 7.9, 106.0, 0.0], + [8.5, 8005.0, 28.6, 680.0, 0.8, 80.0, 0.0, 10.6, 1.6, 110.0, 0.0], + [7.5, 6048.0, 21.2, 460.0, 0.6, 41.0, 0.0, 2.0, 4.8, 60.0, 0.0], + [7.1, 6389.0, 25.3, 907.0, 5.1, 341.0, 0.0, 37.1, 8.9, 64.0, 0.0], + [7.9, 5742.0, 15.6, 488.0, 2.5, 115.0, 0.0, 13.8, 8.5, 126.0, 0.0], + [9.1, 4985.0, 12.2, 484.0, 2.7, 125.0, 0.0, 13.9, 6.4, 160.0, 0.0], + [9.2, 4930.0, 12.4, 439.0, 1.1, 82.0, 0.0, 9.9, 3.0, 66.0, 0.0], + [24.8, 1829.0, 8.0, 130.0, 0.4, 31.0, 18.9, 2.8, 3.0, 17.0, 0.0], + [15.1, 3004.0, 12.5, 288.0, 0.5, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [11.0, 8867.0, 6.1, 310.0, 10.5, 18.0, 16.8, 4.0, 16.0, 7.0, 177.0], + [6.7, 6035.0, 8.4, 422.0, 15.1, 9.0, 26.0, 3.0, 23.5, 11.0, 60.0], + [20.8, 1473.0, 10.8, 9.0, 0.2, 3.0, 44.2, 0.0, 0.2, 2.0, 0.0], + [16.1, 2817.0, 20.6, 17.0, 0.6, 6.0, 55.8, 0.2, 0.0, 0.0, 0.0], + [32.6, 1857.0, 2.9, 238.0, 1.0, 52.0, 18.6, 2.8, 6.5, 1.0, 0.0], + [24.2, 1874.0, 7.4, 448.0, 16.4, 19.0, 28.1, 0.8, 10.3, 4.0, 0.0], + [14.1, 1689.0, 3.5, 49.0, 1.7, 3.0, 16.9, 0.6, 2.5, 0.0, 17.0], + [17.9, 2534.0, 15.7, 661.0, 1.0, 48.0, 0.0, 9.6, 8.1, 471.0, 0.0], + [16.7, 1198.0, 8.6, 18.0, 0.2, 8.0, 2.7, 0.4, 0.5, 0.0, 0.0], + [20.3, 2234.0, 20.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [9.8, 4628.0, 41.7, 0.0, 0.0, 0.0, 0.2, 0.0, 0.5, 5.0, 0.0], + [39.6, 1145.0, 2.9, 166.0, 0.1, 34.0, 0.2, 2.1, 2.9, 69.0, 0.0], + [36.4, 1246.0, 2.2, 214.0, 0.1, 32.0, 0.4, 2.5, 2.4, 87.0, 0.0], + [29.2, 1553.0, 3.4, 213.0, 0.1, 33.0, 0.0, 0.0, 2.0, 0.0, 0.0], + [22.6, 2007.0, 3.6, 309.0, 0.2, 46.0, 0.4, 1.0, 4.0, 120.0, 0.0], + [14.6, 3107.0, 8.5, 404.0, 0.2, 62.0, 0.0, 0.9, 0.0, 0.0, 0.0], + [26.8, 1692.0, 2.2, 333.0, 0.2, 139.0, 169.2, 6.4, 50.8, 316.0, 525.0], + [27.6, 1643.0, 3.1, 245.0, 0.1, 20.0, 0.0, 2.8, 3.0, 86.0, 0.0], + [36.6, 1239.0, 3.3, 140.0, 0.1, 15.0, 0.0, 1.7, 2.7, 54.0, 0.0], + [30.7, 1477.0, 3.5, 196.0, 0.2, 80.0, 0.0, 17.4, 2.7, 60.0, 0.0], + [24.2, 1874.0, 4.4, 249.0, 0.3, 37.0, 0.0, 18.2, 3.6, 79.0, 0.0], + [25.6, 1772.0, 10.4, 152.0, 0.2, 23.0, 0.0, 1.8, 1.8, 71.0, 0.0], + [27.4, 1655.0, 6.7, 212.0, 0.2, 31.0, 0.0, 9.9, 3.3, 50.0, 0.0], + [16.0, 2835.0, 18.8, 164.0, 0.1, 26.0, 0.0, 1.4, 1.8, 0.0, 0.0], + [30.3, 1497.0, 1.8, 184.0, 0.1, 30.0, 0.1, 0.9, 1.8, 68.0, 46.0], + [42.3, 1072.0, 1.7, 156.0, 0.1, 24.0, 0.0, 1.4, 2.4, 57.0, 0.0], + [13.0, 3489.0, 5.8, 705.0, 6.8, 45.0, 3.5, 1.0, 4.9, 209.0, 0.0], + [4.4, 9072.0, 5.8, 27.0, 0.5, 36.0, 7.3, 3.6, 2.7, 5.0, 544.0], + [6.1, 4982.0, 4.9, 60.0, 0.4, 30.0, 17.4, 2.5, 3.5, 28.0, 498.0], + [26.0, 2380.0, 1.0, 21.0, 0.5, 14.0, 0.0, 0.5, 0.0, 4.0, 952.0], + [30.9, 4439.0, 2.2, 40.0, 1.1, 18.0, 11.1, 3.6, 1.3, 10.0, 1993.0], + [7.1, 5750.0, 2.4, 138.0, 3.7, 80.0, 69.0, 4.3, 5.8, 37.0, 862.0], + [3.7, 8949.0, 2.6, 125.0, 4.0, 36.0, 7.2, 9.0, 4.5, 26.0, 5369.0], + [4.7, 6080.0, 2.7, 73.0, 2.8, 43.0, 188.5, 6.1, 4.3, 89.0, 608.0], + [7.3, 3915.0, 0.9, 51.0, 3.0, 23.0, 0.9, 1.4, 1.4, 9.0, 313.0], + [8.2, 2247.0, 0.4, 27.0, 1.1, 22.0, 112.4, 1.8, 3.4, 11.0, 449.0], + [3.6, 11844.0, 5.8, 166.0, 3.8, 59.0, 16.6, 4.7, 5.9, 21.0, 1184.0], + [34.0, 16810.0, 14.3, 336.0, 1.8, 118.0, 6.7, 29.4, 7.1, 198.0, 2522.0], + [8.1, 4592.0, 1.1, 106.0, 0.0, 138.0, 918.4, 5.7, 13.8, 33.0, 2755.0], + [5.1, 7649.0, 9.6, 138.0, 2.7, 54.0, 290.7, 8.4, 5.4, 83.0, 1912.0], + [16.8, 4894.0, 3.7, 20.0, 0.4, 10.0, 21.5, 0.5, 1.0, 31.0, 196.0], + [20.4, 4030.0, 3.0, 8.0, 0.3, 8.0, 0.8, 0.8, 0.8, 5.0, 81.0], + [21.3, 3993.0, 2.4, 16.0, 0.4, 8.0, 2.0, 2.8, 0.8, 7.0, 399.0], + [27.7, 1945.0, 0.4, 33.0, 0.3, 12.0, 16.3, 1.4, 2.1, 17.0, 272.0], + [10.0, 5386.0, 1.0, 54.0, 2.0, 65.0, 53.9, 1.6, 4.3, 32.0, 431.0], + [7.1, 6389.0, 7.5, 364.0, 4.0, 134.0, 3.5, 8.3, 7.7, 56.0, 0.0], + [10.4, 5452.0, 5.2, 136.0, 0.2, 16.0, 12.0, 1.6, 2.7, 42.0, 218.0], + [13.8, 4109.0, 2.3, 136.0, 0.6, 45.0, 34.9, 4.9, 2.5, 37.0, 370.0], + [8.6, 6263.0, 1.3, 63.0, 0.7, 38.0, 53.2, 3.4, 2.5, 36.0, 1253.0], + [7.6, 3917.0, 1.6, 71.0, 0.6, 43.0, 57.9, 3.5, 2.4, 67.0, 862.0], + [15.7, 2889.0, 8.5, 87.0, 1.7, 173.0, 86.8, 1.2, 4.3, 55.0, 57.0], + [9.0, 4284.0, 12.8, 99.0, 2.5, 154.0, 85.7, 3.9, 4.3, 65.0, 257.0], + [9.4, 4524.0, 13.5, 104.0, 2.5, 136.0, 4.5, 6.3, 1.4, 24.0, 136.0], + [7.9, 5742.0, 20.0, 1367.0, 4.2, 345.0, 2.9, 28.7, 18.4, 162.0, 0.0], + [8.9, 5097.0, 17.4, 1055.0, 3.7, 459.0, 5.1, 26.9, 38.2, 93.0, 0.0], + [5.9, 7688.0, 26.9, 1691.0, 11.4, 792.0, 0.0, 38.4, 24.6, 217.0, 0.0], + [22.4, 2025.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 5.1, 50.0, 0.0], + [17.4, 652.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.3, 42.0, 0.0], + [8.6, 2637.0, 8.7, 237.0, 3.0, 72.0, 0.0, 2.0, 11.9, 40.0, 0.0], + [16.2, 1400.0, 8.0, 77.0, 1.3, 39.0, 0.0, 0.9, 3.4, 14.0, 0.0], + [51.7, 8773.0, 34.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [13.7, 4996.0, 14.7, 0.0, 0.5, 74.0, 0.0, 0.0, 0.0, 5.0, 0.0], + [13.6, 3752.0, 9.0, 0.0, 10.3, 244.0, 0.0, 1.9, 7.5, 146.0, 0.0], + [20.5, 2213.0, 6.4, 11.0, 0.4, 7.0, 0.2, 0.2, 0.4, 3.0, 0.0] ] # recommended daily allowance for a moderately active man diff --git a/examples/contrib/strimko2.py b/examples/contrib/strimko2.py index d3b95a07ba2..fe818b9ddef 100644 --- a/examples/contrib/strimko2.py +++ b/examples/contrib/strimko2.py @@ -1,13 +1,13 @@ # Copyright 2010 Hakan Kjellerstrand hakank@gmail.com # -# Licensed under the Apache License, Version 2.0 (the 'License'); +# 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, +# 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. @@ -59,8 +59,8 @@ def main(streams='', placed=''): if streams == '': streams = [[1, 1, 2, 2, 2, 2, 2], [1, 1, 2, 3, 3, 3, 2], [1, 4, 1, 3, 3, 5, 5], [4, 4, 3, 1, 3, 5, 5], - [4, 6, 6, 6, 7, 7, 5], [6, 4, 6, 4, 5, 5, - 7], [6, 6, 4, 7, 7, 7, 7]] + [4, 6, 6, 6, 7, 7, 5], [6, 4, 6, 4, 5, 5, 7], + [6, 6, 4, 7, 7, 7, 7]] # Note: This is 1-based placed = [[2, 1, 1], [2, 3, 7], [2, 5, 6], [2, 7, 4], [3, 2, 7], [3, 6, 1], @@ -137,7 +137,7 @@ def main(streams='', placed=''): if __name__ == '__main__': if len(sys.argv) > 1: problem_file = sys.argv[1] - exec (compile(open(problem_file).read(), problem_file, 'exec')) + exec(compile(open(problem_file).read(), problem_file, 'exec')) main(streams, placed) else: main() diff --git a/examples/contrib/toNum.py b/examples/contrib/toNum.py index 04b1cbfe5b8..68b631bc0ce 100644 --- a/examples/contrib/toNum.py +++ b/examples/contrib/toNum.py @@ -32,8 +32,8 @@ def toNum(solver, t, s, base): tlen = len(t) - solver.Add(s == solver.Sum([(base**(tlen - i - 1)) * t[i] - for i in range(tlen)])) + solver.Add( + s == solver.Sum([(base**(tlen - i - 1)) * t[i] for i in range(tlen)])) def main(unused_argv): diff --git a/examples/contrib/vendor_scheduling.py b/examples/contrib/vendor_scheduling.py index 40fad033e8c..276b2abfcd9 100644 --- a/examples/contrib/vendor_scheduling.py +++ b/examples/contrib/vendor_scheduling.py @@ -1,6 +1,7 @@ from __future__ import print_function from ortools.constraint_solver import pywrapcp + def main(): # Create the solver. solver = pywrapcp.Solver('Vendors scheduling') @@ -18,12 +19,12 @@ def main(): # Last columns are : # index_of_the_schedule, sum of worked hours (per work type). # The index is useful for branching. - possible_schedules = [[1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, - 8], [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, - 4], [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 2, - 5], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 4], - [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 4, - 3], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0]] + possible_schedules = [[1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 8], + [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 4], + [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 2, 5], + [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 4], + [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 4, 3], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0]] num_possible_schedules = len(possible_schedules) selected_schedules = [] @@ -77,7 +78,8 @@ def main(): num_solutions += 1 for i in range(num_vendors): - print('Vendor %i: ' % i, possible_schedules[selected_schedules[i].Value()]) + print('Vendor %i: ' % i, + possible_schedules[selected_schedules[i].Value()]) print() print('Statistics per day:') @@ -94,5 +96,6 @@ def main(): print('branches:', solver.Branches()) print('WallTime:', solver.WallTime(), 'ms') + if __name__ == '__main__': main() diff --git a/examples/contrib/wedding_optimal_chart.py b/examples/contrib/wedding_optimal_chart.py index 00685a0553f..7cca649f95e 100644 --- a/examples/contrib/wedding_optimal_chart.py +++ b/examples/contrib/wedding_optimal_chart.py @@ -62,23 +62,23 @@ def main(): # Connection matrix: who knows who, and how strong # is the relation - C = [[1, 50, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0], [50, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0], [1, 1, 1, 50, 1, 1, 1, 1, 10, 0, 0, 0, 0, 0, 0, 0, - 0], [1, 1, 50, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 50, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0], [1, 1, 1, 1, 50, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0], [1, 1, 1, 1, 1, 1, 1, 50, 1, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 50, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0], [1, 1, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 50, 1, 1, 1, 1, 1, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 1, 1, 1, 1, 1, 1, - 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, - 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 - ], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 - ], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]] + C = [[1, 50, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [50, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 50, 1, 1, 1, 1, 10, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 50, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 50, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 50, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 50, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 50, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 50, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]] # Names of the guests. B: Bride side, G: Groom side names = [ diff --git a/examples/contrib/word_square.py b/examples/contrib/word_square.py index 0d20730ea77..3eae4ac593d 100644 --- a/examples/contrib/word_square.py +++ b/examples/contrib/word_square.py @@ -103,6 +103,8 @@ def main(words, word_len, num_answers=20): # print E print_solution(E, words) num_solutions += 1 + if num_solutions > num_answers: + break solver.EndSearch() @@ -151,10 +153,10 @@ def read_words(word_list, word_len, limit): return all_words -word_dict = "/usr/share/dict/words" -word_len = 2 +word_dict = "examples/data/words/list.txt" +word_len = 4 limit = 1000000 -num_answers = 20 +num_answers = 5 if __name__ == "__main__": diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index 8b2bc7df489..b88eda1e68c 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -1,17 +1,44 @@ -if(NOT BUILD_CXX) - return() +if (NOT BUILD_CXX) + return() endif() project(ortools_examples) -if(APPLE) - set(CMAKE_INSTALL_RPATH - "@loader_path/../..;@loader_path/../lib;@loader_path") +if (APPLE) + set(CMAKE_INSTALL_RPATH + "@loader_path/../..;@loader_path/../lib;@loader_path") else() - set(CMAKE_INSTALL_RPATH "$ORIGIN/../../:$ORIGIN/../lib:$ORIGIN/") + set(CMAKE_INSTALL_RPATH "$ORIGIN/../../:$ORIGIN/../lib:$ORIGIN/") +endif() + +set(_SRCS + cvrptw_lib.cc + fap_model_printer.cc + fap_parser.cc + fap_utilities.cc + parse_dimacs_assignment.cc + ) + +if(MSVC) + add_library(${PROJECT_NAME} STATIC ${_SRCS}) +else() + add_library(${PROJECT_NAME} SHARED ${_SRCS}) endif() get_filename_component(PARENT_SOURCE_DIR ${PROJECT_SOURCE_DIR} DIRECTORY) +get_filename_component(PARENT_SOURCE_DIR ${PARENT_SOURCE_DIR} DIRECTORY) +target_include_directories(${PROJECT_NAME} PUBLIC ${PARENT_SOURCE_DIR}) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11) +target_link_libraries(${PROJECT_NAME} PUBLIC ortools::ortools) + +include(GNUInstallDirs) +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) foreach(EXECUTABLE costas_array_sat diff --git a/examples/cpp/cvrp_disjoint_tw.cc b/examples/cpp/cvrp_disjoint_tw.cc index 08f858f2d00..aef1eb2f3ff 100644 --- a/examples/cpp/cvrp_disjoint_tw.cc +++ b/examples/cpp/cvrp_disjoint_tw.cc @@ -25,21 +25,28 @@ #include #include "examples/cpp/cvrptw_lib.h" -#include "ortools/base/callback.h" +#include "google/protobuf/text_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/constraint_solver/routing.h" -#include "ortools/constraint_solver/routing_enums.pb.h" -#include "ortools/constraint_solver/routing_flags.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" using operations_research::ACMRandom; +using operations_research::Assignment; +using operations_research::DefaultRoutingSearchParameters; using operations_research::GetSeed; using operations_research::LocationContainer; using operations_research::RandomDemand; +using operations_research::RoutingDimension; +using operations_research::RoutingIndexManager; using operations_research::RoutingModel; +using operations_research::RoutingNodeIndex; using operations_research::RoutingSearchParameters; using operations_research::ServiceTimePlusTransition; +using operations_research::Solver; DEFINE_int32(vrp_orders, 100, "Nodes in the problem."); DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance."); @@ -48,6 +55,9 @@ DEFINE_bool(vrp_use_deterministic_random_seed, false, "Use deterministic random seeds."); DEFINE_bool(vrp_use_same_vehicle_costs, false, "Use same vehicle costs in the routing model"); +DEFINE_string(routing_search_parameters, "", + "Text proto RoutingSearchParameters (possibly partial) that will " + "override the DefaultRoutingSearchParameters()"); const char* kTime = "Time"; const char* kCapacity = "Capacity"; @@ -61,13 +71,9 @@ int main(int argc, char** argv) { // VRP of size FLAGS_vrp_size. // Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of // the routes are at node 0. - const RoutingModel::NodeIndex kDepot(0); - RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); - RoutingSearchParameters parameters = - operations_research::BuildSearchParametersFromFlags(); - parameters.set_first_solution_strategy( - operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC); - parameters.mutable_local_search_operators()->set_use_path_lns(false); + const RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); // Setting up locations. const int64 kXMax = 100000; @@ -79,35 +85,48 @@ int main(int argc, char** argv) { } // Setting the cost function. - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance)); + const int vehicle_cost = + routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) { + return locations.ManhattanDistance(manager.IndexToNode(i), + manager.IndexToNode(j)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); // Adding capacity dimension constraints. const int64 kVehicleCapacity = 40; const int64 kNullCapacitySlack = 0; - RandomDemand demand(routing.nodes(), kDepot, + RandomDemand demand(manager.num_nodes(), kDepot, FLAGS_vrp_use_deterministic_random_seed); demand.Initialize(); - routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand), - kNullCapacitySlack, kVehicleCapacity, - /*fix_start_cumul_to_zero=*/true, kCapacity); + routing.AddDimension( + routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) { + return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j)); + }), + kNullCapacitySlack, kVehicleCapacity, + /*fix_start_cumul_to_zero=*/true, kCapacity); // Adding time dimension constraints. const int64 kTimePerDemandUnit = 300; const int64 kHorizon = 24 * 3600; ServiceTimePlusTransition time( - kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand), - NewPermanentCallback(&locations, &LocationContainer::ManhattanTime)); + kTimePerDemandUnit, + [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { + return demand.Demand(i, j); + }, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); routing.AddDimension( - NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute), + routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) { + return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j)); + }), kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime); - const operations_research::RoutingDimension& time_dimension = - routing.GetDimensionOrDie(kTime); + const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); // Adding disjoint time windows. - operations_research::Solver* solver = routing.solver(); + Solver* solver = routing.solver(); ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); - for (int order = 1; order < routing.nodes(); ++order) { + for (int order = 1; order < manager.num_nodes(); ++order) { std::vector forbid_points(2 * FLAGS_vrp_windows, 0); for (int i = 0; i < forbid_points.size(); ++i) { forbid_points[i] = randomizer.Uniform(kHorizon); @@ -126,19 +145,19 @@ int main(int argc, char** argv) { // Adding penalty costs to allow skipping orders. const int64 kPenalty = 10000000; - const RoutingModel::NodeIndex kFirstNodeAfterDepot(1); - for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; - order < routing.nodes(); ++order) { - std::vector orders(1, order); + const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; + order < manager.num_nodes(); ++order) { + std::vector orders(1, manager.NodeToIndex(order)); routing.AddDisjunction(orders, kPenalty); } // Adding same vehicle constraint costs for consecutive nodes. if (FLAGS_vrp_use_same_vehicle_costs) { - std::vector group; - for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; - order < routing.nodes(); ++order) { - group.push_back(order); + std::vector group; + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; + order < manager.num_nodes(); ++order) { + group.push_back(manager.NodeToIndex(order)); if (group.size() == kMaxNodesPerGroup) { routing.AddSoftSameVehicleConstraint(group, kSameVehicleCost); group.clear(); @@ -150,10 +169,12 @@ int main(int argc, char** argv) { } // Solve, returns a solution if any (owned by RoutingModel). - const operations_research::Assignment* solution = - routing.SolveWithParameters(parameters); + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); + const Assignment* solution = routing.SolveWithParameters(parameters); if (solution != nullptr) { - DisplayPlan(routing, *solution, FLAGS_vrp_use_same_vehicle_costs, + DisplayPlan(manager, routing, *solution, FLAGS_vrp_use_same_vehicle_costs, kMaxNodesPerGroup, kSameVehicleCost, routing.GetDimensionOrDie(kCapacity), routing.GetDimensionOrDie(kTime)); diff --git a/examples/cpp/cvrptw.cc b/examples/cpp/cvrptw.cc index 587acf17be3..9696115f7e7 100644 --- a/examples/cpp/cvrptw.cc +++ b/examples/cpp/cvrptw.cc @@ -24,20 +24,26 @@ #include #include "examples/cpp/cvrptw_lib.h" -#include "ortools/base/callback.h" +#include "google/protobuf/text_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/random.h" #include "ortools/constraint_solver/routing.h" -#include "ortools/constraint_solver/routing_enums.pb.h" -#include "ortools/constraint_solver/routing_flags.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" using operations_research::ACMRandom; +using operations_research::Assignment; +using operations_research::DefaultRoutingSearchParameters; using operations_research::GetSeed; using operations_research::LocationContainer; using operations_research::RandomDemand; +using operations_research::RoutingDimension; +using operations_research::RoutingIndexManager; using operations_research::RoutingModel; +using operations_research::RoutingNodeIndex; using operations_research::RoutingSearchParameters; using operations_research::ServiceTimePlusTransition; @@ -47,6 +53,9 @@ DEFINE_bool(vrp_use_deterministic_random_seed, false, "Use deterministic random seeds."); DEFINE_bool(vrp_use_same_vehicle_costs, false, "Use same vehicle costs in the routing model"); +DEFINE_string(routing_search_parameters, "", + "Text proto RoutingSearchParameters (possibly partial) that will " + "override the DefaultRoutingSearchParameters()"); const char* kTime = "Time"; const char* kCapacity = "Capacity"; @@ -60,13 +69,9 @@ int main(int argc, char** argv) { // VRP of size FLAGS_vrp_size. // Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of // the routes are at node 0. - const RoutingModel::NodeIndex kDepot(0); - RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); - RoutingSearchParameters parameters = - operations_research::BuildSearchParametersFromFlags(); - parameters.set_first_solution_strategy( - operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC); - parameters.mutable_local_search_operators()->set_use_path_lns(false); + const RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); // Setting up locations. const int64 kXMax = 100000; @@ -78,54 +83,67 @@ int main(int argc, char** argv) { } // Setting the cost function. - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance)); + const int vehicle_cost = + routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) { + return locations.ManhattanDistance(manager.IndexToNode(i), + manager.IndexToNode(j)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); // Adding capacity dimension constraints. const int64 kVehicleCapacity = 40; const int64 kNullCapacitySlack = 0; - RandomDemand demand(routing.nodes(), kDepot, + RandomDemand demand(manager.num_nodes(), kDepot, FLAGS_vrp_use_deterministic_random_seed); demand.Initialize(); - routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand), - kNullCapacitySlack, kVehicleCapacity, - /*fix_start_cumul_to_zero=*/true, kCapacity); + routing.AddDimension( + routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) { + return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j)); + }), + kNullCapacitySlack, kVehicleCapacity, + /*fix_start_cumul_to_zero=*/true, kCapacity); // Adding time dimension constraints. const int64 kTimePerDemandUnit = 300; const int64 kHorizon = 24 * 3600; ServiceTimePlusTransition time( - kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand), - NewPermanentCallback(&locations, &LocationContainer::ManhattanTime)); + kTimePerDemandUnit, + [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { + return demand.Demand(i, j); + }, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); routing.AddDimension( - NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute), + routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) { + return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j)); + }), kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime); - const operations_research::RoutingDimension& time_dimension = - routing.GetDimensionOrDie(kTime); + const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); // Adding time windows. ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); const int64 kTWDuration = 5 * 3600; - for (int order = 1; order < routing.nodes(); ++order) { + for (int order = 1; order < manager.num_nodes(); ++order) { const int64 start = randomizer.Uniform(kHorizon - kTWDuration); time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration); } // Adding penalty costs to allow skipping orders. const int64 kPenalty = 10000000; - const RoutingModel::NodeIndex kFirstNodeAfterDepot(1); - for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; - order < routing.nodes(); ++order) { - std::vector orders(1, order); + const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; + order < manager.num_nodes(); ++order) { + std::vector orders(1, manager.NodeToIndex(order)); routing.AddDisjunction(orders, kPenalty); } // Adding same vehicle constraint costs for consecutive nodes. if (FLAGS_vrp_use_same_vehicle_costs) { - std::vector group; - for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; - order < routing.nodes(); ++order) { - group.push_back(order); + std::vector group; + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; + order < manager.num_nodes(); ++order) { + group.push_back(manager.NodeToIndex(order)); if (group.size() == kMaxNodesPerGroup) { routing.AddSoftSameVehicleConstraint(group, kSameVehicleCost); group.clear(); @@ -137,10 +155,12 @@ int main(int argc, char** argv) { } // Solve, returns a solution if any (owned by RoutingModel). - const operations_research::Assignment* solution = - routing.SolveWithParameters(parameters); + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); + const Assignment* solution = routing.SolveWithParameters(parameters); if (solution != nullptr) { - DisplayPlan(routing, *solution, FLAGS_vrp_use_same_vehicle_costs, + DisplayPlan(manager, routing, *solution, FLAGS_vrp_use_same_vehicle_costs, kMaxNodesPerGroup, kSameVehicleCost, routing.GetDimensionOrDie(kCapacity), routing.GetDimensionOrDie(kTime)); diff --git a/examples/cpp/cvrptw_lib.h b/examples/cpp/cvrptw_lib.h index 932c2b45300..7c9c7f02a3f 100644 --- a/examples/cpp/cvrptw_lib.h +++ b/examples/cpp/cvrptw_lib.h @@ -37,17 +37,15 @@ class LocationContainer { void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); } void AddRandomLocation(int64 x_max, int64 y_max); void AddRandomLocation(int64 x_max, int64 y_max, int duplicates); - int64 ManhattanDistance( - operations_research::RoutingModel::NodeIndex from, - operations_research::RoutingModel::NodeIndex to) const; - int64 NegManhattanDistance( - operations_research::RoutingModel::NodeIndex from, - operations_research::RoutingModel::NodeIndex to) const; - int64 ManhattanTime(operations_research::RoutingModel::NodeIndex from, - operations_research::RoutingModel::NodeIndex to) const; - - bool SameLocation(operations_research::RoutingModel::NodeIndex node1, - operations_research::RoutingModel::NodeIndex node2) const; + int64 ManhattanDistance(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const; + int64 NegManhattanDistance(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const; + int64 ManhattanTime(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const; + + bool SameLocation(RoutingIndexManager::NodeIndex node1, + RoutingIndexManager::NodeIndex node2) const; int64 SameLocationFromIndex(int64 node1, int64 node2) const; private: @@ -67,23 +65,22 @@ class LocationContainer { MTRandom randomizer_; const int64 speed_; - gtl::ITIVector - locations_; + gtl::ITIVector locations_; }; // Random demand. class RandomDemand { public: - RandomDemand(int size, operations_research::RoutingModel::NodeIndex depot, + RandomDemand(int size, RoutingIndexManager::NodeIndex depot, bool use_deterministic_seed); void Initialize(); - int64 Demand(operations_research::RoutingModel::NodeIndex from, - operations_research::RoutingModel::NodeIndex to) const; + int64 Demand(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const; private: std::unique_ptr demand_; const int size_; - const operations_research::RoutingModel::NodeIndex depot_; + const RoutingIndexManager::NodeIndex depot_; const bool use_deterministic_seed_; }; @@ -92,16 +89,15 @@ class ServiceTimePlusTransition { public: ServiceTimePlusTransition( int64 time_per_demand_unit, - operations_research::RoutingModel::NodeEvaluator2* demand, - operations_research::RoutingModel::NodeEvaluator2* transition_time); - int64 Compute(operations_research::RoutingModel::NodeIndex from, - operations_research::RoutingModel::NodeIndex to) const; + operations_research::RoutingNodeEvaluator2 demand, + operations_research::RoutingNodeEvaluator2 transition_time); + int64 Compute(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const; private: const int64 time_per_demand_unit_; - std::unique_ptr demand_; - std::unique_ptr - transition_time_; + operations_research::RoutingNodeEvaluator2 demand_; + operations_research::RoutingNodeEvaluator2 transition_time_; }; // Stop service time + transition time callback. @@ -109,21 +105,21 @@ class StopServiceTimePlusTransition { public: StopServiceTimePlusTransition( int64 stop_time, const LocationContainer& location_container, - operations_research::RoutingModel::NodeEvaluator2* transition_time); - int64 Compute(operations_research::RoutingModel::NodeIndex from, - operations_research::RoutingModel::NodeIndex to) const; + operations_research::RoutingNodeEvaluator2 transition_time); + int64 Compute(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const; private: const int64 stop_time_; const LocationContainer& location_container_; - std::unique_ptr demand_; - std::unique_ptr - transition_time_; + operations_research::RoutingNodeEvaluator2 demand_; + operations_research::RoutingNodeEvaluator2 transition_time_; }; // Route plan displayer. // TODO(user): Move the display code to the routing library. void DisplayPlan( + const operations_research::RoutingIndexManager& manager, const operations_research::RoutingModel& routing, const operations_research::Assignment& plan, bool use_same_vehicle_costs, int64 max_nodes_per_group, int64 same_vehicle_cost, diff --git a/examples/cpp/cvrptw_with_breaks.cc b/examples/cpp/cvrptw_with_breaks.cc index 2e44d521da2..17d5c810fee 100644 --- a/examples/cpp/cvrptw_with_breaks.cc +++ b/examples/cpp/cvrptw_with_breaks.cc @@ -27,29 +27,42 @@ #include +#include "absl/strings/str_cat.h" #include "examples/cpp/cvrptw_lib.h" -#include "ortools/base/callback.h" +#include "google/protobuf/text_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/random.h" #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" -#include "ortools/constraint_solver/routing_flags.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" using operations_research::ACMRandom; +using operations_research::Assignment; +using operations_research::DefaultRoutingSearchParameters; +using operations_research::FirstSolutionStrategy; using operations_research::GetSeed; +using operations_research::IntervalVar; using operations_research::LocationContainer; using operations_research::RandomDemand; +using operations_research::RoutingDimension; +using operations_research::RoutingIndexManager; using operations_research::RoutingModel; +using operations_research::RoutingNodeIndex; using operations_research::RoutingSearchParameters; using operations_research::ServiceTimePlusTransition; +using operations_research::Solver; DEFINE_int32(vrp_orders, 100, "Nodes in the problem."); DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance."); DEFINE_bool(vrp_use_deterministic_random_seed, false, "Use deterministic random seeds."); +DEFINE_string(routing_search_parameters, "", + "Text proto RoutingSearchParameters (possibly partial) that will " + "override the DefaultRoutingSearchParameters()"); const char* kTime = "Time"; const char* kCapacity = "Capacity"; @@ -61,14 +74,14 @@ int main(int argc, char** argv) { // VRP of size FLAGS_vrp_size. // Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of // the routes are at node 0. - const RoutingModel::NodeIndex kDepot(0); - RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); - RoutingSearchParameters parameters = - operations_research::BuildSearchParametersFromFlags(); + const RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); parameters.set_first_solution_strategy( - operations_research::FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION); - parameters.mutable_local_search_operators()->set_use_path_lns(false); - parameters.mutable_local_search_operators()->set_use_inactive_lns(false); + FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION); // Setting up locations. const int64 kXMax = 100000; @@ -80,35 +93,48 @@ int main(int argc, char** argv) { } // Setting the cost function. - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance)); + const int vehicle_cost = + routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) { + return locations.ManhattanDistance(manager.IndexToNode(i), + manager.IndexToNode(j)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); // Adding capacity dimension constraints. const int64 kVehicleCapacity = 40; const int64 kNullCapacitySlack = 0; - RandomDemand demand(routing.nodes(), kDepot, + RandomDemand demand(manager.num_nodes(), kDepot, FLAGS_vrp_use_deterministic_random_seed); demand.Initialize(); - routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand), - kNullCapacitySlack, kVehicleCapacity, - /*fix_start_cumul_to_zero=*/true, kCapacity); + routing.AddDimension( + routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) { + return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j)); + }), + kNullCapacitySlack, kVehicleCapacity, + /*fix_start_cumul_to_zero=*/true, kCapacity); // Adding time dimension constraints. const int64 kTimePerDemandUnit = 300; const int64 kHorizon = 24 * 3600; ServiceTimePlusTransition time( - kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand), - NewPermanentCallback(&locations, &LocationContainer::ManhattanTime)); + kTimePerDemandUnit, + [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { + return demand.Demand(i, j); + }, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); routing.AddDimension( - NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute), + routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) { + return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j)); + }), kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime); - operations_research::RoutingDimension* const time_dimension = - routing.GetMutableDimension(kTime); + RoutingDimension* const time_dimension = routing.GetMutableDimension(kTime); // Adding time windows. ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); const int64 kTWDuration = 5 * 3600; - for (int order = 1; order < routing.nodes(); ++order) { + for (int order = 1; order < manager.num_nodes(); ++order) { const int64 start = randomizer.Uniform(kHorizon - kTWDuration); time_dimension->CumulVar(order)->SetRange(start, start + kTWDuration); routing.AddToAssignment(time_dimension->SlackVar(order)); @@ -129,19 +155,24 @@ int main(int argc, char** argv) { // - 40min breaks between 11:00am and 1:00pm // or // - 2 x 30min breaks between 10:00am and 3:00pm, at least 1h apart + // First, fill service time vector. + std::vector service_times(routing.Size()); + for (int node = 0; node < routing.Size(); node++) { + const RoutingIndexManager::NodeIndex index(node); + service_times[node] = kTimePerDemandUnit * demand.Demand(index, index); + if (node >= routing.nodes()) service_times[node] = 0; + } const std::vector> break_data = { {/*start_min*/ 11, /*start_max*/ 13, /*duration*/ 2400}, {/*start_min*/ 10, /*start_max*/ 15, /*duration*/ 1800}, {/*start_min*/ 10, /*start_max*/ 15, /*duration*/ 1800}}; - operations_research::Solver* const solver = routing.solver(); + Solver* const solver = routing.solver(); for (int vehicle = 0; vehicle < FLAGS_vrp_vehicles; ++vehicle) { - std::vector breaks; + std::vector breaks; for (int i = 0; i < break_data.size(); ++i) { - operations_research::IntervalVar* const break_interval = - solver->MakeFixedDurationIntervalVar( - break_data[i][0] * 3600, break_data[i][1] * 3600, - break_data[i][2], true, - absl::StrCat("Break ", i, " on vehicle ", vehicle)); + IntervalVar* const break_interval = solver->MakeFixedDurationIntervalVar( + break_data[i][0] * 3600, break_data[i][1] * 3600, break_data[i][2], + true, absl::StrCat("Break ", i, " on vehicle ", vehicle)); breaks.push_back(break_interval); } // break1 performed iff break2 performed @@ -149,27 +180,26 @@ int main(int argc, char** argv) { breaks[2]->PerformedExpr())); // break2 start 1h after break1. solver->AddConstraint(solver->MakeIntervalVarRelationWithDelay( - breaks[2], operations_research::Solver::STARTS_AFTER_END, breaks[1], - 3600)); + breaks[2], Solver::STARTS_AFTER_END, breaks[1], 3600)); // break0 performed iff break2 unperformed solver->AddConstraint(solver->MakeNonEquality(breaks[0]->PerformedExpr(), breaks[2]->PerformedExpr())); - time_dimension->SetBreakIntervalsOfVehicle(std::move(breaks), vehicle); + time_dimension->SetBreakIntervalsOfVehicle(std::move(breaks), vehicle, + service_times); } // Adding penalty costs to allow skipping orders. const int64 kPenalty = 10000000; - const RoutingModel::NodeIndex kFirstNodeAfterDepot(1); - for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; + const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; order < routing.nodes(); ++order) { - std::vector orders(1, order); + std::vector orders(1, manager.NodeToIndex(order)); routing.AddDisjunction(orders, kPenalty); } // Solve, returns a solution if any (owned by RoutingModel). - const operations_research::Assignment* solution = - routing.SolveWithParameters(parameters); + const Assignment* solution = routing.SolveWithParameters(parameters); if (solution != nullptr) { LOG(INFO) << "Breaks: "; for (const auto& break_interval : @@ -181,7 +211,7 @@ int main(int argc, char** argv) { LOG(INFO) << break_interval.Var()->name() << " unperformed"; } } - DisplayPlan(routing, *solution, false, 0, 0, + DisplayPlan(manager, routing, *solution, false, 0, 0, routing.GetDimensionOrDie(kCapacity), routing.GetDimensionOrDie(kTime)); } else { diff --git a/examples/cpp/cvrptw_with_refueling.cc b/examples/cpp/cvrptw_with_refueling.cc index 570a1fc166b..9eb02136d02 100644 --- a/examples/cpp/cvrptw_with_refueling.cc +++ b/examples/cpp/cvrptw_with_refueling.cc @@ -22,29 +22,36 @@ #include #include "examples/cpp/cvrptw_lib.h" -#include "ortools/base/callback.h" +#include "google/protobuf/text_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/random.h" #include "ortools/constraint_solver/routing.h" -#include "ortools/constraint_solver/routing_enums.pb.h" -#include "ortools/constraint_solver/routing_flags.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" using operations_research::ACMRandom; +using operations_research::Assignment; +using operations_research::DefaultRoutingSearchParameters; using operations_research::GetSeed; using operations_research::LocationContainer; using operations_research::RandomDemand; +using operations_research::RoutingDimension; +using operations_research::RoutingIndexManager; using operations_research::RoutingModel; +using operations_research::RoutingNodeIndex; using operations_research::RoutingSearchParameters; using operations_research::ServiceTimePlusTransition; -using operations_research::StringAppendF; -using operations_research::StringPrintf; DEFINE_int32(vrp_orders, 100, "Nodes in the problem."); DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance."); DEFINE_bool(vrp_use_deterministic_random_seed, false, "Use deterministic random seeds."); +DEFINE_string(routing_search_parameters, "", + "Text proto RoutingSearchParameters (possibly partial) that will " + "override the DefaultRoutingSearchParameters()"); const char* kTime = "Time"; const char* kCapacity = "Capacity"; @@ -63,13 +70,9 @@ int main(int argc, char** argv) { // VRP of size FLAGS_vrp_size. // Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of // the routes are at node 0. - const RoutingModel::NodeIndex kDepot(0); - RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); - RoutingSearchParameters parameters = - operations_research::BuildSearchParametersFromFlags(); - parameters.set_first_solution_strategy( - operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC); - parameters.mutable_local_search_operators()->set_use_path_lns(false); + const RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); // Setting up locations. const int64 kXMax = 100000; @@ -81,34 +84,47 @@ int main(int argc, char** argv) { } // Setting the cost function. - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance)); + const int vehicle_cost = + routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) { + return locations.ManhattanDistance(manager.IndexToNode(i), + manager.IndexToNode(j)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); // Adding capacity dimension constraints. const int64 kVehicleCapacity = 40; const int64 kNullCapacitySlack = 0; - RandomDemand demand(routing.nodes(), kDepot, + RandomDemand demand(manager.num_nodes(), kDepot, FLAGS_vrp_use_deterministic_random_seed); demand.Initialize(); - routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand), - kNullCapacitySlack, kVehicleCapacity, - /*fix_start_cumul_to_zero=*/true, kCapacity); + routing.AddDimension( + routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) { + return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j)); + }), + kNullCapacitySlack, kVehicleCapacity, + /*fix_start_cumul_to_zero=*/true, kCapacity); // Adding time dimension constraints. const int64 kTimePerDemandUnit = 300; const int64 kHorizon = 24 * 3600; ServiceTimePlusTransition time( - kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand), - NewPermanentCallback(&locations, &LocationContainer::ManhattanTime)); + kTimePerDemandUnit, + [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { + return demand.Demand(i, j); + }, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); routing.AddDimension( - NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute), + routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) { + return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j)); + }), kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime); - const operations_research::RoutingDimension& time_dimension = - routing.GetDimensionOrDie(kTime); + const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); // Adding time windows. ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); const int64 kTWDuration = 5 * 3600; - for (int order = 1; order < routing.nodes(); ++order) { + for (int order = 1; order < manager.num_nodes(); ++order) { if (!IsRefuelNode(order)) { const int64 start = randomizer.Uniform(kHorizon - kTWDuration); time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration); @@ -120,11 +136,12 @@ int main(int argc, char** argv) { // increase by letting slack variable replenish the fuel. const int64 kFuelCapacity = kXMax + kYMax; routing.AddDimension( - NewPermanentCallback(&locations, - &LocationContainer::NegManhattanDistance), + routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) { + return locations.NegManhattanDistance(manager.IndexToNode(i), + manager.IndexToNode(j)); + }), kFuelCapacity, kFuelCapacity, /*fix_start_cumul_to_zero=*/false, kFuel); - const operations_research::RoutingDimension& fuel_dimension = - routing.GetDimensionOrDie(kFuel); + const RoutingDimension& fuel_dimension = routing.GetDimensionOrDie(kFuel); for (int order = 0; order < routing.Size(); ++order) { // Only let slack free for refueling nodes. if (!IsRefuelNode(order) || routing.IsStart(order)) { @@ -136,18 +153,20 @@ int main(int argc, char** argv) { // Adding penalty costs to allow skipping orders. const int64 kPenalty = 100000; - const RoutingModel::NodeIndex kFirstNodeAfterDepot(1); - for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; + const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; order < routing.nodes(); ++order) { - std::vector orders(1, order); + std::vector orders(1, manager.NodeToIndex(order)); routing.AddDisjunction(orders, kPenalty); } // Solve, returns a solution if any (owned by RoutingModel). - const operations_research::Assignment* solution = - routing.SolveWithParameters(parameters); + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); + const Assignment* solution = routing.SolveWithParameters(parameters); if (solution != nullptr) { - DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false, + DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false, /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, routing.GetDimensionOrDie(kCapacity), routing.GetDimensionOrDie(kTime)); diff --git a/examples/cpp/cvrptw_with_resources.cc b/examples/cpp/cvrptw_with_resources.cc index df44ed8c9ab..93bc87c898b 100644 --- a/examples/cpp/cvrptw_with_resources.cc +++ b/examples/cpp/cvrptw_with_resources.cc @@ -24,29 +24,39 @@ #include #include "examples/cpp/cvrptw_lib.h" -#include "ortools/base/callback.h" +#include "google/protobuf/text_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/random.h" #include "ortools/constraint_solver/routing.h" -#include "ortools/constraint_solver/routing_enums.pb.h" -#include "ortools/constraint_solver/routing_flags.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" using operations_research::ACMRandom; +using operations_research::Assignment; +using operations_research::DefaultRoutingSearchParameters; using operations_research::GetSeed; +using operations_research::IntervalVar; +using operations_research::IntVar; using operations_research::LocationContainer; using operations_research::RandomDemand; +using operations_research::RoutingDimension; +using operations_research::RoutingIndexManager; using operations_research::RoutingModel; +using operations_research::RoutingNodeIndex; using operations_research::RoutingSearchParameters; using operations_research::ServiceTimePlusTransition; -using operations_research::StringAppendF; -using operations_research::StringPrintf; +using operations_research::Solver; DEFINE_int32(vrp_orders, 100, "Nodes in the problem."); DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance."); DEFINE_bool(vrp_use_deterministic_random_seed, false, "Use deterministic random seeds."); +DEFINE_string(routing_search_parameters, "", + "Text proto RoutingSearchParameters (possibly partial) that will " + "override the DefaultRoutingSearchParameters()"); const char* kTime = "Time"; const char* kCapacity = "Capacity"; @@ -58,13 +68,9 @@ int main(int argc, char** argv) { // VRP of size FLAGS_vrp_size. // Nodes are indexed from 0 to FLAGS_vrp_orders, the starts and ends of // the routes are at node 0. - const RoutingModel::NodeIndex kDepot(0); - RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); - RoutingSearchParameters parameters = - operations_research::BuildSearchParametersFromFlags(); - parameters.set_first_solution_strategy( - operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC); - parameters.mutable_local_search_operators()->set_use_path_lns(false); + const RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); // Setting up locations. const int64 kXMax = 100000; @@ -76,48 +82,63 @@ int main(int argc, char** argv) { } // Setting the cost function. - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance)); + const int vehicle_cost = + routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) { + return locations.ManhattanDistance(manager.IndexToNode(i), + manager.IndexToNode(j)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); // Adding capacity dimension constraints. const int64 kVehicleCapacity = 40; const int64 kNullCapacitySlack = 0; - RandomDemand demand(routing.nodes(), kDepot, + RandomDemand demand(manager.num_nodes(), kDepot, FLAGS_vrp_use_deterministic_random_seed); demand.Initialize(); - routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand), - kNullCapacitySlack, kVehicleCapacity, - /*fix_start_cumul_to_zero=*/true, kCapacity); + routing.AddDimension( + routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) { + return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j)); + }), + kNullCapacitySlack, kVehicleCapacity, + /*fix_start_cumul_to_zero=*/true, kCapacity); // Adding time dimension constraints. const int64 kTimePerDemandUnit = 300; const int64 kHorizon = 24 * 3600; ServiceTimePlusTransition time( - kTimePerDemandUnit, NewPermanentCallback(&demand, &RandomDemand::Demand), - NewPermanentCallback(&locations, &LocationContainer::ManhattanTime)); + kTimePerDemandUnit, + [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { + return demand.Demand(i, j); + }, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); routing.AddDimension( - NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute), + routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) { + return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j)); + }), kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime); + const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); // Adding time windows. ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); const int64 kTWDuration = 5 * 3600; - for (int order = 1; order < routing.nodes(); ++order) { + for (int order = 1; order < manager.num_nodes(); ++order) { const int64 start = randomizer.Uniform(kHorizon - kTWDuration); - routing.CumulVar(order, kTime)->SetRange(start, start + kTWDuration); + time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration); } // Adding resource constraints at the depot (start and end location of // routes). - std::vector start_end_times; + std::vector start_end_times; for (int i = 0; i < FLAGS_vrp_vehicles; ++i) { - start_end_times.push_back(routing.CumulVar(routing.End(i), kTime)); - start_end_times.push_back(routing.CumulVar(routing.Start(i), kTime)); + start_end_times.push_back(time_dimension.CumulVar(routing.End(i))); + start_end_times.push_back(time_dimension.CumulVar(routing.Start(i))); } // Build corresponding time intervals. const int64 kVehicleSetup = 180; - operations_research::Solver* const solver = routing.solver(); - std::vector intervals; + Solver* const solver = routing.solver(); + std::vector intervals; solver->MakeFixedDurationIntervalVarArray(start_end_times, kVehicleSetup, "depot_interval", &intervals); // Constrain the number of maximum simultaneous intervals at depot. @@ -132,18 +153,20 @@ int main(int argc, char** argv) { // Adding penalty costs to allow skipping orders. const int64 kPenalty = 100000; - const RoutingModel::NodeIndex kFirstNodeAfterDepot(1); - for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; - order < routing.nodes(); ++order) { - std::vector orders(1, order); + const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; + order < manager.num_nodes(); ++order) { + std::vector orders(1, manager.NodeToIndex(order)); routing.AddDisjunction(orders, kPenalty); } // Solve, returns a solution if any (owned by RoutingModel). - const operations_research::Assignment* solution = - routing.SolveWithParameters(parameters); + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); + const Assignment* solution = routing.SolveWithParameters(parameters); if (solution != nullptr) { - DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false, + DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false, /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, routing.GetDimensionOrDie(kCapacity), routing.GetDimensionOrDie(kTime)); diff --git a/examples/cpp/cvrptw_with_stop_times_and_resources.cc b/examples/cpp/cvrptw_with_stop_times_and_resources.cc index 857562c97e3..1020afc9921 100644 --- a/examples/cpp/cvrptw_with_stop_times_and_resources.cc +++ b/examples/cpp/cvrptw_with_stop_times_and_resources.cc @@ -21,33 +21,42 @@ #include +#include "absl/strings/str_cat.h" #include "examples/cpp/cvrptw_lib.h" -#include "ortools/base/callback.h" +#include "google/protobuf/text_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/random.h" #include "ortools/constraint_solver/routing.h" -#include "ortools/constraint_solver/routing_enums.pb.h" -#include "ortools/constraint_solver/routing_flags.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" using operations_research::ACMRandom; +using operations_research::Assignment; +using operations_research::DefaultRoutingSearchParameters; using operations_research::GetSeed; using operations_research::IntervalVar; +using operations_research::IntVar; using operations_research::LocationContainer; using operations_research::RandomDemand; +using operations_research::RoutingDimension; +using operations_research::RoutingIndexManager; using operations_research::RoutingModel; +using operations_research::RoutingNodeIndex; using operations_research::RoutingSearchParameters; +using operations_research::Solver; using operations_research::StopServiceTimePlusTransition; -using operations_research::StringAppendF; -using operations_research::StringPrintf; DEFINE_int32(vrp_stops, 25, "Stop locations in the problem."); DEFINE_int32(vrp_orders_per_stop, 5, "Nodes for each stop."); DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance."); DEFINE_bool(vrp_use_deterministic_random_seed, false, "Use deterministic random seeds."); +DEFINE_string(routing_search_parameters, "", + "Text proto RoutingSearchParameters (possibly partial) that will " + "override the DefaultRoutingSearchParameters()"); const char* kTime = "Time"; const char* kCapacity = "Capacity"; @@ -61,13 +70,9 @@ int main(int argc, char** argv) { const int vrp_orders = FLAGS_vrp_stops * FLAGS_vrp_orders_per_stop; // Nodes are indexed from 0 to vrp_orders, the starts and ends of the routes // are at node 0. - const RoutingModel::NodeIndex kDepot(0); - RoutingModel routing(vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); - RoutingSearchParameters parameters = - operations_research::BuildSearchParametersFromFlags(); - parameters.set_first_solution_strategy( - operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC); - parameters.mutable_local_search_operators()->set_use_path_lns(false); + const RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); // Setting up locations. const int64 kXMax = 100000; @@ -80,28 +85,40 @@ int main(int argc, char** argv) { } // Setting the cost function. - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(&locations, &LocationContainer::ManhattanDistance)); + const int vehicle_cost = + routing.RegisterTransitCallback([&locations, &manager](int64 i, int64 j) { + return locations.ManhattanDistance(manager.IndexToNode(i), + manager.IndexToNode(j)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); // Adding capacity dimension constraints. const int64 kVehicleCapacity = 40; const int64 kNullCapacitySlack = 0; - RandomDemand demand(routing.nodes(), kDepot, + RandomDemand demand(manager.num_nodes(), kDepot, FLAGS_vrp_use_deterministic_random_seed); demand.Initialize(); - routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand), - kNullCapacitySlack, kVehicleCapacity, - /*fix_start_cumul_to_zero=*/true, kCapacity); + routing.AddDimension( + routing.RegisterTransitCallback([&demand, &manager](int64 i, int64 j) { + return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j)); + }), + kNullCapacitySlack, kVehicleCapacity, + /*fix_start_cumul_to_zero=*/true, kCapacity); // Adding time dimension constraints. const int64 kStopTime = 300; const int64 kHorizon = 24 * 3600; StopServiceTimePlusTransition time( kStopTime, locations, - NewPermanentCallback(&locations, &LocationContainer::ManhattanTime)); + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); routing.AddDimension( - NewPermanentCallback(&time, &StopServiceTimePlusTransition::Compute), + routing.RegisterTransitCallback([&time, &manager](int64 i, int64 j) { + return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j)); + }), kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime); + const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); // Adding time windows, for the sake of simplicty same for each stop. ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); @@ -111,12 +128,12 @@ int main(int argc, char** argv) { for (int stop_order = 0; stop_order < FLAGS_vrp_orders_per_stop; ++stop_order) { const int order = stop * FLAGS_vrp_orders_per_stop + stop_order + 1; - routing.CumulVar(order, kTime)->SetRange(start, start + kTWDuration); + time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration); } } // Adding resource constraints at order locations. - operations_research::Solver* const solver = routing.solver(); + Solver* const solver = routing.solver(); std::vector intervals; for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) { std::vector stop_intervals; @@ -128,15 +145,14 @@ int main(int argc, char** argv) { intervals.push_back(interval); stop_intervals.push_back(interval); // Link order and interval. - operations_research::IntVar* const order_start = - routing.CumulVar(order, kTime); + IntVar* const order_start = time_dimension.CumulVar(order); solver->AddConstraint( solver->MakeIsEqualCt(interval->SafeStartExpr(0), order_start, interval->PerformedExpr()->Var())); // Make interval performed iff corresponding order has service time. // An order has no service time iff it is at the same location as the // next order on the route. - operations_research::IntVar* const is_null_duration = + IntVar* const is_null_duration = solver ->MakeElement( [&locations, order](int64 index) { @@ -157,25 +173,27 @@ int main(int argc, char** argv) { stop_intervals, location_usage, 1, absl::StrCat("Client", stop))); } // Minimizing route duration. - for (int vehicle = 0; vehicle < routing.vehicles(); ++vehicle) { + for (int vehicle = 0; vehicle < manager.num_vehicles(); ++vehicle) { routing.AddVariableMinimizedByFinalizer( - routing.CumulVar(routing.End(vehicle), kTime)); + time_dimension.CumulVar(routing.End(vehicle))); } // Adding penalty costs to allow skipping orders. const int64 kPenalty = 100000; - const RoutingModel::NodeIndex kFirstNodeAfterDepot(1); - for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; + const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; order < routing.nodes(); ++order) { - std::vector orders(1, order); + std::vector orders(1, manager.NodeToIndex(order)); routing.AddDisjunction(orders, kPenalty); } // Solve, returns a solution if any (owned by RoutingModel). - const operations_research::Assignment* solution = - routing.SolveWithParameters(parameters); + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); + const Assignment* solution = routing.SolveWithParameters(parameters); if (solution != nullptr) { - DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false, + DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false, /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, routing.GetDimensionOrDie(kCapacity), routing.GetDimensionOrDie(kTime)); diff --git a/examples/cpp/dimacs_assignment.cc b/examples/cpp/dimacs_assignment.cc index 49f8b96f6b7..cc0570d9566 100644 --- a/examples/cpp/dimacs_assignment.cc +++ b/examples/cpp/dimacs_assignment.cc @@ -15,15 +15,15 @@ #include #include #include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_format.h" #include "examples/cpp/parse_dimacs_assignment.h" #include "examples/cpp/print_dimacs_assignment.h" #include "ortools/algorithms/hungarian.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" #include "ortools/graph/ebert_graph.h" #include "ortools/graph/linear_assignment.h" @@ -89,8 +89,8 @@ CostValue BuildAndSolveHungarianInstance( hungarian_cost[tail][head] = cost; } } - std::unordered_map result; - std::unordered_map wish_this_could_be_null; + absl::flat_hash_map result; + absl::flat_hash_map wish_this_could_be_null; WallTimer timer; VLOG(1) << "Beginning Hungarian method."; timer.Start(); @@ -177,14 +177,13 @@ using ::operations_research::ForwardStarGraph; using ::operations_research::ForwardStarStaticGraph; using ::operations_research::SolveDimacsAssignment; using ::operations_research::StarGraph; -using ::operations_research::StringPrintf; int main(int argc, char* argv[]) { std::string usage; if (argc < 1) { - usage = StringPrintf(kUsageTemplate, "solve_dimacs_assignment"); + usage = absl::StrFormat(kUsageTemplate, "solve_dimacs_assignment"); } else { - usage = StringPrintf(kUsageTemplate, argv[0]); + usage = absl::StrFormat(kUsageTemplate, argv[0]); } gflags::SetUsageMessage(usage); gflags::ParseCommandLineFlags(&argc, &argv, true); diff --git a/examples/cpp/dobble_ls.cc b/examples/cpp/dobble_ls.cc index 1ba9ec2a7c3..82b8cef275e 100644 --- a/examples/cpp/dobble_ls.cc +++ b/examples/cpp/dobble_ls.cc @@ -18,7 +18,7 @@ // generalized: we have N cards, each with K different symbols, and // there are N different symbols overall. // -// This is a feasability problem. We transform that into an +// This is a feasibility problem. We transform that into an // optimization problem where we penalize cards whose intersection is // of cardinality different from 1. A feasible solution of the // original problem is a solution with a zero cost. @@ -32,11 +32,11 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/map_util.h" #include "ortools/base/random.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/bitset.h" @@ -640,7 +640,8 @@ void SolveDobble(int num_cards, int num_symbols, int num_symbols_per_card) { std::vector > card_symbol_vars(num_cards); std::vector all_card_symbol_vars; for (int card_index = 0; card_index < num_cards; ++card_index) { - solver.MakeBoolVarArray(num_symbols, StringPrintf("card_%i_", card_index), + solver.MakeBoolVarArray(num_symbols, + absl::StrFormat("card_%i_", card_index), &card_symbol_vars[card_index]); for (int symbol_index = 0; symbol_index < num_symbols; ++symbol_index) { all_card_symbol_vars.push_back( diff --git a/examples/cpp/fap_parser.h b/examples/cpp/fap_parser.h index 92e50b77056..9deb1a71380 100644 --- a/examples/cpp/fap_parser.h +++ b/examples/cpp/fap_parser.h @@ -1,4 +1,4 @@ -// Copyright 2010-2018 Google LLC +// Copyright 2010-2017 Google // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -21,12 +21,11 @@ #include #include -#include #include -#include "ortools/base/file.h" +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_split.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/split.h" #include "ortools/base/strtoint.h" namespace operations_research { @@ -236,7 +235,7 @@ class ParametersParser { void FindComponents(const std::vector& constraints, const std::map& variables, const int maximum_variable_id, - std::unordered_map* components); + absl::flat_hash_map* components); // Function that computes the impact of a constraint. int EvaluateConstraintImpact(const std::map& variables, @@ -248,7 +247,7 @@ void ParseInstance(const std::string& data_directory, bool find_components, std::map* variables, std::vector* constraints, std::string* objective, std::vector* frequencies, - std::unordered_map* components); + absl::flat_hash_map* components); void ParseFileByLines(const std::string& filename, std::vector* lines) { @@ -603,5 +602,6 @@ void ParseInstance(const std::string& data_directory, bool find_components, } } } + } // namespace operations_research #endif // OR_TOOLS_EXAMPLES_FAP_PARSER_H_ diff --git a/examples/cpp/frequency_assignment_problem.cc b/examples/cpp/frequency_assignment_problem.cc index 797d3cf82b7..016802ae736 100644 --- a/examples/cpp/frequency_assignment_problem.cc +++ b/examples/cpp/frequency_assignment_problem.cc @@ -330,7 +330,7 @@ bool ConstraintImpactComparator(FapConstraint constraint1, } int64 ValueEvaluator( - std::unordered_map>* value_evaluator_map, + absl::flat_hash_map>* value_evaluator_map, int64 variable_index, int64 value) { CHECK(value_evaluator_map != nullptr); // Evaluate the choice. Smaller ranking denotes a better choice. @@ -343,7 +343,7 @@ int64 ValueEvaluator( } // Update the history of assigned values and their rankings of each variable. - std::unordered_map>::iterator it; + absl::flat_hash_map>::iterator it; int64 new_value = value; int64 new_ranking = ranking; if ((it = value_evaluator_map->find(variable_index)) != @@ -578,7 +578,7 @@ void HardFapSolver(const std::map& data_variables, ChooseVariableStrategy(&variable_strategy); // Choose the value selection strategy. DecisionBuilder* db; - std::unordered_map> history; + absl::flat_hash_map> history; if (FLAGS_value_evaluator == "value_evaluator") { LOG(INFO) << "Using ValueEvaluator for value selection strategy."; Solver::IndexEvaluator2 index_evaluator2 = [&history](int64 var, @@ -863,7 +863,7 @@ int main(int argc, char** argv) { std::vector constraints; std::string objective; std::vector values; - std::unordered_map components; + absl::flat_hash_map components; operations_research::ParseInstance(FLAGS_directory, FLAGS_find_components, &variables, &constraints, &objective, &values, &components); diff --git a/examples/cpp/jobshop_sat.cc b/examples/cpp/jobshop_sat.cc index 3ef2e804e1e..40736ded958 100644 --- a/examples/cpp/jobshop_sat.cc +++ b/examples/cpp/jobshop_sat.cc @@ -16,13 +16,11 @@ #include #include +#include "absl/strings/match.h" #include "google/protobuf/text_format.h" #include "google/protobuf/wrappers.pb.h" #include "ortools/base/commandlineflags.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" -#include "ortools/base/stringpiece_utils.h" -#include "ortools/base/strutil.h" #include "ortools/base/timer.h" #include "ortools/data/jobshop_scheduling.pb.h" #include "ortools/data/jobshop_scheduling_parser.h" @@ -87,8 +85,8 @@ int64 ComputeHorizon(const JsspInputProblem& problem) { sum_of_transitions += max_transition; } } - return std::min(max_latest_end, - sum_of_durations + sum_of_transitions + max_earliest_start); + return std::min(max_latest_end, sum_of_durations + sum_of_transitions + + max_earliest_start); // TODO(user): Uses transitions. } @@ -376,7 +374,7 @@ void Solve(const JsspInputProblem& problem) { } // namespace operations_research int main(int argc, char** argv) { - base::SetFlag(&FLAGS_logtostderr, true); + absl::SetFlag(&FLAGS_logtostderr, true); gflags::ParseCommandLineFlags(&argc, &argv, true); if (FLAGS_input.empty()) { LOG(FATAL) << "Please supply a data file with --input="; diff --git a/examples/cpp/linear_programming.cc b/examples/cpp/linear_programming.cc index e37d6f0aa15..9d519811dfa 100644 --- a/examples/cpp/linear_programming.cc +++ b/examples/cpp/linear_programming.cc @@ -1,4 +1,4 @@ -// Copyright 2010-2018 Google LLC +// Copyright 2010-2017 Google // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/examples/cpp/mps_driver.cc b/examples/cpp/mps_driver.cc index 5123d80528e..a9bdf38c786 100644 --- a/examples/cpp/mps_driver.cc +++ b/examples/cpp/mps_driver.cc @@ -17,6 +17,7 @@ #include #include +#include "absl/strings/match.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/message.h" #include "google/protobuf/text_format.h" @@ -24,14 +25,13 @@ #include "ortools/base/file.h" #include "ortools/base/logging.h" #include "ortools/base/status.h" -#include "ortools/base/stringpiece_utils.h" -#include "ortools/base/strutil.h" #include "ortools/base/timer.h" #include "ortools/glop/lp_solver.h" #include "ortools/glop/parameters.pb.h" #include "ortools/lp_data/lp_print_utils.h" #include "ortools/lp_data/mps_reader.h" #include "ortools/lp_data/proto_utils.h" +#include "ortools/util/file_util.h" #include "ortools/util/proto_tools.h" DEFINE_bool(mps_dump_problem, false, "Dumps problem in readable form."); @@ -50,6 +50,7 @@ DEFINE_string(params, "", using google::protobuf::TextFormat; using operations_research::FullProtocolMessageAsString; +using operations_research::ReadFileToProto; using operations_research::glop::GetProblemStatusString; using operations_research::glop::GlopParameters; using operations_research::glop::LinearProgram; @@ -63,7 +64,9 @@ using operations_research::glop::ToDouble; void ReadGlopParameters(GlopParameters* parameters) { if (!FLAGS_params_file.empty()) { std::string params; - CHECK(TextFormat::ParseFromString(params, parameters)) << params; + CHECK_OK(file::GetContents(FLAGS_params_file, ¶ms, file::Defaults())); + CHECK(TextFormat::MergeFromString(params, parameters)) + << FLAGS_params; } if (!FLAGS_params.empty()) { CHECK(TextFormat::MergeFromString(FLAGS_params, parameters)) @@ -89,15 +92,15 @@ int main(int argc, char* argv[]) { const std::string& file_name = file_list[i]; MPSReader mps_reader; operations_research::MPModelProto model_proto; - if (strings::EndsWith(file_name, ".mps") || - strings::EndsWith(file_name, ".mps.gz")) { + if (absl::EndsWith(file_name, ".mps") || + absl::EndsWith(file_name, ".mps.gz")) { if (!mps_reader.LoadFileAndTryFreeFormOnFail(file_name, &linear_program)) { LOG(INFO) << "Parse error for " << file_name; continue; } } else { - file::ReadFileToProto(file_name, &model_proto); + ReadFileToProto(file_name, &model_proto); MPModelProtoToLinearProgram(model_proto, &linear_program); } if (FLAGS_mps_dump_problem) { diff --git a/examples/cpp/nqueens.cc b/examples/cpp/nqueens.cc index 4d1cd635e8c..d99a6eff17e 100644 --- a/examples/cpp/nqueens.cc +++ b/examples/cpp/nqueens.cc @@ -20,11 +20,11 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solveri.h" DEFINE_bool(print, false, "If true, print one of the solution."); @@ -197,7 +197,8 @@ void NQueens(int size) { // model std::vector queens; for (int i = 0; i < size; ++i) { - queens.push_back(s.MakeIntVar(0, size - 1, StringPrintf("queen%04d", i))); + queens.push_back( + s.MakeIntVar(0, size - 1, absl::StrFormat("queen%04d", i))); } s.AddConstraint(s.MakeAllDifferent(queens)); @@ -260,7 +261,7 @@ void NQueens(int size) { } } printf("========= number of solutions:%d\n", num_solutions); - printf(" number of failures: %lld\n", s.failures()); + absl::PrintF(" number of failures: %d\n", s.failures()); } } // namespace operations_research diff --git a/examples/cpp/opb_reader.h b/examples/cpp/opb_reader.h index 86580e2aa9b..81f18905a30 100644 --- a/examples/cpp/opb_reader.h +++ b/examples/cpp/opb_reader.h @@ -19,10 +19,10 @@ #include #include +#include "absl/strings/str_split.h" #include "ortools/base/filelineiter.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/split.h" #include "ortools/base/strtoint.h" #include "ortools/sat/boolean_problem.pb.h" diff --git a/examples/cpp/pdptw.cc b/examples/cpp/pdptw.cc index 8d5fdd530f8..3ecc463ccdc 100644 --- a/examples/cpp/pdptw.cc +++ b/examples/cpp/pdptw.cc @@ -27,7 +27,7 @@ // - each node must be visited within its time window (time range during which // the node is accessible). // The maximum number of vehicles used (i.e. the number of routes used) is -// specified in the data but can be overriden using the --pdp_force_vehicles +// specified in the data but can be overridden using the --pdp_force_vehicles // flag. // // A further description of the problem can be found here: @@ -36,24 +36,35 @@ // Reads data in the format defined by Li & Lim // (https://www.sintef.no/projectweb/top/pdptw/li-lim-benchmark/documentation/). +#include #include +#include "absl/strings/str_format.h" +#include "absl/strings/str_split.h" +#include "google/protobuf/text_format.h" #include "ortools/base/callback.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/file.h" #include "ortools/base/mathutil.h" -#include "ortools/base/split.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/strtoint.h" +#include "ortools/base/timer.h" #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_enums.pb.h" -#include "ortools/constraint_solver/routing_flags.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" DEFINE_string(pdp_file, "", "File containing the Pickup and Delivery Problem to solve."); DEFINE_int32(pdp_force_vehicles, 0, "Force the number of vehicles used (maximum number of routes."); -DECLARE_string(routing_first_solution); +DEFINE_bool(reduce_vehicle_cost_model, true, + "Overrides the homonymous field of " + "DefaultRoutingModelParameters()."); +DEFINE_string(routing_search_parameters, + "first_solution_strategy:ALL_UNPERFORMED", + "Text proto RoutingSearchParameters (possibly partial) that will " + "override the DefaultRoutingSearchParameters()"); namespace operations_research { @@ -67,8 +78,9 @@ typedef std::vector > Coordinates; // Returns the scaled Euclidean distance between two nodes, coords holding the // coordinates of the nodes. -int64 Travel(const Coordinates* const coords, RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) { +int64 Travel(const Coordinates* const coords, + RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) { DCHECK(coords != nullptr); const int xd = coords->at(from.value()).first - coords->at(to.value()).first; const int yd = @@ -79,30 +91,53 @@ int64 Travel(const Coordinates* const coords, RoutingModel::NodeIndex from, // Returns the scaled service time at a given node, service_times holding the // service times. int64 ServiceTime(const std::vector* const service_times, - RoutingModel::NodeIndex node) { + RoutingIndexManager::NodeIndex node) { return kScalingFactor * service_times->at(node.value()); } -// Returns the scaled (distance plus service time) between two nodes, coords +// Returns the scaled (distance plus service time) between two indices, coords // holding the coordinates of the nodes and service_times holding the service // times. // The service time is the time spent to execute a delivery or a pickup. -int64 TravelPlusServiceTime(const Coordinates* const coords, +int64 TravelPlusServiceTime(const RoutingIndexManager& manager, + const Coordinates* const coords, const std::vector* const service_times, - RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) { + int64 from_index, int64 to_index) { + const RoutingIndexManager::NodeIndex from = manager.IndexToNode(from_index); + const RoutingIndexManager::NodeIndex to = manager.IndexToNode(to_index); return ServiceTime(service_times, from) + Travel(coords, from, to); } -// Returns the demand (quantity picked up or delivered) of a node, demands -// holds the demand of each node. -int64 Demand(const std::vector* const demands, - RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) { - return demands->at(from.value()); +// Returns the list of variables to use for the Tabu metaheuristic. +// The current list is: +// - Total cost of the solution, +// - Number of used vehicles, +// - Total schedule duration. +// TODO(user): add total waiting time. +std::vector GetTabuVars(std::vector existing_vars, + operations_research::RoutingModel* routing) { + Solver* const solver = routing->solver(); + std::vector vars(std::move(existing_vars)); + vars.push_back(routing->CostVar()); + + IntVar* used_vehicles = solver->MakeIntVar(0, routing->vehicles()); + std::vector is_used_vars; + // Number of vehicle used + is_used_vars.reserve(routing->vehicles()); + for (int v = 0; v < routing->vehicles(); v++) { + is_used_vars.push_back(solver->MakeIsDifferentCstVar( + routing->NextVar(routing->Start(v)), routing->End(v))); + } + solver->AddConstraint( + solver->MakeEquality(solver->MakeSum(is_used_vars), used_vehicles)); + vars.push_back(used_vehicles); + + return vars; } // Outputs a solution to the current model in a std::string. std::string VerboseOutput(const RoutingModel& routing, + const RoutingIndexManager& manager, const Assignment& assignment, const Coordinates& coords, const std::vector& service_times) { @@ -110,36 +145,39 @@ std::string VerboseOutput(const RoutingModel& routing, const RoutingDimension& time_dimension = routing.GetDimensionOrDie("time"); const RoutingDimension& load_dimension = routing.GetDimensionOrDie("demand"); for (int i = 0; i < routing.vehicles(); ++i) { - StringAppendF(&output, "Vehicle %d: ", i); + absl::StrAppendFormat(&output, "Vehicle %d: ", i); int64 index = routing.Start(i); if (routing.IsEnd(assignment.Value(routing.NextVar(index)))) { output.append("empty"); } else { while (!routing.IsEnd(index)) { - RoutingModel::NodeIndex real_node = routing.IndexToNode(index); - StringAppendF(&output, "%d ", real_node.value()); + absl::StrAppendFormat(&output, "%d ", + manager.IndexToNode(index).value()); const IntVar* vehicle = routing.VehicleVar(index); - StringAppendF(&output, "Vehicle(%lld) ", assignment.Value(vehicle)); + absl::StrAppendFormat(&output, "Vehicle(%d) ", + assignment.Value(vehicle)); const IntVar* arrival = time_dimension.CumulVar(index); - StringAppendF(&output, "Time(%lld..%lld) ", assignment.Min(arrival), - assignment.Max(arrival)); + absl::StrAppendFormat(&output, "Time(%d..%d) ", assignment.Min(arrival), + assignment.Max(arrival)); const IntVar* load = load_dimension.CumulVar(index); - StringAppendF(&output, "Load(%lld..%lld) ", assignment.Min(load), - assignment.Max(load)); - index = assignment.Value(routing.NextVar(index)); - StringAppendF(&output, "Transit(%lld) ", - TravelPlusServiceTime(&coords, &service_times, real_node, - routing.IndexToNode(index))); + absl::StrAppendFormat(&output, "Load(%d..%d) ", assignment.Min(load), + assignment.Max(load)); + const int64 next_index = assignment.Value(routing.NextVar(index)); + absl::StrAppendFormat( + &output, "Transit(%d) ", + TravelPlusServiceTime(manager, &coords, &service_times, index, + next_index)); + index = next_index; } output.append("Route end "); const IntVar* vehicle = routing.VehicleVar(index); - StringAppendF(&output, "Vehicle(%lld) ", assignment.Value(vehicle)); + absl::StrAppendFormat(&output, "Vehicle(%d) ", assignment.Value(vehicle)); const IntVar* arrival = time_dimension.CumulVar(index); - StringAppendF(&output, "Time(%lld..%lld) ", assignment.Min(arrival), - assignment.Max(arrival)); + absl::StrAppendFormat(&output, "Time(%d..%d) ", assignment.Min(arrival), + assignment.Max(arrival)); const IntVar* load = load_dimension.CumulVar(index); - StringAppendF(&output, "Load(%lld..%lld) ", assignment.Min(load), - assignment.Max(load)); + absl::StrAppendFormat(&output, "Load(%d..%d) ", assignment.Min(load), + assignment.Max(load)); } output.append("\n"); } @@ -152,24 +190,19 @@ namespace { // parsed. bool SafeParseInt64Array(const std::string& str, std::vector* parsed_int) { - static const char kWhiteSpaces[] = " \t\n\v\f\r"; - std::vector items = absl::StrSplit( - str, absl::delimiter::AnyOf(kWhiteSpaces), absl::SkipEmpty()); - parsed_int->assign(items.size(), 0); - for (int i = 0; i < items.size(); ++i) { - const char* item = items[i].c_str(); - char* endptr = nullptr; - (*parsed_int)[i] = strto64(item, &endptr, 10); - // The whole item should have been consumed. - if (*endptr != '\0') return false; - } - return true; + std::istringstream input(str); + int64 x; + parsed_int->clear(); + while (input >> x) parsed_int->push_back(x); + return input.eof(); } } // namespace // Builds and solves a model from a file in the format defined by Li & Lim // (https://www.sintef.no/projectweb/top/pdptw/li-lim-benchmark/documentation/). -bool LoadAndSolve(const std::string& pdp_file) { +bool LoadAndSolve(const std::string& pdp_file, + const RoutingModelParameters& model_parameters, + const RoutingSearchParameters& search_parameters) { // Load all the lines of the file in RAM (it shouldn't be too large anyway). std::vector lines; { @@ -207,10 +240,10 @@ bool LoadAndSolve(const std::string& pdp_file) { std::vector open_times; std::vector close_times; std::vector service_times; - std::vector pickups; - std::vector deliveries; + std::vector pickups; + std::vector deliveries; int64 horizon = 0; - RoutingModel::NodeIndex depot(0); + RoutingIndexManager::NodeIndex depot(0); for (int line_index = 1; line_index < lines.size(); ++line_index) { if (!SafeParseInt64Array(lines[line_index], &parsed_int) || parsed_int.size() != 9 || parsed_int[0] < 0 || parsed_int[4] < 0 || @@ -235,68 +268,119 @@ bool LoadAndSolve(const std::string& pdp_file) { open_times.push_back(open_time); close_times.push_back(close_time); service_times.push_back(service_time); - pickups.push_back(RoutingModel::NodeIndex(pickup)); - deliveries.push_back(RoutingModel::NodeIndex(delivery)); + pickups.push_back(RoutingIndexManager::NodeIndex(pickup)); + deliveries.push_back(RoutingIndexManager::NodeIndex(delivery)); if (pickup == 0 && delivery == 0) { - depot = RoutingModel::NodeIndex(pickups.size() - 1); + depot = RoutingIndexManager::NodeIndex(pickups.size() - 1); } horizon = std::max(horizon, close_time); } // Build pickup and delivery model. const int num_nodes = customer_ids.size(); - RoutingModelParameters model_parameters = BuildModelParametersFromFlags(); - RoutingModel routing(num_nodes, num_vehicles, depot, model_parameters); - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(Travel, const_cast(&coords))); - routing.AddDimension( - NewPermanentCallback(&Demand, - const_cast*>(&demands)), - 0, capacity, /*fix_start_cumul_to_zero=*/true, "demand"); - routing.AddDimension( - NewPermanentCallback( - &TravelPlusServiceTime, const_cast(&coords), - const_cast*>(&service_times)), - kScalingFactor * horizon, kScalingFactor * horizon, - /*fix_start_cumul_to_zero=*/true, "time"); + RoutingIndexManager manager(num_nodes, num_vehicles, depot); + RoutingModel routing(manager, model_parameters); + const int vehicle_cost = + routing.RegisterTransitCallback([&coords, &manager](int64 i, int64 j) { + return Travel(const_cast(&coords), + manager.IndexToNode(i), manager.IndexToNode(j)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); + RoutingTransitCallback2 demand_evaluator = [&](int64 from_index, + int64 to_index) { + return demands[manager.IndexToNode(from_index).value()]; + }; + routing.AddDimension(routing.RegisterTransitCallback(demand_evaluator), 0, + capacity, /*fix_start_cumul_to_zero=*/true, "demand"); + RoutingTransitCallback2 time_evaluator = [&](int64 from_index, + int64 to_index) { + return TravelPlusServiceTime(manager, &coords, &service_times, from_index, + to_index); + }; + routing.AddDimension(routing.RegisterTransitCallback(time_evaluator), + kScalingFactor * horizon, kScalingFactor * horizon, + /*fix_start_cumul_to_zero=*/true, "time"); const RoutingDimension& time_dimension = routing.GetDimensionOrDie("time"); Solver* const solver = routing.solver(); - for (RoutingModel::NodeIndex i(0); i < num_nodes; ++i) { - const int64 index = routing.NodeToIndex(i); - if (pickups[i.value()] == 0 && deliveries[i.value()] != 0) { - const int64 delivery_index = routing.NodeToIndex(deliveries[i.value()]); + for (int node = 0; node < num_nodes; ++node) { + const int64 index = + manager.NodeToIndex(RoutingIndexManager::NodeIndex(node)); + if (pickups[node] == 0 && deliveries[node] != 0) { + const int64 delivery_index = manager.NodeToIndex(deliveries[node]); solver->AddConstraint(solver->MakeEquality( routing.VehicleVar(index), routing.VehicleVar(delivery_index))); solver->AddConstraint( solver->MakeLessOrEqual(time_dimension.CumulVar(index), time_dimension.CumulVar(delivery_index))); - routing.AddPickupAndDelivery(i, deliveries[i.value()]); + routing.AddPickupAndDelivery(index, + manager.NodeToIndex(deliveries[node])); } IntVar* const cumul = time_dimension.CumulVar(index); - cumul->SetMin(kScalingFactor * open_times[i.value()]); - cumul->SetMax(kScalingFactor * close_times[i.value()]); + cumul->SetMin(kScalingFactor * open_times[node]); + cumul->SetMax(kScalingFactor * close_times[node]); + } + + if (search_parameters.local_search_metaheuristic() == + LocalSearchMetaheuristic::GENERIC_TABU_SEARCH) { + // Create variable for the total schedule time of the solution. + // This will be used as one of the Tabu criteria. + // This is done here and not in GetTabuVarsCallback as it requires calling + // AddVariableMinimizedByFinalizer and this method must be called early. + std::vector end_cumuls; + std::vector start_cumuls; + for (int i = 0; i < routing.vehicles(); ++i) { + end_cumuls.push_back(time_dimension.CumulVar(routing.End(i))); + start_cumuls.push_back(time_dimension.CumulVar(routing.Start(i))); + } + IntVar* total_time = solver->MakeIntVar(0, 99999999, "total"); + solver->AddConstraint(solver->MakeEquality( + solver->MakeDifference(solver->MakeSum(end_cumuls), + solver->MakeSum(start_cumuls)), + total_time)); + + routing.AddVariableMinimizedByFinalizer(total_time); + + RoutingModel::GetTabuVarsCallback tabu_var_callback = + [total_time](RoutingModel* model) { + return GetTabuVars({total_time}, model); + }; + routing.SetTabuVarsCallback(tabu_var_callback); } + // Adding penalty costs to allow skipping orders. const int64 kPenalty = 10000000; - for (RoutingModel::NodeIndex order(1); order < routing.nodes(); ++order) { - std::vector orders(1, order); + for (RoutingIndexManager::NodeIndex order(1); order < routing.nodes(); + ++order) { + std::vector orders(1, manager.NodeToIndex(order)); routing.AddDisjunction(orders, kPenalty); } - // Set up search parameters. - RoutingSearchParameters parameters = BuildSearchParametersFromFlags(); - if (FLAGS_routing_first_solution.empty()) { - parameters.set_first_solution_strategy( - operations_research::FirstSolutionStrategy::ALL_UNPERFORMED); - } - parameters.mutable_local_search_operators()->set_use_path_lns(false); - // Solve pickup and delivery problem. - const Assignment* assignment = routing.SolveWithParameters(parameters); + SimpleCycleTimer timer; + timer.Start(); + const Assignment* assignment = routing.SolveWithParameters(search_parameters); + timer.Stop(); LOG(INFO) << routing.solver()->LocalSearchProfile(); if (nullptr != assignment) { + LOG(INFO) << VerboseOutput(routing, manager, *assignment, coords, + service_times); LOG(INFO) << "Cost: " << assignment->ObjectiveValue(); - LOG(INFO) << VerboseOutput(routing, *assignment, coords, service_times); + int skipped_nodes = 0; + for (int node = 0; node < routing.Size(); node++) { + if (!routing.IsEnd(node) && !routing.IsStart(node) && + assignment->Value(routing.NextVar(node)) == node) { + skipped_nodes++; + } + } + LOG(INFO) << "Number of skipped nodes: " << skipped_nodes; + int num_used_vehicles = 0; + for (int v = 0; v < routing.vehicles(); v++) { + if (routing.IsVehicleUsed(*assignment, v)) { + num_used_vehicles++; + } + } + LOG(INFO) << "Number of used vehicles: " << num_used_vehicles; + LOG(INFO) << "Time: " << timer.Get(); return true; } return false; @@ -305,9 +389,18 @@ bool LoadAndSolve(const std::string& pdp_file) { } // namespace operations_research int main(int argc, char** argv) { - base::SetFlag(&FLAGS_logtostderr, true); + absl::SetFlag(&FLAGS_logtostderr, true); gflags::ParseCommandLineFlags(&argc, &argv, true); - if (!operations_research::LoadAndSolve(FLAGS_pdp_file)) { + operations_research::RoutingModelParameters model_parameters = + operations_research::DefaultRoutingModelParameters(); + model_parameters.set_reduce_vehicle_cost_model( + FLAGS_reduce_vehicle_cost_model); + operations_research::RoutingSearchParameters search_parameters = + operations_research::DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, &search_parameters)); + if (!operations_research::LoadAndSolve(FLAGS_pdp_file, model_parameters, + search_parameters)) { LOG(INFO) << "Error solving " << FLAGS_pdp_file; } return EXIT_SUCCESS; diff --git a/examples/cpp/print_dimacs_assignment.h b/examples/cpp/print_dimacs_assignment.h index f74f65b8a8e..594cee24037 100644 --- a/examples/cpp/print_dimacs_assignment.h +++ b/examples/cpp/print_dimacs_assignment.h @@ -21,10 +21,10 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/file.h" #include "ortools/base/logging.h" #include "ortools/base/status.h" -#include "ortools/base/stringprintf.h" #include "ortools/graph/ebert_graph.h" #include "ortools/graph/linear_assignment.h" @@ -46,13 +46,13 @@ void PrintDimacsAssignmentProblem( CHECK_OK(file::Open(output_filename, "w", &output, file::Defaults())); const GraphType& graph(assignment.Graph()); std::string output_line = - StringPrintf("p asn %d %d\n", graph.num_nodes(), graph.num_arcs()); + absl::StrFormat("p asn %d %d\n", graph.num_nodes(), graph.num_arcs()); CHECK_OK(file::WriteString(output, output_line, file::Defaults())); for (typename LinearSumAssignment::BipartiteLeftNodeIterator node_it(assignment); node_it.Ok(); node_it.Next()) { - output_line = StringPrintf("n %d\n", node_it.Index() + 1); + output_line = absl::StrFormat("n %d\n", node_it.Index() + 1); CHECK_OK(file::WriteString(output, output_line, file::Defaults())); } @@ -61,8 +61,8 @@ void PrintDimacsAssignmentProblem( for (typename GraphType::ArcIterator arc_it(assignment.Graph()); arc_it.Ok(); arc_it.Next()) { ArcIndex arc = arc_it.Index(); - output_line = StringPrintf("a %d %d %lld\n", graph.Tail(arc) + 1, - graph.Head(arc) + 1, assignment.ArcCost(arc)); + output_line = absl::StrFormat("a %d %d %d\n", graph.Tail(arc) + 1, + graph.Head(arc) + 1, assignment.ArcCost(arc)); CHECK_OK(file::WriteString(output, output_line, file::Defaults())); } } diff --git a/examples/cpp/random_tsp.cc b/examples/cpp/random_tsp.cc index 4d34c99346c..8fcab4ce977 100644 --- a/examples/cpp/random_tsp.cc +++ b/examples/cpp/random_tsp.cc @@ -27,15 +27,17 @@ #include +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" #include "google/protobuf/text_format.h" #include "ortools/base/callback.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/random.h" #include "ortools/constraint_solver/routing.h" -#include "ortools/constraint_solver/routing_enums.pb.h" -#include "ortools/constraint_solver/routing_flags.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" DEFINE_int32(tsp_size, 10, "Size of Traveling Salesman Problem instance."); DEFINE_bool(tsp_use_random_matrix, true, "Use random cost matrix."); @@ -43,6 +45,13 @@ DEFINE_int32(tsp_random_forbidden_connections, 0, "Number of random forbidden connections."); DEFINE_bool(tsp_use_deterministic_random_seed, false, "Use deterministic random seeds."); +DEFINE_string(routing_search_parameters, + "local_search_operators {" + " use_path_lns:BOOL_TRUE" + " use_inactive_lns:BOOL_TRUE" + "}", + "Text proto RoutingSearchParameters (possibly partial) that will " + "override the DefaultRoutingSearchParameters()"); namespace operations_research { @@ -58,7 +67,8 @@ int32 GetSeed() { // Cost/distance functions. // Sample function. -int64 MyDistance(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) { +int64 MyDistance(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) { // Put your distance code here. return (from + to).value(); // for instance } @@ -68,13 +78,11 @@ class RandomMatrix { public: explicit RandomMatrix(int size) : size_(size) {} void Initialize() { - matrix_.reset(new int64[size_ * size_]); + matrix_ = absl::make_unique(size_ * size_); const int64 kDistanceMax = 100; ACMRandom randomizer(GetSeed()); - for (RoutingModel::NodeIndex from = RoutingModel::kFirstNode; from < size_; - ++from) { - for (RoutingModel::NodeIndex to = RoutingModel::kFirstNode; to < size_; - ++to) { + for (RoutingIndexManager::NodeIndex from(0); from < size_; ++from) { + for (RoutingIndexManager::NodeIndex to(0); to < size_; ++to) { if (to != from) { matrix_[MatrixIndex(from, to)] = randomizer.Uniform(kDistanceMax); } else { @@ -83,14 +91,14 @@ class RandomMatrix { } } } - int64 Distance(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { + int64 Distance(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const { return matrix_[MatrixIndex(from, to)]; } private: - int64 MatrixIndex(RoutingModel::NodeIndex from, - RoutingModel::NodeIndex to) const { + int64 MatrixIndex(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const { return (from * size_ + to).value(); } std::unique_ptr matrix_; @@ -103,11 +111,12 @@ void Tsp() { // Second argument = 1 to build a single tour (it's a TSP). // Nodes are indexed from 0 to FLAGS_tsp_size - 1, by default the start of // the route is node 0. - RoutingModel routing(FLAGS_tsp_size, 1, RoutingModel::NodeIndex(0)); - RoutingSearchParameters parameters = BuildSearchParametersFromFlags(); - // Setting first solution heuristic (cheapest addition). - parameters.set_first_solution_strategy( - FirstSolutionStrategy::PATH_CHEAPEST_ARC); + RoutingIndexManager manager(FLAGS_tsp_size, 1, + RoutingIndexManager::NodeIndex(0)); + RoutingModel routing(manager); + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); // Setting the cost function. // Put a permanent callback to the distance accessor here. The callback @@ -116,11 +125,18 @@ void Tsp() { RandomMatrix matrix(FLAGS_tsp_size); if (FLAGS_tsp_use_random_matrix) { matrix.Initialize(); - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(&matrix, &RandomMatrix::Distance)); + const int vehicle_cost = routing.RegisterTransitCallback( + [&matrix, &manager](int64 i, int64 j) { + return matrix.Distance(manager.IndexToNode(i), + manager.IndexToNode(j)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); } else { - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(MyDistance)); + const int vehicle_cost = + routing.RegisterTransitCallback([&manager](int64 i, int64 j) { + return MyDistance(manager.IndexToNode(i), manager.IndexToNode(j)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); } // Forbid node connections (randomly). ACMRandom randomizer(GetSeed()); @@ -145,11 +161,11 @@ void Tsp() { std::string route; for (int64 node = routing.Start(route_number); !routing.IsEnd(node); node = solution->Value(routing.NextVar(node))) { - absl::StrAppend(&route, routing.IndexToNode(node).value(), " (", node, + absl::StrAppend(&route, manager.IndexToNode(node).value(), " (", node, ") -> "); } const int64 end = routing.End(route_number); - absl::StrAppend(&route, routing.IndexToNode(end).value(), " (", end, ")"); + absl::StrAppend(&route, manager.IndexToNode(end).value(), " (", end, ")"); LOG(INFO) << route; } else { LOG(INFO) << "No solution found."; diff --git a/examples/cpp/sat_cnf_reader.h b/examples/cpp/sat_cnf_reader.h index c86a55d0add..ad36a823646 100644 --- a/examples/cpp/sat_cnf_reader.h +++ b/examples/cpp/sat_cnf_reader.h @@ -20,14 +20,14 @@ #include #include +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/filelineiter.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/span.h" -#include "ortools/base/split.h" -#include "ortools/base/string_view.h" #include "ortools/base/strtoint.h" #include "ortools/sat/boolean_problem.pb.h" #include "ortools/sat/cp_model.pb.h" @@ -36,8 +36,6 @@ DEFINE_bool(wcnf_use_strong_slack, true, "If true, when we add a slack variable to reify a soft clause, we " "enforce the fact that when it is true, the clause must be false."); -using ::absl::delimiter::AnyOf; - namespace operations_research { namespace sat { @@ -49,7 +47,7 @@ struct LinearBooleanProblemWrapper { problem->set_original_num_variables(num); } - void AddConstraint(absl::Span clause) { + void AddConstraint(absl::Span clause) { LinearBooleanConstraint* constraint = problem->add_constraints(); constraint->mutable_literals()->Reserve(clause.size()); constraint->mutable_coefficients()->Reserve(clause.size()); @@ -94,7 +92,7 @@ struct CpModelProtoWrapper { return signed_value > 0 ? signed_value - 1 : signed_value; } - void AddConstraint(absl::Span clause) { + void AddConstraint(absl::Span clause) { auto* constraint = problem->add_constraints()->mutable_bool_or(); constraint->mutable_literals()->Reserve(clause.size()); for (const int literal : clause) { @@ -233,8 +231,7 @@ class SatCnfReader { } static const char kWordDelimiters[] = " "; - auto splitter = absl::StrSplit(line, kWordDelimiters, - static_cast(absl::SkipEmpty())); + auto splitter = absl::StrSplit(line, kWordDelimiters, absl::SkipEmpty()); tmp_clause_.clear(); int64 weight = (!is_wcnf_ && interpret_cnf_as_max_sat_) ? 1 : hard_weight_; @@ -312,7 +309,7 @@ class SatCnfReader { int num_variables_; // Temporary storage for ProcessNewLine(). - std::vector words_; + std::vector words_; // We stores the objective in a map because we want the variables to appear // only once in the LinearObjective proto. diff --git a/examples/cpp/sat_runner.cc b/examples/cpp/sat_runner.cc index ecfc446254f..12bd5f46c78 100644 --- a/examples/cpp/sat_runner.cc +++ b/examples/cpp/sat_runner.cc @@ -19,18 +19,20 @@ #include #include +#include "absl/memory/memory.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "examples/cpp/opb_reader.h" #include "examples/cpp/sat_cnf_reader.h" #include "google/protobuf/text_format.h" #include "ortools/algorithms/sparse_permutation.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/file.h" +#include "ortools/base/int_type.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/status.h" -#include "ortools/base/stringpiece_utils.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/strtoint.h" #include "ortools/base/timer.h" #include "ortools/sat/boolean_problem.h" @@ -138,18 +140,18 @@ double GetScaledTrivialBestBound(const LinearBooleanProblem& problem) { return AddOffsetAndScaleObjectiveValue(problem, best_bound); } -void LoadBooleanProblem(const std::string& filename, +bool LoadBooleanProblem(const std::string& filename, LinearBooleanProblem* problem, CpModelProto* cp_model) { - if (strings::EndsWith(filename, ".opb") || - strings::EndsWith(filename, ".opb.bz2")) { + if (absl::EndsWith(filename, ".opb") || + absl::EndsWith(filename, ".opb.bz2")) { OpbReader reader; if (!reader.Load(filename, problem)) { LOG(FATAL) << "Cannot load file '" << filename << "'."; } - } else if (strings::EndsWith(filename, ".cnf") || - strings::EndsWith(filename, ".cnf.gz") || - strings::EndsWith(filename, ".wcnf") || - strings::EndsWith(filename, ".wcnf.gz")) { + } else if (absl::EndsWith(filename, ".cnf") || + absl::EndsWith(filename, ".cnf.gz") || + absl::EndsWith(filename, ".wcnf") || + absl::EndsWith(filename, ".wcnf.gz")) { SatCnfReader reader; if (FLAGS_fu_malik || FLAGS_linear_scan || FLAGS_wpm1 || FLAGS_qmaxsat || FLAGS_core_enc) { @@ -171,6 +173,7 @@ void LoadBooleanProblem(const std::string& filename, LOG(INFO) << "Reading a LinearBooleanProblem."; *problem = ReadFileToProtoOrDie(filename); } + return true; } std::string SolutionString(const LinearBooleanProblem& problem, @@ -210,7 +213,12 @@ int Run() { // Read the problem. LinearBooleanProblem problem; CpModelProto cp_model; - LoadBooleanProblem(FLAGS_input, &problem, &cp_model); + if (!LoadBooleanProblem(FLAGS_input, &problem, &cp_model)) { + CpSolverResponse response; + response.set_status(CpSolverStatus::MODEL_INVALID); + LOG(INFO) << CpSolverResponseStats(response); + return EXIT_SUCCESS; + } if (FLAGS_use_cp_model && cp_model.variables_size() == 0) { LOG(INFO) << "Converting to CpModelProto ..."; cp_model = BooleanProblemToCpModelproto(problem); @@ -231,7 +239,7 @@ int Run() { LOG(INFO) << CpSolverResponseStats(response); if (!FLAGS_output.empty()) { - if (strings::EndsWith(FLAGS_output, ".txt")) { + if (absl::EndsWith(FLAGS_output, ".txt")) { CHECK_OK(file::SetTextProto(FLAGS_output, response, file::Defaults())); } else { CHECK_OK( @@ -317,7 +325,7 @@ int Run() { } if (result == SatSolver::LIMIT_REACHED) { if (FLAGS_qmaxsat) { - solver.reset(new SatSolver()); + solver = absl::make_unique(); solver->SetParameters(parameters); CHECK(LoadBooleanProblem(problem, solver.get())); result = SolveWithCardinalityEncoding(STDOUT_LOG, problem, solver.get(), @@ -381,7 +389,7 @@ int Run() { if (result == SatSolver::FEASIBLE) { StoreAssignment(solver->Assignment(), problem.mutable_assignment()); } - if (strings::EndsWith(FLAGS_output, ".txt")) { + if (absl::EndsWith(FLAGS_output, ".txt")) { CHECK_OK(file::SetTextProto(FLAGS_output, problem, file::Defaults())); } else { CHECK_OK(file::SetBinaryProto(FLAGS_output, problem, file::Defaults())); @@ -408,9 +416,9 @@ int Run() { // Print final statistics. printf("c booleans: %d\n", solver->NumVariables()); - printf("c conflicts: %lld\n", solver->num_failures()); - printf("c branches: %lld\n", solver->num_branches()); - printf("c propagations: %lld\n", solver->num_propagations()); + absl::PrintF("c conflicts: %d\n", solver->num_failures()); + absl::PrintF("c branches: %d\n", solver->num_branches()); + absl::PrintF("c propagations: %d\n", solver->num_propagations()); printf("c walltime: %f\n", wall_timer.Get()); printf("c usertime: %f\n", user_timer.Get()); printf("c deterministic_time: %f\n", solver->deterministic_time()); @@ -434,9 +442,9 @@ int main(int argc, char** argv) { // By default, we want to show how the solver progress. Note that this needs // to be set before InitGoogle() which has the nice side-effect of allowing // the user to override it. - // base::SetFlag(&FLAGS_vmodule, "*cp_model*=1"); + absl::SetFlag(&FLAGS_vmodule, "*cp_model*=1"); gflags::SetUsageMessage(kUsage); gflags::ParseCommandLineFlags(&argc, &argv, true); - base::SetFlag(&FLAGS_alsologtostderr, true); + absl::SetFlag(&FLAGS_alsologtostderr, true); return operations_research::sat::Run(); } diff --git a/examples/cpp/shift_minimization_sat.cc b/examples/cpp/shift_minimization_sat.cc index 520531c2cd8..6e975a03b03 100644 --- a/examples/cpp/shift_minimization_sat.cc +++ b/examples/cpp/shift_minimization_sat.cc @@ -26,16 +26,15 @@ // - The objective it to minimize the number of active workers, while // performing all the jobs. +#include #include #include -#include -#include #include +#include "absl/strings/str_split.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/filelineiter.h" #include "ortools/base/logging.h" -#include "ortools/base/split.h" #include "ortools/base/strtoint.h" #include "ortools/sat/cp_model.h" #include "ortools/sat/model.h" @@ -108,7 +107,7 @@ class ShiftMinimizationParser { } const std::vector words = - absl::StrSplit(line, absl::delimiter::AnyOf(" :\t"), absl::SkipEmpty()); + absl::StrSplit(line, absl::ByAnyChar(" :\t"), absl::SkipEmpty()); switch (load_status_) { case NOT_STARTED: { @@ -296,7 +295,7 @@ void LoadAndSolve(const std::string& file_name) { } // namespace operations_research int main(int argc, char** argv) { - base::SetFlag(&FLAGS_logtostderr, true); + absl::SetFlag(&FLAGS_logtostderr, true); gflags::ParseCommandLineFlags(&argc, &argv, true); if (FLAGS_input.empty()) { LOG(FATAL) << "Please supply a data file with --input="; diff --git a/examples/cpp/solve.cc b/examples/cpp/solve.cc index 4380fe0c898..832770f4abb 100644 --- a/examples/cpp/solve.cc +++ b/examples/cpp/solve.cc @@ -18,26 +18,24 @@ #include #include +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/file.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" -//#include "ortools/base/options.h" -#include "ortools/base/stringpiece_utils.h" #include "ortools/linear_solver/linear_solver.h" #include "ortools/linear_solver/linear_solver.pb.h" -//#include "ortools/linear_solver/model_anonymizer.h" #include "ortools/lp_data/lp_data.h" -#include "ortools/lp_data/mps_reader.h" +#include "ortools/lp_data/model_reader.h" #include "ortools/lp_data/proto_utils.h" #include "ortools/util/file_util.h" DEFINE_string(input, "", "REQUIRED: Input file name."); DEFINE_string(solver, "glop", "The solver to use: bop, cbc, clp, glop, glpk_lp, glpk_mip, " - "gurobi_lp, gurobi_mip, scip, knapsack."); + "gurobi_lp, gurobi_mip, scip, knapsack, sat."); DEFINE_string(params_file, "", "Solver specific parameters file. " @@ -53,10 +51,6 @@ DEFINE_string(output_csv, "", "If non-empty, write the returned solution in csv format with " "each line formed by a variable name and its value."); -// DEFINE_bool(anonymize, false, -// "Whether to anonymize model before solving. Useful if model, " -// "request, and/or response is dumped to a file."); - DEFINE_string(dump_format, "text", "Format in which to dump protos (if flags --dump_model, " "--dump_request, or --dump_response are used). Possible values: " @@ -86,87 +80,22 @@ namespace { void Run() { // Create the solver and set its parameters. MPSolver::OptimizationProblemType type; - if (FLAGS_solver == "glop") { - type = MPSolver::GLOP_LINEAR_PROGRAMMING; -#if defined(USE_GLPK) - } else if (FLAGS_solver == "glpk_lp") { - type = MPSolver::GLPK_LINEAR_PROGRAMMING; -#endif -#if defined(USE_CLP) - } else if (FLAGS_solver == "clp") { - type = MPSolver::CLP_LINEAR_PROGRAMMING; -#endif -#if defined(USE_CPLEX) - } else if (FLAGS_solver == "cplex") { - type = MPSolver::CPLEX_LINEAR_PROGRAMMING; -#endif -#if defined(USE_GUROBI) - } else if (FLAGS_solver == "gurobi_lp") { - type = MPSolver::GUROBI_LINEAR_PROGRAMMING; -#endif -#if defined(USE_SCIP) - } else if (FLAGS_solver == "scip") { - type = MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING; -#endif -#if defined(USE_GUROBI) - } else if (FLAGS_solver == "cbc") { - type = MPSolver::CBC_MIXED_INTEGER_PROGRAMMING; -#endif -#if defined(USE_GLPK) - } else if (FLAGS_solver == "glpk_mip") { - type = MPSolver::GLPK_MIXED_INTEGER_PROGRAMMING; -#endif -#if defined(USE_CPLEX) - } else if (FLAGS_solver == "cplex_mip") { - type = MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING; -#endif -#if defined(USE_GUROBI) - } else if (FLAGS_solver == "gurobi_mip") { - type = MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING; -#endif -#if defined(USE_BOP) - } else if (FLAGS_solver == "bop") { - type = MPSolver::BOP_INTEGER_PROGRAMMING; -#endif - } else { - LOG(FATAL) << "Unsupported --solver: " << FLAGS_solver; - } + QCHECK(MPSolver::ParseSolverType(FLAGS_solver, &type)) + << "Unsupported --solver: " << FLAGS_solver; + // Load the problem into an MPModelProto. MPModelProto model_proto; MPModelRequest request_proto; - if (strings::EndsWith(FLAGS_input, ".mps") || - strings::EndsWith(FLAGS_input, ".mps.gz")) { - glop::LinearProgram linear_program_fixed; - glop::LinearProgram linear_program_free; - glop::MPSReader mps_reader; - mps_reader.set_log_errors(FLAGS_forced_mps_format == "free" || - FLAGS_forced_mps_format == "fixed"); - bool fixed_read = - FLAGS_forced_mps_format != "free" && - mps_reader.LoadFileWithMode(FLAGS_input, false, &linear_program_fixed); - const bool free_read = - FLAGS_forced_mps_format != "fixed" && - mps_reader.LoadFileWithMode(FLAGS_input, true, &linear_program_free); - CHECK(fixed_read || free_read) - << "Error while parsing the mps file '" << FLAGS_input << "' " - << "Use the --forced_mps_format flags to see the errors."; - if (fixed_read && free_read) { - if (linear_program_fixed.name() != linear_program_free.name()) { - LOG(INFO) << "Name of the model differs between fixed and free forms. " - << "Fallbacking to free form."; - fixed_read = false; - } - } - if (!fixed_read) { - LOG(INFO) << "Read file in free format."; - LinearProgramToMPModelProto(linear_program_free, &model_proto); - } else { - LOG(INFO) << "Read file in fixed format."; - LinearProgramToMPModelProto(linear_program_fixed, &model_proto); - } + if (absl::EndsWith(FLAGS_input, ".mps") || + absl::EndsWith(FLAGS_input, ".mps.gz")) { + glop::LinearProgram linear_program; + CHECK(glop::LoadLinearProgramFromMps(FLAGS_input, FLAGS_forced_mps_format, + &linear_program)) + << "Failed to parse mps file " << FLAGS_input; + LinearProgramToMPModelProto(linear_program, &model_proto); } else { - file::ReadFileToProto(FLAGS_input, &model_proto); - file::ReadFileToProto(FLAGS_input, &request_proto); + ReadFileToProto(FLAGS_input, &model_proto); + ReadFileToProto(FLAGS_input, &request_proto); // If the input proto is in binary format, both ReadFileToProto could return // true. Instead use the actual number of variables found to test the // correct format of the input. @@ -185,10 +114,6 @@ void Run() { } } } - // if (FLAGS_anonymize) { - // LOG(INFO) << "Anonymizing the model with autonumbers."; - // operations_research::Anonymize(/*autonumber=*/true, &model_proto); - // } printf("%-12s: '%s'\n", "File", FLAGS_input.c_str()); // Detect format to dump protos. @@ -249,11 +174,10 @@ void Run() { // Solve. MPSolverParameters param; MPSolver::ResultStatus solve_status = MPSolver::NOT_SOLVED; - double solving_time_in_sec = 0; - { - ScopedWallTime timer(&solving_time_in_sec); - solve_status = solver.Solve(param); - } + absl::Duration solving_time; + const absl::Time time_before = absl::Now(); + solve_status = solver.Solve(param); + solving_time = absl::Now() - time_before; // If requested, re-create a corresponding MPModelRequest and save it to file. if (!FLAGS_dump_request.empty()) { @@ -283,9 +207,8 @@ void Run() { solver.FillSolutionResponseProto(&result); std::string csv_file; for (int i = 0; i < result.variable_value_size(); ++i) { - csv_file += - StringPrintf("%s,%e\n", model_proto.variable(i).name().c_str(), - result.variable_value(i)); + csv_file += absl::StrFormat("%s,%e\n", model_proto.variable(i).name(), + result.variable_value(i)); } CHECK_OK(file::SetContents(FLAGS_output_csv, csv_file, file::Defaults())); } @@ -304,12 +227,14 @@ void Run() { .c_str()); printf("%-12s: %15.15e\n", "Objective", has_solution ? solver.Objective().Value() : 0.0); - printf("%-12s: %lld\n", "Iterations", solver.iterations()); + printf("%-12s: %15.15e\n", "BestBound", + has_solution ? solver.Objective().BestBound() : 0.0); + absl::PrintF("%-12s: %d\n", "Iterations", solver.iterations()); // NOTE(user): nodes() for non-MIP solvers crashes in debug mode by design. if (solver.IsMIP()) { - printf("%-12s: %lld\n", "Nodes", solver.nodes()); + absl::PrintF("%-12s: %d\n", "Nodes", solver.nodes()); } - printf("%-12s: %-6.4g\n", "Time", solving_time_in_sec); + printf("%-12s: %-6.4g\n", "Time", absl::ToDoubleSeconds(solving_time)); } } // namespace } // namespace operations_research @@ -318,4 +243,6 @@ int main(int argc, char** argv) { gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags=*/true); CHECK(!FLAGS_input.empty()) << "--input is required"; operations_research::Run(); + + return EXIT_SUCCESS; } diff --git a/examples/cpp/strawberry_fields_with_column_generation.cc b/examples/cpp/strawberry_fields_with_column_generation.cc index 63cbc4c606a..77aba824d4b 100644 --- a/examples/cpp/strawberry_fields_with_column_generation.cc +++ b/examples/cpp/strawberry_fields_with_column_generation.cc @@ -59,10 +59,10 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/stringprintf.h" #include "ortools/linear_solver/linear_solver.h" DEFINE_bool(colgen_verbose, false, "print verbosely"); @@ -275,8 +275,8 @@ class Box { } std::string DebugString() const { - return StringPrintf("[%d,%dx%d,%d]c%d", x_min(), y_min(), x_max(), y_max(), - Cost()); + return absl::StrFormat("[%d,%dx%d,%d]c%d", x_min(), y_min(), x_max(), + y_max(), Cost()); } private: @@ -442,11 +442,11 @@ class CoveringProblem { std::string PrintGrid() const { std::string output = - StringPrintf("width = %d, height = %d, max_boxes = %d\n", width_, - height_, max_boxes_); + absl::StrFormat("width = %d, height = %d, max_boxes = %d\n", width_, + height_, max_boxes_); for (int y = 0; y < height_; ++y) { - StringAppendF(&output, "%s\n", - std::string(grid_ + width_ * y, width_).c_str()); + absl::StrAppendFormat(&output, "%s\n", + std::string(grid_ + width_ * y, width_)); } return output; } @@ -458,7 +458,7 @@ class CoveringProblem { std::string PrintCovering() const { static const double kTolerance = 1e-5; std::string output = - StringPrintf("cost = %lf\n", solver_->Objective().Value()); + absl::StrFormat("cost = %f\n", solver_->Objective().Value()); std::unique_ptr display(new char[(width_ + 1) * height_ + 1]); for (int y = 0; y < height_; ++y) { memcpy(display.get() + y * (width_ + 1), grid_ + width_ * y, @@ -473,8 +473,8 @@ class CoveringProblem { const char box_character = (i->second->solution_value() >= (1. - kTolerance) ? 'A' : 'a') + active_box_index++; - StringAppendF(&output, "%c: box %s with value %lf\n", box_character, - i->first.DebugString().c_str(), value); + absl::StrAppendFormat(&output, "%c: box %s with value %f\n", + box_character, i->first.DebugString(), value); const Box& box = i->first; for (int x = box.x_min(); x <= box.x_max(); ++x) { for (int y = box.y_min(); y <= box.y_max(); ++y) { @@ -606,18 +606,18 @@ int main(int argc, char** argv) { operations_research::MPSolver::OptimizationProblemType solver_type; bool found = false; -#if defined(USE_GLOP) - if (FLAGS_colgen_solver == "glop") { - solver_type = operations_research::MPSolver::GLOP_LINEAR_PROGRAMMING; - found = true; - } -#endif // USE_GLOP #if defined(USE_CLP) if (FLAGS_colgen_solver == "clp") { solver_type = operations_research::MPSolver::CLP_LINEAR_PROGRAMMING; found = true; } #endif // USE_CLP +#if defined(USE_GLOP) + if (FLAGS_colgen_solver == "glop") { + solver_type = operations_research::MPSolver::GLOP_LINEAR_PROGRAMMING; + found = true; + } +#endif // USE_GLOP if (!found) { LOG(ERROR) << "Unknown solver " << FLAGS_colgen_solver; return 1; diff --git a/examples/cpp/tsp.cc b/examples/cpp/tsp.cc index fec14605f65..125378eddba 100644 --- a/examples/cpp/tsp.cc +++ b/examples/cpp/tsp.cc @@ -11,130 +11,139 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include + #include +#include #include "ortools/base/logging.h" #include "ortools/constraint_solver/routing.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" namespace operations_research { -class DataProblem { - private: - std::vector> locations_; - - public: - DataProblem() { - locations_ = {{4, 4}, {2, 0}, {8, 0}, {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}}; + class DataProblem { + private: + std::vector> locations_; + + public: + DataProblem() { + locations_ = { + {4, 4}, + {2, 0}, {8, 0}, + {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} + }; + + // Compute locations in meters using the block dimension defined as follow + // Manhattan average block: 750ft x 264ft -> 228m x 80m + // here we use: 114m x 80m city block + // src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" + std::array cityBlock = {228/2, 80}; + for (auto &i: locations_) { + i[0] = i[0] * cityBlock[0]; + i[1] = i[1] * cityBlock[1]; + } + } - // Compute locations in meters using the block dimension defined as follow - // Manhattan average block: 750ft x 264ft -> 228m x 80m - // here we use: 114m x 80m city block - // src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" - std::array cityBlock = {228 / 2, 80}; - for (auto& i : locations_) { - i[0] = i[0] * cityBlock[0]; - i[1] = i[1] * cityBlock[1]; + std::size_t GetVehicleNumber() const { return 1;} + const std::vector>& GetLocations() const { return locations_;} + RoutingIndexManager::NodeIndex GetDepot() const { return RoutingIndexManager::NodeIndex(0);} + }; + + /*! @brief Manhattan distance implemented as a callback. + * @details It uses an array of positions and + * computes the Manhattan distance between the two positions of two different indices.*/ + class ManhattanDistance { + private: + std::vector> distances_; + public: + ManhattanDistance(const DataProblem& data) { + // Precompute distance between location to have distance callback in O(1) + distances_ = std::vector>( + data.GetLocations().size(), + std::vector( + data.GetLocations().size(), + 0LL)); + for (std::size_t fromNode = 0; fromNode < data.GetLocations().size(); fromNode++) { + for (std::size_t toNode = 0; toNode < data.GetLocations().size(); toNode++) { + if (fromNode != toNode) + distances_[fromNode][toNode] = + std::abs(data.GetLocations()[toNode][0] - data.GetLocations()[fromNode][0]) + + std::abs(data.GetLocations()[toNode][1] - data.GetLocations()[fromNode][1]); + } + } } - } - - std::size_t GetVehicleNumber() const { return 1; } - const std::vector>& GetLocations() const { - return locations_; - } - RoutingModel::NodeIndex GetDepot() const { return RoutingModel::kFirstNode; } -}; -/*! @brief Manhattan distance implemented as a callback. - * @details It uses an array of positions and - * computes the Manhattan distance between the two positions of two different - * indices.*/ -class ManhattanDistance : public RoutingModel::NodeEvaluator2 { - private: - std::vector> distances_; - - public: - ManhattanDistance(const DataProblem& data) { - // Precompute distance between location to have distance callback in O(1) - distances_ = std::vector>( - data.GetLocations().size(), - std::vector(data.GetLocations().size(), 0LL)); - for (std::size_t fromNode = 0; fromNode < data.GetLocations().size(); - fromNode++) { - for (std::size_t toNode = 0; toNode < data.GetLocations().size(); - toNode++) { - if (fromNode != toNode) - distances_[fromNode][toNode] = - std::abs(data.GetLocations()[toNode][0] - - data.GetLocations()[fromNode][0]) + - std::abs(data.GetLocations()[toNode][1] - - data.GetLocations()[fromNode][1]); - } + //! @brief Returns the manhattan distance between the two nodes. + int64 operator()(RoutingIndexManager::NodeIndex FromNode, RoutingIndexManager::NodeIndex ToNode) { + return distances_[FromNode.value()][ToNode.value()]; } + }; + + //! @brief Print the solution + //! @param[in] data Data of the problem. + //! @param[in] manager Index manager used. + //! @param[in] routing Routing solver used. + //! @param[in] solution Solution found by the solver. + void PrintSolution( + const DataProblem& data, + const RoutingIndexManager& manager, + const RoutingModel& routing, + const Assignment& solution) { + LOG(INFO) << "Objective: " << solution.ObjectiveValue(); + // Inspect solution. + int64 index = routing.Start(0); + LOG(INFO) << "Route for Vehicle 0:"; + int64 distance = 0LL; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + distance += const_cast(routing).GetArcCostForVehicle(previous_index, index, 0LL); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; } - bool IsRepeatable() const override { return true; } - - //! @brief Returns the manhattan distance between the two nodes. - int64 Run(RoutingModel::NodeIndex FromNode, - RoutingModel::NodeIndex ToNode) override { - return distances_[FromNode.value()][ToNode.value()]; - } -}; + void Solve() { + // Instantiate the data problem. + DataProblem data; -//! @brief Print the solution -//! @param[in] data Data of the problem. -//! @param[in] routing Routing solver used. -//! @param[in] solution Solution found by the solver. -void PrintSolution(const DataProblem& data, const RoutingModel& routing, - const Assignment& solution) { - LOG(INFO) << "Objective: " << solution.ObjectiveValue(); - // Inspect solution. - int64 index = routing.Start(0); - LOG(INFO) << "Route for Vehicle 0:"; - int64 distance = 0LL; - std::stringstream route; - while (routing.IsEnd(index) == false) { - route << routing.IndexToNode(index).value() << " -> "; - int64 previous_index = index; - index = solution.Value(routing.NextVar(index)); - distance += const_cast(routing).GetArcCostForVehicle( - previous_index, index, 0LL); + // Create Routing Index Manager & Routing Model + RoutingIndexManager manager( + data.GetLocations().size(), + data.GetVehicleNumber(), + data.GetDepot()); + RoutingModel routing(manager); + + // Define weight of each edge + ManhattanDistance distance(data); + const int vehicle_cost = routing.RegisterTransitCallback( + [&distance, &manager](int64 fromNode, int64 toNode) -> int64 { + return distance(manager.IndexToNode(fromNode), manager.IndexToNode(toNode)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); + + // Setting first solution heuristic (cheapest addition). + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy(FirstSolutionStrategy::PATH_CHEAPEST_ARC); + + const Assignment* solution = routing.SolveWithParameters(searchParameters); + PrintSolution(data, manager, routing, *solution); } - LOG(INFO) << route.str() << routing.IndexToNode(index).value(); - LOG(INFO) << "Distance of the route: " << distance << "m"; - LOG(INFO) << ""; - LOG(INFO) << "Advanced usage:"; - LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; -} - -void Solve() { - // Instantiate the data problem. - DataProblem data; - - // Create Routing Model - RoutingModel routing(data.GetLocations().size(), data.GetVehicleNumber(), - data.GetDepot()); - - // Define weight of each edge - ManhattanDistance distance(data); - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(&distance, &ManhattanDistance::Run)); - - // Setting first solution heuristic (cheapest addition). - RoutingSearchParameters searchParameters = - RoutingModel::DefaultSearchParameters(); - searchParameters.set_first_solution_strategy( - FirstSolutionStrategy::PATH_CHEAPEST_ARC); - - const Assignment* solution = routing.SolveWithParameters(searchParameters); - PrintSolution(data, routing, *solution); -} } // namespace operations_research int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); FLAGS_logtostderr = 1; operations_research::Solve(); - return EXIT_SUCCESS; + return 0; } diff --git a/examples/cpp/variable_intervals_sat.cc b/examples/cpp/variable_intervals_sat.cc index be9dc8aa583..82ccf6d2567 100644 --- a/examples/cpp/variable_intervals_sat.cc +++ b/examples/cpp/variable_intervals_sat.cc @@ -17,12 +17,14 @@ void Solve() { const IntVar start_p1 = cp_model.NewIntVar(Domain(500, 800)); const IntVar duration_p1 = cp_model.NewIntVar(Domain(1, 360)); const IntVar end_p1 = cp_model.NewIntVar(Domain(500, 1000)); - const IntervalVar p1 = cp_model.NewIntervalVar(start_p1, duration_p1, end_p1); + const IntervalVar p1 = + cp_model.NewIntervalVar(start_p1, duration_p1, end_p1); const IntVar start_p2 = cp_model.NewIntVar(Domain(500, 800)); const IntVar duration_p2 = cp_model.NewIntVar(Domain(1, 360)); const IntVar end_p2 = cp_model.NewIntVar(Domain(500, 1000)); - const IntervalVar p2 = cp_model.NewIntervalVar(start_p2, duration_p2, end_p2); + const IntervalVar p2 = + cp_model.NewIntervalVar(start_p2, duration_p2, end_p2); cp_model.AddEquality(LinearExpr::Sum({duration_p1, duration_p2}), 360); cp_model.AddLessOrEqual(end_p1, start_p2); diff --git a/examples/cpp/vrp.cc b/examples/cpp/vrp.cc index 452aa5ca153..764f9fd8d95 100644 --- a/examples/cpp/vrp.cc +++ b/examples/cpp/vrp.cc @@ -11,149 +11,161 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include + #include +#include #include "ortools/base/logging.h" #include "ortools/constraint_solver/routing.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" namespace operations_research { -class DataProblem { - private: - std::vector> locations_; - - public: - DataProblem() { - locations_ = {{4, 4}, {2, 0}, {8, 0}, {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}}; - - // Compute locations in meters using the block dimension defined as follow - // Manhattan average block: 750ft x 264ft -> 228m x 80m - // here we use: 114m x 80m city block - // src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" - std::array cityBlock = {228 / 2, 80}; - for (auto& i : locations_) { - i[0] = i[0] * cityBlock[0]; - i[1] = i[1] * cityBlock[1]; - } - } + class DataProblem { + private: + std::vector> locations_; + + public: + DataProblem() { + locations_ = { + {4, 4}, + {2, 0}, {8, 0}, + {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} + }; + + // Compute locations in meters using the block dimension defined as follow + // Manhattan average block: 750ft x 264ft -> 228m x 80m + // here we use: 114m x 80m city block + // src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" + std::array cityBlock = {228/2, 80}; + for (auto &i: locations_) { + i[0] = i[0] * cityBlock[0]; + i[1] = i[1] * cityBlock[1]; + } + } - std::size_t GetVehicleNumber() const { return 4; } - const std::vector>& GetLocations() const { - return locations_; - } - RoutingModel::NodeIndex GetDepot() const { return RoutingModel::kFirstNode; } -}; - -/*! @brief Manhattan distance implemented as a callback. - * @details It uses an array of positions and - * computes the Manhattan distance between the two positions of two different - * indices.*/ -class ManhattanDistance : public RoutingModel::NodeEvaluator2 { - private: - std::vector> distances_; - - public: - ManhattanDistance(const DataProblem& data) { - // Precompute distance between location to have distance callback in O(1) - distances_ = std::vector>( - data.GetLocations().size(), - std::vector(data.GetLocations().size(), 0LL)); - for (std::size_t fromNode = 0; fromNode < data.GetLocations().size(); - fromNode++) { - for (std::size_t toNode = 0; toNode < data.GetLocations().size(); - toNode++) { - if (fromNode != toNode) - distances_[fromNode][toNode] = - std::abs(data.GetLocations()[toNode][0] - - data.GetLocations()[fromNode][0]) + - std::abs(data.GetLocations()[toNode][1] - - data.GetLocations()[fromNode][1]); + std::size_t GetVehicleNumber() const { return 4;} + const std::vector>& GetLocations() const { return locations_;} + RoutingIndexManager::NodeIndex GetDepot() const { return RoutingIndexManager::NodeIndex(0);} + }; + + /*! @brief Manhattan distance implemented as a callback. + * @details It uses an array of positions and + * computes the Manhattan distance between the two positions of two different indices.*/ + class ManhattanDistance { + private: + std::vector> distances_; + public: + ManhattanDistance(const DataProblem& data) { + // Precompute distance between location to have distance callback in O(1) + distances_ = std::vector>( + data.GetLocations().size(), + std::vector( + data.GetLocations().size(), + 0LL)); + for (std::size_t fromNode = 0; fromNode < data.GetLocations().size(); fromNode++) { + for (std::size_t toNode = 0; toNode < data.GetLocations().size(); toNode++) { + if (fromNode != toNode) + distances_[fromNode][toNode] = + std::abs(data.GetLocations()[toNode][0] - data.GetLocations()[fromNode][0]) + + std::abs(data.GetLocations()[toNode][1] - data.GetLocations()[fromNode][1]); + } } } - } - - bool IsRepeatable() const override { return true; } - //! @brief Returns the manhattan distance between the two nodes. - int64 Run(RoutingModel::NodeIndex FromNode, - RoutingModel::NodeIndex ToNode) override { - return distances_[FromNode.value()][ToNode.value()]; + //! @brief Returns the manhattan distance between the two nodes. + int64 operator()(RoutingIndexManager::NodeIndex FromNode, RoutingIndexManager::NodeIndex ToNode) { + return distances_[FromNode.value()][ToNode.value()]; + } + }; + + //! @brief Add distance Dimension. + //! @param[in] data Data of the problem. + //! @param[in] callback transit cost callback. + //! @param[in, out] routing Routing solver used. + static void AddDistanceDimension(const DataProblem& data, const int callback, RoutingModel* routing) { + std::string distance("Distance"); + routing->AddDimension( + callback, + 0, // null slack + 3000, // maximum distance per vehicle + true, // start cumul to zero + distance); + RoutingDimension* distanceDimension = routing->GetMutableDimension(distance); + // Try to minimize the max distance among vehicles. + // /!\ It doesn't mean the standard deviation is minimized + distanceDimension->SetGlobalSpanCostCoefficient(100); } -}; - -//! @brief Add distance Dimension. -//! @param[in] data Data of the problem. -//! @param[in, out] routing Routing solver used. -static void AddDistanceDimension(const DataProblem& data, - RoutingModel* routing) { - std::string distance("Distance"); - routing->AddDimension(new ManhattanDistance(data), - 0, // null slack - 3000, // maximum distance per vehicle - true, // start cumul to zero - distance); - RoutingDimension* distanceDimension = routing->GetMutableDimension(distance); - // Try to minimize the max distance among vehicles. - // /!\ It doesn't mean the standard deviation is minimized - distanceDimension->SetGlobalSpanCostCoefficient(100); -} -//! @brief Print the solution -//! @param[in] data Data of the problem. -//! @param[in] routing Routing solver used. -//! @param[in] solution Solution found by the solver. -void PrintSolution(const DataProblem& data, const RoutingModel& routing, - const Assignment& solution) { - LOG(INFO) << "Objective: " << solution.ObjectiveValue(); - // Inspect solution. - for (int i = 0; i < data.GetVehicleNumber(); ++i) { - int64 index = routing.Start(i); - LOG(INFO) << "Route for Vehicle " << i << ":"; - int64 distance = 0LL; - std::stringstream route; - while (routing.IsEnd(index) == false) { - route << routing.IndexToNode(index).value() << " -> "; - int64 previous_index = index; - index = solution.Value(routing.NextVar(index)); - distance += const_cast(routing).GetArcCostForVehicle( - previous_index, index, i); + //! @brief Print the solution + //! @param[in] data Data of the problem. + //! @param[in] manager Index manager used. + //! @param[in] routing Routing solver used. + //! @param[in] solution Solution found by the solver. + void PrintSolution( + const DataProblem& data, + const RoutingIndexManager& manager, + const RoutingModel& routing, + const Assignment& solution) { + LOG(INFO) << "Objective: " << solution.ObjectiveValue(); + // Inspect solution. + for (int i=0; i < data.GetVehicleNumber(); ++i) { + int64 index = routing.Start(i); + LOG(INFO) << "Route for Vehicle " << i << ":"; + int64 distance = 0LL; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + distance += const_cast(routing).GetArcCostForVehicle(previous_index, index, i); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << distance << "m"; } - LOG(INFO) << route.str() << routing.IndexToNode(index).value(); - LOG(INFO) << "Distance of the route: " << distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; } - LOG(INFO) << ""; - LOG(INFO) << "Advanced usage:"; - LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; -} - -void Solve() { - // Instantiate the data problem. - DataProblem data; - // Create Routing Model - RoutingModel routing(data.GetLocations().size(), data.GetVehicleNumber(), - data.GetDepot()); + void Solve() { + // Instantiate the data problem. + DataProblem data; - // Define weight of each edge - ManhattanDistance distance(data); - routing.SetArcCostEvaluatorOfAllVehicles( - NewPermanentCallback(&distance, &ManhattanDistance::Run)); - AddDistanceDimension(data, &routing); - - // Setting first solution heuristic (cheapest addition). - auto searchParameters = RoutingModel::DefaultSearchParameters(); - searchParameters.set_first_solution_strategy( - FirstSolutionStrategy::PATH_CHEAPEST_ARC); - - const Assignment* solution = routing.SolveWithParameters(searchParameters); - PrintSolution(data, routing, *solution); -} + // Create Routing Index Manager & Routing Model + RoutingIndexManager manager( + data.GetLocations().size(), + data.GetVehicleNumber(), + data.GetDepot()); + RoutingModel routing(manager); + + // Define weight of each edge + ManhattanDistance distance(data); + const int vehicle_cost = routing.RegisterTransitCallback( + [&distance, &manager](int64 fromNode, int64 toNode) -> int64 { + return distance(manager.IndexToNode(fromNode), manager.IndexToNode(toNode)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); + AddDistanceDimension(data, vehicle_cost, &routing); + + // Setting first solution heuristic (cheapest addition). + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + + const Assignment* solution = routing.SolveWithParameters(searchParameters); + PrintSolution(data, manager, routing, *solution); + } } // namespace operations_research int main(int argc, char** argv) { google::InitGoogleLogging(argv[0]); FLAGS_logtostderr = 1; operations_research::Solve(); - return EXIT_SUCCESS; + return 0; } diff --git a/examples/cpp/weighted_tardiness_sat.cc b/examples/cpp/weighted_tardiness_sat.cc index 8d29ead5e7b..eac49a2ef69 100644 --- a/examples/cpp/weighted_tardiness_sat.cc +++ b/examples/cpp/weighted_tardiness_sat.cc @@ -15,15 +15,14 @@ #include #include +#include "absl/strings/match.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" #include "google/protobuf/text_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/filelineiter.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" -#include "ortools/base/numbers.h" -#include "ortools/base/split.h" -#include "ortools/base/strtoint.h" -#include "ortools/base/strutil.h" #include "ortools/base/timer.h" #include "ortools/sat/cp_model.h" #include "ortools/sat/model.h" @@ -246,7 +245,7 @@ void ParseAndSolve() { } // namespace operations_research int main(int argc, char** argv) { - base::SetFlag(&FLAGS_logtostderr, true); + absl::SetFlag(&FLAGS_logtostderr, true); gflags::ParseCommandLineFlags(&argc, &argv, true); if (FLAGS_input.empty()) { LOG(FATAL) << "Please supply a data file with --input="; diff --git a/examples/data/tests/test.mps b/examples/data/tests/test.mps new file mode 100644 index 00000000000..9aac1af05ee --- /dev/null +++ b/examples/data/tests/test.mps @@ -0,0 +1,58 @@ +NAME MIN_SIZE_MAX_FEATURES + +ROWS + N COST + E ROW1 + L ROW2 + G ROW3 + E ROW4 + E ROW5 + G ROW6 + E ROW7 + L ROW8 + N UNCONST. + +COLUMNS + X1 ROW1 1 UNCONST. 1 + X2 ROW1 1 UNCONST. 2 + X1 COST 1 + X2 COST 2 + X1 ROW2 3 + X2 ROW2 4 + X1 ROW3 1 + X2 ROW3 2 + X3 ROW3 3 ROW7 1 + X4 ROW3 4 ROW7 1 + X5 ROW3 5 ROW4 2 + X6 ROW3 6 ROW4 13 + X7 ROW5 1 ROW6 7 + X8 ROW5 3 ROW6 2 + X7 ROW8 1 + X8 ROW8 1 + + +RHS + ROW1 2 + ROW2 6 + ROW3 -85 + ROW4 2 + ROW5 8 + ROW6 0 + ROW7 50 + ROW8 50 + +BOUNDS + FR X1 + UP X2 125. + FX X3 12 + MI X4 + LO X5 5. + PL X6 + +RANGES + RANGE1 ROW2 20. + ROW3 25. + ROW4 12. + ROW7 -100. +ENDATA + diff --git a/examples/data/tests/test2.mps b/examples/data/tests/test2.mps new file mode 100644 index 00000000000..79b89f59fec --- /dev/null +++ b/examples/data/tests/test2.mps @@ -0,0 +1,36 @@ +NAME EXAMPLE +ROWS + N OBJ + G ROW01 + L ROW02 + E ROW03 + G ROW04 + L ROW05 +COLUMNS + COL01 OBJ 1.0 + COL01 ROW01 3.0 ROW05 5.6 + COL02 ROW01 1.0 ROW02 2.0 + COL03 ROW02 1.1 ROW03 1.0 + COL04 ROW01 -2.0 ROW04 2.8 + COL05 OBJ 2.0 + COL05 ROW01 -1.0 ROW05 1.0 + COL06 ROW03 1.0 + COL07 ROW04 -1.2 + COL08 OBJ -1.0 + COL08 ROW01 -1.0 ROW05 1.9 +RHS + RHS1 ROW01 2.5 + RHS1 ROW02 2.1 + RHS1 ROW03 4.0 + RHS1 ROW04 1.8 + RHS1 ROW05 15.0 +RANGES + RNG1 ROW04 3.2 + RNG1 ROW05 12.0 +BOUNDS + LO BND1 COL01 2.5 + UP BND1 COL02 4.1 + LO BND1 COL05 0.5 + UP BND1 COL05 4.0 + UP BND1 COL08 4.3 +ENDATA \ No newline at end of file diff --git a/examples/data/words/list.txt b/examples/data/words/list.txt new file mode 100644 index 00000000000..186cbd31125 --- /dev/null +++ b/examples/data/words/list.txt @@ -0,0 +1,71729 @@ +A +AMD +AOL +Aachen +Aaliyah +Aaron +Abbas +Abbasid +Abbott +Abby +Abdul +Abe +Abel +Abelard +Abelson +Aberdeen +Abernathy +Abidjan +Abigail +Abilene +Abner +Abraham +Abram +Abrams +Absalom +Abuja +Abyssinia +Abyssinian +Ac +Acadia +Acapulco +Accenture +Accra +Acevedo +Achaean +Achebe +Achernar +Acheson +Achilles +Aconcagua +Acosta +Acropolis +Acrux +Actaeon +Acton +Acts +Acuff +Ada +Adam +Adams +Adan +Adana +Adar +Addams +Adderley +Addie +Addison +Adela +Adelaide +Adele +Adeline +Aden +Adenauer +Adhara +Adidas +Adirondack +Adirondacks +Adkins +Adler +Adolf +Adolfo +Adolph +Adonis +Adonises +Adrian +Adriana +Adriatic +Adrienne +Advent +Adventist +Advents +Advil +Aegean +Aelfric +Aeneas +Aeneid +Aeolus +Aeroflot +Aeschylus +Aesculapius +Aesop +Afghan +Afghani +Afghanistan +Afghans +Africa +African +Africans +Afrikaans +Afrikaner +Afrikaners +Afro +Afrocentrism +Afros +Ag +Agamemnon +Agassi +Agassiz +Agatha +Aggie +Aglaia +Agnes +Agnew +Agni +Agra +Agricola +Agrippa +Agrippina +Aguilar +Aguinaldo +Aguirre +Agustin +Ahab +Ahmad +Ahmadabad +Ahmadinejad +Ahmed +Ahriman +Aida +Aiken +Aileen +Aimee +Ainu +Airedale +Airedales +Aires +Aisha +Ajax +Akbar +Akhmatova +Akihito +Akita +Akiva +Akkad +Akron +Al +Alabama +Alabaman +Alabamans +Alabamian +Alabamians +Aladdin +Alamo +Alamogordo +Alan +Alana +Alar +Alaric +Alaska +Alaskan +Alaskans +Alba +Albania +Albanian +Albanians +Albany +Albee +Alberio +Albert +Alberta +Alberto +Albigensian +Albion +Albireo +Albuquerque +Alcatraz +Alcestis +Alcibiades +Alcindor +Alcmena +Alcoa +Alcott +Alcuin +Alcyone +Aldan +Aldebaran +Alden +Alderamin +Aldo +Aldrin +Alec +Aleichem +Alejandra +Alejandro +Alembert +Aleppo +Aleut +Aleutian +Alex +Alexander +Alexandra +Alexandria +Alexei +Alexis +Alfonso +Alfonzo +Alford +Alfred +Alfreda +Alfredo +Algenib +Alger +Algeria +Algerian +Algerians +Algieba +Algiers +Algol +Algonquian +Algonquians +Algonquin +Alhambra +Alhena +Ali +Alice +Alicia +Alighieri +Aline +Alioth +Alisa +Alisha +Alison +Alissa +Alistair +Alkaid +Allah +Allahabad +Allan +Alleghenies +Allegheny +Allegra +Allen +Allende +Allentown +Allie +Allison +Allstate +Allyson +Alma +Almach +Almaty +Almighty +Almohad +Almoravid +Alnilam +Alnitak +Alonzo +Alpert +Alphard +Alphecca +Alpheratz +Alphonse +Alphonso +Alpine +Alpo +Alps +Alsace +Alsatian +Alsop +Alston +Altai +Altaic +Altair +Altamira +Althea +Altiplano +Altman +Altoids +Alton +Aludra +Alva +Alvarado +Alvarez +Alvaro +Alvin +Alyce +Alyson +Alyssa +Alzheimer +Am +Amadeus +Amado +Amalia +Amanda +Amarillo +Amaru +Amaterasu +Amati +Amazon +Amazons +Amber +Amelia +Amenhotep +Amerasian +America +American +Americana +Americanism +Americanisms +Americanization +Americanizations +Americanize +Americanized +Americanizes +Americanizing +Americans +Americas +Amerind +Amerindian +Amerindians +Amerinds +Ameslan +Amharic +Amherst +Amie +Amiga +Amish +Amman +Amoco +Amos +Amparo +Ampere +Amritsar +Amsterdam +Amtrak +Amundsen +Amur +Amway +Amy +Ana +Anabaptist +Anabel +Anacin +Anacreon +Anaheim +Analects +Ananias +Anasazi +Anastasia +Anatole +Anatolia +Anatolian +Anaxagoras +Anchorage +Andalusia +Andalusian +Andaman +Andean +Andersen +Anderson +Andes +Andorra +Andre +Andrea +Andrei +Andres +Andretti +Andrew +Andrews +Andrianampoinimerina +Android +Andromache +Andromeda +Andropov +Andy +Angara +Angel +Angela +Angeles +Angelia +Angelica +Angelico +Angelina +Angeline +Angelique +Angelita +Angelo +Angelou +Angevin +Angie +Angkor +Anglia +Anglican +Anglicanism +Anglicanisms +Anglicans +Anglicize +Anglo +Anglophile +Angola +Angolan +Angolans +Angora +Angoras +Anguilla +Angus +Aniakchak +Anibal +Anita +Ankara +Ann +Anna +Annabel +Annabelle +Annam +Annapolis +Annapurna +Anne +Annette +Annie +Annmarie +Anouilh +Anselm +Anselmo +Anshan +Antaeus +Antananarivo +Antarctic +Antarctica +Antares +Anthony +Anthropocene +Antichrist +Antichrists +Antietam +Antigone +Antigua +Antilles +Antioch +Antipas +Antofagasta +Antoine +Antoinette +Anton +Antone +Antonia +Antoninus +Antonio +Antonius +Antony +Antwan +Antwerp +Anubis +Anzac +Apache +Apaches +Apalachicola +Apatosaurus +Apennines +Aphrodite +Apia +Apocrypha +Apollinaire +Apollo +Apollonian +Apollos +Appalachia +Appalachian +Appalachians +Appaloosa +Apple +Appleseed +Appleton +Appomattox +Apr +April +Aprils +Apuleius +Aquafresh +Aquarius +Aquariuses +Aquila +Aquinas +Aquino +Aquitaine +Ara +Arab +Arabia +Arabian +Arabians +Arabic +Arabs +Araby +Araceli +Arafat +Araguaya +Aral +Aramaic +Aramco +Arapaho +Ararat +Araucanian +Arawak +Arawakan +Arbitron +Arcadia +Arcadian +Archean +Archibald +Archie +Archimedes +Arctic +Arcturus +Arden +Arequipa +Ares +Argentina +Argentine +Argentinian +Argentinians +Argo +Argonaut +Argonne +Argos +Argus +Ariadne +Arianism +Ariel +Aries +Arieses +Ariosto +Aristarchus +Aristides +Aristophanes +Aristotelian +Aristotle +Arius +Arizona +Arizonan +Arizonans +Arizonian +Arizonians +Arjuna +Arkansan +Arkansas +Arkhangelsk +Arkwright +Arlene +Arline +Arlington +Armageddon +Armageddons +Armagnac +Armand +Armando +Armani +Armenia +Armenian +Armenians +Arminius +Armonk +Armour +Armstrong +Arneb +Arnhem +Arno +Arnold +Arnulfo +Aron +Arrhenius +Arron +Art +Artaxerxes +Artemis +Arthur +Arthurian +Artie +Arturo +Aruba +Aryan +Aryans +As +Asama +Ascella +Asgard +Ashanti +Ashcroft +Ashe +Ashikaga +Ashkenazim +Ashkhabad +Ashlee +Ashley +Ashmolean +Ashurbanipal +Asia +Asiago +Asian +Asians +Asiatic +Asiatics +Asimov +Asmara +Asoka +Aspell +Aspen +Asperger +Aspidiske +Asquith +Assad +Assam +Assamese +Assisi +Assyria +Assyrian +Assyrians +Astaire +Astana +Astarte +Aston +Astor +Astoria +Astrakhan +AstroTurf +Asturias +Asunción +Aswan +Atacama +Atahualpa +Atalanta +Atari +Atatürk +Athabasca +Athena +Athenian +Athenians +Athens +Atkins +Atkinson +Atlanta +Atlantes +Atlantic +Atlantis +Atlas +Atlases +Atman +Atreus +Atria +Atropos +Ats +Attic +Attica +Attila +Attlee +Attucks +Atwood +Au +Aubrey +Auckland +Auden +Audi +Audion +Audra +Audrey +Audubon +Aug +Augean +Augsburg +August +Augusta +Augustan +Augustine +Augusts +Augustus +Aurangzeb +Aurelia +Aurelio +Aurelius +Aureomycin +Auriga +Aurora +Auschwitz +Aussie +Aussies +Austen +Austerlitz +Austin +Austins +Australasia +Australia +Australian +Australians +Australoid +Australopithecus +Austria +Austrian +Austrians +Austronesian +Autumn +Ava +Avalon +Aventine +Avernus +Averroes +Avery +Avesta +Avicenna +Avignon +Avila +Avior +Avis +Avogadro +Avon +Axum +Ayala +Ayers +Aymara +Ayrshire +Ayurveda +Ayyubid +Azana +Azania +Azazel +Azerbaijan +Azerbaijani +Azores +Azov +Aztec +Aztecan +Aztecs +Aztlan +B +BBB +BMW +BP +BSD +Ba +Baal +Baath +Baathist +Babbage +Babbitt +Babel +Babels +Babylon +Babylonian +Babylons +Bacall +Bacardi +Bacchanalia +Bacchus +Bach +Backus +Bacon +Bactria +Baden +Badlands +Baedeker +Baez +Baffin +Baggies +Baghdad +Baguio +Bahama +Bahamas +Bahamian +Bahamians +Bahia +Bahrain +Baikal +Bailey +Baird +Bakelite +Baker +Bakersfield +Baku +Bakunin +Balanchine +Balaton +Balboa +Balder +Baldwin +Balearic +Balfour +Bali +Balinese +Balkan +Balkans +Balkhash +Ball +Ballard +Balthazar +Baltic +Baltimore +Baluchistan +Balzac +Bamako +Bambi +Banach +Bancroft +Bandung +Bangalore +Bangkok +Bangladesh +Bangladeshi +Bangladeshis +Bangor +Bangui +Banjarmasin +Banjul +Banks +Banneker +Bannister +Banting +Bantu +Bantus +Baotou +Baptist +Baptiste +Baptists +Barabbas +Barack +Barbadian +Barbadians +Barbados +Barbara +Barbarella +Barbarossa +Barbary +Barber +Barbie +Barbour +Barbra +Barbuda +Barcelona +Barclay +Bardeen +Barents +Barker +Barkley +Barlow +Barnabas +Barnaby +Barnard +Barnaul +Barnes +Barnett +Barney +Barnum +Baroda +Barquisimeto +Barr +Barranquilla +Barrera +Barrett +Barrie +Barron +Barry +Barrymore +Barth +Bartholdi +Bartholomew +Bartlett +Barton +Bartók +Baruch +Baryshnikov +Basel +Basho +Basie +Basil +Basque +Basques +Basra +Bass +Basseterre +Bastille +Bataan +Bates +Bathsheba +Batista +Batman +Battle +Batu +Baudelaire +Baudouin +Bauer +Bauhaus +Baum +Bavaria +Bavarian +Baxter +Bayer +Bayes +Bayesian +Bayeux +Baylor +Bayonne +Bayreuth +Baywatch +Beach +Beadle +Bean +Beard +Beardmore +Beardsley +Bearnaise +Beasley +Beatlemania +Beatles +Beatrice +Beatrix +Beatriz +Beau +Beaufort +Beaujolais +Beaumarchais +Beaumont +Beauregard +Beauvoir +Bechtel +Beck +Becker +Becket +Beckett +Becky +Becquerel +Bede +Bedouin +Bedouins +Beebe +Beecher +Beefaroni +Beelzebub +Beerbohm +Beethoven +Beeton +Begin +Behan +Behring +Beiderbecke +Beijing +Beirut +Bekesy +Bela +Belarus +Belau +Belem +Belfast +Belgian +Belgians +Belgium +Belgrade +Belinda +Belize +Bell +Bella +Bellamy +Bellatrix +Belleek +Bellini +Bellow +Belmont +Belmopan +Belshazzar +Beltane +Belushi +Ben +Benacerraf +Benares +Benchley +Bender +Bendix +Benedict +Benedictine +Benelux +Benet +Benetton +Bengal +Bengali +Benghazi +Benin +Benita +Benito +Benjamin +Bennett +Bennie +Benny +Benson +Bentham +Bentley +Benton +Benz +Benzedrine +Beowulf +Berber +Berbers +Berenice +Beretta +Berg +Bergen +Berger +Bergerac +Bergman +Bergson +Beria +Bering +Berkeley +Berkshire +Berkshires +Berle +Berlin +Berliner +Berlins +Berlioz +Berlitz +Bermuda +Bermudas +Bern +Bernadette +Bernadine +Bernanke +Bernard +Bernardo +Bernays +Bernbach +Bernhardt +Bernice +Bernie +Bernini +Bernoulli +Bernstein +Berra +Berry +Bert +Berta +Bertelsmann +Bertha +Bertie +Bertillon +Bertram +Bertrand +Beryl +Berzelius +Bess +Bessel +Bessemer +Bessie +Best +Betelgeuse +Beth +Bethany +Bethe +Bethesda +Bethlehem +Bethune +Betsy +Bette +Bettie +Betty +Bettye +Beulah +Beverley +Beverly +Beyer +Bhopal +Bhutan +Bhutto +Bialystok +Bianca +Bible +Bibles +Biblical +Bic +Biddle +Biden +Bierce +Bigfoot +Biggles +Biko +Bilbao +Bilbo +Bill +Billie +Billings +Billy +Bimini +Bioko +Bird +Birdseye +Birkenstock +Birmingham +Biro +Biscay +Biscayne +Bishkek +Bishop +Bismarck +Bismark +Bisquick +Bissau +BitTorrent +Bizet +Bjerknes +Bjork +Black +Blackbeard +Blackburn +Blackfoot +Blacks +Blackshirt +Blackstone +Blackwell +Blaine +Blair +Blake +Blanca +Blanchard +Blanche +Blankenship +Blantyre +Blatz +Blavatsky +Blenheim +Blevins +Bligh +Bloch +Blockbuster +Bloemfontein +Blondel +Blondie +Bloom +Bloomer +Bloomfield +Bloomingdale +Bloomsbury +Blu +Blucher +Bluebeard +Bluetooth +Blythe +Boas +Bob +Bobbi +Bobbie +Bobbitt +Bobby +Boccaccio +Bodhidharma +Bodhisattva +Boeing +Boeotia +Boeotian +Boer +Boers +Boethius +Bogart +Bogotá +Bohemia +Bohemian +Bohemians +Bohr +Boise +Bojangles +Boleyn +Bolivar +Bolivia +Bolivian +Bolivians +Bollywood +Bologna +Bolshevik +Bolsheviks +Bolshevism +Bolshevist +Bolshoi +Bolton +Boltzmann +Bombay +Bonaparte +Bonaventure +Bond +Bonhoeffer +Boniface +Bonita +Bonn +Bonner +Bonneville +Bonnie +Bono +Booker +Boole +Boolean +Boone +Booth +Bordeaux +Borden +Bordon +Boreas +Borg +Borges +Borgia +Borglum +Boris +Bork +Borlaug +Born +Borneo +Borobudur +Borodin +Boru +Bosch +Bose +Bosnia +Bosporus +Boston +Bostonian +Bostons +Boswell +Botox +Botswana +Botticelli +Boulder +Boulez +Bourbaki +Bourbon +Bournemouth +Bovary +Bowditch +Bowell +Bowen +Bowers +Bowery +Bowie +Bowman +Boyd +Boyer +Boyle +Boötes +Brad +Bradbury +Braddock +Bradford +Bradley +Bradly +Bradshaw +Bradstreet +Brady +Bragg +Brahe +Brahma +Brahmagupta +Brahman +Brahmanism +Brahmanisms +Brahmans +Brahmaputra +Brahmas +Brahms +Braille +Brailles +Brain +Brampton +Bran +Branch +Brandeis +Branden +Brandenburg +Brandi +Brandie +Brando +Brandon +Brandt +Brandy +Brant +Braque +Brasilia +Bratislava +Brattain +Bray +Brazil +Brazilian +Brazilians +Brazos +Brazzaville +Breakspear +Brecht +Breckenridge +Bremen +Brenda +Brendan +Brennan +Brenner +Brent +Brenton +Brest +Bret +Breton +Brett +Brewer +Brewster +Brezhnev +Brian +Briana +Brianna +Brice +Bridalveil +Bridgeport +Bridger +Bridges +Bridget +Bridgetown +Bridgett +Bridgette +Bridgman +Brie +Brigadoon +Briggs +Brigham +Bright +Brighton +Brigid +Brigitte +Brillo +Brinkley +Brisbane +Bristol +Brit +Britain +Britannia +Britannic +Britannica +British +Britisher +Britney +Briton +Britons +Brits +Britt +Brittany +Britten +Brittney +Brno +Broadway +Broadways +Brobdingnag +Brobdingnagian +Brock +Brokaw +Bronson +Bronte +Brontosaurus +Bronx +Brooke +Brooklyn +Brooks +Brown +Browne +Brownian +Brownie +Brownies +Browning +Brownshirt +Brownsville +Brubeck +Bruce +Bruckner +Brummel +Brunei +Brunelleschi +Brunhilde +Bruno +Brunswick +Brussels +Brut +Brutus +Bryan +Bryant +Bryce +Brynner +Bryon +Brzezinski +Btu +Buber +Buchanan +Bucharest +Buchenwald +Buchwald +Buck +Buckingham +Buckley +Buckner +Bud +Budapest +Buddha +Buddhas +Buddhism +Buddhisms +Buddhist +Buddhists +Buddy +Budweiser +Buffalo +Buffy +Buford +Bugatti +Bugzilla +Buick +Bujumbura +Bukhara +Bukharin +Bulawayo +Bulfinch +Bulganin +Bulgar +Bulgari +Bulgaria +Bulgarian +Bulgarians +Bullock +Bullwinkle +Bultmann +Bumppo +Bunche +Bundesbank +Bundestag +Bunin +Bunker +Bunsen +Bunyan +Burbank +Burberry +Burch +Burger +Burgess +Burgoyne +Burgundian +Burgundies +Burgundy +Burke +Burks +Burl +Burma +Burmese +Burnett +Burns +Burnside +Burr +Burris +Burroughs +Bursa +Burt +Burton +Burundi +Busch +Bush +Bushido +Bushnell +Butler +Butterfingers +Buxtehude +Buñuel +Byblos +Byers +Byrd +Byron +Byronic +Byzantine +Byzantines +Byzantium +C +CVS +Ca +Cabernet +Cabot +Cabral +Cabrera +Cabrini +Cadillac +Cadiz +Caedmon +Caerphilly +Caesar +Caesars +Cage +Cagney +Cahokia +Caiaphas +Cain +Cains +Cairo +Caitlin +Cajun +Cajuns +Calais +Calcutta +Calder +Calderon +Caldwell +Caleb +Caledonia +Calgary +Calhoun +Cali +Caliban +California +Californian +Californians +Caligula +Callaghan +Callahan +Callao +Callas +Callie +Calliope +Callisto +Caloocan +Calvary +Calvert +Calvin +Calvinism +Calvinisms +Calvinist +Calvinistic +Calvinists +Camacho +Cambodia +Cambodian +Cambodians +Cambrian +Cambridge +Camel +Camelopardalis +Camelot +Camembert +Camemberts +Cameron +Cameroon +Cameroons +Camilla +Camille +Camoens +Campanella +Campbell +Campinas +Campos +Camry +Camus +Canaan +Canada +Canadian +Canadians +Canaletto +Canaries +Canaveral +Canberra +Cancer +Cancers +Cancun +Candace +Candice +Candide +Candy +Cannes +Cannon +Canon +Canopus +Cantabrigian +Canterbury +Canton +Cantonese +Cantor +Cantrell +Cantu +Canute +Capablanca +Capek +Capella +Capet +Capetian +Capetown +Caph +Capistrano +Capitol +Capitoline +Capitols +Capone +Capote +Capra +Capri +Capricorn +Capricorns +Capuchin +Capulet +Cara +Caracalla +Caracas +Caravaggio +Carboloy +Carboniferous +Carborundum +Cardenas +Cardiff +Cardin +Cardozo +Carey +Carib +Caribbean +Caribbeans +Carina +Carissa +Carl +Carla +Carlene +Carlin +Carlo +Carlos +Carlsbad +Carlson +Carlton +Carly +Carlyle +Carmela +Carmella +Carmelo +Carmen +Carmichael +Carmine +Carnap +Carnation +Carnegie +Carney +Carnot +Carol +Carole +Carolina +Caroline +Carolingian +Carolinian +Carolyn +Carpathian +Carpathians +Carpenter +Carr +Carranza +Carrie +Carrier +Carrillo +Carroll +Carson +Carter +Cartesian +Carthage +Carthaginian +Cartier +Cartwright +Caruso +Carver +Cary +Casablanca +Casals +Casandra +Casanova +Casanovas +Cascades +Case +Casey +Cash +Casio +Caspar +Caspian +Cassandra +Cassatt +Cassie +Cassiopeia +Cassius +Castaneda +Castillo +Castlereagh +Castor +Castries +Castro +Catalan +Catalina +Catalonia +Catawba +Caterpillar +Cathay +Cather +Catherine +Cathleen +Catholic +Catholicism +Catholicisms +Catholics +Cathryn +Cathy +Catiline +Cato +Catskill +Catskills +Catt +Catullus +Caucasian +Caucasians +Caucasoid +Caucasus +Cauchy +Cavendish +Cavour +Caxton +Cayenne +Cayman +Cayuga +Cd +Ceausescu +Cebu +Cebuano +Cecelia +Cecil +Cecile +Cecilia +Cecily +Cedric +Celebes +Celeste +Celia +Celina +Cellini +Celsius +Celt +Celtic +Celtics +Celts +Cenozoic +Centaurus +Centigrade +Cepheid +Cepheus +Cerberus +Cerenkov +Ceres +Cerf +Cervantes +Cesar +Cesarean +Cessna +Cetus +Ceylon +Cezanne +Chablis +Chad +Chadwick +Chagall +Chaitanya +Chaitin +Chaldean +Challenger +Chamberlain +Chambers +Champlain +Champollion +Chan +Chance +Chancellorsville +Chandigarh +Chandler +Chandon +Chandra +Chandragupta +Chandrasekhar +Chanel +Chaney +Chang +Changchun +Changsha +Chantilly +Chaplin +Chapman +Chappaquiddick +Chapultepec +Charbray +Chardonnay +Charity +Charlemagne +Charlene +Charles +Charleston +Charlestons +Charley +Charlie +Charlotte +Charlottetown +Charmaine +Charmin +Charolais +Charon +Chartism +Chartres +Charybdis +Chase +Chasity +Chateaubriand +Chattahoochee +Chattanooga +Chatterley +Chatterton +Chaucer +Chauncey +Chautauqua +Chavez +Chayefsky +Che +Chechen +Chechnya +Cheddar +Cheer +Cheerios +Cheetos +Cheever +Chekhov +Chelsea +Chelyabinsk +Chen +Cheney +Chengdu +Chennai +Cheops +Cheri +Cherie +Chernenko +Chernobyl +Chernomyrdin +Cherokee +Cherokees +Cherry +Cheryl +Chesapeake +Cheshire +Chester +Chesterfield +Chesterton +Chevalier +Cheviot +Chevrolet +Chevron +Chevy +Cheyenne +Cheyennes +Chi +Chianti +Chiantis +Chiba +Chibcha +Chicago +Chicagoan +Chicana +Chicano +Chickasaw +Chiclets +Chihuahua +Chihuahuas +Chile +Chilean +Chileans +Chimborazo +Chimera +Chimu +China +Chinatown +Chinese +Chinook +Chinooks +Chipewyan +Chippendale +Chippewa +Chiquita +Chirico +Chisholm +Chisinau +Chittagong +Chivas +Chloe +Choctaw +Chomsky +Chongqing +Chopin +Chopra +Chou +Chretien +Chris +Christ +Christa +Christchurch +Christendom +Christendoms +Christensen +Christi +Christian +Christianities +Christianity +Christians +Christie +Christina +Christine +Christmas +Christmases +Christoper +Christopher +Christs +Chrysler +Chrysostom +Chrystal +Chuck +Chukchi +Chumash +Chung +Church +Churchill +Churriguera +Chuvash +Ci +Cicero +Cid +Cimabue +Cincinnati +Cinderella +Cinderellas +Cindy +CinemaScope +Cinerama +Cipro +Circe +Cisco +Citibank +Citigroup +Citroen +Cl +Claiborne +Clair +Claire +Clairol +Clancy +Clapeyron +Clapton +Clara +Clare +Clarence +Clarendon +Clarice +Clarissa +Clark +Clarke +Claude +Claudette +Claudia +Claudine +Claudio +Claudius +Claus +Clausewitz +Clausius +Clay +Clayton +Clearasil +Clem +Clemenceau +Clemens +Clement +Clementine +Clements +Clemons +Clemson +Cleo +Cleopatra +Cleveland +Cliburn +Cliff +Clifford +Clifton +Cline +Clint +Clinton +Clio +Clive +Clorets +Clorox +Closure +Clotho +Clouseau +Clovis +Clyde +Clydesdale +Clytemnestra +Cobain +Cobb +Cochabamba +Cochin +Cochise +Cochran +Cockney +Cocteau +Cody +Coffey +Cognac +Cohan +Cohen +Coimbatore +Cointreau +Coke +Cokes +Colbert +Colby +Cole +Coleen +Coleman +Coleridge +Colette +Colfax +Colgate +Colin +Colleen +Collier +Collin +Collins +Cologne +Colombia +Colombian +Colombians +Colombo +Colon +Colorado +Colosseum +Colt +Coltrane +Columbia +Columbine +Columbus +Comanche +Comanches +Combs +Comintern +Commons +Commonwealth +Communion +Communions +Communism +Communist +Communists +Como +Comoros +Compaq +Compton +CompuServe +Comte +Conakry +Conan +Concepción +Concetta +Concord +Concorde +Concords +Condillac +Condorcet +Conestoga +Confederacy +Confederate +Confederates +Confucian +Confucianism +Confucianisms +Confucians +Confucius +Congo +Congolese +Congregationalist +Congregationalists +Congress +Congresses +Congreve +Conley +Connecticut +Connemara +Conner +Connery +Connie +Connolly +Connors +Conrad +Conrail +Constable +Constance +Constantine +Constantinople +Constitution +Consuelo +Continent +Continental +Contreras +Conway +Cook +Cooke +Cooley +Coolidge +Cooper +Cooperstown +Coors +Copacabana +Copeland +Copenhagen +Copernican +Copernicus +Copland +Copley +Copperfield +Coppertone +Coppola +Coptic +Cora +Cordelia +Cordilleras +Cordoba +Corey +Corfu +Corina +Corine +Corinne +Corinth +Corinthian +Corinthians +Coriolanus +Coriolis +Corleone +Cormack +Corneille +Cornelia +Cornelius +Cornell +Corning +Cornish +Cornwall +Cornwallis +Coronado +Corot +Correggio +Corrine +Corsica +Corsican +Cortes +Corteses +Cortland +Corvallis +Corvette +Corvus +Cory +Cosby +Cossack +Costco +Costello +Costner +Cote +Cotonou +Cotopaxi +Cotswold +Cotton +Coulomb +Coulter +Couperin +Courbet +Courtney +Cousteau +Coventries +Coventry +Coward +Cowley +Cowper +Cox +Coy +Cozumel +Cr +Crabbe +Craft +Craig +Cranach +Crane +Cranmer +Crater +Crawford +Cray +Crayola +Creation +Creator +Crecy +Cree +Creek +Creighton +Creole +Creoles +Creon +Cressida +Crest +Cretaceous +Cretan +Crete +Crichton +Crick +Crimea +Crimean +Criollo +Crisco +Cristina +Croat +Croatia +Croatian +Croatians +Croats +Croce +Crockett +Croesus +Cromwell +Cromwellian +Cronin +Cronkite +Cronus +Crookes +Crosby +Cross +Crowley +Cruikshank +Cruise +Crusoe +Crux +Cruz +Cryptozoic +Crystal +Cs +Csonka +Ctesiphon +Cthulhu +Cu +Cuba +Cuban +Cubans +Cuchulain +Cuisinart +Culbertson +Cullen +Cumberland +Cummings +Cunard +Cunningham +Cupid +Curacao +Curie +Curitiba +Currier +Curry +Curt +Curtis +Custer +Cuvier +Cuzco +Cybele +Cyclades +Cyclops +Cygnus +Cymbeline +Cynthia +Cyprian +Cypriot +Cypriots +Cyprus +Cyrano +Cyril +Cyrillic +Cyrus +Czech +Czechia +Czechoslovakia +Czechoslovakian +Czechoslovakians +Czechs +Czerny +D +Dachau +Dacron +Dacrons +Dada +Dadaism +Daedalus +Daguerre +Dagwood +Dahomey +Daimler +Daisy +Dakar +Dakota +Dakotan +Dakotas +Dalai +Dale +Daley +Dali +Dalian +Dallas +Dalmatian +Dalmatians +Dalton +Damascus +Damian +Damien +Damion +Damocles +Damon +Dana +Dane +Danelaw +Danes +Dangerfield +Danial +Daniel +Danielle +Daniels +Danish +Dannie +Danny +Danone +Dante +Danton +Danube +Danubian +Daphne +Darby +Darcy +Dardanelles +Dare +Daren +Darfur +Darin +Dario +Darius +Darjeeling +Darla +Darlene +Darling +Darnell +Darrel +Darrell +Darren +Darrin +Darrow +Darryl +Darth +Dartmoor +Dartmouth +Darvon +Darwin +Darwinian +Darwinism +Daryl +Daugherty +Daumier +Davao +Dave +Davenport +David +Davids +Davidson +Davies +Davis +Davy +Dawes +Dawn +Dawson +Day +Dayton +DeGeneres +Deadhead +Dean +Deana +Deandre +Deann +Deanna +Deanne +Debbie +Debby +Debian +Debora +Deborah +Debouillet +Debra +Debs +Debussy +Dec +Decalogue +Decatur +Decca +Deccan +December +Decembers +Decker +Dedekind +Dee +Deena +Deere +Defoe +Degas +Deidre +Deimos +Deirdre +Deity +Dejesus +Delacroix +Delacruz +Delaney +Delano +Delaware +Delawarean +Delawareans +Delawares +Delbert +Deleon +Delgado +Delhi +Delia +Delibes +Delicious +Delilah +Delius +Dell +Della +Delmar +Delmarva +Delmer +Delmonico +Delores +Deloris +Delphi +Delphic +Delphinus +Delta +Demavend +Demerol +Demeter +Demetrius +Deming +Democrat +Democratic +Democrats +Democritus +Demosthenes +Dempsey +Dena +Deneb +Denebola +Deng +Denis +Denise +Denmark +Dennis +Denny +Denver +Deon +Depp +Derby +Derek +Derick +Derrick +Derrida +Descartes +Desdemona +Desiree +Desmond +Detroit +Deuteronomy +Devanagari +Devi +Devin +Devon +Devonian +Dewar +Dewayne +Dewey +Dewitt +Dexedrine +Dexter +Dhaka +Dhaulagiri +Di +DiCaprio +DiMaggio +Diaghilev +Dial +Diana +Diane +Diann +Dianna +Dianne +Diaspora +Dick +Dickens +Dickerson +Dickinson +Dickson +Dictaphone +Diderot +Dido +Didrikson +Diefenbaker +Diego +Diem +Dietrich +Dijkstra +Dijon +Dilbert +Dillard +Dillinger +Dillon +Dina +Dinah +Dino +Diocletian +Diogenes +Dion +Dionne +Dionysian +Dionysus +Diophantine +Dior +Dipper +Dirac +Dirichlet +Dirk +Dis +Disney +Disneyland +Disraeli +Diwali +Dix +Dixie +Dixiecrat +Dixieland +Dixielands +Dixon +Djibouti +Dmitri +Dnepropetrovsk +Dnieper +Dniester +Dobbin +Doberman +Dobro +Doctor +Doctorow +Dodge +Dodgson +Dodoma +Dodson +Doe +Doha +Dolby +Dole +Dollie +Dolly +Dolores +Domesday +Domingo +Dominguez +Dominic +Dominica +Dominican +Dominicans +Dominick +Dominique +Domitian +Don +Dona +Donahue +Donald +Donaldson +Donatello +Donetsk +Donizetti +Donn +Donna +Donne +Donnell +Donner +Donnie +Donny +Donovan +Dooley +Doolittle +Doonesbury +Doppler +Dora +Dorcas +Doreen +Dorian +Doric +Doris +Doritos +Dorothea +Dorothy +Dorset +Dorsey +Dorthy +Dortmund +Dostoevsky +Dot +Dotson +Douala +Douay +Doubleday +Doug +Douglas +Douglass +Douro +Dover +Dow +Downs +Downy +Doyle +Draco +Draconian +Dracula +Drake +Dramamine +Drambuie +Drano +Dravidian +Dreiser +Dresden +Drew +Dreyfus +Dristan +Dropbox +Drudge +Druid +Dryden +Dschubba +DuPont +Duane +Dubai +Dubcek +Dubhe +Dublin +Dubrovnik +Duchamp +Dudley +Duffy +Duisburg +Duke +Dulles +Duluth +Dumas +Dumbledore +Dumbo +Dumpster +Dunant +Dunbar +Duncan +Dunedin +Dunkirk +Dunlap +Dunn +Dunne +Duracell +Duran +Durant +Durante +Durban +Durex +Durham +Durhams +Durkheim +Duroc +Durocher +Duse +Dushanbe +Dustbuster +Dustin +Dusty +Dutch +Dutchman +Dutchmen +Duvalier +Dvina +Dvorák +Dwayne +Dwight +Dyer +Dylan +Dyson +Dzerzhinsky +Dzungaria +Dürer +Düsseldorf +E +ECMAScript +Eakins +Earhart +Earl +Earle +Earlene +Earline +Earnest +Earnestine +Earnhardt +Earp +East +Easter +Eastern +Easterner +Easters +Eastman +Easts +Eastwood +Eaton +Eben +Ebeneezer +Ebert +Ebola +Ebonics +Ebony +Ebro +Ecclesiastes +Eco +Ecuador +Ecuadoran +Ecuadorans +Ecuadorian +Ecuadorians +Ed +Edam +Edams +Edda +Eddie +Eddington +Eddy +Eden +Edens +Edgar +Edgardo +Edinburgh +Edison +Edith +Edmond +Edmonton +Edmund +Edna +Edsel +Eduardo +Edward +Edwardian +Edwardo +Edwards +Edwin +Edwina +Eeyore +Effie +Efrain +Efren +Eggo +Egypt +Egyptian +Egyptians +Egyptology +Ehrenberg +Ehrlich +Eichmann +Eiffel +Eileen +Einstein +Einsteins +Eire +Eisenhower +Eisenstein +Eisner +Elaine +Elam +Elanor +Elastoplast +Elba +Elbe +Elbert +Elbrus +Eldon +Eleanor +Eleazar +Electra +Elena +Elgar +Eli +Elias +Elijah +Elinor +Eliot +Elisa +Elisabeth +Elise +Eliseo +Elisha +Eliza +Elizabeth +Elizabethan +Elizabethans +Ella +Ellen +Ellesmere +Ellie +Ellington +Elliot +Elliott +Ellis +Ellison +Elma +Elmer +Elmo +Elnath +Elnora +Elohim +Eloise +Eloy +Elroy +Elsa +Elsie +Elsinore +Eltanin +Elton +Elul +Elva +Elvia +Elvin +Elvira +Elvis +Elway +Elwood +Elysian +Elysium +Elysiums +Elysée +Emacs +Emanuel +Emerson +Emery +Emil +Emile +Emilia +Emilio +Emily +Eminem +Emma +Emmanuel +Emmett +Emmy +Emory +Encarta +Endymion +Engels +England +English +Englisher +Englishes +Englishman +Englishmen +Englishwoman +Englishwomen +Enid +Enif +Eniwetok +Enkidu +Enoch +Enos +Enrico +Enrique +Enron +Enterprise +Eocene +Epcot +Ephesian +Ephesus +Ephraim +Epictetus +Epicurean +Epicurus +Epimethius +Epiphanies +Epiphany +Episcopal +Episcopalian +Episcopalians +Epsom +Epson +Epstein +Equuleus +Erasmus +Erato +Eratosthenes +Erebus +Erector +Erewhon +Erhard +Eric +Erica +Erich +Erick +Ericka +Erickson +Ericson +Ericsson +Eridanus +Erie +Erik +Erika +Erin +Eris +Eritrea +Erlenmeyer +Erma +Erna +Ernest +Ernestine +Ernesto +Ernie +Ernst +Eros +Eroses +Errol +Erse +ErvIn +Erwin +Es +Esau +Escher +Escherichia +Eskimo +Eskimos +Esmeralda +Esperanto +Esperanza +Espinoza +Essen +Essene +Essequibo +Essex +Essie +Establishment +Esteban +Estela +Estella +Estelle +Ester +Esterházy +Estes +Esther +Estonia +Estonian +Estonians +Estrada +Ethan +Ethel +Ethelred +Ethernet +Ethiopia +Ethiopian +Ethiopians +Etna +Eton +Etruria +Etruscan +Etta +Eucharist +Eucharistic +Eucharists +Euclid +Euclidean +Eugene +Eugenia +Eugenie +Eugenio +Eula +Euler +Eumenides +Eunice +Euphrates +Eurasia +Eurasian +Eurasians +Euripides +Eurodollar +Eurodollars +Europa +Europe +European +Europeans +Eurydice +Eustachian +Euterpe +Eva +Evan +Evangelina +Evangeline +Evans +Evansville +Eve +Evelyn +Evenki +EverReady +Everest +Everett +Everette +Everglades +Evert +Evian +Evita +Ewing +Excalibur +Excedrin +Excellencies +Excellency +Exercycle +Exocet +Exodus +Exxon +Eyck +Eyre +Eysenck +Ezekiel +Ezra +F +FDR +FNMA +FSF +Fabergé +Fabian +Facebook +Faeroe +Fafnir +Fagin +Fahd +Fahrenheit +Fairbanks +Faisal +Faisalabad +Faith +Falasha +Falkland +Falklands +Fallopian +Falstaff +Falwell +Fannie +Fanny +Faraday +Fargo +Farley +Farmer +Farragut +Farrakhan +Farrell +Farrow +Farsi +Fassbinder +Fatah +Fates +Father +Fathers +Fatima +Fatimid +Faulkner +Faulknerian +Fauntleroy +Faust +Faustian +Faustino +Faustus +Fawkes +Fay +Faye +Fe +Feb +Februaries +February +FedEx +Federalist +Federico +Feds +Felecia +Felice +Felicia +Felicity +Felipe +Felix +Fellini +Fenian +Ferber +Ferdinand +Fergus +Ferguson +Ferlinghetti +Fermat +Fermi +Fern +Fernandez +Fernando +Ferrari +Ferraro +Ferrell +Ferris +Feynman +Fez +Fiat +Fiberglas +Fibonacci +Fichte +Fidel +Fido +Fielding +Fields +Figaro +Figueroa +Fiji +Fijian +Fijians +Filipino +Filipinos +Fillmore +Filofax +Finch +Finland +Finley +Finn +Finnbogadottir +Finnegan +Finnish +Finns +Fiona +Firefox +Firestone +Fischer +Fisher +Fisk +Fitch +Fitzgerald +Fitzpatrick +Fitzroy +Fizeau +Flanagan +Flanders +Flatt +Flaubert +Fleischer +Fleming +Flemish +Fletcher +Flint +Flintstones +Flo +Flora +Florence +Florentine +Flores +Florida +Floridan +Florine +Florsheim +Flory +Flossie +Flowers +Floyd +Flynn +Foch +Fokker +Foley +Folgers +Folsom +Fomalhaut +Fonda +Foosball +Forbes +Ford +Foreman +Forest +Forester +Formica +Formicas +Formosa +Formosan +Forrest +Forster +Fortaleza +Fosse +Foster +Fotomat +Foucault +Fourier +Fourneyron +Fowler +Fox +Fr +Fragonard +Fran +France +Frances +Francesca +Francine +Francis +Francisca +Franciscan +Francisco +Franck +Franco +Francois +Francoise +Franglais +Frank +Frankel +Frankenstein +Frankfort +Frankfurt +Frankfurter +Frankie +Franklin +Franks +Franny +Franz +Fraser +Frazier +Fred +Freda +Freddie +Freddy +Frederic +Frederick +Fredericton +Fredric +Fredrick +Freeman +Freemason +Freemasonries +Freemasonry +Freemasons +Freetown +Freida +Fremont +French +Frenches +Frenchman +Frenchmen +Frenchwoman +Frenchwomen +Freon +Fresnel +Fresno +Freud +Freudian +Frey +Freya +Friday +Fridays +Frieda +Friedan +Friedman +Frigga +Frigidaire +Frisbee +Frisco +Frisian +Frito +Fritz +Frobisher +Froissart +Fromm +Fronde +Frontenac +Frost +Frostbelt +Fry +Frye +Fuchs +Fuentes +Fugger +Fuji +Fujitsu +Fujiwara +Fukuoka +Fukuyama +Fulani +Fulbright +Fuller +Fulton +Funafuti +Fundy +Furtwängler +Fushun +Fuzhou +Fuzzbuster +G +GE +GNU +GTE +Gable +Gabon +Gaborone +Gabriel +Gabriela +Gabrielle +Gacrux +Gadsden +Gaea +Gael +Gaelic +Gagarin +Gage +Gaia +Gail +Gaiman +Gaines +Gainsborough +Galahad +Galahads +Galapagos +Galatea +Galatia +Galatians +Galbraith +Gale +Galen +Galibi +Galilean +Galilee +Galileo +Gall +Gallagher +Gallegos +Gallic +Gallo +Galloway +Gallup +Galois +Galsworthy +Galvani +Galveston +Gamay +Gambia +Gamble +Gamow +Gandhi +Gandhian +Ganesha +Ganges +Gangtok +Gantry +Ganymede +Gap +Garbo +Garcia +Gardner +Gareth +Garfield +Garfunkel +Gargantua +Garibaldi +Garland +Garner +Garrett +Garrick +Garrison +Garry +Garth +Garvey +Gary +Garza +Gascony +Gasser +Gates +Gatling +Gatorade +Gatsby +Gatun +Gauguin +Gaul +Gauls +Gauss +Gaussian +Gautama +Gautier +Gavin +Gawain +Gay +Gayle +Gaza +Gaziantep +Gd +Gdansk +Ge +Geffen +Gehenna +Gehrig +Geiger +Gelbvieh +Geller +Gemini +Geminis +Gena +Genaro +Gene +Genesis +Genet +Geneva +Genevieve +Genghis +Genoa +Genoas +Gentoo +Gentry +Geo +Geoffrey +George +Georges +Georgetown +Georgette +Georgia +Georgian +Georgians +Georgina +Gerald +Geraldine +Gerard +Gerardo +Gerber +Gere +Geritol +German +Germanic +Germans +Germany +Geronimo +Gerry +Gershwin +Gertrude +Gestapo +Gestapos +Gethsemane +Getty +Gettysburg +Gewürztraminer +Ghana +Ghanaian +Ghats +Ghazvanid +Ghent +Ghibelline +Giacometti +Giannini +Giauque +Gibbon +Gibbs +Gibraltar +Gibraltars +Gibson +Gide +Gideon +Gielgud +Gienah +Gil +Gila +Gilbert +Gilberto +Gilchrist +Gilda +Gilead +Giles +Gilgamesh +Gill +Gillespie +Gillette +Gilliam +Gillian +Gilligan +Gilmore +Gina +Ginger +Gingrich +Ginny +Gino +Ginsberg +Ginsburg +Ginsu +Giorgione +Giotto +Giovanni +Giraudoux +Giselle +Gish +GitHub +Giuliani +Giuseppe +Giza +Gladstone +Gladstones +Gladys +Glaser +Glasgow +Glass +Glastonbury +Glaswegian +Glaxo +Gleason +Glen +Glenda +Glendale +Glenlivet +Glenn +Glenna +Gloria +Gloucester +Glover +Gnostic +Gnosticism +Goa +Gobi +God +Goddard +Godiva +Godot +Godthaab +Godunov +Godzilla +Goebbels +Goering +Goethals +Goethe +Goff +Gog +Gogol +Goiania +Golan +Golconda +Golda +Goldberg +Golden +Goldie +Goldilocks +Golding +Goldman +Goldsmith +Goldwater +Goldwyn +Golgi +Golgotha +Goliath +Gomez +Gomorrah +Gompers +Gomulka +Gondwanaland +Gonzales +Gonzalez +Gonzalo +Good +Goodall +Goodman +Goodrich +Goodwill +Goodwin +Goodyear +Google +Goolagong +Gopher +Gorbachev +Gordian +Gordimer +Gordon +Gore +Goren +Gorey +Gorgas +Gorgonzola +Gorky +Gospel +Gospels +Goth +Gotham +Gothic +Gothics +Goths +Gouda +Goudas +Gould +Gounod +Goya +Grable +Gracchus +Grace +Graceland +Gracie +Graciela +Grady +Graffias +Grafton +Graham +Grahame +Grail +Grammy +Grampians +Granada +Grant +Grass +Graves +Gray +Grecian +Greece +Greek +Greeks +Greeley +Green +Greene +Greenland +Greenpeace +Greensboro +Greensleeves +Greenspan +Greenwich +Greer +Greg +Gregg +Gregorian +Gregorio +Gregory +Grenada +Grenadines +Grendel +Grenoble +Gresham +Greta +Gretchen +Gretel +Gretzky +Grey +Grieg +Griffin +Griffith +Grimes +Grimm +Grinch +Gris +Gromyko +Gropius +Gross +Grosz +Grotius +Grover +Grumman +Grundy +Grus +Gruyeres +Gruyère +Grünewald +Guadalajara +Guadalcanal +Guadalquivir +Guadalupe +Guadeloupe +Guallatiri +Guam +Guangzhou +Guantanamo +Guarani +Guarnieri +Guatemala +Guatemalan +Guatemalans +Guayaquil +Gucci +Guelph +Guernsey +Guernseys +Guerra +Guerrero +Guevara +Guggenheim +Guiana +Guillermo +Guinea +Guinean +Guineans +Guinevere +Guinness +Guiyang +Guizot +Gujarat +Gujarati +Gujranwala +Gullah +Gulliver +Gumbel +Gunther +Guofeng +Gupta +Gurkha +Gus +Gustav +Gustavo +Gustavus +Gutenberg +Guthrie +Gutierrez +Guy +Guyana +Guyanese +Guzman +Gwalior +Gwen +Gwendoline +Gwendolyn +Gwyn +Gypsies +Gypsy +Gödel +Göteborg +H +HBO +HSBC +Haas +Habakkuk +Haber +Hadar +Hades +Hadrian +Hafiz +Hagar +Haggai +Hagiographa +Hague +Hahn +Haifa +Haiphong +Haiti +Haitian +Haitians +Hakka +Hakluyt +Hal +Haldane +Hale +Haleakala +Haley +Halifax +Hall +Halley +Halliburton +Hallie +Hallmark +Halloween +Halloweens +Hallstatt +Halon +Hals +Halsey +Ham +Haman +Hamburg +Hamburgs +Hamhung +Hamilcar +Hamill +Hamilton +Hamiltonian +Hamitic +Hamlet +Hamlin +Hammarskjold +Hammerstein +Hammett +Hammond +Hammurabi +Hampshire +Hampton +Hamsun +Han +Hancock +Handel +Handy +Haney +Hangul +Hangzhou +Hank +Hanna +Hannah +Hannibal +Hanoi +Hanover +Hanoverian +Hans +Hansel +Hansen +Hanson +Hanukkah +Hanukkahs +Hapsburg +Harare +Harbin +Hardin +Harding +Hardy +Hargreaves +Harlan +Harlem +Harlequin +Harley +Harlow +Harmon +Harold +Harper +Harrell +Harriet +Harriett +Harrington +Harris +Harrisburg +Harrison +Harrods +Harry +Hart +Harte +Hartford +Hartline +Hartman +Harvard +Harvey +Hasbro +Hasidim +Hastings +Hatfield +Hathaway +Hatsheput +Hatteras +Hattie +Hauptmann +Hausa +Hausdorff +Havana +Havanas +Havarti +Havel +Havoline +Hawaii +Hawaiian +Hawaiians +Hawking +Hawkins +Hawthorne +Hay +Hayden +Haydn +Hayes +Haynes +Hays +Haywood +Hayworth +Hazel +Hazlitt +He +Head +Hearst +Heath +Heather +Heaviside +Hebe +Hebert +Hebraic +Hebrew +Hebrews +Hebrides +Hecate +Hector +Hecuba +Heep +Hefner +Hegel +Hegelian +Hegira +Heidegger +Heidelberg +Heidi +Heifetz +Heimlich +Heine +Heineken +Heinlein +Heinrich +Heinz +Heisenberg +Heisman +Helen +Helena +Helene +Helga +Helicon +Heliopolis +Helios +Hellenic +Hellenism +Hellenisms +Hellenistic +Hellenization +Hellenize +Heller +Hellespont +Hellman +Helmholtz +Helsinki +Helvetius +Hemingway +Hench +Henderson +Hendricks +Hendrix +Henley +Hennessy +Henri +Henrietta +Henry +Hensley +Henson +Hepburn +Hephaestus +Hepplewhite +Hera +Heraclitus +Herbart +Herbert +Herculaneum +Hercules +Herder +Hereford +Herero +Heriberto +Herman +Hermaphroditus +Hermes +Herminia +Hermitage +Hermite +Hermosillo +Hernandez +Herod +Herodotus +Herrera +Herrick +Herring +Herschel +Hersey +Hershel +Hershey +Hertz +Hertzsprung +Herzegovina +Herzl +Heshvan +Hesiod +Hesperus +Hess +Hesse +Hessian +Hester +Heston +Hettie +Hewitt +Hewlett +Heyerdahl +Heywood +Hezbollah +Hezekiah +Hg +Hialeah +Hiawatha +Hibernia +Hickman +Hickok +Hicks +Hieronymus +Higgins +Highlander +Highlanders +Highness +Hilario +Hilary +Hilbert +Hilda +Hildebrand +Hilfiger +Hill +Hillary +Hillel +Hilton +Himalaya +Himalayas +Himmler +Hinayana +Hindemith +Hindenburg +Hindi +Hindu +Hinduism +Hinduisms +Hindus +Hindustan +Hindustani +Hines +Hinton +Hipparchus +Hippocrates +Hippocratic +Hiram +Hirobumi +Hirohito +Hiroshima +Hispanic +Hispanics +Hispaniola +Hiss +Hitachi +Hitchcock +Hitler +Hitlers +Hittite +Hmong +Hobart +Hobbes +Hobbs +Hockney +Hodge +Hodges +Hodgkin +Hoff +Hoffa +Hoffman +Hofstadter +Hogan +Hogarth +Hogwarts +Hohenlohe +Hohenstaufen +Hohenzollern +Hohhot +Hohokam +Hokkaido +Hokusai +Holbein +Holcomb +Holden +Holder +Holiday +Holland +Hollands +Hollerith +Holley +Hollie +Hollis +Holloway +Holly +Hollywood +Holman +Holmes +Holocaust +Holocene +Holst +Holstein +Holsteins +Holt +Homer +Homeric +Honda +Honduran +Hondurans +Honduras +Honecker +Honeywell +Hong +Honiara +Honolulu +Honshu +Hood +Hooke +Hooker +Hooper +Hoosier +Hooters +Hoover +Hoovers +Hope +Hopewell +Hopi +Hopkins +Hopper +Horace +Horacio +Horatio +Hormel +Hormuz +Horn +Hornblower +Horne +Horowitz +Horthy +Horton +Horus +Hosea +Hotpoint +Hottentot +Houdini +House +Housman +Houston +Houyhnhnm +Hovhaness +Howard +Howe +Howell +Howells +Hoyle +Hrothgar +Huang +Hubbard +Hubble +Huber +Hubert +Huck +Hudson +Huerta +Huey +Huff +Huffman +Huggins +Hugh +Hughes +Hugo +Huguenot +Huguenots +Hui +Huitzilopotchli +Hull +Humberto +Humboldt +Hume +Hummer +Humphrey +Humvee +Hun +Hungarian +Hungarians +Hungary +Huns +Hunspell +Hunt +Hunter +Huntington +Huntley +Huntsville +Hurley +Huron +Hurst +Hus +Hussein +Husserl +Hussite +Huston +Hutchinson +Hutton +Hutu +Huxley +Huygens +Hyades +Hyde +Hyderabad +Hydra +Hymen +Hyperion +Hyundai +Hz +Héloise +I +IBM +IKEA +ING +ISO +Iaccoca +Iago +Ian +Iapetus +Ibadan +Iberia +Iberian +Ibiza +Iblis +Ibo +Ibsen +Icahn +Icarus +Iceland +Icelander +Icelanders +Icelandic +Idaho +Idahoan +Idahoans +Idahoes +Idahos +Ieyasu +Ignacio +Ignatius +Igor +Iguassu +Ijsselmeer +Ike +Ikhnaton +Ila +Ilene +Iliad +Illinois +Illuminati +Ilyushin +Imelda +Imhotep +Imodium +Imogene +Imus +Ina +Inca +Incas +Inchon +Independence +India +Indian +Indiana +Indianan +Indianans +Indianapolis +Indians +Indies +Indira +Indochina +Indochinese +Indonesia +Indonesian +Indonesians +Indore +Indra +Indus +Indy +Ines +Inez +Inge +Inglewood +Ingram +Ingres +Ingrid +Innocent +Inonu +Inquisition +Instagram +Instamatic +Intel +Intelsat +Internationale +Internet +Interpol +Inuit +Inuits +Inuktitut +Invar +Ionesco +Ionian +Ionic +Ionics +Iowa +Iowan +Iowans +Iowas +Iphigenia +Iqaluit +Iqbal +Iquitos +Ira +Iran +Iranian +Iranians +Iraq +Iraqi +Iraqis +Ireland +Irene +Iris +Irish +Irisher +Irishman +Irishmen +Irishwoman +Irishwomen +Irkutsk +Irma +Iroquoian +Iroquois +Irrawaddy +Irtish +Irvin +Irving +Irwin +Isaac +Isabel +Isabella +Isabelle +Isaiah +Iscariot +Isfahan +Isherwood +Ishim +Ishmael +Ishtar +Isiah +Isidro +Isis +Islam +Islamabad +Islamic +Islamism +Islamist +Islams +Ismael +Ismail +Isolde +Ispell +Israel +Israeli +Israelis +Israelite +Israels +Issac +Issachar +Istanbul +Isuzu +Itaipu +Italian +Italians +Italy +Itasca +Ithaca +Ithacan +Ito +Iva +Ivan +Ivanhoe +Ives +Ivory +Ivy +Iyar +Izaak +Izanagi +Izanami +Izhevsk +Izmir +Izod +Izvestia +J +JFK +Jack +Jackie +Jacklyn +Jackson +Jacksonian +Jacksonville +Jacky +Jaclyn +Jacob +Jacobean +Jacobi +Jacobin +Jacobite +Jacobs +Jacobson +Jacquard +Jacqueline +Jacquelyn +Jacques +Jacuzzi +Jagger +Jagiellon +Jaguar +Jahangir +Jaime +Jain +Jainism +Jaipur +Jakarta +Jake +Jamaal +Jamaica +Jamaican +Jamaicans +Jamal +Jamar +Jame +Jamel +James +Jamestown +Jami +Jamie +Jan +Jana +Janacek +Jane +Janell +Janelle +Janet +Janette +Janice +Janie +Janine +Janis +Janissary +Janjaweed +Janna +Jannie +Jansen +Jansenist +Januaries +January +Janus +Japan +Japanese +Japaneses +Japura +Jared +Jarlsberg +Jarred +Jarrett +Jarrod +Jarvis +Jasmine +Jason +Jasper +Jataka +Java +JavaScript +Javanese +Javas +Javier +Jaxartes +Jay +Jayapura +Jayawardene +Jaycee +Jaycees +Jayne +Jayson +Jean +Jeanette +Jeanie +Jeanine +Jeanne +Jeannette +Jeannie +Jeannine +Jed +Jedi +Jeep +Jeeves +Jeff +Jefferey +Jefferson +Jeffersonian +Jeffery +Jeffrey +Jeffry +Jehoshaphat +Jehovah +Jekyll +Jenifer +Jenkins +Jenna +Jenner +Jennie +Jennifer +Jennings +Jenny +Jensen +Jephthah +Jerald +Jeremiah +Jeremiahs +Jeremy +Jeri +Jericho +Jermaine +Jeroboam +Jerold +Jerome +Jerri +Jerrod +Jerrold +Jerry +Jersey +Jerseys +Jerusalem +Jess +Jesse +Jessica +Jessie +Jesuit +Jesuits +Jesus +Jetway +Jew +Jewel +Jewell +Jewish +Jewishness +Jewry +Jews +Jezebel +Jezebels +Jidda +Jilin +Jill +Jillian +Jim +Jimenez +Jimmie +Jimmy +Jinan +Jinnah +Jinny +Jivaro +Jo +Joan +Joann +Joanna +Joanne +Joaquin +Job +Jobs +Jocasta +Jocelyn +Jock +Jockey +Jodi +Jodie +Jody +Joe +Joel +Joey +Jogjakarta +Johann +Johanna +Johannes +Johannesburg +John +Johnathan +Johnathon +Johnie +Johnnie +Johnny +Johns +Johnson +Johnston +Jolene +Joliet +Jolson +Jon +Jonah +Jonahs +Jonas +Jonathan +Jonathon +Jones +Joni +Jonson +Joplin +Jordan +Jordanian +Jordanians +Jorge +Jose +Josef +Josefa +Josefina +Joseph +Josephine +Josephs +Josephson +Josephus +Joshua +Josiah +Josie +Josue +Joule +Jove +Jovian +Joy +Joyce +Joycean +Joyner +Juan +Juana +Juanita +Juarez +Jubal +Judaeo +Judah +Judaic +Judaism +Judaisms +Judas +Judases +Judd +Jude +Judea +Judith +Judson +Judy +Juggernaut +Jules +Julia +Julian +Juliana +Julianne +Julie +Julies +Juliet +Juliette +Julio +Julius +Julliard +July +June +Juneau +Junes +Jung +Jungfrau +Jungian +Junior +Juniors +Juno +Jupiter +Jurassic +Jurua +Justice +Justin +Justine +Justinian +Jutland +Juvenal +K +KFC +Kaaba +Kabul +Kafka +Kafkaesque +Kagoshima +Kahlua +Kaifeng +Kaiser +Kaitlin +Kalahari +Kalamazoo +Kalashnikov +Kalb +Kalevala +Kalgoorlie +Kali +Kalmyk +Kama +Kamchatka +Kamehameha +Kampala +Kampuchea +Kanchenjunga +Kandahar +Kandinsky +Kane +Kannada +Kano +Kanpur +Kansan +Kansans +Kansas +Kant +Kantian +Kaohsiung +Kaposi +Kara +Karachi +Karaganda +Karakorum +Karamazov +Kareem +Karen +Karenina +Kari +Karin +Karina +Karl +Karla +Karloff +Karo +Karol +Karroo +Karyn +Kasai +Kasey +Kashmir +Kasparov +Kate +Katelyn +Katharine +Katherine +Katheryn +Kathiawar +Kathie +Kathleen +Kathrine +Kathryn +Kathy +Katie +Katina +Katmai +Katmandu +Katowice +Katrina +Katy +Kauai +Kaufman +Kaunas +Kaunda +Kawabata +Kawasaki +Kay +Kaye +Kayla +Kazakh +Kazakhstan +Kazan +Kazantzakis +Keaton +Keats +Keck +Keenan +Keewatin +Keillor +Keisha +Keith +Keller +Kelley +Kelli +Kellie +Kellogg +Kelly +Kelsey +Kelvin +Kemerovo +Kemp +Kempis +Kendall +Kendra +Kendrick +Kenmore +Kennan +Kennedy +Kenneth +Kennith +Kenny +Kent +Kenton +Kentuckian +Kentuckians +Kentucky +Kenya +Kenyan +Kenyans +Kenyatta +Kenyon +Keogh +Keokuk +Kepler +Kerensky +Keri +Kermit +Kern +Kerouac +Kerr +Kerri +Kerry +Kettering +Keven +Kevin +Kevlar +Kevorkian +Kewpie +Key +Keynes +Keynesian +Khabarovsk +Khachaturian +Khalid +Khan +Kharkov +Khartoum +Khayyam +Khazar +Khmer +Khoikhoi +Khoisan +Khomeini +Khorana +Khrushchev +Khufu +Khulna +Khwarizmi +Khyber +Kickapoo +Kidd +Kiel +Kierkegaard +Kieth +Kiev +Kigali +Kikuyu +Kilauea +Kilimanjaro +Kilroy +Kim +Kimberley +Kimberly +King +Kingston +Kingstown +Kinney +Kinsey +Kinshasa +Kiowa +Kip +Kipling +Kirby +Kirchhoff +Kirchner +Kirghistan +Kirghiz +Kiribati +Kirinyaga +Kirk +Kirkland +Kirkpatrick +Kirov +Kirsten +Kisangani +Kishinev +Kislev +Kissinger +Kit +Kitakyushu +Kitchener +Kitty +Kiwanis +Klan +Klansman +Klaus +Klee +Kleenex +Kleenexes +Klein +Klimt +Kline +Klingon +Klondike +Klondikes +Kmart +Knapp +Knesset +Kngwarreye +Knickerbocker +Knievel +Knight +Knopf +Knossos +Knowles +Knox +Knoxville +Knudsen +Knuth +Kobe +Koch +Kochab +Kodachrome +Kodak +Kodaly +Kodiak +Koestler +Kohinoor +Kohl +Koizumi +Kojak +Kolyma +Kommunizma +Kong +Kongo +Konrad +Koontz +Koppel +Koran +Korans +Korea +Korean +Koreans +Kornberg +Kory +Korzybski +Kosciusko +Kossuth +Kosygin +Koufax +Kowloon +Kr +Kraft +Krakatoa +Krakow +Kramer +Krasnodar +Krasnoyarsk +Krebs +Kremlin +Kremlinologist +Kresge +Kringle +Kris +Krishna +Krishnamurti +Krista +Kristen +Kristi +Kristie +Kristin +Kristina +Kristine +Kristopher +Kristy +Kroc +Kroger +Kronecker +Kropotkin +Kruger +Krugerrand +Krupp +Krystal +Kshatriya +Kublai +Kubrick +Kuhn +Kuibyshev +Kulthumm +Kunming +Kuomintang +Kurd +Kurdish +Kurdistan +Kurosawa +Kurt +Kurtis +Kusch +Kutuzov +Kuwait +Kuwaiti +Kuwaitis +Kuznets +Kuznetsk +Kwakiutl +Kwan +Kwangju +Kwanzaa +Kwanzaas +Kyle +Kyoto +Kyrgyzstan +Kyushu +L +LBJ +La +Laban +Labrador +Labradors +Lacey +Lachesis +Lacy +Ladoga +Ladonna +Lafayette +Lafitte +Lagos +Lagrange +Lagrangian +Lahore +Laius +Lajos +Lakeisha +Lakewood +Lakisha +Lakota +Lakshmi +Lamar +Lamarck +Lamaze +Lamb +Lambert +Lamborghini +Lambrusco +Lamont +Lana +Lanai +Lancashire +Lancaster +Lance +Lancelot +Land +Landon +Landry +Landsat +Landsteiner +Lane +Lang +Langerhans +Langland +Langley +Langmuir +Lanka +Lanny +Lansing +Lanzhou +Lao +Laocoon +Laos +Laotian +Laotians +Laplace +Lapland +Lapp +Lapps +Lara +Laramie +Lardner +Laredo +Larousse +Larry +Lars +Larsen +Larson +Las +Lascaux +Lassa +Lassen +Lassie +Latasha +Lateran +Latin +Latina +Latiner +Latino +Latinos +Latins +Latisha +Latonya +Latoya +Latrobe +Latvia +Latvian +Latvians +Laud +Lauder +Laue +Laundromat +Laura +Laurasia +Laurel +Lauren +Laurence +Laurent +Lauri +Laurie +Laval +Lavern +Laverne +Lavoisier +Lavonne +Lawanda +Lawrence +Lawson +Layamon +Layla +Lazaro +Lazarus +Le +Lea +Leach +Leadbelly +Leah +Leakey +Lean +Leander +Leann +Leanna +Leanne +Lear +Learjet +Leary +Leavenworth +Lebanese +Lebanon +Lebesgue +Leblanc +Leda +Lederberg +Lee +Leeds +Leeuwenhoek +Leeward +Left +Legendre +Leger +Leghorn +Lego +Legree +Lehman +Leibniz +Leicester +Leiden +Leif +Leigh +Leila +Leipzig +Lela +Leland +Lelia +Lemaitre +Lemuel +Lemuria +Len +Lena +Lenard +Lenin +Leningrad +Leninism +Leninist +Lennon +Lenny +Leno +Lenoir +Lenora +Lenore +Lent +Lenten +Lents +Leo +Leola +Leon +Leona +Leonard +Leonardo +Leoncavallo +Leonel +Leonid +Leonidas +Leonor +Leopold +Leopoldo +Leos +Lepidus +Lepke +Lepus +Lerner +Leroy +Les +Lesa +Lesley +Leslie +Lesotho +Lesseps +Lessie +Lester +Lestrade +Leta +Letha +Lethe +Leticia +Letitia +Letterman +Levant +Levesque +Levi +Leviathan +Levine +Leviticus +Levitt +Levy +Lew +Lewinsky +Lewis +Lexington +Lexus +Lhasa +Lhotse +Li +Libby +Liberace +Liberia +Liberian +Liberians +Libra +Libras +LibreOffice +Libreville +Librium +Libya +Libyan +Libyans +Lichtenstein +Lidia +Lie +Lieberman +Liebfraumilch +Liechtenstein +Liege +Lila +Lilia +Lilian +Liliana +Lilith +Liliuokalani +Lille +Lillian +Lillie +Lilliput +Lilliputian +Lilliputians +Lilly +Lilongwe +Lily +Lima +Limbaugh +Limburger +Limoges +Limousin +Limpopo +Lin +Lina +Lincoln +Lincolns +Lind +Linda +Lindbergh +Lindsay +Lindsey +Lindy +Linnaeus +Linotype +Linton +Linus +Linux +Linwood +Lionel +Lipizzaner +Lippi +Lippmann +Lipscomb +Lipton +Lisa +Lisbon +Lissajous +Lister +Listerine +Liston +Liszt +Lithuania +Lithuanian +Lithuanians +Little +Litton +Liverpool +Liverpudlian +Livia +Livingston +Livingstone +Livonia +Livy +Liz +Liza +Lizzie +Lizzy +Ljubljana +Llewellyn +Lloyd +Loafer +Loafers +Lobachevsky +Lochinvar +Locke +Lockean +Lockheed +Lockwood +Lodge +Lodz +Loewe +Loewi +Loews +Logan +Lohengrin +Loire +Lois +Loki +Lola +Lolita +Lollard +Lollobrigida +Lombard +Lombardi +Lombardy +Lome +Lon +London +Londoner +Long +Longfellow +Longstreet +Lonnie +Lopez +Lora +Loraine +Lord +Lords +Lorelei +Loren +Lorena +Lorene +Lorentz +Lorenz +Lorenzo +Loretta +Lori +Lorie +Lorna +Lorraine +Lorre +Lorrie +Los +Lot +Lothario +Lott +Lottie +Lou +Louella +Louie +Louis +Louisa +Louise +Louisiana +Louisianan +Louisianans +Louisianian +Louisianians +Louisville +Lourdes +Louvre +Love +Lovecraft +Lovelace +Lowe +Lowell +Lowenbrau +Lowery +Loyang +Loyd +Loyola +Luanda +Luann +Lubavitcher +Lubbock +Lubumbashi +Lucas +Luce +Lucia +Lucian +Luciano +Lucien +Lucifer +Lucile +Lucille +Lucinda +Lucio +Lucite +Lucius +Lucknow +Lucretia +Lucretius +Lucy +Luddite +Ludhiana +Ludwig +Luella +Lufthansa +Luftwaffe +Luger +Lugosi +Luigi +Luis +Luisa +Luke +Lula +Lully +Lulu +Lumière +Luna +Lupe +Lupercalia +Lupus +Luria +Lusaka +Lusitania +Luther +Lutheran +Lutheranism +Lutherans +Luvs +Luxembourg +Luxembourger +Luxembourgers +Luz +Luzon +Lvov +LyX +Lycra +Lycurgus +Lydia +Lyell +Lyle +Lyly +Lyman +Lyme +Lynch +Lynda +Lyndon +Lynette +Lynn +Lynne +Lynnette +Lyon +Lyons +Lyra +Lysenko +Lysistrata +Lysol +M +MCI +MGM +MHz +MIT +Maalox +Mabel +Mable +MacArthur +MacBride +MacDonald +MacLeish +Macao +Macaulay +Macbeth +Maccabeus +Mace +Macedon +Macedonia +Macedonian +Macedonians +Mach +Machiavelli +Machiavellian +Macias +Macintosh +Mack +Mackenzie +Mackinac +Mackinaw +Macmillan +Macon +Macumba +Macy +Madagascan +Madagascans +Madagascar +Madden +Maddox +Madeira +Madeiras +Madeleine +Madeline +Madelyn +Madge +Madison +Madonna +Madonnas +Madras +Madrid +Madurai +Mae +Maeterlinck +Mafia +Mafias +Mafioso +Magdalena +Magdalene +Magellan +Magellanic +Maggie +Maghreb +Magi +Maginot +Magnitogorsk +Magog +Magoo +Magritte +Magsaysay +Magyar +Magyars +Mahabharata +Maharashtra +Mahavira +Mahayana +Mahayanist +Mahdi +Mahfouz +Mahican +Mahicans +Mahler +Mai +Maidenform +Maigret +Mailer +Maillol +Maiman +Maimonides +Maine +Maisie +Maitreya +Major +Majorca +Majuro +Makarios +Malabar +Malabo +Malacca +Malachi +Malagasy +Malamud +Malaprop +Malawi +Malay +Malayalam +Malayan +Malays +Malaysia +Malaysian +Malaysians +Malcolm +Maldive +Maldives +Maldivian +Maldivians +Maldonado +Male +Mali +Malian +Malians +Malibu +Malinda +Malinowski +Mallarmé +Mallomars +Mallory +Malone +Malory +Malplaquet +Malraux +Malta +Maltese +Malthus +Malthusian +Mameluke +Mamet +Mamie +Mammon +Mamore +Managua +Manama +Manasseh +Manchester +Manchu +Manchuria +Manchurian +Mancini +Mandalay +Mandarin +Mandela +Mandelbrot +Mandingo +Mandrell +Mandy +Manet +Manfred +Manhattan +Manhattans +Mani +Manichean +Manila +Manilas +Manitoba +Manitoulin +Manley +Mann +Mannheim +Manning +Mansfield +Manson +Mantegna +Mantle +Manuel +Manuela +Manx +Mao +Maoism +Maoisms +Maoist +Maoists +Maori +Maoris +Mapplethorpe +Maputo +Mar +Mara +Maracaibo +Marat +Maratha +Marathi +Marathon +Marc +Marceau +Marcel +Marcelino +Marcella +Marcelo +March +Marches +Marci +Marcia +Marciano +Marcie +Marco +Marconi +Marcos +Marcus +Marcy +Marduk +Margaret +Margarita +Margarito +Marge +Margery +Margie +Margo +Margret +Margrethe +Marguerite +Mari +Maria +Marian +Mariana +Marianas +Marianne +Mariano +Maribel +Maricela +Marie +Marietta +Marilyn +Marin +Marina +Marine +Marines +Mario +Marion +Maris +Marisa +Marisol +Marissa +Maritain +Maritza +Marius +Marjorie +Marjory +Mark +Markab +Markham +Markov +Marks +Marla +Marlboro +Marlborough +Marlene +Marley +Marlin +Marlon +Marlowe +Marmara +Marne +Maronite +Marple +Marquesas +Marquette +Marquez +Marquis +Marquita +Marrakesh +Marriott +Mars +Marsala +Marseillaise +Marseilles +Marsh +Marsha +Marshall +Marta +Martel +Martha +Martial +Martian +Martians +Martin +Martina +Martinez +Martinique +Marty +Marva +Marvell +Marvin +Marx +Marxism +Marxisms +Marxist +Marxists +Mary +Maryann +Maryanne +Maryellen +Maryland +Marylander +Marylou +Masada +Masai +Masaryk +Mascagni +Masefield +Maserati +Maseru +Mashhad +Mason +Masonic +Masonite +Masons +Mass +Massachusetts +Massasoit +Massenet +Masses +Massey +MasterCard +Masters +Mather +Mathew +Mathews +Mathewson +Mathias +Mathis +Matilda +Matisse +Mattel +Matterhorn +Matthew +Matthews +Matthias +Mattie +Maud +Maude +Maugham +Maui +Maupassant +Maura +Maureen +Mauriac +Maurice +Mauricio +Maurine +Mauritania +Mauritius +Mauro +Maurois +Mauryan +Mauser +Mavis +Max +Maximilian +Maxine +Maxwell +May +Maya +Mayan +Mayans +Mayas +Mayer +Mayfair +Mayflower +Maynard +Mayo +Mayra +Mays +Maytag +Mazama +Mazarin +Mazatlan +Mazda +Mazola +Mazzini +Mbabane +Mbini +McAdam +McBride +McCain +McCall +McCarthy +McCarthyism +McCartney +McCarty +McClain +McClellan +McClure +McConnell +McCormick +McCoy +McCray +McCullough +McDaniel +McDonald +McDonnell +McDowell +McEnroe +McFadden +McFarland +McGee +McGovern +McGowan +McGuffey +McGuire +McIntosh +McIntyre +McKay +McKee +McKenzie +McKinley +McKinney +McKnight +McLaughlin +McLean +McLeod +McLuhan +McMahon +McMillan +McNamara +McNaughton +McNeil +McPherson +McQueen +McVeigh +Md +Mead +Meade +Meadows +Meagan +Meany +Mecca +Meccas +Medan +Medea +Medellin +Media +Medicaid +Medicaids +Medicare +Medicares +Medici +Medina +Mediterranean +Mediterraneans +Medusa +Meg +Megan +Meghan +Meier +Meighen +Meiji +Meir +Mejia +Mekong +Mel +Melanesia +Melanesian +Melanie +Melba +Melbourne +Melchior +Melchizedek +Melendez +Melinda +Melisa +Melisande +Melissa +Mellon +Melody +Melpomene +Melton +Melva +Melville +Melvin +Memling +Memphis +Menander +Mencius +Mencken +Mendel +Mendeleev +Mendelian +Mendelssohn +Mendez +Mendocino +Mendoza +Menelaus +Menelik +Menes +Menkalinan +Menkar +Menkent +Mennen +Mennonite +Mennonites +Menominee +Menotti +Mensa +Mentholatum +Menuhin +Menzies +Mephistopheles +Merak +Mercado +Mercator +Mercedes +Mercer +Mercia +Merck +Mercuries +Mercurochrome +Mercury +Meredith +Merino +Merle +Merlin +Merlot +Merovingian +Merriam +Merrick +Merrill +Merrimack +Merritt +Merthiolate +Merton +Mervin +Mesa +Mesabi +Mesmer +Mesolithic +Mesopotamia +Mesozoic +Messerschmidt +Messiaen +Messiah +Messiahs +Messianic +Metallica +Metamucil +Methodism +Methodisms +Methodist +Methodists +Methuselah +Metternich +Meuse +Mexicali +Mexican +Mexicans +Mexico +Meyer +Meyerbeer +Meyers +Mfume +Mg +MiG +Mia +Miami +Miamis +Miaplacidus +Micah +Micawber +Michael +Micheal +Michel +Michelangelo +Michele +Michelin +Michelle +Michelob +Michelson +Michigan +Michigander +Michiganders +Mick +Mickey +Mickie +Micky +Micmac +Micronesia +Micronesian +Microsoft +Midas +Middleton +Midland +Midway +Midwest +Midwestern +Miguel +Mike +Mikhail +Mikoyan +Milagros +Milan +Mildred +Miles +Milford +Milken +Mill +Millard +Millay +Miller +Millet +Millicent +Millie +Millikan +Mills +Milne +Milo +Milosevic +Milquetoast +Miltiades +Milton +Miltonic +Miltown +Milwaukee +Mimi +Mimosa +Minamoto +Mindanao +Mindoro +Mindy +Minerva +Ming +Mingus +Minneapolis +Minnelli +Minnesota +Minnesotan +Minnesotans +Minnie +Minoan +Minoans +Minolta +Minos +Minot +Minotaur +Minsk +Minsky +Mintaka +Minuit +Miocene +Mir +Mira +Mirabeau +Mirach +Miranda +Mirfak +Miriam +Miro +Mirzam +Miskito +Miss +Mississauga +Mississippi +Mississippian +Mississippians +Missouri +Missourian +Missourians +Missy +Mistassini +Mister +Misty +Mitch +Mitchel +Mitchell +Mitford +Mithra +Mithridates +Mitsubishi +Mitterrand +Mitty +Mitzi +Mixtec +Mizar +Mn +Mnemosyne +Mo +Mobil +Mobile +Mobutu +Modesto +Modigliani +Moe +Moet +Mogadishu +Mohacs +Mohamed +Mohammad +Mohammedan +Mohammedanism +Mohammedanisms +Mohammedans +Mohawk +Mohawks +Moho +Mohorovicic +Moira +Moises +Moiseyev +Mojave +Moldavia +Moldova +Moliere +Molina +Moll +Mollie +Molly +Molnar +Moloch +Molokai +Molotov +Moluccas +Mombasa +Mona +Monaco +Mondale +Monday +Mondays +Mondrian +Monera +Monet +Mongol +Mongolia +Mongolian +Mongolians +Mongoloid +Mongols +Monica +Monique +Monk +Monmouth +Monongahela +Monroe +Monrovia +Mons +Monsanto +Montague +Montaigne +Montana +Montanan +Montanans +Montcalm +Monte +Montenegrin +Montenegro +Monterrey +Montesquieu +Montessori +Monteverdi +Montevideo +Montezuma +Montgolfier +Montgomery +Monticello +Montoya +Montpelier +Montrachet +Montreal +Montserrat +Monty +Moody +Moog +Moon +Mooney +Moor +Moore +Moorish +Moors +Morales +Moran +Moravia +Moravian +Mordred +More +Moreno +Morgan +Moriarty +Morin +Morison +Morita +Morley +Mormon +Mormonism +Mormonisms +Mormons +Moro +Moroccan +Moroccans +Morocco +Moroni +Morpheus +Morphy +Morris +Morrison +Morrow +Morse +Mort +Mortimer +Morton +Mosaic +Moscow +Moseley +Moselle +Moses +Moslem +Mosley +Moss +Mosul +Motorola +Motown +Motrin +Mott +Mount +Mountbatten +Mountie +Mounties +Moussorgsky +Mouthe +Mouton +Mowgli +Mozambican +Mozambicans +Mozambique +Mozart +Mozilla +Ms +Muawiya +Mubarak +Mueller +Muenster +Mugabe +Muhammad +Muhammadan +Muhammadanism +Muhammadanisms +Muhammadans +Muir +Mujib +Mulder +Mullen +Muller +Mulligan +Mullikan +Mullins +Mulroney +Multan +Mumbai +Mumford +Munch +Munich +Munoz +Munro +Muppet +Murasaki +Murat +Murchison +Murdoch +Muriel +Murillo +Murine +Murmansk +Murphy +Murray +Murrow +Murrumbidgee +Muscat +Muscovite +Muscovy +Muse +Musharraf +Musial +Muskogee +Muslim +Muslims +Mussolini +Mussorgsky +Mutsuhito +Muzak +MySpace +Myanmar +Mycenae +Mycenaean +Myers +Mylar +Mylars +Myles +Myra +Myrdal +Myrna +Myron +Myrtle +Mysore +Myst +Münchhausen +N +NASCAR +NORAD +NSA +Na +Nabisco +Nabokov +Nader +Nadia +Nadine +Nagasaki +Nagoya +Nagpur +Nagy +Nahuatl +Nahum +Naipaul +Nair +Nairobi +Naismith +Nam +Namath +Namibia +Namibian +Namibians +Nan +Nanak +Nanchang +Nancy +Nanette +Nanjing +Nannie +Nanook +Nansen +Nantes +Nantucket +Naomi +Naphtali +Napier +Naples +Napoleon +Napoleonic +Napster +Narcissus +Narmada +Narnia +Narragansett +Nash +Nashua +Nashville +Nassau +Nasser +Nat +Natalia +Natalie +Natasha +Natchez +Nate +Nathan +Nathaniel +Nathans +Nation +Nationwide +Naugahyde +Nauru +Nautilus +Navajo +Navajoes +Navajos +Navarre +Navarro +Navratilova +Nazarene +Nazareth +Nazca +Nazi +Nazis +Nazism +Nazisms +Nd +Ndjamena +Ne +Neal +Neanderthal +Neanderthals +Neapolitan +Nebraska +Nebraskan +Nebraskans +Nebuchadnezzar +Ned +Nefertiti +Negev +Negro +Negroes +Negroid +Negroids +Negros +Nehemiah +Nehru +Neil +Nelda +Nell +Nellie +Nelly +Nelsen +Nelson +Nembutal +Nemesis +Neogene +Neolithic +Nepal +Nepalese +Nepali +Neptune +Nereid +Nerf +Nero +Neruda +Nescafe +Nesselrode +Nestle +Nestor +Nestorius +Netflix +Netherlander +Netherlanders +Netherlands +Netscape +Nettie +Netzahualcoyotl +Neva +Nevada +Nevadan +Nevadans +Nevis +Nevsky +Newark +Newcastle +Newfoundland +Newfoundlands +Newman +Newport +Newsweek +Newton +Newtonian +Nexis +Ngaliema +Nguyen +Ni +Niagara +Niamey +Nibelung +Nicaea +Nicaragua +Nicaraguan +Nicaraguans +Niccolo +Nice +Nicene +Nichiren +Nicholas +Nichole +Nichols +Nicholson +Nick +Nickelodeon +Nicklaus +Nickolas +Nicobar +Nicodemus +Nicola +Nicolas +Nicole +Nicosia +Niebuhr +Nielsen +Nietzsche +Nieves +Nigel +Niger +Nigeria +Nigerian +Nigerians +Nightingale +Nijinsky +Nike +Nikita +Nikkei +Nikki +Nikolai +Nikolayev +Nikon +Nile +Nimitz +Nimrod +Nina +Nineveh +Nintendo +Niobe +Nippon +Nirenberg +Nirvana +Nisan +Nisei +Nissan +Nita +Nivea +Nixon +Nkrumah +NoDoz +Noah +Nobel +Nobelist +Nobelists +Noble +Noe +Noel +Noelle +Noels +Noemi +Nokia +Nola +Nolan +Nome +Nona +Nootka +Nora +Norbert +Norberto +Nordic +Nordics +Noreen +Norfolk +Noriega +Norma +Norman +Normand +Normandy +Normans +Norplant +Norris +Norse +Norseman +Norsemen +North +Northampton +Northeast +Northeasts +Northerner +Northrop +Northrup +Norths +Northwest +Northwests +Norton +Norway +Norwegian +Norwegians +Norwich +Nosferatu +Nostradamus +Nottingham +Nouakchott +Noumea +Nova +Novartis +November +Novembers +Novgorod +Novocain +Novocaine +Novokuznetsk +Novosibirsk +Noxzema +Noyce +Noyes +Np +Nubia +Nubian +Nukualofa +Numbers +Nunavut +Nunez +Nunki +Nuremberg +Nureyev +NutraSweet +NyQuil +Nyasa +Nyerere +O +OHSA +OK +OKed +OKing +OKs +Oahu +Oakland +Oakley +Oates +Oaxaca +Ob +Obadiah +Obama +Obamacare +Oberlin +Oberon +Occam +Occident +Occidental +Occidentals +Oceania +Oceanus +Ochoa +Oct +Octavia +Octavio +October +Octobers +Odell +Oder +Odessa +Odets +Odin +Odis +Odom +Odysseus +Odyssey +Oedipal +Oedipus +Oersted +Ofelia +Offenbach +OfficeMax +Ogbomosho +Ogden +Ogilvy +Oglethorpe +Ohio +Ohioan +Ohioans +Oise +Ojibwa +Ojibwas +Okeechobee +Okefenokee +Okhotsk +Okinawa +Oklahoma +Oklahoman +Oktoberfest +Ola +Olaf +Olajuwon +Olav +Oldenburg +Oldfield +Oldsmobile +Olduvai +Olen +Olenek +Olga +Oligocene +Olin +Olive +Oliver +Olivetti +Olivia +Olivier +Ollie +Olmec +Olmsted +Olsen +Olson +Olympia +Olympiad +Olympiads +Olympian +Olympians +Olympias +Olympic +Olympics +Olympus +Omaha +Omahas +Oman +Omar +Omayyad +Omdurman +Omsk +Onassis +Oneal +Onega +Onegin +Oneida +Onion +Ono +Onondaga +Onsager +Ontario +Oort +Opal +Opel +OpenOffice +Ophelia +Ophiuchus +Oppenheimer +Oprah +Ora +Oracle +Oran +Orange +Oranjestad +Orbison +Ordovician +Oregon +Oregonian +Oregonians +Oreo +Orestes +Orient +Oriental +Orientals +Orin +Orinoco +Orion +Oriya +Orizaba +Orkney +Orlando +Orleans +Orlon +Orlons +Orly +Orpheus +Orphic +Orr +Ortega +Ortiz +Orval +Orville +Orwell +Orwellian +Os +Osage +Osaka +Osbert +Osborn +Osborne +Oscar +Oscars +Osceola +Osgood +Oshawa +Oshkosh +Osiris +Oslo +Osman +Ostrogoth +Ostwald +Osvaldo +Oswald +Othello +Otis +Ottawa +Ottawas +Otto +Ottoman +Ouagadougou +Ouija +Ovid +Owen +Owens +Oxford +Oxfords +Oxnard +Oxonian +Oxus +Oxycontin +Oz +Ozark +Ozarks +Ozymandias +Ozzie +P +Pa +Paar +Pablo +Pablum +Pabst +Pace +Pacheco +Pacific +Pacino +Packard +Paderewski +Padilla +Paganini +Page +Paglia +Pahlavi +Paige +Paine +Pakistan +Pakistani +Pakistanis +Palau +Palembang +Paleocene +Paleogene +Paleolithic +Paleozoic +Palermo +Palestine +Palestinian +Palestinians +Palestrina +Paley +Palikir +Palisades +Palladio +Palmer +Palmerston +Palmolive +Palmyra +Palomar +Pam +Pamela +Pamirs +Pampers +Pan +Panama +Panamanian +Panamanians +Panamas +Panasonic +Pandora +Pangaea +Pankhurst +Panmunjom +Pansy +Pantagruel +Pantaloon +Pantheon +Panza +Paracelsus +Paraclete +Paradise +Paraguay +Paraguayan +Paraguayans +Paramaribo +Paramount +Paraná +Parcheesi +Pareto +Paris +Parisian +Parisians +Park +Parker +Parkinson +Parkman +Parks +Parliament +Parmesan +Parmesans +Parnassus +Parnell +Parr +Parrish +Parsi +Parsifal +Parsons +Parthenon +Parthia +Pasadena +Pascal +Pasquale +Passion +Passions +Passover +Passovers +Pasternak +Pasteur +Pat +Patagonia +Patagonian +Pate +Patel +Paterson +Patna +Patrica +Patrice +Patricia +Patrick +Patsy +Patterson +Patti +Patton +Patty +Paul +Paula +Paulette +Pauli +Pauline +Pauling +Pavarotti +Pavlov +Pavlova +Pavlovian +Pawnee +PayPal +Payne +Pb +Pd +Peabody +Peace +Peale +Pearl +Pearlie +Pearson +Peary +Pechora +Peck +Peckinpah +Pecos +Pedro +Peel +Peg +Pegasus +Pegasuses +Peggy +Pei +Peiping +Pekinese +Peking +Pekingese +Pekingeses +Pekings +Pele +Pelee +Peloponnese +Pembroke +Pena +Penderecki +Penelope +Penn +Penney +Pennington +Pennsylvania +Pennsylvanian +Pennsylvanians +Penny +Pennzoil +Pensacola +Pentagon +Pentateuch +Pentax +Pentecost +Pentecostal +Pentecostals +Pentecosts +Pentium +Peoria +Pepin +Pepsi +Pepys +Pequot +Percheron +Percival +Percy +Perelman +Perez +Periclean +Pericles +Perkins +Perl +Perm +Permalloy +Permian +Pernod +Peron +Perot +Perrier +Perry +Perseid +Persephone +Persepolis +Perseus +Pershing +Persia +Persian +Persians +Perth +Peru +Peruvian +Peruvians +Peshawar +Pete +Peter +Peters +Petersen +Peterson +Petra +Petrarch +Petty +Peugeot +Pfizer +Phaedra +Phaethon +Phanerozoic +Pharaoh +Pharaohs +Pharisee +Pharisees +Phekda +Phelps +Phidias +Philadelphia +Philby +Philip +Philippe +Philippians +Philippine +Philippines +Philips +Philistine +Phillip +Phillipa +Phillips +Philly +Phipps +Phobos +Phoebe +Phoenicia +Phoenix +Photostat +Photostats +Photostatted +Photostatting +Phrygia +Phyllis +Piaf +Piaget +Pianola +Picasso +Piccadilly +Pickering +Pickett +Pickford +Pickwick +Pict +Piedmont +Pierce +Pierre +Pierrot +Pike +Pilate +Pilates +Pilcomayo +Pilgrim +Pillsbury +Pinatubo +Pincus +Pindar +Pinkerton +Pinocchio +Pinochet +Pinter +Pippin +Piraeus +Pirandello +Pisa +Pisces +Pisistratus +Pissaro +Pitcairn +Pitt +Pittman +Pitts +Pittsburgh +Pius +Pizarro +Planck +Plantagenet +Plasticine +Plataea +Plath +Plato +Platonic +Platonism +Platonist +Platte +Plautus +PlayStation +Playboy +Playtex +Pleiades +Pleistocene +Plexiglas +Plexiglases +Pliny +Pliocene +Plutarch +Pluto +Plymouth +Po +Pocahontas +Pocono +Poconos +Podgorica +Podhoretz +Podunk +Poe +Pogo +Poincaré +Poiret +Poirot +Poisson +Poitier +Pokémon +Poland +Polanski +Polaris +Polaroid +Polaroids +Pole +Poles +Polish +Politburo +Polk +Pollard +Pollock +Pollux +Polly +Pollyanna +Polo +Poltava +Polyhymnia +Polynesia +Polynesian +Polynesians +Polyphemus +Pomerania +Pomeranian +Pomona +Pompadour +Pompeii +Pompey +Ponce +Pontchartrain +Pontiac +Pontianak +Pooh +Poole +Poona +Pope +Popeye +Popocatepetl +Popper +Poppins +Popsicle +Porfirio +Porrima +Porsche +Porter +Portia +Portland +Portsmouth +Portugal +Portuguese +Poseidon +Post +Potemkin +Potomac +Potsdam +Pottawatomie +Potter +Potts +Pound +Poussin +Powell +PowerPC +PowerPoint +Powers +Powhatan +Poznan +Prada +Prado +Praetorian +Prague +Praia +Prakrit +Pratchett +Pratt +Pravda +Praxiteles +Preakness +Precambrian +Preminger +Premyslid +Prensa +Prentice +Presbyterian +Presbyterianism +Presbyterians +Prescott +Presley +Preston +Pretoria +Priam +Pribilof +Price +Priestley +Prince +Princeton +Principe +Priscilla +Prius +Procrustean +Procrustes +Procter +Procyon +Prohibition +Prokofiev +Promethean +Prometheus +Proserpine +Protagoras +Proterozoic +Protestant +Protestantism +Protestantisms +Protestants +Proteus +Proudhon +Proust +Provencals +Provence +Provençal +Proverbs +Providence +Providences +Provo +Prozac +Prudence +Prudential +Pruitt +Prussia +Prussian +Prut +Pryor +Psalms +Psalter +Psalters +Psyche +Pt +Ptah +Ptolemaic +Ptolemies +Ptolemy +Pu +Puccini +Puck +Puckett +Puebla +Pueblo +Puerto +Puget +Pugh +Pulaski +Pulitzer +Pullman +Pullmans +Punch +Punic +Punjab +Punjabi +Purana +Purcell +Purdue +Purim +Purims +Purina +Puritan +Puritanism +Puritanisms +Purus +Pusan +Pusey +Pushkin +Pushtu +Putin +Putnam +Puzo +Pygmalion +Pygmies +Pygmy +Pyle +Pym +Pynchon +Pyongyang +Pyotr +Pyrenees +Pyrex +Pyrexes +Pyrrhic +Pythagoras +Pythagorean +Pythias +Python +Pétain +Pôrto +Q +Qaddafi +Qantas +Qatar +Qingdao +Qiqihar +Qom +Quaalude +Quaker +Quakers +Quaoar +Quasimodo +Quaternary +Quayle +Quebec +Quechua +Queen +Queens +Queensland +Quentin +Quetzalcoatl +Quezon +Quincy +Quinn +Quintilian +Quinton +Quirinal +Quisling +Quito +Quixote +Quixotism +Qumran +Quonset +Quran +Québecois +R +RCA +Ra +Rabat +Rabelais +Rabelaisian +Rabin +Rachael +Rachel +Rachelle +Rachmaninoff +Racine +Radcliffe +Rae +Rafael +Raffles +Ragnarök +Rainier +Raleigh +Ralph +Rama +Ramada +Ramadan +Ramadans +Ramakrishna +Ramanujan +Ramayana +Rambo +Ramirez +Ramiro +Ramon +Ramona +Ramos +Ramsay +Ramses +Ramsey +Rand +Randal +Randall +Randell +Randi +Randolph +Randy +Rangoon +Rankin +Rankine +Raoul +Raphael +Rapunzel +Raquel +Rasalgethi +Rasalhague +Rasmussen +Rasputin +Rasta +Rastaban +Rastafarian +Rastafarianism +Rather +Ratliff +Raul +Ravel +Rawalpindi +Ray +RayBan +Rayburn +Rayleigh +Raymond +Raymundo +Reagan +Reaganomics +Realtor +Reasoner +Reba +Rebekah +Recife +Red +Redford +Redgrave +Redmond +Reebok +Reed +Reese +Reeves +Refugio +Reggie +Regina +Reginae +Reginald +Regor +Regulus +Rehnquist +Reich +Reid +Reilly +Reinaldo +Reinhardt +Reinhold +Remarque +Rembrandt +Remington +Remus +Rena +Renaissance +Renaissances +Renault +Rene +Renee +Reno +Renoir +Representative +Republican +Republicans +Resurrection +Reuben +Reunion +Reuters +Reuther +Reva +Revelations +Revere +Reverend +Revlon +Rex +Reyes +Reykjavik +Reyna +Reynaldo +Reynolds +Rhea +Rhee +Rheingau +Rhenish +Rhiannon +Rhine +Rhineland +Rhoda +Rhode +Rhodes +Rhodesia +Rhonda +Rhone +Ribbentrop +Ricardo +Rice +Rich +Richard +Richards +Richardson +Richelieu +Richie +Richmond +Richter +Richthofen +Rick +Rickenbacker +Rickey +Rickie +Rickover +Ricky +Rico +Riddle +Ride +Riefenstahl +Riel +Riemann +Riesling +Riga +Rigel +Riggs +Rigoberto +Rigoletto +Riley +Rilke +Rimbaud +Ringling +Ringo +Rio +Rios +Ripley +Risorgimento +Rita +Ritalin +Ritz +Rivas +Rivera +Rivers +Riverside +Riviera +Rivieras +Riyadh +Rizal +Rn +Roach +Rob +Robbie +Robbin +Robbins +Robby +Roberson +Robert +Roberta +Roberto +Roberts +Robertson +Robeson +Robespierre +Robin +Robinson +Robitussin +Robles +Robson +Robt +Robyn +Rocco +Rocha +Rochambeau +Roche +Rochelle +Rochester +Rock +Rockefeller +Rockford +Rockies +Rockne +Rockwell +Rocky +Rod +Roddenberry +Roderick +Rodger +Rodgers +Rodin +Rodney +Rodolfo +Rodrick +Rodrigo +Rodriguez +Rodriquez +Roeg +Roentgen +Rogelio +Roger +Rogers +Roget +Rojas +Roku +Rolaids +Roland +Rolando +Rolex +Rolland +Rollerblade +Rollins +Rolodex +Rolvaag +Roman +Romanesque +Romania +Romanian +Romanians +Romanies +Romano +Romanov +Romans +Romansh +Romanticism +Romany +Rome +Romeo +Romero +Romes +Rommel +Romney +Romulus +Ron +Ronald +Ronda +Ronnie +Ronny +Ronstadt +Rooney +Roosevelt +Root +Roquefort +Roqueforts +Rorschach +Rory +Rosa +Rosales +Rosalie +Rosalind +Rosalinda +Rosalyn +Rosanna +Rosanne +Rosario +Roscoe +Rose +Roseann +Roseau +Rosecrans +Rosella +Rosemarie +Rosemary +Rosenberg +Rosendo +Rosenzweig +Rosetta +Rosicrucian +Rosie +Roslyn +Ross +Rossetti +Rossini +Rostand +Rostov +Rostropovich +Roswell +Rotarian +Roth +Rothko +Rothschild +Rotterdam +Rottweiler +Rouault +Rourke +Rousseau +Rove +Rover +Rowe +Rowena +Rowland +Rowling +Roxanne +Roxie +Roxy +Roy +Royal +Royce +Rozelle +Rubaiyat +Rubbermaid +Ruben +Rubens +Rubicon +Rubik +Rubin +Rubinstein +Ruby +Ruchbah +Rudolf +Rudolph +Rudy +Rudyard +Rufus +Ruhr +Ruiz +Rukeyser +Rumpelstiltskin +Rumsfeld +Runnymede +Runyon +Rupert +Rush +Rushdie +Rushmore +Ruskin +Russel +Russell +Russia +Russian +Russians +Russo +Rustbelt +Rusty +Rutan +Rutgers +Ruth +Rutherford +Ruthie +Rutledge +Rwanda +Rwandan +Rwandans +Rwandas +Ryan +Rydberg +Ryder +Ryukyu +S +SAP +SARS +SUSE +SVN +Saab +Saar +Saarinen +Saatchi +Sabbath +Sabbaths +Sabik +Sabin +Sabina +Sabine +Sabre +Sabrina +Sacajawea +Sacco +Sachs +Sacramento +Sadat +Saddam +Sadducee +Sade +Sadie +Sadr +Safavid +Safeway +Sagan +Saginaw +Sagittarius +Sagittariuses +Sahara +Sahel +Saigon +Saiph +Sakai +Sakha +Sakhalin +Sakharov +Saki +Saks +Sal +Saladin +Salado +Salamis +Salas +Salazar +Salem +Salerno +Salinas +Salinger +Salisbury +Salish +Salk +Sallie +Sallust +Sally +Salome +Salton +Salvador +Salvadoran +Salvadorans +Salvadorian +Salvadorians +Salvatore +Salween +Salyut +Samantha +Samar +Samara +Samaritan +Samaritans +Samarkand +Sammie +Sammy +Samoa +Samoan +Samoset +Samoyed +Sampson +Samson +Samsonite +Samsung +Samuel +Samuelson +San +Sana +Sanchez +Sancho +Sand +Sandburg +Sanders +Sandinista +Sandoval +Sandra +Sandy +Sanford +Sanforized +Sang +Sanger +Sanhedrin +Sanka +Sankara +Sanskrit +Santa +Santana +Santayana +Santeria +Santiago +Santos +Sappho +Sapporo +Sara +Saracen +Saracens +Saragossa +Sarah +Sarajevo +Saran +Sarasota +Saratov +Sarawak +Sardinia +Sargasso +Sargent +Sargon +Sarnoff +Saroyan +Sarto +Sartre +Sasha +Saskatchewan +Saskatoon +Sasquatch +Sassanian +Sassoon +Satan +Satanism +Satanist +Saturday +Saturdays +Saturn +Saturnalia +Saudi +Saudis +Saul +Saunders +Saundra +Saussure +Savage +Savannah +Savior +Savonarola +Savoy +Savoyard +Sawyer +Saxon +Saxons +Saxony +Sayers +Sb +Scala +Scandinavia +Scandinavian +Scandinavians +Scaramouch +Scarborough +Scarlatti +Scheat +Schedar +Scheherazade +Schelling +Schenectady +Schiaparelli +Schick +Schiller +Schindler +Schlesinger +Schliemann +Schlitz +Schmidt +Schnabel +Schnauzer +Schneider +Schoenberg +Schopenhauer +Schrieffer +Schroeder +Schrödinger +Schubert +Schultz +Schulz +Schumann +Schumpeter +Schuyler +Schuylkill +Schwartz +Schwarzenegger +Schwarzkopf +Schweitzer +Schweppes +Schwinger +Schwinn +Scientology +Scipio +Scopes +Scorpio +Scorpios +Scorpius +Scorsese +Scot +Scotch +Scotches +Scotchman +Scotchmen +Scotia +Scotland +Scots +Scotsman +Scotsmen +Scotswoman +Scotswomen +Scott +Scottie +Scottish +Scottsdale +Scrabble +Scranton +Scriabin +Scribner +Scripture +Scriptures +Scrooge +Scruggs +Scud +Sculley +Scylla +Scythia +Scythian +Se +Seaborg +Seagram +Sean +Sears +Seattle +Sebastian +Seconal +Secretariat +Secretary +Seder +Seders +Sedna +Seebeck +Seeger +Sega +Segovia +Segre +Segundo +Seiko +Seine +Seinfeld +Sejong +Selassie +Selectric +Selena +Seleucid +Seleucus +Selim +Seljuk +Selkirk +Sellers +Selma +Selznick +Semarang +Seminole +Seminoles +Semiramis +Semite +Semites +Semitic +Semitics +Semtex +Senate +Senates +Sendai +Seneca +Senecas +Senegal +Senegalese +Senghor +Senior +Sennacherib +Sennett +Sensurround +Seoul +Sephardi +Sepoy +September +Septembers +Septuagint +Septuagints +Sequoya +Serb +Serbia +Serbian +Serbians +Serbs +Serena +Serengeti +Sergei +Sergio +Serpens +Serra +Serrano +Set +Seth +Seton +Seurat +Seuss +Sevastopol +Severn +Severus +Seville +Seward +Sextans +Sexton +Seychelles +Seyfert +Seymour +Shackleton +Shaffer +Shaka +Shakespeare +Shakespearean +Shana +Shane +Shanghai +Shankara +Shanna +Shannon +Shantung +Shapiro +Shari +Sharif +Sharlene +Sharon +Sharp +Sharpe +Sharron +Shasta +Shaula +Shaun +Shauna +Shavian +Shavuot +Shaw +Shawn +Shawna +Shawnee +Shcharansky +Shea +Sheba +Shebeli +Sheena +Sheetrock +Sheffield +Sheila +Shelby +Sheldon +Shelia +Shell +Shelley +Shelly +Shelton +Shenandoah +Shenyang +Sheol +Shepard +Shepherd +Sheppard +Sheratan +Sheraton +Sheree +Sheri +Sheridan +Sherlock +Sherman +Sherpa +Sherri +Sherrie +Sherry +Sherwood +Sheryl +Shetland +Shetlands +Shevardnadze +Shevat +Shields +Shijiazhuang +Shikoku +Shillong +Shiloh +Shinto +Shintoism +Shintoisms +Shintos +Shiraz +Shirley +Shiva +Shockley +Short +Shorthorn +Shoshone +Shostakovitch +Shrek +Shreveport +Shriner +Shropshire +Shula +Shylock +Shylockian +Si +Siam +Siamese +Sian +Sibelius +Siberia +Siberian +Sibyl +Sicilian +Sicilians +Sicily +Sid +Siddhartha +Sidney +Siegfried +Siemens +Sierpinski +Sigismund +Sigmund +Sigurd +Sihanouk +Sikh +Sikhism +Sikhs +Sikkim +Sikkimese +Sikorsky +Silas +Silurian +Silva +Silvia +Simenon +Simmental +Simmons +Simon +Simone +Simpson +Simpsons +Sims +Sinai +Sinatra +Sinclair +Sindbad +Sindhi +Singapore +Singer +Singh +Singleton +Sinhalese +Sinkiang +Sioux +Sirius +Sister +Sisters +Sistine +Sisyphean +Sisyphus +Siva +Sivan +Sjaelland +Skinner +Skippy +Skopje +Skye +Skylab +Skype +Slackware +Slashdot +Slater +Slav +Slavic +Slavonic +Slavs +Slinky +Sloan +Sloane +Slocum +Slovak +Slovakia +Slovakian +Slovaks +Slovenia +Slovenian +Slovenians +Slurpee +Small +Smetana +Smirnoff +Smith +Smithson +Smithsonian +Smokey +Smolensk +Smollett +Smuts +Sn +Snake +Snapple +Snead +Snell +Snickers +Snider +Snoopy +Snow +Snowbelt +Snyder +Soave +Socorro +Socrates +Socratic +Soddy +Sodom +Sofia +Soho +Solis +Solomon +Solon +Solzhenitsyn +Somali +Somalia +Somalian +Somalians +Somalis +Somme +Somoza +Son +Sondheim +Sondra +Songhai +Songhua +Sonia +Sonja +Sonny +Sontag +Sony +Sonya +Sophia +Sophie +Sophoclean +Sophocles +Sopwith +Sorbonne +Sosa +Soto +Souphanouvong +Sourceforge +Sousa +South +Southampton +Southeast +Southeasts +Southerner +Southerners +Southey +Souths +Southwest +Southwests +Soviet +Soweto +Soyinka +Soyuz +Spaatz +Spackle +Spahn +Spain +Spam +Spaniard +Spaniards +Spanish +Sparks +Sparta +Spartacus +Spartan +Spartans +Spears +Speer +Spence +Spencer +Spencerian +Spengler +Spenglerian +Spenser +Spenserian +Sperry +Sphinx +Spica +Spielberg +Spillane +Spinoza +Spinx +Spiro +Spirograph +Spitsbergen +Spitz +Spock +Spokane +Springfield +Springsteen +Sprint +Sprite +Sputnik +Squanto +Squibb +Srinagar +Srivijaya +Stacey +Staci +Stacie +Stacy +Stael +Stafford +StairMaster +Stalin +Stalingrad +Stalinist +Stallone +Stamford +Stan +Standish +Stanford +Stanislavsky +Stanley +Stanton +Staples +Starbucks +Stark +Starkey +Starr +Staten +Staubach +Steadicam +Steele +Stefan +Stefanie +Stein +Steinbeck +Steinem +Steiner +Steinmetz +Steinway +Stella +Stendhal +Stengel +Stephan +Stephanie +Stephen +Stephens +Stephenson +Sterling +Stern +Sterne +Sterno +Stetson +Steuben +Steve +Steven +Stevens +Stevenson +Stevie +Stewart +Stieglitz +Stilton +Stimson +Stine +Stirling +Stockhausen +Stockholm +Stockton +Stoic +Stoicism +Stokes +Stolichnaya +Stolypin +Stone +Stonehenge +Stoppard +Stout +Stowe +Strabo +Stradivarius +Strasbourg +Strauss +Stravinsky +Streisand +Strickland +Strindberg +Stromboli +Strong +Stu +Stuart +Stuarts +Studebaker +Stuttgart +Stuyvesant +Stygian +Styrofoam +Styrofoams +Styron +Styx +Suarez +Subaru +Sucre +Sucrets +Sudan +Sudanese +Sudetenland +Sudoku +Sudra +Sue +Suetonius +Suez +Suffolk +Sufi +Sufism +Suharto +Sui +Sukarno +Sukkot +Sukkoth +Sukkoths +Sulawesi +Suleiman +Sulla +Sullivan +Sumatra +Sumeria +Sumerian +Summer +Summers +Sumner +Sumter +Sunbeam +Sunbelt +Sundanese +Sundas +Sunday +Sundays +Sung +Sunkist +Sunni +Sunnyvale +Superbowl +Superfund +Superglue +Superior +Superman +Surabaya +Surat +Suriname +Surya +Susan +Susana +Susanna +Susanne +Susie +Susquehanna +Sussex +Sutherland +Sutton +Suva +Suwanee +Suzanne +Suzette +Suzhou +Suzuki +Suzy +Svalbard +Sven +Svengali +Swahili +Swahilis +Swammerdam +Swanee +Swansea +Swanson +Swazi +Swaziland +Swede +Sweden +Swedenborg +Swedes +Swedish +Sweeney +Sweet +Swift +Swinburne +Swiss +Swissair +Swisses +Switzerland +Sybil +Sydney +Sykes +Sylvester +Sylvia +Sylvie +Synge +Syracuse +Syria +Syriac +Syrian +Syrians +Szilard +Szymborska +Sèvres +T +TWA +Tabasco +Tabatha +Tabitha +Tabriz +Tacitus +Tacoma +Tad +Tadzhik +Taegu +Taejon +Taft +Tagalog +Tagore +Tagus +Tahiti +Tahitian +Tahitians +Tahoe +Taichung +Taine +Taipei +Taiping +Taiwan +Taiwanese +Taiyuan +Tajikistan +Taklamakan +Talbot +Taliban +Taliesin +Tallahassee +Tallchief +Talley +Talleyrand +Tallinn +Talmud +Talmudic +Talmuds +Tamara +Tameka +Tamera +Tamerlane +Tami +Tamika +Tamil +Tammany +Tammi +Tammie +Tammuz +Tammy +Tampa +Tampax +Tamra +Tamworth +Tancred +Taney +Tanganyika +Tangiers +Tangshan +Tania +Tanisha +Tanner +Tannhäuser +Tantalus +Tanya +Tanzania +Tanzanian +Tanzanians +Tao +Taoism +Taoisms +Taoist +Taoists +Tara +Tarantino +Tarawa +Tarazed +Tarbell +Target +Tarim +Tarkenton +Tarkington +Tartar +Tartars +Tartary +Tartuffe +Tarzan +Tasha +Tashkent +Tasman +Tasmania +Tasmanian +Tass +Tatar +Tatars +Tate +Tatum +Taurus +Tauruses +Tawney +Taylor +Tb +Tbilisi +Tchaikovsky +Teasdale +Technicolor +Tecumseh +Ted +Teddy +Teflon +Teflons +Tegucigalpa +Tehran +TelePrompter +Telemachus +Telemann +Teletype +Tell +Teller +Telugu +Tempe +Templar +Tennessee +Tennyson +Tenochtitlan +Teotihuacan +Terence +Teresa +Tereshkova +Teri +Terkel +Terpsichore +Terr +Terra +Terran +Terrance +Terrell +Terrence +Terri +Terrie +Terry +Tertiary +Tesla +Tess +Tessa +Tessie +Tet +Tethys +Tetons +Teutonic +Tevet +Texaco +Texan +Texans +Texas +Th +Thackeray +Thad +Thaddeus +Thai +Thailand +Thais +Thales +Thalia +Thames +Thanh +Thanksgiving +Thanksgivings +Thant +Thar +Tharp +Thatcher +Thea +Thebes +Theiler +Thelma +Themistocles +Theocritus +Theodora +Theodore +Theodoric +Theodosius +Theosophy +Theravada +Theresa +Therese +Thermopylae +Thermos +Theron +Theseus +Thespian +Thespis +Thessalonian +Thessaloníki +Thessaly +Thieu +Thimbu +Thomas +Thomism +Thomistic +Thompson +Thomson +Thor +Thorazine +Thoreau +Thornton +Thoroughbred +Thorpe +Thoth +Thrace +Thracian +Thucydides +Thule +Thunderbird +Thurber +Thurman +Thurmond +Thursday +Thursdays +Thutmose +Ti +Tia +Tianjin +Tiber +Tiberius +Tibet +Tibetan +Tibetans +Ticketmaster +Ticonderoga +Tide +Tienanmen +Tientsin +Tiffany +Tigris +Tijuana +Tillich +Tillman +Tilsit +Tim +Timbuktu +Timex +Timmy +Timon +Timor +Timothy +Timur +Timurid +Tina +Ting +Tinkerbell +Tinkertoy +Tinseltown +Tintoretto +Tippecanoe +Tipperary +Tiresias +Tisha +Tishri +Titan +Titania +Titanic +Titian +Titicaca +Tito +Titus +Tlaloc +Tlingit +Tobago +Toby +Tocantins +Tocqueville +Tod +Todd +Togo +Tojo +Tokay +Tokugawa +Tokyo +Toledo +Toledos +Tolkien +Tolstoy +Toltec +Tolyatti +Tom +Tomas +Tombaugh +Tomlin +Tommie +Tommy +Tompkins +Tomsk +Tonga +Tongan +Tongans +Toni +Tonia +Tonto +Tony +Tonya +Topeka +Topsy +Torah +Torahs +Tories +Toronto +Torquemada +Torrance +Torrens +Torres +Torricelli +Tortola +Tortuga +Torvalds +Tory +Tosca +Toscanini +Toshiba +Toto +Toulouse +Townes +Townsend +Toynbee +Toyoda +Toyota +Tracey +Traci +Tracie +Tracy +Trafalgar +Trailways +Trajan +Tran +Transcaucasia +Transvaal +Transylvania +Trappist +Travis +Travolta +Treasuries +Treasury +Treblinka +Trekkie +Trent +Trenton +Trevelyan +Trevino +Trevor +Trey +Triangulum +Triassic +Tricia +Trident +Trieste +Trimurti +Trina +Trinidad +Trinities +Trinity +Tripitaka +Tripoli +Trippe +Trisha +Tristan +Triton +Trobriand +Troilus +Trojan +Trojans +Trollope +Trondheim +Tropicana +Trotsky +Troy +Troyes +Truckee +Trudeau +Trudy +Truffaut +Trujillo +Truman +Trumbull +Trump +Truth +Tsimshian +Tsiolkovsky +Tsitsihar +Tsongkhapa +Tswana +Tuamotu +Tuareg +Tubman +Tucker +Tucson +Tucuman +Tudor +Tuesday +Tuesdays +Tulane +Tull +Tulsa +Tulsidas +Tums +Tungus +Tunguska +Tunis +Tunisia +Tunisian +Tunisians +Tunney +Tupi +Tupperware +Tupungato +Turgenev +Turin +Turing +Turk +Turkestan +Turkey +Turkish +Turkmenistan +Turks +Turner +Turpin +Tuscaloosa +Tuscan +Tuscany +Tuscarora +Tuscon +Tuskegee +Tussaud +Tut +Tutankhamen +Tutsi +Tutu +Tuvalu +Twain +Tweed +Tweedledee +Tweedledum +Twila +Twinkies +Twitter +Twizzlers +Ty +Tycho +Tylenol +Tyler +Tyndale +Tyndall +Tyre +Tyree +Tyrone +Tyson +U +UBS +UCLA +UPS +Ubangi +Ubuntu +Ucayali +Uccello +Udall +Ufa +Uganda +Ugandan +Ugandans +Uighur +Ujungpandang +Ukraine +Ukrainian +Ukrainians +Ulster +Ultrasuede +Ulyanovsk +Ulysses +Umbriel +Underwood +Ungava +Unicode +Unilever +Union +Unions +Uniroyal +Unitarian +Unitarianism +Unitarianisms +Unitarians +Unitas +Unukalhai +Upanishads +Updike +Upjohn +Upton +Ur +Ural +Urals +Urania +Uranus +Urban +Urdu +Urey +Uriah +Uriel +Uris +Urquhart +Ursa +Ursula +Ursuline +Uruguay +Uruguayan +Uruguayans +Urumqi +Usenet +Ustinov +Utah +Ute +Utopia +Utopian +Utopians +Utopias +Utrecht +Utrillo +Uzbek +Uzbekistan +Uzi +V +Vader +Vaduz +Val +Valarie +Valdez +Valencia +Valenti +Valentin +Valentine +Valentino +Valenzuela +Valeria +Valerian +Valerie +Valhalla +Valium +Valiums +Valkyrie +Valkyries +Valletta +Valois +Valparaiso +Valvoline +Valéry +Van +Vance +Vancouver +Vandal +Vanderbilt +Vandyke +Vanessa +Vang +Vanuatu +Vanzetti +Varanasi +Varese +Vargas +Vaseline +Vaselines +Vasquez +Vassar +Vatican +Vauban +Vaughan +Vaughn +Vazquez +Veblen +Veda +Vedanta +Vedas +Vega +Vegas +Vegemite +Vela +Velcro +Velcros +Velez +Velma +Velveeta +Velásquez +Velázquez +Venetian +Venetians +Venezuela +Venezuelan +Venezuelans +Venice +Venn +Ventolin +Venus +Venuses +Venusian +Vera +Veracruz +Verde +Verdi +Verdun +Verizon +Verlaine +Vermeer +Vermont +Vermonter +Vern +Verna +Verne +Vernon +Verona +Veronese +Veronica +Versailles +Vesalius +Vespasian +Vespucci +Vesta +Vesuvius +Viacom +Viagra +Vicente +Vichy +Vicki +Vickie +Vicksburg +Vicky +Victor +Victoria +Victorian +Victorians +Victrola +Vidal +Vienna +Viennese +Vientiane +Vietcong +Vietminh +Vietnam +Vietnamese +Vijayanagar +Vijayawada +Viking +Vikings +Vila +Villa +Villarreal +Villon +Vilma +Vilnius +Vilyui +Vince +Vincent +Vindemiatrix +Vinson +Viola +Violet +Virgie +Virgil +Virginia +Virginian +Virginians +Virgo +Virgos +Visa +Visakhapatnam +Visayans +Vishnu +Visigoth +Vistula +Vitim +Vito +Vitus +Vivaldi +Vivekananda +Vivian +Vivienne +Vlad +Vladimir +Vladivostok +Vlaminck +Vlasic +VoIP +Vogue +Volcker +Voldemort +Volga +Volgograd +Volkswagen +Volstead +Volta +Voltaire +Volvo +Vonda +Vonnegut +Voronezh +Vorster +Voyager +Vuitton +Vulcan +Vulgate +Vulgates +W +Wabash +Waco +Wade +Wagner +Wagnerian +Wahhabi +Waikiki +Waite +Wake +Waksman +Wald +Waldemar +Walden +Waldensian +Waldheim +Waldo +Waldorf +Wales +Walesa +Walgreen +Walker +Walkman +Wall +Wallace +Wallenstein +Waller +Wallis +Walloon +Walls +Walmart +Walpole +Walpurgisnacht +Walsh +Walt +Walter +Walters +Walton +Wanamaker +Wanda +Wang +Wankel +Ward +Ware +Warhol +Waring +Warner +Warren +Warsaw +Warwick +Wasatch +Washington +Washingtonian +Washingtonians +Wassermann +Waterbury +Waterford +Watergate +Waterloo +Waterloos +Waters +Watkins +Watson +Watt +Watteau +Watts +Watusi +Waugh +Wayne +Weaver +Webb +Weber +Webern +Webster +Websters +Weddell +Wedgwood +Wednesday +Wednesdays +Weeks +Wehrmacht +Wei +Weierstrass +Weill +Weinberg +Weiss +Weissmuller +Weizmann +Weldon +Welland +Weller +Welles +Wellington +Wellingtons +Wells +Welsh +Welshman +Welshmen +Wendell +Wendi +Wendy +Wesak +Wesley +Wesleyan +Wessex +Wesson +West +Western +Westerner +Westerns +Westinghouse +Westminster +Weston +Westphalia +Wests +Weyden +Wezen +Wharton +Wheaties +Wheatstone +Wheeler +Wheeling +Whig +Whigs +Whipple +Whirlpool +Whistler +Whitaker +White +Whitefield +Whitehall +Whitehead +Whitehorse +Whiteley +Whites +Whitfield +Whitley +Whitman +Whitney +Whitsunday +Whitsundays +Whittier +WiFi +Wicca +Wichita +Wiemar +Wiesel +Wiesenthal +Wiggins +Wigner +Wii +Wikileaks +Wikipedia +Wilberforce +Wilbert +Wilbur +Wilburn +Wilcox +Wilda +Wilde +Wilder +Wiles +Wiley +Wilford +Wilfred +Wilfredo +Wilhelm +Wilhelmina +Wilkerson +Wilkes +Wilkins +Wilkinson +Will +Willa +Willamette +Willard +Willemstad +William +Williams +Williamson +Willie +Willis +Willy +Wilma +Wilmer +Wilmington +Wilson +Wilsonian +Wilton +Wimbledon +Wimsey +Winchell +Winchester +Windbreaker +Windex +Windhoek +Windows +Windsor +Windsors +Windward +Winesap +Winfred +Winfrey +Winifred +Winkle +Winnebago +Winnie +Winnipeg +Winston +Winters +Winthrop +Wisconsin +Wisconsinite +Wisconsinites +Wise +Witt +Wittgenstein +Witwatersrand +Wm +Wobegon +Wodehouse +Wolf +Wolfe +Wolff +Wolfgang +Wollongong +Wollstonecraft +Wolsey +Wonder +Wonderbra +Wong +Wood +Woodard +Woodhull +Woodrow +Woods +Woodstock +Woodward +Woolf +Woolite +Woolongong +Woolworth +Wooster +Wooten +Worcester +Worcesters +Worcestershire +Wordsworth +Workman +Worms +Wotan +Wovoka +Wozniak +Wozzeck +Wrangell +Wren +Wright +Wrigley +Wroclaw +Wu +Wuhan +Wurlitzer +Wyatt +Wycherley +Wycliffe +Wyeth +Wylie +Wynn +Wyoming +Wyomingite +Wyomingites +X +XEmacs +Xanadu +Xanthippe +Xavier +Xe +Xenakis +Xenia +Xenophon +Xerox +Xeroxes +Xerxes +Xhosa +Xiaoping +Ximenes +Xingu +Xiongnu +Xmas +Xmases +Xochipilli +Xuzhou +Y +Yacc +Yahoo +Yahtzee +Yahweh +Yakima +Yakut +Yakutsk +Yale +Yalow +Yalta +Yalu +Yamagata +Yamaha +Yamoussoukro +Yang +Yangon +Yangtze +Yank +Yankee +Yankees +Yanks +Yaobang +Yaounde +Yaqui +Yaroslavl +Yataro +Yates +Yeager +Yeats +Yekaterinburg +Yellowknife +Yellowstone +Yeltsin +Yemen +Yemeni +Yemenis +Yenisei +Yerevan +Yerkes +Yesenia +Yevtushenko +Yggdrasil +Yiddish +Ymir +Yoda +Yoknapatawpha +Yoko +Yokohama +Yolanda +Yong +Yonkers +York +Yorkie +Yorkshire +Yorktown +Yoruba +Yosemite +Yossarian +YouTube +Young +Youngstown +Ypres +Ypsilanti +Yuan +Yucatan +Yugoslav +Yugoslavia +Yugoslavian +Yugoslavians +Yukon +Yule +Yules +Yuletide +Yuletides +Yunnan +Yuri +Yves +Yvette +Yvonne +Z +Zachariah +Zachary +Zachery +Zagreb +Zaire +Zairian +Zambezi +Zambia +Zambian +Zambians +Zamboni +Zamenhof +Zamora +Zane +Zanuck +Zanzibar +Zapata +Zaporozhye +Zapotec +Zappa +Zara +Zealand +Zebedee +Zechariah +Zedekiah +Zedong +Zeffirelli +Zeke +Zelig +Zelma +Zen +Zenger +Zeno +Zens +Zephaniah +Zephyrus +Zeppelin +Zest +Zeus +Zhengzhou +Zhivago +Zhukov +Zibo +Ziegfeld +Ziegler +Ziggy +Zimbabwe +Zimbabwean +Zimbabweans +Zimmerman +Zinfandel +Zion +Zionism +Zionisms +Zionist +Zionists +Zions +Ziploc +Zn +Zoe +Zola +Zollverein +Zoloft +Zomba +Zorn +Zoroaster +Zoroastrian +Zoroastrianism +Zoroastrianisms +Zorro +Zosma +Zr +Zsigmondy +Zubenelgenubi +Zubeneschamali +Zukor +Zulu +Zulus +Zuni +Zwingli +Zworykin +Zyrtec +Zyuganov +Zürich +a +aardvark +aardvarks +aback +abacus +abacuses +abaft +abalone +abalones +abandon +abandoned +abandoning +abandonment +abandons +abase +abased +abasement +abases +abash +abashed +abashes +abashing +abasing +abate +abated +abatement +abates +abating +abattoir +abattoirs +abbess +abbesses +abbey +abbeys +abbot +abbots +abbreviate +abbreviated +abbreviates +abbreviating +abbreviation +abbreviations +abbé +abbés +abdicate +abdicated +abdicates +abdicating +abdication +abdications +abdomen +abdomens +abdominal +abduct +abducted +abductee +abductees +abducting +abduction +abductions +abductor +abductors +abducts +abeam +abed +aberrant +aberration +aberrations +abet +abets +abetted +abetter +abetters +abetting +abettor +abettors +abeyance +abhor +abhorred +abhorrence +abhorrent +abhorring +abhors +abide +abides +abiding +abilities +ability +abject +abjectly +abjuration +abjurations +abjure +abjured +abjures +abjuring +ablative +ablatives +ablaze +able +abler +ablest +abloom +ablution +ablutions +ably +abnegate +abnegated +abnegates +abnegating +abnegation +abnormal +abnormalities +abnormality +abnormally +aboard +abode +abodes +abolish +abolished +abolishes +abolishing +abolition +abolitionist +abolitionists +abominable +abominably +abominate +abominated +abominates +abominating +abomination +abominations +aboriginal +aboriginals +aborigine +aborigines +abort +aborted +aborting +abortion +abortionist +abortionists +abortions +abortive +aborts +abound +abounded +abounding +abounds +about +above +aboveboard +abracadabra +abrade +abraded +abrades +abrading +abrasion +abrasions +abrasive +abrasively +abrasiveness +abrasives +abreast +abridge +abridged +abridges +abridging +abridgment +abridgments +abroad +abrogate +abrogated +abrogates +abrogating +abrogation +abrogations +abrupt +abrupter +abruptest +abruptly +abruptness +abscess +abscessed +abscesses +abscessing +abscissa +abscissas +abscond +absconded +absconding +absconds +absence +absences +absent +absented +absentee +absenteeism +absentees +absenting +absently +absents +absinthe +absolute +absolutely +absolutes +absolutest +absolution +absolutism +absolve +absolved +absolves +absolving +absorb +absorbed +absorbency +absorbent +absorbents +absorbing +absorbs +absorption +abstain +abstained +abstainer +abstainers +abstaining +abstains +abstemious +abstention +abstentions +abstinence +abstinent +abstract +abstracted +abstractedly +abstracting +abstraction +abstractions +abstractly +abstractness +abstractnesses +abstracts +abstruse +abstrusely +abstruseness +absurd +absurder +absurdest +absurdities +absurdity +absurdly +abundance +abundances +abundant +abundantly +abuse +abused +abuser +abusers +abuses +abusing +abusive +abusively +abusiveness +abut +abutment +abutments +abuts +abutted +abutting +abuzz +abysmal +abysmally +abyss +abysses +acacia +acacias +academia +academic +academical +academically +academician +academicians +academics +academies +academy +acanthus +acanthuses +accede +acceded +accedes +acceding +accelerate +accelerated +accelerates +accelerating +acceleration +accelerations +accelerator +accelerators +accent +accented +accenting +accents +accentuate +accentuated +accentuates +accentuating +accentuation +accept +acceptability +acceptable +acceptably +acceptance +acceptances +accepted +accepting +accepts +access +accessed +accesses +accessibility +accessible +accessibly +accessing +accession +accessioned +accessioning +accessions +accessories +accessory +accident +accidental +accidentally +accidentals +accidents +acclaim +acclaimed +acclaiming +acclaims +acclamation +acclimate +acclimated +acclimates +acclimating +acclimation +acclimatization +acclimatize +acclimatized +acclimatizes +acclimatizing +accolade +accolades +accommodate +accommodated +accommodates +accommodating +accommodation +accommodations +accompanied +accompanies +accompaniment +accompaniments +accompanist +accompanists +accompany +accompanying +accomplice +accomplices +accomplish +accomplished +accomplishes +accomplishing +accomplishment +accomplishments +accord +accordance +accorded +according +accordingly +accordion +accordions +accords +accost +accosted +accosting +accosts +account +accountability +accountable +accountancy +accountant +accountants +accounted +accounting +accounts +accouterments +accoutrements +accredit +accreditation +accredited +accrediting +accredits +accretion +accretions +accrual +accruals +accrue +accrued +accrues +accruing +acculturation +accumulate +accumulated +accumulates +accumulating +accumulation +accumulations +accumulative +accumulator +accuracy +accurate +accurately +accurateness +accursed +accusation +accusations +accusative +accusatives +accusatory +accuse +accused +accuser +accusers +accuses +accusing +accusingly +accustom +accustomed +accustoming +accustoms +ace +aced +acerbic +acerbity +aces +acetaminophen +acetate +acetates +acetic +acetone +acetylene +ache +ached +aches +achier +achiest +achievable +achieve +achieved +achievement +achievements +achiever +achievers +achieves +achieving +aching +achoo +achromatic +achy +acid +acidic +acidified +acidifies +acidify +acidifying +acidity +acidly +acids +acidulous +acing +acknowledge +acknowledged +acknowledgement +acknowledgements +acknowledges +acknowledging +acknowledgment +acknowledgments +acme +acmes +acne +acolyte +acolytes +aconite +aconites +acorn +acorns +acoustic +acoustical +acoustically +acoustics +acquaint +acquaintance +acquaintances +acquainted +acquainting +acquaints +acquiesce +acquiesced +acquiescence +acquiescent +acquiesces +acquiescing +acquirable +acquire +acquired +acquirement +acquires +acquiring +acquisition +acquisitions +acquisitive +acquisitiveness +acquit +acquits +acquittal +acquittals +acquitted +acquitting +acre +acreage +acreages +acres +acrid +acrider +acridest +acrimonious +acrimony +acrobat +acrobatic +acrobatics +acrobats +acronym +acronyms +across +acrostic +acrostics +acrylic +acrylics +act +acted +acting +actinium +action +actionable +actions +activate +activated +activates +activating +activation +active +actively +actives +activism +activist +activists +activities +activity +actor +actors +actress +actresses +acts +actual +actualities +actuality +actualization +actualize +actualized +actualizes +actualizing +actually +actuarial +actuaries +actuary +actuate +actuated +actuates +actuating +actuator +actuators +acuity +acumen +acupuncture +acupuncturist +acupuncturists +acute +acutely +acuteness +acuter +acutes +acutest +ad +adage +adages +adagio +adagios +adamant +adamantly +adapt +adaptability +adaptable +adaptation +adaptations +adapted +adapter +adapters +adapting +adaptive +adapts +add +added +addend +addenda +addends +addendum +adder +adders +addict +addicted +addicting +addiction +addictions +addictive +addicts +adding +addition +additional +additionally +additions +additive +additives +addle +addled +addles +addling +address +addressable +addressed +addressee +addressees +addresses +addressing +adds +adduce +adduced +adduces +adducing +adenoid +adenoidal +adenoids +adept +adeptly +adeptness +adepts +adequacy +adequate +adequately +adhere +adhered +adherence +adherent +adherents +adheres +adhering +adhesion +adhesive +adhesives +adiabatic +adieu +adieus +adipose +adiós +adjacent +adjacently +adjectival +adjectivally +adjective +adjectives +adjoin +adjoined +adjoining +adjoins +adjourn +adjourned +adjourning +adjournment +adjournments +adjourns +adjudge +adjudged +adjudges +adjudging +adjudicate +adjudicated +adjudicates +adjudicating +adjudication +adjudicator +adjudicators +adjunct +adjuncts +adjuration +adjurations +adjure +adjured +adjures +adjuring +adjust +adjustable +adjusted +adjuster +adjusters +adjusting +adjustment +adjustments +adjusts +adjutant +adjutants +adman +admen +administer +administered +administering +administers +administrate +administrated +administrates +administrating +administration +administrations +administrative +administratively +administrator +administrators +admirable +admirably +admiral +admirals +admiralty +admiration +admire +admired +admirer +admirers +admires +admiring +admiringly +admissibility +admissible +admission +admissions +admit +admits +admittance +admitted +admittedly +admitting +admixture +admixtures +admonish +admonished +admonishes +admonishing +admonishment +admonishments +admonition +admonitions +admonitory +ado +adobe +adobes +adolescence +adolescences +adolescent +adolescents +adopt +adopted +adopting +adoption +adoptions +adoptive +adopts +adorable +adorably +adoration +adore +adored +adores +adoring +adoringly +adorn +adorned +adorning +adornment +adornments +adorns +adrenal +adrenaline +adrenals +adrift +adroit +adroitly +adroitness +ads +adulate +adulated +adulates +adulating +adulation +adult +adulterant +adulterants +adulterate +adulterated +adulterates +adulterating +adulteration +adulterer +adulterers +adulteress +adulteresses +adulteries +adulterous +adultery +adulthood +adults +adumbrate +adumbrated +adumbrates +adumbrating +adumbration +advance +advanced +advancement +advancements +advances +advancing +advantage +advantaged +advantageous +advantageously +advantages +advantaging +advent +adventitious +advents +adventure +adventured +adventurer +adventurers +adventures +adventuresome +adventuress +adventuresses +adventuring +adventurous +adventurously +adverb +adverbial +adverbials +adverbs +adversarial +adversaries +adversary +adverse +adversely +adverser +adversest +adversities +adversity +advert +adverted +adverting +advertise +advertised +advertisement +advertisements +advertiser +advertisers +advertises +advertising +adverts +advice +advisability +advisable +advise +advised +advisedly +advisement +adviser +advisers +advises +advising +advisor +advisories +advisors +advisory +advocacy +advocate +advocated +advocates +advocating +adware +adz +adze +adzes +aegis +aeon +aeons +aerate +aerated +aerates +aerating +aeration +aerator +aerators +aerial +aerialist +aerialists +aerials +aerie +aeries +aerobatics +aerobic +aerobics +aerodynamic +aerodynamically +aerodynamics +aeronautical +aeronautics +aerosol +aerosols +aerospace +aesthete +aesthetes +aesthetic +aesthetically +aesthetics +afar +affability +affable +affably +affair +affairs +affect +affectation +affectations +affected +affecting +affection +affectionate +affectionately +affections +affects +affidavit +affidavits +affiliate +affiliated +affiliates +affiliating +affiliation +affiliations +affinities +affinity +affirm +affirmation +affirmations +affirmative +affirmatively +affirmatives +affirmed +affirming +affirms +affix +affixed +affixes +affixing +afflict +afflicted +afflicting +affliction +afflictions +afflicts +affluence +affluent +affluently +afford +affordable +afforded +affording +affords +afforest +afforestation +afforested +afforesting +afforests +affray +affrays +affront +affronted +affronting +affronts +afghan +afghans +aficionado +aficionados +afield +afire +aflame +afloat +aflutter +afoot +aforementioned +aforesaid +aforethought +afoul +afraid +afresh +aft +after +afterbirth +afterbirths +afterburner +afterburners +aftercare +aftereffect +aftereffects +afterglow +afterglows +afterlife +afterlives +aftermath +aftermaths +afternoon +afternoons +aftershave +aftershaves +aftershock +aftershocks +aftertaste +aftertastes +afterthought +afterthoughts +afterward +afterwards +afterword +afterwords +again +against +agape +agar +agate +agates +agave +age +aged +ageing +ageings +ageism +ageless +agencies +agency +agenda +agendas +agent +agents +ages +agglomerate +agglomerated +agglomerates +agglomerating +agglomeration +agglomerations +agglutinate +agglutinated +agglutinates +agglutinating +agglutination +agglutinations +aggrandize +aggrandized +aggrandizement +aggrandizes +aggrandizing +aggravate +aggravated +aggravates +aggravating +aggravation +aggravations +aggregate +aggregated +aggregates +aggregating +aggregation +aggregations +aggression +aggressive +aggressively +aggressiveness +aggressor +aggressors +aggrieve +aggrieved +aggrieves +aggrieving +aghast +agile +agilely +agility +aging +agings +agitate +agitated +agitates +agitating +agitation +agitations +agitator +agitators +agleam +aglitter +aglow +agnostic +agnosticism +agnostics +ago +agog +agonies +agonize +agonized +agonizes +agonizing +agonizingly +agony +agrarian +agrarians +agree +agreeable +agreeably +agreed +agreeing +agreement +agreements +agrees +agribusiness +agribusinesses +agricultural +agriculturalist +agriculturalists +agriculture +agronomist +agronomists +agronomy +aground +ague +ah +aha +ahead +ahem +ahoy +aid +aide +aided +aides +aiding +aids +ail +ailed +aileron +ailerons +ailing +ailment +ailments +ails +aim +aimed +aiming +aimless +aimlessly +aimlessness +aims +air +airborne +airbrush +airbrushed +airbrushes +airbrushing +aircraft +airdrop +airdropped +airdropping +airdrops +aired +airfare +airfares +airfield +airfields +airfoil +airfoils +airhead +airheads +airier +airiest +airily +airiness +airing +airings +airless +airlift +airlifted +airlifting +airlifts +airline +airliner +airliners +airlines +airmail +airmailed +airmailing +airmails +airman +airmen +airplane +airplanes +airport +airports +airs +airship +airships +airsick +airsickness +airspace +airstrip +airstrips +airtight +airwaves +airway +airways +airworthy +airy +aisle +aisles +ajar +akimbo +akin +alabaster +alacrity +alarm +alarmed +alarming +alarmingly +alarmist +alarmists +alarms +alas +alb +albacore +albacores +albatross +albatrosses +albeit +albino +albinos +albs +album +albumen +albumin +albums +alchemist +alchemists +alchemy +alcohol +alcoholic +alcoholics +alcoholism +alcohols +alcove +alcoves +alder +alderman +aldermen +alders +alderwoman +alderwomen +ale +alert +alerted +alerting +alertly +alertness +alerts +ales +alfalfa +alfresco +alga +algae +algebra +algebraic +algebraically +algebras +algorithm +algorithmic +algorithms +alias +aliased +aliases +aliasing +alibi +alibied +alibiing +alibis +alien +alienable +alienate +alienated +alienates +alienating +alienation +aliened +aliening +aliens +alight +alighted +alighting +alights +align +aligned +aligning +alignment +alignments +aligns +alike +alimentary +alimony +alive +alkali +alkalies +alkaline +alkalinity +alkalis +alkaloid +alkaloids +all +allay +allayed +allaying +allays +allegation +allegations +allege +alleged +allegedly +alleges +allegiance +allegiances +alleging +allegorical +allegorically +allegories +allegory +allegro +allegros +alleluia +alleluias +allergen +allergenic +allergens +allergic +allergies +allergist +allergists +allergy +alleviate +alleviated +alleviates +alleviating +alleviation +alley +alleys +alleyway +alleyways +alliance +alliances +allied +allies +alligator +alligators +alliteration +alliterations +alliterative +allocate +allocated +allocates +allocating +allocation +allocations +allot +allotment +allotments +allots +allotted +allotting +allover +allow +allowable +allowance +allowances +allowed +allowing +allows +alloy +alloyed +alloying +alloys +allspice +allude +alluded +alludes +alluding +allure +allured +allures +alluring +allusion +allusions +allusive +allusively +alluvial +alluvium +alluviums +ally +allying +almanac +almanacs +almighty +almond +almonds +almost +alms +aloe +aloes +aloft +aloha +alohas +alone +along +alongside +aloof +aloofness +aloud +alpaca +alpacas +alpha +alphabet +alphabetic +alphabetical +alphabetically +alphabetize +alphabetized +alphabetizes +alphabetizing +alphabets +alphanumeric +alphas +alpine +already +alright +also +altar +altars +alter +alterable +alteration +alterations +altercation +altercations +altered +altering +alternate +alternated +alternately +alternates +alternating +alternation +alternations +alternative +alternatively +alternatives +alternator +alternators +alters +although +altimeter +altimeters +altitude +altitudes +alto +altogether +altos +altruism +altruist +altruistic +altruistically +altruists +alum +aluminum +alumna +alumnae +alumni +alumnus +alums +always +am +amalgam +amalgamate +amalgamated +amalgamates +amalgamating +amalgamation +amalgamations +amalgams +amanuenses +amanuensis +amaranth +amaranths +amaryllis +amaryllises +amass +amassed +amasses +amassing +amateur +amateurish +amateurism +amateurs +amatory +amaze +amazed +amazement +amazes +amazing +amazingly +amazon +amazons +ambassador +ambassadorial +ambassadors +ambassadorship +ambassadorships +amber +ambergris +ambiance +ambiances +ambidextrous +ambidextrously +ambience +ambiences +ambient +ambiguities +ambiguity +ambiguous +ambiguously +ambition +ambitions +ambitious +ambitiously +ambitiousness +ambivalence +ambivalent +ambivalently +amble +ambled +ambles +ambling +ambrosia +ambulance +ambulances +ambulatories +ambulatory +ambush +ambushed +ambushes +ambushing +ameba +amebae +amebas +amebic +ameliorate +ameliorated +ameliorates +ameliorating +amelioration +amen +amenable +amend +amendable +amended +amending +amendment +amendments +amends +amenities +amenity +amethyst +amethysts +amiability +amiable +amiably +amicability +amicable +amicably +amid +amidships +amidst +amigo +amigos +amino +amir +amirs +amiss +amity +ammeter +ammeters +ammo +ammonia +ammunition +amnesia +amnesiac +amnesiacs +amnestied +amnesties +amnesty +amnestying +amniocenteses +amniocentesis +amoeba +amoebae +amoebas +amoebic +amok +among +amoral +amorality +amorally +amorous +amorously +amorousness +amorphous +amorphously +amorphousness +amortization +amortizations +amortize +amortized +amortizes +amortizing +amount +amounted +amounting +amounts +amour +amours +amp +amperage +ampere +amperes +ampersand +ampersands +amphetamine +amphetamines +amphibian +amphibians +amphibious +amphitheater +amphitheaters +ample +ampler +amplest +amplification +amplifications +amplified +amplifier +amplifiers +amplifies +amplify +amplifying +amplitude +amplitudes +amply +ampoule +ampoules +amps +ampule +ampules +amputate +amputated +amputates +amputating +amputation +amputations +amputee +amputees +amuck +amulet +amulets +amuse +amused +amusement +amusements +amuses +amusing +amusingly +an +anachronism +anachronisms +anachronistic +anaconda +anacondas +anaerobic +anagram +anagrams +anal +analgesia +analgesic +analgesics +analog +analogies +analogous +analogously +analogs +analogue +analogues +analogy +analyses +analysis +analyst +analysts +analytic +analytical +analyticalally +analytically +analyze +analyzed +analyzer +analyzers +analyzes +analyzing +anapest +anapests +anarchic +anarchically +anarchism +anarchist +anarchistic +anarchists +anarchy +anathema +anathemas +anatomic +anatomical +anatomically +anatomies +anatomist +anatomists +anatomy +ancestor +ancestors +ancestral +ancestress +ancestresses +ancestries +ancestry +anchor +anchorage +anchorages +anchored +anchoring +anchorite +anchorites +anchorman +anchormen +anchorpeople +anchorperson +anchorpersons +anchors +anchorwoman +anchorwomen +anchovies +anchovy +ancient +ancienter +ancientest +ancients +ancillaries +ancillary +and +andante +andantes +andiron +andirons +androgen +androgynous +android +androids +anecdotal +anecdote +anecdotes +anemia +anemic +anemometer +anemometers +anemone +anemones +anesthesia +anesthesiologist +anesthesiologists +anesthesiology +anesthetic +anesthetics +anesthetist +anesthetists +anesthetize +anesthetized +anesthetizes +anesthetizing +aneurysm +aneurysms +anew +angel +angelic +angelically +angels +anger +angered +angering +angers +angina +angioplasties +angioplasty +angiosperm +angiosperms +angle +angled +angler +anglers +angles +angleworm +angleworms +angling +angora +angoras +angrier +angriest +angrily +angry +angst +angstrom +angstroms +anguish +anguished +anguishes +anguishing +angular +angularities +angularity +animal +animals +animate +animated +animatedly +animates +animating +animation +animations +animator +animators +anime +animism +animist +animistic +animists +animosities +animosity +animus +anion +anions +anise +aniseed +ankh +ankhs +ankle +ankles +anklet +anklets +annals +anneal +annealed +annealing +anneals +annex +annexation +annexations +annexed +annexes +annexing +annihilate +annihilated +annihilates +annihilating +annihilation +annihilator +annihilators +anniversaries +anniversary +annotate +annotated +annotates +annotating +annotation +annotations +announce +announced +announcement +announcements +announcer +announcers +announces +announcing +annoy +annoyance +annoyances +annoyed +annoying +annoyingly +annoys +annual +annually +annuals +annuities +annuity +annul +annular +annulled +annulling +annulment +annulments +annuls +anode +anodes +anodyne +anodynes +anoint +anointed +anointing +anointment +anoints +anomalies +anomalous +anomaly +anon +anons +anonymity +anonymous +anonymously +anopheles +anorak +anoraks +anorexia +anorexic +anorexics +another +answer +answerable +answered +answering +answers +ant +antacid +antacids +antagonism +antagonisms +antagonist +antagonistic +antagonistically +antagonists +antagonize +antagonized +antagonizes +antagonizing +antarctic +ante +anteater +anteaters +antebellum +antecedent +antecedents +antechamber +antechambers +anted +antedate +antedated +antedates +antedating +antediluvian +anteing +antelope +antelopes +antenna +antennae +antennas +anterior +anteroom +anterooms +antes +anthem +anthems +anther +anthers +anthill +anthills +anthologies +anthologist +anthologists +anthologize +anthologized +anthologizes +anthologizing +anthology +anthracite +anthrax +anthropocentric +anthropoid +anthropoids +anthropological +anthropologist +anthropologists +anthropology +anthropomorphic +anthropomorphism +anti +antiabortion +antiaircraft +antibiotic +antibiotics +antibodies +antibody +antic +anticipate +anticipated +anticipates +anticipating +anticipation +anticipations +anticipatory +anticked +anticking +anticlimactic +anticlimax +anticlimaxes +anticlockwise +antics +anticyclone +anticyclones +antidepressant +antidepressants +antidote +antidotes +antifreeze +antigen +antigens +antihero +antiheroes +antihistamine +antihistamines +antiknock +antimatter +antimony +antiparticle +antiparticles +antipasti +antipasto +antipastos +antipathetic +antipathies +antipathy +antipersonnel +antiperspirant +antiperspirants +antiphonal +antiphonals +antipodes +antiquarian +antiquarians +antiquaries +antiquary +antiquate +antiquated +antiquates +antiquating +antique +antiqued +antiques +antiquing +antiquities +antiquity +antis +antiseptic +antiseptically +antiseptics +antislavery +antisocial +antitheses +antithesis +antithetical +antithetically +antitoxin +antitoxins +antitrust +antiviral +antivirals +antivirus +antiwar +antler +antlered +antlers +antonym +antonyms +ants +anus +anuses +anvil +anvils +anxieties +anxiety +anxious +anxiously +any +anybodies +anybody +anyhow +anymore +anyone +anyplace +anything +anythings +anytime +anyway +anywhere +aorta +aortas +apace +apart +apartheid +apartment +apartments +apathetic +apathetically +apathy +ape +aped +aperitif +aperitifs +aperture +apertures +apes +apex +apexes +aphasia +aphasic +aphasics +aphelia +aphelion +aphelions +aphid +aphids +aphorism +aphorisms +aphoristic +aphrodisiac +aphrodisiacs +apiaries +apiary +apiece +aping +aplenty +aplomb +apocalypse +apocalypses +apocalyptic +apocryphal +apogee +apogees +apolitical +apologetic +apologetically +apologia +apologias +apologies +apologist +apologists +apologize +apologized +apologizes +apologizing +apology +apoplectic +apoplexies +apoplexy +apostasies +apostasy +apostate +apostates +apostle +apostles +apostolic +apostrophe +apostrophes +apothecaries +apothecary +apotheoses +apotheosis +appall +appalled +appalling +appallingly +appalls +apparatus +apparatuses +apparel +appareled +appareling +apparels +apparent +apparently +apparition +apparitions +appeal +appealed +appealing +appeals +appear +appearance +appearances +appeared +appearing +appears +appease +appeased +appeasement +appeasements +appeaser +appeasers +appeases +appeasing +appellant +appellants +appellate +appellation +appellations +append +appendage +appendages +appendectomies +appendectomy +appended +appendices +appendicitis +appending +appendix +appendixes +appends +appertain +appertained +appertaining +appertains +appetite +appetites +appetizer +appetizers +appetizing +appetizingly +applaud +applauded +applauding +applauds +applause +apple +applejack +apples +applesauce +appliance +appliances +applicability +applicable +applicant +applicants +application +applications +applicator +applicators +applied +applies +appliqué +appliquéd +appliquéing +appliqués +apply +applying +appoint +appointed +appointee +appointees +appointing +appointive +appointment +appointments +appoints +apportion +apportioned +apportioning +apportionment +apportions +apposite +appositely +appositeness +apposition +appositive +appositives +appraisal +appraisals +appraise +appraised +appraiser +appraisers +appraises +appraising +appreciable +appreciably +appreciate +appreciated +appreciates +appreciating +appreciation +appreciations +appreciative +appreciatively +apprehend +apprehended +apprehending +apprehends +apprehension +apprehensions +apprehensive +apprehensively +apprehensiveness +apprentice +apprenticed +apprentices +apprenticeship +apprenticeships +apprenticing +apprise +apprised +apprises +apprising +approach +approachable +approached +approaches +approaching +approbation +approbations +appropriate +appropriated +appropriately +appropriateness +appropriates +appropriating +appropriation +appropriations +approval +approvals +approve +approved +approves +approving +approvingly +approximate +approximated +approximately +approximates +approximating +approximation +approximations +apps +appurtenance +appurtenances +apricot +apricots +apron +aprons +apropos +apse +apses +apt +apter +aptest +aptitude +aptitudes +aptly +aptness +aqua +aquaculture +aquamarine +aquamarines +aquanaut +aquanauts +aquaplane +aquaplaned +aquaplanes +aquaplaning +aquarium +aquariums +aquas +aquatic +aquatics +aquavit +aqueduct +aqueducts +aqueous +aquifer +aquifers +aquiline +arabesque +arabesques +arable +arachnid +arachnids +arbiter +arbiters +arbitrarily +arbitrariness +arbitrary +arbitrate +arbitrated +arbitrates +arbitrating +arbitration +arbitrator +arbitrators +arbor +arboreal +arboretum +arboretums +arbors +arborvitae +arborvitaes +arbutus +arbutuses +arc +arcade +arcades +arcane +arced +arch +archaeological +archaeologist +archaeologists +archaeology +archaic +archaically +archaism +archaisms +archangel +archangels +archbishop +archbishopric +archbishoprics +archbishops +archdeacon +archdeacons +archdiocese +archdioceses +archduke +archdukes +arched +archenemies +archenemy +archeological +archeologist +archeologists +archeology +archer +archers +archery +arches +archest +archetypal +archetype +archetypes +arching +archipelago +archipelagoes +archipelagos +architect +architects +architectural +architecturally +architecture +architectures +archive +archived +archives +archiving +archivist +archivists +archly +archness +archway +archways +arcing +arcs +arctic +arctics +ardent +ardently +ardor +ardors +arduous +arduously +arduousness +are +area +areas +arena +arenas +ares +argon +argosies +argosy +argot +argots +arguable +arguably +argue +argued +argues +arguing +argument +argumentation +argumentative +arguments +argyle +argyles +aria +arias +arid +aridity +aright +arise +arisen +arises +arising +aristocracies +aristocracy +aristocrat +aristocratic +aristocratically +aristocrats +arithmetic +arithmetical +arithmetically +ark +arks +arm +armada +armadas +armadillo +armadillos +armament +armaments +armature +armatures +armband +armbands +armchair +armchairs +armed +armful +armfuls +armhole +armholes +armies +arming +armistice +armistices +armlet +armlets +armor +armored +armorer +armorers +armories +armoring +armors +armory +armpit +armpits +armrest +armrests +arms +army +aroma +aromas +aromatherapy +aromatic +aromatics +arose +around +arousal +arouse +aroused +arouses +arousing +arpeggio +arpeggios +arraign +arraigned +arraigning +arraignment +arraignments +arraigns +arrange +arranged +arrangement +arrangements +arranger +arrangers +arranges +arranging +arrant +array +arrayed +arraying +arrays +arrears +arrest +arrested +arresting +arrests +arrival +arrivals +arrive +arrived +arrives +arriving +arrogance +arrogant +arrogantly +arrogate +arrogated +arrogates +arrogating +arrow +arrowhead +arrowheads +arrowroot +arrows +arroyo +arroyos +arsenal +arsenals +arsenic +arson +arsonist +arsonists +art +arterial +arteries +arteriosclerosis +artery +artful +artfully +artfulness +arthritic +arthritics +arthritis +arthropod +arthropods +artichoke +artichokes +article +articles +articulate +articulated +articulately +articulateness +articulates +articulating +articulation +articulations +artier +artiest +artifact +artifacts +artifice +artificer +artificers +artifices +artificial +artificiality +artificially +artillery +artisan +artisans +artist +artiste +artistes +artistic +artistically +artistry +artists +artless +artlessly +artlessness +arts +artsier +artsiest +artsy +artwork +artworks +arty +as +asbestos +ascend +ascendancy +ascendant +ascendants +ascended +ascending +ascends +ascension +ascensions +ascent +ascents +ascertain +ascertainable +ascertained +ascertaining +ascertains +ascetic +asceticism +ascetics +ascot +ascots +ascribable +ascribe +ascribed +ascribes +ascribing +ascription +aseptic +asexual +asexually +ash +ashamed +ashamedly +ashcan +ashcans +ashed +ashen +ashes +ashier +ashiest +ashing +ashore +ashram +ashrams +ashtray +ashtrays +ashy +aside +asides +asinine +asininities +asininity +ask +askance +asked +askew +asking +asks +aslant +asleep +asocial +asp +asparagus +aspartame +aspect +aspects +aspen +aspens +asperities +asperity +aspersion +aspersions +asphalt +asphalted +asphalting +asphalts +asphyxia +asphyxiate +asphyxiated +asphyxiates +asphyxiating +asphyxiation +asphyxiations +aspic +aspics +aspirant +aspirants +aspirate +aspirated +aspirates +aspirating +aspiration +aspirations +aspire +aspired +aspires +aspirin +aspiring +aspirins +asps +ass +assail +assailable +assailant +assailants +assailed +assailing +assails +assassin +assassinate +assassinated +assassinates +assassinating +assassination +assassinations +assassins +assault +assaulted +assaulter +assaulting +assaults +assay +assayed +assaying +assays +assemblage +assemblages +assemble +assembled +assembler +assemblers +assembles +assemblies +assembling +assembly +assemblyman +assemblymen +assemblywoman +assemblywomen +assent +assented +assenting +assents +assert +asserted +asserting +assertion +assertions +assertive +assertively +assertiveness +asserts +asses +assess +assessed +assesses +assessing +assessment +assessments +assessor +assessors +asset +assets +asseverate +asseverated +asseverates +asseverating +asshole +assholes +assiduous +assiduously +assiduousness +assign +assignable +assignation +assignations +assigned +assigning +assignment +assignments +assigns +assimilate +assimilated +assimilates +assimilating +assimilation +assist +assistance +assistant +assistants +assisted +assisting +assists +assize +assizes +associate +associated +associates +associating +association +associations +associative +assonance +assort +assorted +assorting +assortment +assortments +assorts +assuage +assuaged +assuages +assuaging +assume +assumed +assumes +assuming +assumption +assumptions +assurance +assurances +assure +assured +assuredly +assureds +assures +assuring +aster +asterisk +asterisked +asterisking +asterisks +astern +asteroid +asteroids +asters +asthma +asthmatic +asthmatics +astigmatic +astigmatism +astigmatisms +astir +astonish +astonished +astonishes +astonishing +astonishingly +astonishment +astound +astounded +astounding +astoundingly +astounds +astrakhan +astral +astray +astride +astringency +astringent +astringents +astrologer +astrologers +astrological +astrology +astronaut +astronautics +astronauts +astronomer +astronomers +astronomic +astronomical +astronomically +astronomy +astrophysicist +astrophysicists +astrophysics +astute +astutely +astuteness +astuter +astutest +asunder +asylum +asylums +asymmetric +asymmetrical +asymmetrically +asymmetry +asymptotic +asymptotically +asynchronous +asynchronously +at +atavism +atavistic +ate +atelier +ateliers +atheism +atheist +atheistic +atheists +atherosclerosis +athlete +athletes +athletic +athletically +athletics +atlas +atlases +atmosphere +atmospheres +atmospheric +atmospherically +atoll +atolls +atom +atomic +atomizer +atomizers +atoms +atonal +atonality +atone +atoned +atonement +atones +atoning +atop +atria +atrium +atrocious +atrociously +atrociousness +atrocities +atrocity +atrophied +atrophies +atrophy +atrophying +attach +attached +attaching +attachment +attachments +attaché +attachés +attack +attacked +attacker +attackers +attacking +attacks +attain +attainable +attained +attaining +attainment +attainments +attains +attar +attempt +attempted +attempting +attempts +attend +attendance +attendances +attendant +attendants +attended +attender +attending +attends +attention +attentions +attentive +attentively +attentiveness +attenuate +attenuated +attenuates +attenuating +attenuation +attest +attestation +attestations +attested +attesting +attests +attic +attics +attire +attired +attires +attiring +attitude +attitudes +attitudinize +attitudinized +attitudinizes +attitudinizing +attorney +attorneys +attract +attracted +attracting +attraction +attractions +attractive +attractively +attractiveness +attracts +attributable +attribute +attributed +attributes +attributing +attribution +attributions +attributive +attributively +attributives +attrition +attune +attuned +attunes +attuning +atwitter +atypical +atypically +auburn +auction +auctioned +auctioneer +auctioneers +auctioning +auctions +audacious +audaciously +audaciousness +audacity +audibility +audible +audibles +audibly +audience +audiences +audio +audiophile +audiophiles +audios +audiovisual +audit +audited +auditing +audition +auditioned +auditioning +auditions +auditor +auditorium +auditoriums +auditors +auditory +audits +auger +augers +aught +aughts +augment +augmentation +augmentations +augmented +augmenting +augments +augur +augured +auguries +auguring +augurs +augury +august +auguster +augustest +auk +auks +aunt +aunts +aura +aural +aurally +auras +aureola +aureole +aureoles +auricle +auricles +auspice +auspices +auspicious +auspiciously +auspiciousness +austere +austerely +austerer +austerest +austerities +austerity +authentic +authentically +authenticate +authenticated +authenticates +authenticating +authentication +authentications +authenticity +author +authored +authoring +authoritarian +authoritarianism +authoritarians +authoritative +authoritatively +authoritativeness +authorities +authority +authorization +authorizations +authorize +authorized +authorizes +authorizing +authors +authorship +autism +autistic +auto +autobiographical +autobiographies +autobiography +autocracies +autocracy +autocrat +autocratic +autocratically +autocrats +autograph +autographed +autographing +autographs +autoimmune +automate +automated +automates +automatic +automatically +automatics +automating +automation +automaton +automatons +automobile +automobiled +automobiles +automobiling +automotive +autonomous +autonomously +autonomy +autopilot +autopilots +autopsied +autopsies +autopsy +autopsying +autos +autoworker +autoworkers +autumn +autumnal +autumns +auxiliaries +auxiliary +avail +availability +available +availed +availing +avails +avalanche +avalanches +avarice +avaricious +avariciously +avast +avatar +avatars +avenge +avenged +avenger +avengers +avenges +avenging +avenue +avenues +aver +average +averaged +averages +averaging +averred +averring +avers +averse +aversion +aversions +avert +averted +averting +averts +avian +aviaries +aviary +aviation +aviator +aviators +aviatrices +aviatrix +aviatrixes +avid +avidity +avidly +avionics +avocado +avocados +avocation +avocations +avoid +avoidable +avoidably +avoidance +avoided +avoiding +avoids +avoirdupois +avow +avowal +avowals +avowed +avowedly +avowing +avows +avuncular +await +awaited +awaiting +awaits +awake +awaken +awakened +awakening +awakenings +awakens +awakes +awaking +award +awarded +awarding +awards +aware +awareness +awash +away +awe +awed +aweigh +awes +awesome +awesomely +awestruck +awful +awfuller +awfullest +awfully +awfulness +awhile +awing +awkward +awkwarder +awkwardest +awkwardly +awkwardness +awl +awls +awning +awnings +awoke +awoken +awol +awry +ax +axed +axes +axial +axing +axiom +axiomatic +axiomatically +axioms +axis +axle +axles +axon +axons +ayatollah +ayatollahs +aye +ayes +azalea +azaleas +azimuth +azimuths +azure +azures +b +baa +baaed +baaing +baas +babble +babbled +babbler +babblers +babbles +babbling +babe +babel +babels +babes +babied +babier +babies +babiest +baboon +baboons +babushka +babushkas +baby +babyhood +babying +babyish +babysat +babysit +babysits +babysitter +babysitters +babysitting +baccalaureate +baccalaureates +bacchanal +bacchanalian +bacchanalians +bacchanals +bachelor +bachelors +bacilli +bacillus +back +backache +backaches +backbit +backbite +backbiter +backbiters +backbites +backbiting +backbitten +backboard +backboards +backbone +backbones +backbreaking +backdate +backdated +backdates +backdating +backdrop +backdrops +backed +backer +backers +backfield +backfields +backfire +backfired +backfires +backfiring +backgammon +background +backgrounds +backhand +backhanded +backhanding +backhands +backhoe +backhoes +backing +backings +backlash +backlashes +backless +backlog +backlogged +backlogging +backlogs +backpack +backpacked +backpacker +backpackers +backpacking +backpacks +backpedal +backpedaled +backpedaling +backpedals +backrest +backrests +backs +backside +backsides +backslapper +backslappers +backslash +backslashes +backslid +backslide +backslider +backsliders +backslides +backsliding +backspace +backspaced +backspaces +backspacing +backspin +backstabbing +backstage +backstairs +backstop +backstopped +backstopping +backstops +backstories +backstory +backstretch +backstretches +backstroke +backstroked +backstrokes +backstroking +backtrack +backtracked +backtracking +backtracks +backup +backups +backward +backwardness +backwards +backwash +backwater +backwaters +backwoods +backyard +backyards +bacon +bacteria +bacterial +bacteriological +bacteriologist +bacteriologists +bacteriology +bacterium +bad +badder +baddest +bade +badge +badger +badgered +badgering +badgers +badges +badinage +badlands +badly +badminton +badmouth +badmouthed +badmouthing +badmouths +badness +baffle +baffled +bafflement +baffles +baffling +bag +bagatelle +bagatelles +bagel +bagels +baggage +bagged +baggier +baggiest +bagginess +bagging +baggy +bagpipe +bagpipes +bags +bah +bail +bailed +bailiff +bailiffs +bailing +bailiwick +bailiwicks +bailout +bailouts +bails +bait +baited +baiting +baits +baize +bake +baked +baker +bakeries +bakers +bakery +bakes +baking +balalaika +balalaikas +balance +balanced +balances +balancing +balconies +balcony +bald +balded +balder +balderdash +baldest +balding +baldly +baldness +balds +bale +baled +baleen +baleful +balefully +bales +baling +balk +balked +balkier +balkiest +balking +balks +balky +ball +ballad +balladeer +balladeers +ballads +ballast +ballasted +ballasting +ballasts +balled +ballerina +ballerinas +ballet +ballets +balling +ballistic +ballistics +balloon +ballooned +ballooning +balloonist +balloonists +balloons +ballot +balloted +balloting +ballots +ballpark +ballparks +ballplayer +ballplayers +ballpoint +ballpoints +ballroom +ballrooms +balls +ballsier +ballsiest +ballsy +ballyhoo +ballyhooed +ballyhooing +ballyhoos +balm +balmier +balmiest +balminess +balms +balmy +baloney +balsa +balsam +balsams +balsas +baluster +balusters +balustrade +balustrades +bamboo +bamboos +bamboozle +bamboozled +bamboozles +bamboozling +ban +banal +banalities +banality +banana +bananas +band +bandage +bandaged +bandages +bandaging +bandana +bandanas +bandanna +bandannas +banded +bandied +bandier +bandies +bandiest +banding +bandit +banditry +bandits +bandoleer +bandoleers +bandolier +bandoliers +bands +bandstand +bandstands +bandwagon +bandwagons +bandwidth +bandy +bandying +bane +baneful +banes +bang +banged +banging +bangle +bangles +bangs +bani +banish +banished +banishes +banishing +banishment +banister +banisters +banjo +banjoist +banjoists +banjos +bank +bankbook +bankbooks +banked +banker +bankers +banking +banknote +banknotes +bankroll +bankrolled +bankrolling +bankrolls +bankrupt +bankruptcies +bankruptcy +bankrupted +bankrupting +bankrupts +banks +banned +banner +banners +banning +banns +banquet +banqueted +banqueting +banquets +bans +banshee +banshees +bantam +bantams +bantamweight +bantamweights +banter +bantered +bantering +banters +banyan +banyans +baobab +baobabs +baptism +baptismal +baptisms +baptist +baptisteries +baptistery +baptistries +baptistry +baptists +baptize +baptized +baptizes +baptizing +bar +barb +barbacoa +barbarian +barbarians +barbaric +barbarism +barbarisms +barbarities +barbarity +barbarous +barbarously +barbecue +barbecued +barbecues +barbecuing +barbed +barbell +barbells +barber +barbered +barbering +barberries +barberry +barbers +barbershop +barbershops +barbing +barbiturate +barbiturates +barbs +bard +bards +bare +bareback +bared +barefaced +barefoot +barefooted +barehanded +bareheaded +barely +bareness +barer +bares +barest +barf +barfed +barfing +barfs +bargain +bargained +bargainer +bargaining +bargains +barge +barged +barges +barging +baring +barista +baristas +baritone +baritones +barium +bark +barked +barker +barkers +barking +barks +barley +barmaid +barmaids +barman +barn +barnacle +barnacles +barns +barnstorm +barnstormed +barnstorming +barnstorms +barnyard +barnyards +barometer +barometers +barometric +baron +baroness +baronesses +baronet +baronets +baronial +barons +baroque +barrack +barracks +barracuda +barracudas +barrage +barraged +barrages +barraging +barred +barrel +barreled +barreling +barrelled +barrelling +barrels +barren +barrener +barrenest +barrenness +barrens +barrette +barrettes +barricade +barricaded +barricades +barricading +barrier +barriers +barring +barrings +barrio +barrios +barrister +barristers +barroom +barrooms +barrow +barrows +bars +bartender +bartenders +barter +bartered +bartering +barters +basal +basalt +base +baseball +baseballs +baseboard +baseboards +based +baseless +baseline +baselines +basely +baseman +basemen +basement +basements +baseness +baser +bases +basest +bash +bashed +bashes +bashful +bashfully +bashfulness +bashing +basic +basically +basics +basil +basilica +basilicas +basin +basing +basins +basis +bask +basked +basket +basketball +basketballs +baskets +basking +basks +bass +basses +bassinet +bassinets +bassist +bassists +basso +bassoon +bassoonist +bassoonists +bassoons +bassos +bast +bastard +bastardize +bastardized +bastardizes +bastardizing +bastards +baste +basted +bastes +basting +bastion +bastions +bat +batch +batched +batches +batching +bate +bated +bates +bath +bathe +bathed +bather +bathers +bathes +bathhouse +bathhouses +bathing +bathmat +bathmats +bathos +bathrobe +bathrobes +bathroom +bathrooms +baths +bathtub +bathtubs +batik +batiks +bating +baton +batons +bats +batsman +batsmen +battalion +battalions +batted +batten +battened +battening +battens +batter +battered +batteries +battering +batters +battery +battier +battiest +batting +battle +battled +battlefield +battlefields +battleground +battlegrounds +battlement +battlements +battles +battleship +battleships +battling +batty +bauble +baubles +baud +bauxite +bawdier +bawdiest +bawdily +bawdiness +bawdy +bawl +bawled +bawling +bawls +bay +bayberries +bayberry +bayed +baying +bayonet +bayoneted +bayoneting +bayonets +bayou +bayous +bays +bazaar +bazaars +bazillion +bazillions +bazooka +bazookas +be +beach +beachcomber +beachcombers +beached +beaches +beachhead +beachheads +beaching +beacon +beacons +bead +beaded +beadier +beadiest +beading +beads +beady +beagle +beagles +beak +beaked +beaker +beakers +beaks +beam +beamed +beaming +beams +bean +beanbag +beanbags +beaned +beaning +beans +bear +bearable +beard +bearded +bearding +beards +bearer +bearers +bearing +bearings +bearish +bears +bearskin +bearskins +beast +beastlier +beastliest +beastliness +beastly +beasts +beat +beaten +beater +beaters +beatific +beatification +beatifications +beatified +beatifies +beatify +beatifying +beating +beatings +beatitude +beatitudes +beatnik +beatniks +beats +beau +beaus +beauteous +beauteously +beautician +beauticians +beauties +beautification +beautified +beautifier +beautifiers +beautifies +beautiful +beautifully +beautify +beautifying +beauty +beaux +beaver +beavered +beavering +beavers +bebop +bebops +becalm +becalmed +becalming +becalms +became +because +beck +beckon +beckoned +beckoning +beckons +becks +become +becomes +becoming +becomingly +bed +bedazzle +bedazzled +bedazzles +bedazzling +bedbug +bedbugs +bedclothes +bedded +bedder +bedding +bedeck +bedecked +bedecking +bedecks +bedevil +bedeviled +bedeviling +bedevilment +bedevils +bedfellow +bedfellows +bedlam +bedlams +bedpan +bedpans +bedraggle +bedraggled +bedraggles +bedraggling +bedridden +bedrock +bedrocks +bedroll +bedrolls +bedroom +bedrooms +beds +bedside +bedsides +bedsore +bedsores +bedspread +bedspreads +bedstead +bedsteads +bedtime +bedtimes +bee +beech +beeches +beechnut +beechnuts +beef +beefburger +beefed +beefier +beefiest +beefing +beefs +beefsteak +beefsteaks +beefy +beehive +beehives +beekeeper +beekeepers +beekeeping +beeline +beelines +been +beep +beeped +beeper +beepers +beeping +beeps +beer +beers +bees +beeswax +beet +beetle +beetled +beetles +beetling +beets +beeves +befall +befallen +befalling +befalls +befell +befit +befits +befitted +befitting +befog +befogged +befogging +befogs +before +beforehand +befoul +befouled +befouling +befouls +befriend +befriended +befriending +befriends +befuddle +befuddled +befuddles +befuddling +beg +began +begat +beget +begets +begetting +beggar +beggared +beggaring +beggarly +beggars +begged +begging +begin +beginner +beginners +beginning +beginnings +begins +begone +begonia +begonias +begot +begotten +begrudge +begrudged +begrudges +begrudging +begrudgingly +begs +beguile +beguiled +beguiles +beguiling +beguilingly +begun +behalf +behalves +behave +behaved +behaves +behaving +behavior +behavioral +behead +beheaded +beheading +beheads +beheld +behemoth +behemoths +behest +behests +behind +behinds +behold +beholden +beholder +beholders +beholding +beholds +behoove +behooved +behooves +behooving +beige +being +beings +belabor +belabored +belaboring +belabors +belated +belatedly +belay +belayed +belaying +belays +belch +belched +belches +belching +beleaguer +beleaguered +beleaguering +beleaguers +belfries +belfry +belie +belied +belief +beliefs +belies +believable +believe +believed +believer +believers +believes +believing +belittle +belittled +belittles +belittling +bell +belladonna +bellboy +bellboys +belle +belled +belles +bellhop +bellhops +bellicose +bellicosity +bellied +bellies +belligerence +belligerency +belligerent +belligerently +belligerents +belling +bellow +bellowed +bellowing +bellows +bells +bellwether +bellwethers +belly +bellyache +bellyached +bellyaches +bellyaching +bellybutton +bellybuttons +bellyful +bellyfuls +bellying +belong +belonged +belonging +belongings +belongs +beloved +beloveds +below +belt +belted +belting +belts +beltway +beltways +belying +bemoan +bemoaned +bemoaning +bemoans +bemuse +bemused +bemuses +bemusing +bench +benched +benches +benching +benchmark +benchmarks +bend +bender +bending +bends +beneath +benediction +benedictions +benefaction +benefactions +benefactor +benefactors +benefactress +benefactresses +benefice +beneficence +beneficent +beneficently +benefices +beneficial +beneficially +beneficiaries +beneficiary +benefit +benefited +benefiting +benefits +benevolence +benevolences +benevolent +benevolently +benighted +benign +benignly +bent +bents +benumb +benumbed +benumbing +benumbs +benzene +bequeath +bequeathed +bequeathing +bequeaths +bequest +bequests +berate +berated +berates +berating +bereave +bereaved +bereavement +bereavements +bereaves +bereaving +bereft +beret +berets +berg +bergs +beriberi +berm +berms +berried +berries +berry +berrying +berserk +berth +berthed +berthing +berths +beryl +beryllium +beryls +beseech +beseeches +beseeching +beset +besets +besetting +beside +besides +besiege +besieged +besieger +besiegers +besieges +besieging +besmirch +besmirched +besmirches +besmirching +besom +besoms +besot +besots +besotted +besotting +besought +bespeak +bespeaking +bespeaks +bespoke +bespoken +best +bested +bestial +bestiality +bestiaries +bestiary +besting +bestir +bestirred +bestirring +bestirs +bestow +bestowal +bestowals +bestowed +bestowing +bestows +bestridden +bestride +bestrides +bestriding +bestrode +bests +bestseller +bestsellers +bet +beta +betake +betaken +betakes +betaking +betas +betcha +bethink +bethinking +bethinks +bethought +betide +betided +betides +betiding +betoken +betokened +betokening +betokens +betook +betray +betrayal +betrayals +betrayed +betrayer +betrayers +betraying +betrays +betroth +betrothal +betrothals +betrothed +betrothing +betroths +bets +better +bettered +bettering +betterment +betters +betting +bettor +bettors +between +betwixt +bevel +beveled +beveling +bevels +beverage +beverages +bevies +bevy +bewail +bewailed +bewailing +bewails +beware +bewared +bewares +bewaring +bewilder +bewildered +bewildering +bewilderment +bewilders +bewitch +bewitched +bewitches +bewitching +beyond +biannual +biannually +bias +biased +biases +biasing +biassed +biassing +biathlon +biathlons +bib +bible +bibles +biblical +bibliographer +bibliographers +bibliographic +bibliographical +bibliographies +bibliography +bibliophile +bibliophiles +bibs +bibulous +bicameral +bicentennial +bicentennials +bicep +biceps +bicker +bickered +bickering +bickers +bicuspid +bicuspids +bicycle +bicycled +bicycles +bicycling +bicyclist +bicyclists +bid +bidden +bidder +bidders +biddies +bidding +biddy +bide +bides +bidet +bidets +biding +bidirectional +bids +biennial +biennially +biennials +bier +biers +bifocal +bifocals +bifurcate +bifurcated +bifurcates +bifurcating +bifurcation +bifurcations +big +bigamist +bigamists +bigamous +bigamy +bigger +biggest +biggie +biggies +bighearted +bighorn +bighorns +bight +bights +bigmouth +bigmouths +bigness +bigot +bigoted +bigotries +bigotry +bigots +bigwig +bigwigs +bike +biked +biker +bikers +bikes +biking +bikini +bikinis +bilateral +bilaterally +bile +bilge +bilges +bilingual +bilinguals +bilious +bilk +bilked +bilking +bilks +bill +billboard +billboards +billed +billet +billeted +billeting +billets +billfold +billfolds +billiards +billies +billing +billings +billion +billionaire +billionaires +billions +billionth +billionths +billow +billowed +billowing +billows +billowy +bills +billy +bimbo +bimbos +bimonthlies +bimonthly +bin +binaries +binary +bind +binder +binderies +binders +bindery +binding +bindings +binds +binge +binged +bingeing +binges +binging +bingo +binnacle +binnacles +binned +binning +binocular +binoculars +binomial +binomials +bins +biochemical +biochemicals +biochemist +biochemistry +biochemists +biodegradable +biodiversity +biofeedback +biographer +biographers +biographical +biographies +biography +biological +biologically +biologist +biologists +biology +biomedical +bionic +biophysicist +biophysicists +biophysics +biopsied +biopsies +biopsy +biopsying +biorhythm +biorhythms +biosphere +biospheres +biotechnology +bipartisan +bipartite +biped +bipedal +bipeds +biplane +biplanes +bipolar +biracial +birch +birched +birches +birching +bird +birdbath +birdbaths +birdbrained +birdcage +birdcages +birded +birdhouse +birdhouses +birdie +birdied +birdieing +birdies +birding +birds +birdseed +birdwatcher +birdwatchers +biretta +birettas +birth +birthday +birthdays +birthed +birther +birthers +birthing +birthmark +birthmarks +birthplace +birthplaces +birthrate +birthrates +birthright +birthrights +births +birthstone +birthstones +biscuit +biscuits +bisect +bisected +bisecting +bisection +bisections +bisector +bisectors +bisects +bisexual +bisexuality +bisexuals +bishop +bishopric +bishoprics +bishops +bismuth +bison +bisque +bistro +bistros +bit +bitch +bitched +bitches +bitchier +bitchiest +bitching +bitchy +bitcoin +bitcoins +bite +bites +biting +bitingly +bitmap +bits +bitten +bitter +bitterer +bitterest +bitterly +bittern +bitterness +bitterns +bitters +bittersweet +bittersweets +bitumen +bituminous +bivalve +bivalves +bivouac +bivouacked +bivouacking +bivouacs +biweeklies +biweekly +bizarre +bizarrely +blab +blabbed +blabbermouth +blabbermouths +blabbing +blabs +black +blackball +blackballed +blackballing +blackballs +blackberries +blackberry +blackberrying +blackbird +blackbirds +blackboard +blackboards +blackcurrant +blacked +blacken +blackened +blackening +blackens +blacker +blackest +blackguard +blackguards +blackhead +blackheads +blacking +blackish +blackjack +blackjacked +blackjacking +blackjacks +blacklist +blacklisted +blacklisting +blacklists +blackmail +blackmailed +blackmailer +blackmailers +blackmailing +blackmails +blackness +blackout +blackouts +blacks +blacksmith +blacksmiths +blackthorn +blackthorns +blacktop +blacktopped +blacktopping +blacktops +bladder +bladders +blade +blades +blah +blame +blamed +blameless +blamelessly +blamer +blames +blameworthy +blaming +blanch +blanched +blanches +blanching +blancmange +bland +blander +blandest +blandishment +blandishments +blandly +blandness +blank +blanked +blanker +blankest +blanket +blanketed +blanketing +blankets +blanking +blankly +blankness +blanks +blare +blared +blares +blaring +blarney +blarneyed +blarneying +blarneys +blaspheme +blasphemed +blasphemer +blasphemers +blasphemes +blasphemies +blaspheming +blasphemous +blasphemously +blasphemy +blast +blasted +blaster +blasters +blasting +blastoff +blastoffs +blasts +blasé +blatant +blatantly +blaze +blazed +blazer +blazers +blazes +blazing +blazon +blazoned +blazoning +blazons +bleach +bleached +bleacher +bleachers +bleaches +bleaching +bleak +bleaker +bleakest +bleakly +bleakness +blearier +bleariest +blearily +bleary +bleat +bleated +bleating +bleats +bled +bleed +bleeder +bleeders +bleeding +bleeds +bleep +bleeped +bleeping +bleeps +blemish +blemished +blemishes +blemishing +blench +blenched +blenches +blenching +blend +blended +blender +blenders +blending +blends +bless +blessed +blessedly +blessedness +blesses +blessing +blessings +blew +blight +blighted +blighting +blights +blimp +blimps +blind +blinded +blinder +blinders +blindest +blindfold +blindfolded +blindfolding +blindfolds +blinding +blindingly +blindly +blindness +blinds +blindside +blindsided +blindsides +blindsiding +bling +blink +blinked +blinker +blinkered +blinkering +blinkers +blinking +blinks +blintz +blintze +blintzes +blip +blips +bliss +blissful +blissfully +blissfulness +blister +blistered +blistering +blisters +blithe +blithely +blither +blithest +blitz +blitzed +blitzes +blitzing +blizzard +blizzards +bloat +bloated +bloating +bloats +blob +blobbed +blobbing +blobs +bloc +block +blockade +blockaded +blockades +blockading +blockage +blockages +blockbuster +blockbusters +blocked +blockhead +blockheads +blockhouse +blockhouses +blocking +blocks +blocs +blog +blogged +blogger +bloggers +blogging +blogs +blond +blonde +blonder +blondes +blondest +blondness +blonds +blood +bloodbath +bloodbaths +bloodcurdling +blooded +bloodhound +bloodhounds +bloodied +bloodier +bloodies +bloodiest +blooding +bloodless +bloodlessly +bloodmobile +bloodmobiles +bloods +bloodshed +bloodshot +bloodstain +bloodstained +bloodstains +bloodstream +bloodstreams +bloodsucker +bloodsuckers +bloodthirstier +bloodthirstiest +bloodthirstiness +bloodthirsty +bloody +bloodying +bloom +bloomed +bloomer +bloomers +blooming +blooms +blooper +bloopers +blossom +blossomed +blossoming +blossoms +blot +blotch +blotched +blotches +blotchier +blotchiest +blotching +blotchy +blots +blotted +blotter +blotters +blotting +blouse +bloused +blouses +blousing +blow +blower +blowers +blowgun +blowguns +blowing +blown +blowout +blowouts +blows +blowsier +blowsiest +blowsy +blowtorch +blowtorches +blowup +blowups +blowzier +blowziest +blowzy +blubber +blubbered +blubbering +blubbers +bludgeon +bludgeoned +bludgeoning +bludgeons +blue +bluebell +bluebells +blueberries +blueberry +bluebird +bluebirds +bluebottle +bluebottles +blued +bluefish +bluefishes +bluegrass +blueing +bluejacket +bluejackets +bluejay +bluejays +bluenose +bluenoses +blueprint +blueprinted +blueprinting +blueprints +bluer +blues +bluest +bluestocking +bluestockings +bluff +bluffed +bluffer +bluffers +bluffest +bluffing +bluffs +bluing +bluish +blunder +blunderbuss +blunderbusses +blundered +blunderer +blunderers +blundering +blunders +blunt +blunted +blunter +bluntest +blunting +bluntly +bluntness +blunts +blur +blurb +blurbs +blurred +blurrier +blurriest +blurring +blurry +blurs +blurt +blurted +blurting +blurts +blush +blushed +blusher +blushers +blushes +blushing +bluster +blustered +blustering +blusters +blustery +boa +boar +board +boarded +boarder +boarders +boarding +boardinghouse +boardinghouses +boardroom +boardrooms +boards +boardwalk +boardwalks +boars +boas +boast +boasted +boaster +boasters +boastful +boastfully +boastfulness +boasting +boasts +boat +boated +boater +boaters +boating +boatman +boatmen +boats +boatswain +boatswains +bob +bobbed +bobbies +bobbin +bobbing +bobbins +bobble +bobbled +bobbles +bobbling +bobby +bobcat +bobcats +bobolink +bobolinks +bobs +bobsled +bobsledded +bobsledding +bobsleds +bobtail +bobtails +bobwhite +bobwhites +bode +boded +bodega +bodegas +bodes +bodice +bodices +bodies +bodily +boding +bodkin +bodkins +body +bodybuilding +bodyguard +bodyguards +bodywork +bog +bogey +bogeyed +bogeying +bogeyman +bogeymen +bogeys +bogged +boggier +boggiest +bogging +boggle +boggled +boggles +boggling +boggy +bogie +bogies +bogs +bogus +bohemian +bohemians +boil +boiled +boiler +boilerplate +boilers +boiling +boilings +boils +boisterous +boisterously +boisterousness +bola +bolas +bold +bolder +boldest +boldface +boldly +boldness +bole +bolero +boleros +boles +boll +bolls +bologna +bolster +bolstered +bolstering +bolsters +bolt +bolted +bolting +bolts +bomb +bombard +bombarded +bombardier +bombardiers +bombarding +bombardment +bombardments +bombards +bombast +bombastic +bombed +bomber +bombers +bombing +bombings +bombs +bombshell +bombshells +bonanza +bonanzas +bonbon +bonbons +bond +bondage +bonded +bonding +bonds +bondsman +bondsmen +bone +boned +bonehead +boneheads +boneless +boner +boners +bones +bonfire +bonfires +bong +bonged +bonging +bongo +bongos +bongs +bonier +boniest +boning +bonito +bonitos +bonkers +bonnet +bonnets +bonnier +bonniest +bonny +bonsai +bonus +bonuses +bony +boo +boob +boobed +boobies +boobing +boobs +booby +boodle +boodles +booed +boogie +boogied +boogieing +boogies +booing +book +bookcase +bookcases +booked +bookend +bookends +bookie +bookies +booking +bookings +bookish +bookkeeper +bookkeepers +bookkeeping +booklet +booklets +bookmaker +bookmakers +bookmaking +bookmark +bookmarked +bookmarking +bookmarks +bookmobile +bookmobiles +books +bookseller +booksellers +bookshelf +bookshelves +bookshop +bookshops +bookstore +bookstores +bookworm +bookworms +boom +boomed +boomerang +boomeranged +boomeranging +boomerangs +booming +booms +boon +boondocks +boondoggle +boondoggled +boondoggles +boondoggling +boons +boor +boorish +boorishly +boors +boos +boost +boosted +booster +boosters +boosting +boosts +boot +bootblack +bootblacks +booted +bootee +bootees +booth +booths +bootie +booties +booting +bootleg +bootlegged +bootlegger +bootleggers +bootlegging +bootlegs +bootless +boots +bootstrap +bootstraps +booty +booze +boozed +boozer +boozers +boozes +boozier +booziest +boozing +boozy +bop +bopped +bopping +bops +borax +bordello +bordellos +border +bordered +bordering +borderland +borderlands +borderline +borderlines +borders +bore +bored +boredom +borer +borers +bores +boring +boringly +born +borne +boron +borough +boroughs +borrow +borrowed +borrower +borrowers +borrowing +borrows +borsch +borscht +bosh +bosom +bosoms +boss +bossed +bosses +bossier +bossiest +bossily +bossiness +bossing +bossy +bosun +bosuns +botanical +botanist +botanists +botany +botch +botched +botches +botching +both +bother +bothered +bothering +bothers +bothersome +botnet +botnets +bottle +bottled +bottleneck +bottlenecks +bottles +bottling +bottom +bottomed +bottoming +bottomless +bottoms +botulism +boudoir +boudoirs +bouffant +bouffants +bough +boughs +bought +bouillabaisse +bouillabaisses +bouillon +bouillons +boulder +boulders +boulevard +boulevards +bounce +bounced +bouncer +bouncers +bounces +bouncier +bounciest +bouncing +bouncy +bound +boundaries +boundary +bounded +bounden +bounder +bounders +bounding +boundless +bounds +bounteous +bounties +bountiful +bountifully +bounty +bouquet +bouquets +bourbon +bourgeois +bourgeoisie +bout +boutique +boutiques +boutonnière +boutonnières +bouts +bovine +bovines +bow +bowdlerize +bowdlerized +bowdlerizes +bowdlerizing +bowed +bowel +bowels +bower +bowers +bowing +bowl +bowled +bowlegged +bowler +bowlers +bowling +bowls +bowman +bowmen +bows +bowsprit +bowsprits +bowstring +bowstrings +box +boxcar +boxcars +boxed +boxer +boxers +boxes +boxing +boxwood +boy +boycott +boycotted +boycotting +boycotts +boyfriend +boyfriends +boyhood +boyhoods +boyish +boyishly +boyishness +boys +boysenberries +boysenberry +bozo +bozos +bra +brace +braced +bracelet +bracelets +braces +bracing +bracken +bracket +bracketed +bracketing +brackets +brackish +bract +bracts +brad +brads +brag +braggart +braggarts +bragged +bragger +braggers +bragging +brags +braid +braided +braiding +braids +braille +brain +brainchild +brainchildren +brained +brainier +brainiest +braining +brainless +brains +brainstorm +brainstormed +brainstorming +brainstorms +brainteaser +brainteasers +brainwash +brainwashed +brainwashes +brainwashing +brainy +braise +braised +braises +braising +brake +braked +brakeman +brakemen +brakes +braking +bramble +brambles +bran +branch +branched +branches +branching +brand +branded +brandied +brandies +branding +brandish +brandished +brandishes +brandishing +brands +brandy +brandying +bras +brash +brasher +brashest +brashly +brashness +brass +brasses +brassier +brassiere +brassieres +brassiest +brassy +brat +brats +brattier +brattiest +bratty +bravado +brave +braved +bravely +braver +bravery +braves +bravest +braving +bravo +bravos +bravura +bravuras +brawl +brawled +brawler +brawlers +brawling +brawls +brawn +brawnier +brawniest +brawniness +brawny +bray +brayed +braying +brays +brazen +brazened +brazening +brazenly +brazenness +brazens +brazier +braziers +breach +breached +breaches +breaching +bread +breadbasket +breadbaskets +breaded +breadfruit +breadfruits +breading +breads +breadth +breadths +breadwinner +breadwinners +break +breakable +breakables +breakage +breakages +breakdown +breakdowns +breaker +breakers +breakfast +breakfasted +breakfasting +breakfasts +breaking +breakneck +breakpoints +breaks +breakthrough +breakthroughs +breakup +breakups +breakwater +breakwaters +breast +breastbone +breastbones +breasted +breasting +breastplate +breastplates +breasts +breaststroke +breaststrokes +breastwork +breastworks +breath +breathable +breathe +breathed +breather +breathers +breathes +breathier +breathiest +breathing +breathless +breathlessly +breathlessness +breaths +breathtaking +breathtakingly +breathy +bred +breech +breeches +breed +breeder +breeders +breeding +breeds +breeze +breezed +breezes +breezier +breeziest +breezily +breeziness +breezing +breezy +brethren +breviaries +breviary +brevity +brew +brewed +brewer +breweries +brewers +brewery +brewing +brews +briar +briars +bribe +bribed +bribery +bribes +bribing +brick +brickbat +brickbats +bricked +bricking +bricklayer +bricklayers +bricklaying +bricks +bridal +bridals +bride +bridegroom +bridegrooms +brides +bridesmaid +bridesmaids +bridge +bridged +bridgehead +bridgeheads +bridges +bridgework +bridging +bridle +bridled +bridles +bridling +brief +briefcase +briefcases +briefed +briefer +briefest +briefing +briefings +briefly +briefness +briefs +brier +briers +brig +brigade +brigades +brigand +brigandage +brigands +brigantine +brigantines +bright +brighten +brightened +brightening +brightens +brighter +brightest +brightly +brightness +brigs +brilliance +brilliancy +brilliant +brilliantly +brilliants +brim +brimful +brimmed +brimming +brims +brimstone +brindled +brine +bring +bringing +brings +brinier +briniest +brink +brinkmanship +brinks +briny +briquet +briquets +briquette +briquettes +brisk +brisked +brisker +briskest +brisket +briskets +brisking +briskly +briskness +brisks +bristle +bristled +bristles +bristlier +bristliest +bristling +bristly +britches +brittle +brittleness +brittler +brittlest +broach +broached +broaches +broaching +broad +broadband +broadcast +broadcaster +broadcasters +broadcasting +broadcasts +broadcloth +broaden +broadened +broadening +broadens +broader +broadest +broadloom +broadly +broadness +broads +broadside +broadsided +broadsides +broadsiding +broadsword +broadswords +brocade +brocaded +brocades +brocading +broccoli +brochure +brochures +brogan +brogans +brogue +brogues +broil +broiled +broiler +broilers +broiling +broils +broke +broken +brokenhearted +broker +brokerage +brokerages +brokered +brokering +brokers +bromide +bromides +bromine +bronchi +bronchial +bronchitis +bronchus +bronco +broncos +brontosaur +brontosaurs +brontosaurus +brontosauruses +bronze +bronzed +bronzes +bronzing +brooch +brooches +brood +brooded +brooder +brooders +brooding +broods +brook +brooked +brooking +brooks +broom +brooms +broomstick +broomsticks +broth +brothel +brothels +brother +brotherhood +brotherhoods +brotherliness +brotherly +brothers +broths +brought +brouhaha +brouhahas +brow +browbeat +browbeaten +browbeating +browbeats +brown +browned +browner +brownest +brownie +brownies +browning +brownish +brownout +brownouts +browns +brownstone +brownstones +brows +browse +browsed +browser +browsers +browses +browsing +brr +bruin +bruins +bruise +bruised +bruiser +bruisers +bruises +bruising +brunch +brunched +brunches +brunching +brunet +brunets +brunette +brunettes +brunt +brush +brushed +brushes +brushing +brushwood +brusque +brusquely +brusqueness +brusquer +brusquest +brutal +brutalities +brutality +brutalize +brutalized +brutalizes +brutalizing +brutally +brute +brutes +brutish +brutishly +bubble +bubbled +bubbles +bubblier +bubbliest +bubbling +bubbly +buccaneer +buccaneered +buccaneering +buccaneers +buck +buckboard +buckboards +bucked +bucket +bucketed +bucketful +bucketfuls +bucketing +buckets +buckeye +buckeyes +bucking +buckle +buckled +buckler +bucklers +buckles +buckling +buckram +bucks +bucksaw +bucksaws +buckshot +buckskin +buckskins +buckteeth +bucktooth +bucktoothed +buckwheat +buckyball +buckyballs +bucolic +bucolics +bud +budded +buddies +budding +buddings +buddy +budge +budged +budgerigar +budgerigars +budges +budget +budgetary +budgeted +budgeting +budgets +budgie +budgies +budging +buds +buff +buffalo +buffaloed +buffaloes +buffaloing +buffed +buffer +buffered +buffering +buffers +buffet +buffeted +buffeting +buffets +buffing +buffoon +buffoonery +buffoons +buffs +bug +bugaboo +bugaboos +bugbear +bugbears +bugged +bugger +buggers +buggier +buggies +buggiest +bugging +buggy +bugle +bugled +bugler +buglers +bugles +bugling +bugs +build +builder +builders +building +buildings +builds +buildup +buildups +built +builtin +bulb +bulbous +bulbs +bulge +bulged +bulges +bulgier +bulgiest +bulging +bulgy +bulimia +bulimic +bulimics +bulk +bulked +bulkhead +bulkheads +bulkier +bulkiest +bulkiness +bulking +bulks +bulky +bull +bulldog +bulldogged +bulldogging +bulldogs +bulldoze +bulldozed +bulldozer +bulldozers +bulldozes +bulldozing +bulled +bullet +bulletin +bulletined +bulletining +bulletins +bulletproof +bulletproofed +bulletproofing +bulletproofs +bullets +bullfight +bullfighter +bullfighters +bullfighting +bullfights +bullfinch +bullfinches +bullfrog +bullfrogs +bullheaded +bullhorn +bullhorns +bullied +bullies +bulling +bullion +bullish +bullock +bullocks +bullpen +bullpens +bullring +bullrings +bulls +bullshit +bullshits +bullshitted +bullshitting +bully +bullying +bulrush +bulrushes +bulwark +bulwarks +bum +bumble +bumblebee +bumblebees +bumbled +bumbler +bumblers +bumbles +bumbling +bummed +bummer +bummers +bummest +bumming +bump +bumped +bumper +bumpers +bumpier +bumpiest +bumping +bumpkin +bumpkins +bumps +bumptious +bumpy +bums +bun +bunch +bunched +bunches +bunching +buncombe +bundle +bundled +bundles +bundling +bung +bungalow +bungalows +bunged +bunghole +bungholes +bunging +bungle +bungled +bungler +bunglers +bungles +bungling +bungs +bunion +bunions +bunk +bunked +bunker +bunkers +bunkhouse +bunkhouses +bunking +bunks +bunkum +bunnies +bunny +buns +bunt +bunted +bunting +buntings +bunts +buoy +buoyancy +buoyant +buoyantly +buoyed +buoying +buoys +bur +burble +burbled +burbles +burbling +burden +burdened +burdening +burdens +burdensome +burdock +bureau +bureaucracies +bureaucracy +bureaucrat +bureaucratic +bureaucratically +bureaucrats +bureaus +burg +burgeon +burgeoned +burgeoning +burgeons +burger +burgers +burgher +burghers +burglar +burglaries +burglarize +burglarized +burglarizes +burglarizing +burglars +burglary +burgle +burgled +burgles +burgling +burgs +burial +burials +buried +buries +burka +burkas +burlap +burlesque +burlesqued +burlesques +burlesquing +burlier +burliest +burliness +burly +burn +burned +burner +burners +burning +burnish +burnished +burnishes +burnishing +burnoose +burnooses +burnous +burnouses +burnout +burnouts +burns +burnt +burp +burped +burping +burps +burr +burred +burring +burrito +burritos +burro +burros +burrow +burrowed +burrowing +burrows +burrs +burs +bursar +bursars +bursitis +burst +bursting +bursts +bury +burying +bus +busbies +busboy +busboys +busby +bused +buses +bush +bushed +bushel +busheled +busheling +bushels +bushes +bushier +bushiest +bushiness +bushing +bushings +bushman +bushmen +bushwhack +bushwhacked +bushwhacker +bushwhackers +bushwhacking +bushwhacks +bushy +busied +busier +busies +busiest +busily +business +businesses +businesslike +businessman +businessmen +businesswoman +businesswomen +busing +buss +bust +busted +buster +busters +busting +bustle +bustled +bustles +bustling +busts +busy +busybodies +busybody +busying +busyness +busywork +but +butane +butch +butcher +butchered +butcheries +butchering +butchers +butchery +butches +butler +butlers +buts +butt +butte +butted +butter +buttercup +buttercups +buttered +butterfat +butterfingers +butterflied +butterflies +butterfly +butterflying +butterier +butteries +butteriest +buttering +buttermilk +butternut +butternuts +butters +butterscotch +buttery +buttes +butting +buttock +buttocks +button +buttoned +buttonhole +buttonholed +buttonholes +buttonholing +buttoning +buttons +buttress +buttressed +buttresses +buttressing +butts +buxom +buy +buyer +buyers +buying +buyout +buyouts +buys +buzz +buzzard +buzzards +buzzed +buzzer +buzzers +buzzes +buzzing +buzzkill +buzzkills +buzzword +buzzwords +by +bye +byes +bygone +bygones +bylaw +bylaws +byline +bylines +bypass +bypassed +bypasses +bypassing +byplay +byproduct +byproducts +bystander +bystanders +byte +bytes +byway +byways +byword +bywords +c +cab +cabal +cabals +cabana +cabanas +cabaret +cabarets +cabbage +cabbages +cabbed +cabbie +cabbies +cabbing +cabby +cabin +cabinet +cabinetmaker +cabinetmakers +cabinets +cabins +cable +cablecast +cablecasting +cablecasts +cabled +cablegram +cablegrams +cables +cabling +caboodle +caboose +cabooses +cabs +cacao +cacaos +cache +cached +caches +cachet +cachets +caching +cackle +cackled +cackles +cackling +cacophonies +cacophonous +cacophony +cacti +cactus +cad +cadaver +cadaverous +cadavers +caddie +caddied +caddies +caddish +caddying +cadence +cadences +cadenza +cadenzas +cadet +cadets +cadge +cadged +cadger +cadgers +cadges +cadging +cadmium +cadre +cadres +cads +caducei +caduceus +caesarean +caesareans +caesura +caesuras +cafeteria +cafeterias +caffeinated +caffeine +caftan +caftans +café +cafés +cage +caged +cages +cagey +cagier +cagiest +cagily +caginess +caging +cahoot +cahoots +cairn +cairns +caisson +caissons +cajole +cajoled +cajolery +cajoles +cajoling +cake +caked +cakes +caking +calabash +calabashes +calamine +calamities +calamitous +calamity +calcified +calcifies +calcify +calcifying +calcine +calcined +calcines +calcining +calcite +calcium +calculable +calculate +calculated +calculates +calculating +calculation +calculations +calculator +calculators +calculi +calculus +caldron +caldrons +calendar +calendared +calendaring +calendars +calf +calfskin +caliber +calibers +calibrate +calibrated +calibrates +calibrating +calibration +calibrations +calibrator +calibrators +calico +calicoes +calicos +caliper +calipered +calipering +calipers +caliph +caliphate +caliphates +caliphs +calisthenic +calisthenics +calk +calked +calking +calks +call +callable +called +caller +callers +calligrapher +calligraphers +calligraphy +calling +callings +calliope +calliopes +callous +calloused +callouses +callousing +callously +callousness +callow +callower +callowest +calls +callus +callused +calluses +callusing +calm +calmed +calmer +calmest +calming +calmly +calmness +calms +caloric +calorie +calories +calorific +calumniate +calumniated +calumniates +calumniating +calumnies +calumny +calve +calved +calves +calving +calypso +calypsos +calyx +calyxes +cam +camaraderie +camber +cambered +cambering +cambers +cambium +cambiums +cambric +camcorder +camcorders +came +camel +camellia +camellias +camels +cameo +cameos +camera +cameraman +cameramen +cameras +camerawoman +camerawomen +camisole +camisoles +camomile +camomiles +camouflage +camouflaged +camouflages +camouflaging +camp +campaign +campaigned +campaigner +campaigners +campaigning +campaigns +campanile +campaniles +camped +camper +campers +campfire +campfires +campground +campgrounds +camphor +campier +campiest +camping +camps +campsite +campsites +campus +campuses +campy +cams +camshaft +camshafts +can +canal +canals +canapé +canapés +canard +canards +canaries +canary +canasta +cancan +cancans +cancel +canceled +canceling +cancellation +cancellations +cancelled +cancelling +cancels +cancer +cancerous +cancers +candelabra +candelabras +candelabrum +candid +candidacies +candidacy +candidate +candidates +candidly +candidness +candied +candies +candle +candled +candlelight +candles +candlestick +candlesticks +candling +candor +candy +candying +cane +caned +canes +canine +canines +caning +canister +canisters +canker +cankered +cankering +cankerous +cankers +cannabis +cannabises +canned +canneries +cannery +cannibal +cannibalism +cannibalistic +cannibalize +cannibalized +cannibalizes +cannibalizing +cannibals +cannier +canniest +cannily +canniness +canning +cannon +cannonade +cannonaded +cannonades +cannonading +cannonball +cannonballs +cannoned +cannoning +cannons +cannot +canny +canoe +canoed +canoeing +canoeist +canoeists +canoes +canon +canonical +canonization +canonizations +canonize +canonized +canonizes +canonizing +canons +canopied +canopies +canopy +canopying +cans +cant +cantaloupe +cantaloupes +cantankerous +cantankerously +cantankerousness +cantata +cantatas +canted +canteen +canteens +canter +cantered +cantering +canters +canticle +canticles +cantilever +cantilevered +cantilevering +cantilevers +canting +canto +canton +cantons +cantor +cantors +cantos +cants +canvas +canvasback +canvasbacks +canvased +canvases +canvasing +canvass +canvassed +canvasser +canvassers +canvasses +canvassing +canyon +canyons +cap +capabilities +capability +capable +capably +capacious +capaciously +capaciousness +capacitance +capacities +capacitor +capacitors +capacity +caparison +caparisoned +caparisoning +caparisons +cape +caped +caper +capered +capering +capers +capes +capillaries +capillary +capital +capitalism +capitalist +capitalistic +capitalists +capitalization +capitalize +capitalized +capitalizes +capitalizing +capitals +capitol +capitols +capitulate +capitulated +capitulates +capitulating +capitulation +capitulations +caplet +caplets +capon +capons +capped +capping +cappuccino +cappuccinos +caprice +caprices +capricious +capriciously +capriciousness +caps +capsize +capsized +capsizes +capsizing +capstan +capstans +capsule +capsuled +capsules +capsuling +captain +captaincies +captaincy +captained +captaining +captains +caption +captioned +captioning +captions +captious +captivate +captivated +captivates +captivating +captivation +captive +captives +captivities +captivity +captor +captors +capture +captured +captures +capturing +car +carafe +carafes +caramel +caramels +carapace +carapaces +carat +carats +caravan +caravans +caraway +caraways +carbide +carbides +carbine +carbines +carbohydrate +carbohydrates +carbon +carbonate +carbonated +carbonates +carbonating +carbonation +carbons +carboy +carboys +carbs +carbuncle +carbuncles +carburetor +carburetors +carcass +carcasses +carcinogen +carcinogenic +carcinogenics +carcinogens +carcinoma +carcinomas +card +cardboard +carded +cardiac +cardigan +cardigans +cardinal +cardinals +carding +cardio +cardiogram +cardiograms +cardiologist +cardiologists +cardiology +cardiopulmonary +cardiovascular +cards +cardsharp +cardsharps +care +cared +careen +careened +careening +careens +career +careered +careering +careers +carefree +careful +carefuller +carefullest +carefully +carefulness +caregiver +caregivers +careless +carelessly +carelessness +cares +caress +caressed +caresses +caressing +caret +caretaker +caretakers +carets +careworn +carfare +cargo +cargoes +caribou +caribous +caricature +caricatured +caricatures +caricaturing +caricaturist +caricaturists +caries +carillon +carillons +caring +carjack +carjacked +carjacker +carjackers +carjacking +carjackings +carjacks +carmine +carmines +carnage +carnal +carnally +carnation +carnations +carnelian +carnelians +carnival +carnivals +carnivore +carnivores +carnivorous +carol +caroled +caroler +carolers +caroling +carols +carom +caromed +caroming +caroms +carotid +carotids +carousal +carousals +carouse +caroused +carousel +carousels +carouser +carousers +carouses +carousing +carp +carpal +carpals +carped +carpel +carpels +carpenter +carpentered +carpentering +carpenters +carpentry +carpet +carpetbag +carpetbagged +carpetbagger +carpetbaggers +carpetbagging +carpetbags +carpeted +carpeting +carpets +carpi +carping +carport +carports +carps +carpus +carrel +carrels +carriage +carriages +carriageway +carried +carrier +carriers +carries +carrion +carrot +carrots +carry +carryall +carryalls +carrying +carryout +cars +carsick +carsickness +cart +carted +cartel +cartels +cartilage +cartilages +cartilaginous +carting +cartographer +cartographers +cartography +carton +cartons +cartoon +cartooned +cartooning +cartoonist +cartoonists +cartoons +cartridge +cartridges +carts +cartwheel +cartwheeled +cartwheeling +cartwheels +carve +carved +carver +carvers +carves +carving +carvings +caryatid +caryatids +cascade +cascaded +cascades +cascading +case +cased +casein +caseload +caseloads +casement +casements +cases +casework +caseworker +caseworkers +cash +cashback +cashed +cashes +cashew +cashews +cashier +cashiered +cashiering +cashiers +cashing +cashmere +casing +casings +casino +casinos +cask +casket +caskets +casks +cassava +cassavas +casserole +casseroled +casseroles +casseroling +cassette +cassettes +cassia +cassias +cassock +cassocks +cast +castanet +castanets +castaway +castaways +caste +caster +casters +castes +castigate +castigated +castigates +castigating +castigation +castigator +castigators +casting +castings +castle +castled +castles +castling +castoff +castoffs +castor +castors +castrate +castrated +castrates +castrating +castration +castrations +casts +casual +casually +casualness +casuals +casualties +casualty +casuist +casuistry +casuists +cat +cataclysm +cataclysmic +cataclysms +catacomb +catacombs +catafalque +catafalques +catalepsy +cataleptic +cataleptics +catalog +cataloged +cataloger +catalogers +cataloging +catalogs +catalogue +catalogued +catalogues +cataloguing +catalpa +catalpas +catalysis +catalyst +catalysts +catalytic +catalyze +catalyzed +catalyzes +catalyzing +catamaran +catamarans +catapult +catapulted +catapulting +catapults +cataract +cataracts +catarrh +catastrophe +catastrophes +catastrophic +catastrophically +catatonic +catatonics +catbird +catbirds +catboat +catboats +catcall +catcalled +catcalling +catcalls +catch +catchall +catchalls +catcher +catchers +catches +catchier +catchiest +catching +catchings +catchment +catchphrase +catchword +catchwords +catchy +catechism +catechisms +catechize +catechized +catechizes +catechizing +categorical +categorically +categories +categorization +categorizations +categorize +categorized +categorizes +categorizing +category +cater +catered +caterer +caterers +catering +caterings +caterpillar +caterpillars +caters +caterwaul +caterwauled +caterwauling +caterwauls +catfish +catfishes +catgut +catharses +catharsis +cathartic +cathartics +cathedral +cathedrals +catheter +catheters +cathode +cathodes +catholic +catholicity +cation +cations +catkin +catkins +catnap +catnapped +catnapping +catnaps +catnip +cats +catsup +cattail +cattails +cattier +cattiest +cattily +cattiness +cattle +cattleman +cattlemen +catty +catwalk +catwalks +caucus +caucused +caucuses +caucusing +caudal +caught +cauldron +cauldrons +cauliflower +cauliflowers +caulk +caulked +caulking +caulks +causal +causalities +causality +causally +causation +causative +cause +caused +causeless +causes +causeway +causeways +causing +caustic +caustically +caustics +cauterize +cauterized +cauterizes +cauterizing +caution +cautionary +cautioned +cautioning +cautions +cautious +cautiously +cautiousness +cavalcade +cavalcades +cavalier +cavaliers +cavalries +cavalry +cavalryman +cavalrymen +cave +caveat +caveats +caved +caveman +cavemen +cavern +cavernous +caverns +caves +caviar +cavil +caviled +caviling +cavils +caving +cavities +cavity +cavort +cavorted +cavorting +cavorts +caw +cawed +cawing +caws +cayenne +cease +ceased +ceasefire +ceaseless +ceaselessly +ceases +ceasing +cedar +cedars +cede +ceded +cedes +cedilla +cedillas +ceding +ceiling +ceilings +celebrant +celebrants +celebrate +celebrated +celebrates +celebrating +celebration +celebrations +celebratory +celebrities +celebrity +celerity +celery +celesta +celestas +celestial +celibacy +celibate +celibates +cell +cellar +cellars +cellist +cellists +cello +cellophane +cellos +cells +cellular +cellulars +cellulite +celluloid +cellulose +cement +cemented +cementing +cements +cemeteries +cemetery +cenotaph +cenotaphs +censer +censers +censor +censored +censoring +censorious +censoriously +censors +censorship +censure +censured +censures +censuring +census +censused +censuses +censusing +cent +centaur +centaurs +centenarian +centenarians +centenaries +centenary +centennial +centennials +center +centered +centerfold +centerfolds +centering +centerpiece +centerpieces +centers +centigrade +centigram +centigrams +centiliter +centiliters +centime +centimes +centimeter +centimeters +centipede +centipedes +central +centralization +centralize +centralized +centralizes +centralizing +centrally +centrals +centrifugal +centrifuge +centrifuged +centrifuges +centrifuging +centripetal +centrist +centrists +cents +centuries +centurion +centurions +century +cephalic +ceramic +ceramics +cereal +cereals +cerebellum +cerebellums +cerebra +cerebral +cerebrum +cerebrums +ceremonial +ceremonially +ceremonials +ceremonies +ceremonious +ceremoniously +ceremony +cerise +certain +certainly +certainties +certainty +certifiable +certificate +certificated +certificates +certificating +certification +certifications +certified +certifies +certify +certifying +certitude +cerulean +cervical +cervices +cervix +cesarean +cesareans +cesium +cessation +cessations +cession +cessions +cesspool +cesspools +cetacean +cetaceans +chafe +chafed +chafes +chaff +chaffed +chaffinch +chaffinches +chaffing +chaffs +chafing +chagrin +chagrined +chagrining +chagrins +chain +chained +chaining +chains +chainsaw +chainsawed +chainsawing +chainsaws +chair +chaired +chairing +chairlift +chairlifts +chairman +chairmanship +chairmen +chairperson +chairpersons +chairs +chairwoman +chairwomen +chaise +chaises +chalet +chalets +chalice +chalices +chalk +chalkboard +chalkboards +chalked +chalkier +chalkiest +chalking +chalks +chalky +challenge +challenged +challenger +challengers +challenges +challenging +chamber +chamberlain +chamberlains +chambermaid +chambermaids +chambers +chambray +chameleon +chameleons +chamois +chamomile +chamomiles +champ +champagne +champagnes +champed +champing +champion +championed +championing +champions +championship +championships +champs +chance +chanced +chancel +chancelleries +chancellery +chancellor +chancellors +chancels +chanceries +chancery +chances +chancier +chanciest +chancing +chancy +chandelier +chandeliers +chandler +chandlers +change +changeable +changed +changeling +changelings +changeover +changeovers +changes +changing +channel +channeled +channeling +channels +chant +chanted +chanter +chanters +chantey +chanteys +chanticleer +chanticleers +chanting +chants +chaos +chaotic +chaotically +chap +chaparral +chaparrals +chapel +chapels +chaperon +chaperone +chaperoned +chaperones +chaperoning +chaperons +chaplain +chaplaincies +chaplaincy +chaplains +chaplet +chaplets +chapped +chapping +chaps +chapter +chapters +char +character +characteristic +characteristically +characteristics +characterization +characterizations +characterize +characterized +characterizes +characterizing +characters +charade +charades +charbroil +charbroiled +charbroiling +charbroils +charcoal +charcoals +charge +chargeable +charged +charger +chargers +charges +charging +charier +chariest +charily +chariot +charioteer +charioteers +chariots +charisma +charismatic +charismatics +charitable +charitably +charities +charity +charlatan +charlatans +charm +charmed +charmer +charmers +charming +charmingly +charms +charred +charring +chars +chart +charted +charter +chartered +chartering +charters +charting +chartreuse +charts +charwoman +charwomen +chary +chase +chased +chaser +chasers +chases +chasing +chasm +chasms +chassis +chaste +chastely +chasten +chastened +chastening +chastens +chaster +chastest +chastise +chastised +chastisement +chastisements +chastises +chastising +chastity +chasuble +chasubles +chat +chateaus +chats +chatted +chattel +chattels +chatter +chatterbox +chatterboxes +chattered +chatterer +chatterers +chattering +chatters +chattier +chattiest +chattily +chattiness +chatting +chatty +chauffeur +chauffeured +chauffeuring +chauffeurs +chauvinism +chauvinist +chauvinistic +chauvinists +cheap +cheapen +cheapened +cheapening +cheapens +cheaper +cheapest +cheaply +cheapness +cheapskate +cheapskates +cheat +cheated +cheater +cheaters +cheating +cheats +check +checkbook +checkbooks +checked +checker +checkerboard +checkerboards +checkered +checkering +checkers +checking +checklist +checklists +checkmate +checkmated +checkmates +checkmating +checkout +checkouts +checkpoint +checkpoints +checkroom +checkrooms +checks +checkup +checkups +cheddar +cheek +cheekbone +cheekbones +cheeked +cheekier +cheekiest +cheekily +cheekiness +cheeking +cheeks +cheeky +cheep +cheeped +cheeping +cheeps +cheer +cheered +cheerful +cheerfuller +cheerfullest +cheerfully +cheerfulness +cheerier +cheeriest +cheerily +cheeriness +cheering +cheerleader +cheerleaders +cheerless +cheerlessly +cheerlessness +cheers +cheery +cheese +cheeseburger +cheeseburgers +cheesecake +cheesecakes +cheesecloth +cheesed +cheeses +cheesier +cheesiest +cheesing +cheesy +cheetah +cheetahs +chef +chefs +chemical +chemically +chemicals +chemise +chemises +chemist +chemistry +chemists +chemotherapy +chenille +cherish +cherished +cherishes +cherishing +cheroot +cheroots +cherries +cherry +cherub +cherubic +cherubim +cherubs +chervil +chess +chessboard +chessboards +chessman +chessmen +chest +chestnut +chestnuts +chests +chevron +chevrons +chew +chewed +chewer +chewers +chewier +chewiest +chewing +chews +chewy +chi +chiaroscuro +chic +chicaneries +chicanery +chicer +chicest +chichi +chichis +chick +chickadee +chickadees +chicken +chickened +chickening +chickenpox +chickens +chickpea +chickpeas +chicks +chickweed +chicle +chicories +chicory +chide +chided +chides +chiding +chief +chiefer +chiefest +chiefly +chiefs +chieftain +chieftains +chiffon +chigger +chiggers +chignon +chignons +chilblain +chilblains +child +childbearing +childbirth +childbirths +childcare +childhood +childhoods +childish +childishly +childishness +childless +childlessness +childlike +childproof +childproofed +childproofing +childproofs +children +chili +chilies +chill +chilled +chiller +chillers +chillest +chillier +chilliest +chilliness +chilling +chillings +chills +chilly +chimaera +chimaeras +chime +chimed +chimera +chimeras +chimerical +chimes +chiming +chimney +chimneys +chimp +chimpanzee +chimpanzees +chimps +chin +china +chinchilla +chinchillas +chink +chinked +chinking +chinks +chinned +chinning +chino +chinos +chins +chinstrap +chinstraps +chintz +chintzier +chintziest +chintzy +chip +chipmunk +chipmunks +chipped +chipper +chippers +chipping +chips +chiropodist +chiropodists +chiropody +chiropractic +chiropractics +chiropractor +chiropractors +chirp +chirped +chirping +chirps +chirrup +chirruped +chirruping +chirrups +chisel +chiseled +chiseler +chiselers +chiseling +chiselled +chiseller +chisellers +chiselling +chisels +chit +chitchat +chitchats +chitchatted +chitchatting +chitin +chitlins +chits +chitterlings +chivalrous +chivalrously +chivalry +chive +chives +chloride +chlorides +chlorinate +chlorinated +chlorinates +chlorinating +chlorination +chlorine +chlorofluorocarbon +chlorofluorocarbons +chloroform +chloroformed +chloroforming +chloroforms +chlorophyll +chock +chocked +chocking +chocks +chocolate +chocolates +choice +choicer +choices +choicest +choir +choirs +choke +choked +choker +chokers +chokes +choking +choler +cholera +choleric +cholesterol +chomp +chomped +chomping +chomps +choose +chooses +choosier +choosiest +choosing +choosy +chop +chopped +chopper +choppered +choppering +choppers +choppier +choppiest +choppily +choppiness +chopping +choppy +chops +chopstick +chopsticks +choral +chorale +chorales +chorals +chord +chords +chore +choreograph +choreographed +choreographer +choreographers +choreographic +choreographing +choreographs +choreography +chores +chorister +choristers +chortle +chortled +chortles +chortling +chorus +chorused +choruses +chorusing +chose +chosen +chow +chowder +chowders +chowed +chowing +chows +christen +christened +christening +christenings +christens +chromatic +chrome +chromed +chromes +chroming +chromium +chromosome +chromosomes +chronic +chronically +chronicle +chronicled +chronicler +chroniclers +chronicles +chronicling +chronological +chronologically +chronologies +chronology +chronometer +chronometers +chrysalis +chrysalises +chrysanthemum +chrysanthemums +chubbier +chubbiest +chubbiness +chubby +chuck +chucked +chuckhole +chuckholes +chucking +chuckle +chuckled +chuckles +chuckling +chucks +chug +chugged +chugging +chugs +chum +chummed +chummier +chummiest +chumminess +chumming +chummy +chump +chumps +chums +chunk +chunkier +chunkiest +chunkiness +chunks +chunky +church +churches +churchgoer +churchgoers +churchman +churchmen +churchyard +churchyards +churl +churlish +churlishly +churlishness +churls +churn +churned +churning +churns +chute +chutes +chutney +chutzpah +château +châteaux +châtelaine +châtelaines +ciabatta +ciabattas +cicada +cicadas +cicatrices +cicatrix +cider +ciders +cigar +cigarette +cigarettes +cigarillo +cigarillos +cigars +cilantro +cilia +cilium +cinch +cinched +cinches +cinching +cinchona +cinchonas +cincture +cinctures +cinder +cindered +cindering +cinders +cinema +cinemas +cinematic +cinematographer +cinematographers +cinematography +cinnabar +cinnamon +cipher +ciphered +ciphering +ciphers +circa +circadian +circle +circled +circles +circlet +circlets +circling +circuit +circuited +circuiting +circuitous +circuitously +circuitry +circuits +circular +circularity +circularize +circularized +circularizes +circularizing +circulars +circulate +circulated +circulates +circulating +circulation +circulations +circulatory +circumcise +circumcised +circumcises +circumcising +circumcision +circumcisions +circumference +circumferences +circumflex +circumflexes +circumlocution +circumlocutions +circumnavigate +circumnavigated +circumnavigates +circumnavigating +circumnavigation +circumnavigations +circumscribe +circumscribed +circumscribes +circumscribing +circumscription +circumscriptions +circumspect +circumspection +circumstance +circumstanced +circumstances +circumstancing +circumstantial +circumstantially +circumvent +circumvented +circumventing +circumvention +circumvents +circus +circuses +cirrhosis +cirrus +cistern +cisterns +citadel +citadels +citation +citations +cite +cited +cites +cities +citing +citizen +citizenry +citizens +citizenship +citric +citron +citronella +citrons +citrous +citrus +citruses +city +civet +civets +civic +civics +civil +civilian +civilians +civilities +civility +civilization +civilizations +civilize +civilized +civilizes +civilizing +civilly +civvies +clack +clacked +clacking +clacks +clad +claim +claimant +claimants +claimed +claiming +claims +clairvoyance +clairvoyant +clairvoyants +clam +clambake +clambakes +clamber +clambered +clambering +clambers +clammed +clammier +clammiest +clamminess +clamming +clammy +clamor +clamored +clamoring +clamorous +clamors +clamp +clampdown +clampdowns +clamped +clamping +clamps +clams +clan +clandestine +clandestinely +clang +clanged +clanging +clangor +clangs +clank +clanked +clanking +clanks +clannish +clans +clap +clapboard +clapboarded +clapboarding +clapboards +clapped +clapper +clappers +clapping +claps +claptrap +claret +clarets +clarification +clarifications +clarified +clarifies +clarify +clarifying +clarinet +clarinetist +clarinetists +clarinets +clarinettist +clarinettists +clarion +clarioned +clarioning +clarions +clarity +clash +clashed +clashes +clashing +clasp +clasped +clasping +clasps +class +classed +classes +classic +classical +classically +classicism +classicist +classicists +classics +classier +classiest +classifiable +classification +classifications +classified +classifieds +classifies +classify +classifying +classiness +classing +classless +classmate +classmates +classroom +classrooms +classy +clatter +clattered +clattering +clatters +clause +clauses +claustrophobia +claustrophobic +clavichord +clavichords +clavicle +clavicles +claw +clawed +clawing +claws +clay +clayey +clayier +clayiest +clean +cleaned +cleaner +cleaners +cleanest +cleaning +cleanings +cleanlier +cleanliest +cleanliness +cleanly +cleanness +cleans +cleanse +cleansed +cleanser +cleansers +cleanses +cleansing +cleanup +cleanups +clear +clearance +clearances +cleared +clearer +clearest +clearing +clearinghouse +clearinghouses +clearings +clearly +clearness +clears +cleat +cleats +cleavage +cleavages +cleave +cleaved +cleaver +cleavers +cleaves +cleaving +clef +clefs +cleft +clefts +clematis +clematises +clemency +clement +clench +clenched +clenches +clenching +clerestories +clerestory +clergies +clergy +clergyman +clergymen +clergywoman +clergywomen +cleric +clerical +clerics +clerk +clerked +clerking +clerks +clever +cleverer +cleverest +cleverly +cleverness +clew +clewed +clewing +clews +cliché +clichéd +clichés +click +clickable +clicked +clicking +clicks +client +clients +clientèle +clientèles +cliff +cliffhanger +cliffhangers +cliffs +climactic +climate +climates +climatic +climax +climaxed +climaxes +climaxing +climb +climbed +climber +climbers +climbing +climbs +clime +climes +clinch +clinched +clincher +clinchers +clinches +clinching +cling +clingier +clingiest +clinging +clings +clingy +clinic +clinical +clinically +clinician +clinicians +clinics +clink +clinked +clinker +clinkers +clinking +clinks +clip +clipboard +clipboards +clipped +clipper +clippers +clipping +clippings +clips +clique +cliques +cliquish +clit +clitoral +clitoris +clitorises +clits +cloak +cloaked +cloaking +cloakroom +cloakrooms +cloaks +clobber +clobbered +clobbering +clobbers +cloche +cloches +clock +clocked +clocking +clocks +clockwise +clockwork +clockworks +clod +clodhopper +clodhoppers +clods +clog +clogged +clogging +clogs +cloister +cloistered +cloistering +cloisters +clomp +clomped +clomping +clomps +clone +cloned +clones +cloning +clop +clopped +clopping +clops +close +closed +closefisted +closely +closemouthed +closeness +closeout +closeouts +closer +closes +closest +closet +closeted +closeting +closets +closing +closure +closures +clot +cloth +clothe +clothed +clothes +clothesline +clotheslines +clothespin +clothespins +clothier +clothiers +clothing +cloths +clots +clotted +clotting +cloture +clotures +cloud +cloudburst +cloudbursts +clouded +cloudier +cloudiest +cloudiness +clouding +cloudless +clouds +cloudy +clout +clouted +clouting +clouts +clove +cloven +clover +cloverleaf +cloverleafs +cloverleaves +clovers +cloves +clown +clowned +clowning +clownish +clownishly +clownishness +clowns +cloy +cloyed +cloying +cloys +club +clubbed +clubbing +clubfeet +clubfoot +clubhouse +clubhouses +clubs +cluck +clucked +clucking +clucks +clue +clued +clueless +clues +cluing +clump +clumped +clumping +clumps +clumsier +clumsiest +clumsily +clumsiness +clumsy +clung +clunk +clunked +clunker +clunkers +clunkier +clunkiest +clunking +clunks +clunky +cluster +clustered +clustering +clusters +clutch +clutched +clutches +clutching +clutter +cluttered +cluttering +clutters +coach +coached +coaches +coaching +coachman +coachmen +coagulant +coagulants +coagulate +coagulated +coagulates +coagulating +coagulation +coal +coaled +coalesce +coalesced +coalescence +coalesces +coalescing +coaling +coalition +coalitions +coals +coarse +coarsely +coarsen +coarsened +coarseness +coarsening +coarsens +coarser +coarsest +coast +coastal +coasted +coaster +coasters +coasting +coastline +coastlines +coasts +coat +coated +coating +coatings +coats +coauthor +coauthored +coauthoring +coauthors +coax +coaxed +coaxes +coaxing +cob +cobalt +cobble +cobbled +cobbler +cobblers +cobbles +cobblestone +cobblestones +cobbling +cobra +cobras +cobs +cobweb +cobwebs +cocaine +cocci +coccis +coccus +coccyges +coccyx +cochlea +cochleae +cochleas +cock +cockade +cockades +cockamamie +cockatoo +cockatoos +cocked +cockerel +cockerels +cockeyed +cockfight +cockfights +cockier +cockiest +cockily +cockiness +cocking +cockle +cockles +cockleshell +cockleshells +cockney +cockneys +cockpit +cockpits +cockroach +cockroaches +cocks +cockscomb +cockscombs +cocksucker +cocksuckers +cocksure +cocktail +cocktails +cocky +cocoa +cocoas +coconut +coconuts +cocoon +cocooned +cocooning +cocoons +cod +coda +codas +codded +codding +coddle +coddled +coddles +coddling +code +coded +codeine +codependency +codependent +codependents +codes +codex +codfish +codfishes +codger +codgers +codices +codicil +codicils +codification +codifications +codified +codifies +codify +codifying +coding +cods +coed +coeds +coeducation +coeducational +coefficient +coefficients +coequal +coequals +coerce +coerced +coerces +coercing +coercion +coercive +coeval +coevals +coexist +coexisted +coexistence +coexisting +coexists +coffee +coffeecake +coffeecakes +coffeehouse +coffeehouses +coffeepot +coffeepots +coffees +coffer +coffers +coffin +coffined +coffining +coffins +cog +cogency +cogent +cogently +cogitate +cogitated +cogitates +cogitating +cogitation +cognac +cognacs +cognate +cognates +cognition +cognitive +cognizance +cognizant +cognomen +cognomens +cogs +cogwheel +cogwheels +cohabit +cohabitation +cohabited +cohabiting +cohabits +cohere +cohered +coherence +coherent +coherently +coheres +cohering +cohesion +cohesive +cohesively +cohesiveness +cohort +cohorts +coif +coifed +coiffed +coiffing +coiffure +coiffured +coiffures +coiffuring +coifing +coifs +coil +coiled +coiling +coils +coin +coinage +coinages +coincide +coincided +coincidence +coincidences +coincident +coincidental +coincidentally +coincides +coinciding +coined +coining +coins +coital +coitus +coke +coked +cokes +coking +cola +colander +colanders +colas +cold +colder +coldest +coldly +coldness +colds +coleslaw +colic +colicky +coliseum +coliseums +colitis +collaborate +collaborated +collaborates +collaborating +collaboration +collaborations +collaborative +collaborator +collaborators +collage +collages +collapse +collapsed +collapses +collapsible +collapsing +collar +collarbone +collarbones +collared +collaring +collars +collate +collated +collateral +collates +collating +collation +collations +colleague +colleagues +collect +collectable +collectables +collected +collectible +collectibles +collecting +collection +collections +collective +collectively +collectives +collectivism +collectivist +collectivists +collectivize +collectivized +collectivizes +collectivizing +collector +collectors +collects +colleen +colleens +college +colleges +collegian +collegians +collegiate +collide +collided +collides +colliding +collie +collier +collieries +colliers +colliery +collies +collision +collisions +collocate +collocated +collocates +collocating +collocation +collocations +colloid +colloids +colloquial +colloquialism +colloquialisms +colloquially +colloquies +colloquium +colloquiums +colloquy +collude +colluded +colludes +colluding +collusion +collusive +cologne +colognes +colon +colonel +colonels +colones +colonial +colonialism +colonialist +colonialists +colonials +colonies +colonist +colonists +colonization +colonize +colonized +colonizer +colonizers +colonizes +colonizing +colonnade +colonnades +colonoscopies +colonoscopy +colons +colony +color +coloration +coloratura +coloraturas +colorblind +colored +coloreds +colorfast +colorful +colorfully +coloring +colorless +colors +colossal +colossally +colossi +colossus +cols +colt +coltish +colts +columbine +columbines +column +columned +columnist +columnists +columns +coma +comas +comatose +comb +combat +combatant +combatants +combated +combating +combative +combats +combed +combination +combinations +combine +combined +combines +combing +combining +combo +combos +combs +combustibility +combustible +combustibles +combustion +come +comeback +comebacks +comedian +comedians +comedic +comedienne +comediennes +comedies +comedown +comedowns +comedy +comelier +comeliest +comeliness +comely +comer +comers +comes +comestible +comestibles +comet +comets +comeuppance +comeuppances +comfier +comfiest +comfort +comfortable +comfortably +comforted +comforter +comforters +comforting +comfortingly +comforts +comfy +comic +comical +comically +comics +coming +comings +comity +comma +command +commandant +commandants +commanded +commandeer +commandeered +commandeering +commandeers +commander +commanders +commanding +commandment +commandments +commando +commandos +commands +commas +commemorate +commemorated +commemorates +commemorating +commemoration +commemorations +commemorative +commence +commenced +commencement +commencements +commences +commencing +commend +commendable +commendably +commendation +commendations +commended +commending +commends +commensurable +commensurate +comment +commentaries +commentary +commentate +commentated +commentates +commentating +commentator +commentators +commented +commenting +comments +commerce +commercial +commercialism +commercialization +commercialize +commercialized +commercializes +commercializing +commercially +commercials +commingle +commingled +commingles +commingling +commiserate +commiserated +commiserates +commiserating +commiseration +commiserations +commissar +commissariat +commissariats +commissaries +commissars +commissary +commission +commissioned +commissioner +commissioners +commissioning +commissions +commit +commitment +commitments +commits +committal +committals +committed +committee +committees +committing +commode +commodes +commodious +commodities +commodity +commodore +commodores +common +commoner +commoners +commonest +commonly +commonplace +commonplaces +commons +commonwealth +commonwealths +commotion +commotions +communal +communally +commune +communed +communes +communicable +communicant +communicants +communicate +communicated +communicates +communicating +communication +communications +communicative +communicator +communicators +communing +communion +communions +communique +communiques +communism +communist +communistic +communists +communities +community +commutation +commutations +commutative +commute +commuted +commuter +commuters +commutes +commuting +compact +compacted +compacter +compactest +compacting +compaction +compactly +compactness +compactor +compactors +compacts +companies +companion +companionable +companions +companionship +companionway +companionways +company +comparability +comparable +comparably +comparative +comparatively +comparatives +compare +compared +compares +comparing +comparison +comparisons +compartment +compartmentalize +compartmentalized +compartmentalizes +compartmentalizing +compartments +compass +compassed +compasses +compassing +compassion +compassionate +compassionately +compatibility +compatible +compatibles +compatibly +compatriot +compatriots +compel +compelled +compelling +compellingly +compels +compendium +compendiums +compensate +compensated +compensates +compensating +compensation +compensations +compensatory +compete +competed +competence +competences +competencies +competency +competent +competently +competes +competing +competition +competitions +competitive +competitively +competitiveness +competitor +competitors +compilation +compilations +compile +compiled +compiler +compilers +compiles +compiling +complacence +complacency +complacent +complacently +complain +complainant +complainants +complained +complainer +complainers +complaining +complains +complaint +complaints +complaisance +complaisant +complaisantly +complected +complement +complementary +complemented +complementing +complements +complete +completed +completely +completeness +completer +completes +completest +completing +completion +complex +complexes +complexion +complexioned +complexions +complexities +complexity +compliance +compliant +complicate +complicated +complicates +complicating +complication +complications +complicity +complied +complies +compliment +complimentary +complimented +complimenting +compliments +comply +complying +component +components +comport +comported +comporting +comportment +comports +compose +composed +composer +composers +composes +composing +composite +composites +composition +compositions +compositor +compositors +compost +composted +composting +composts +composure +compote +compotes +compound +compounded +compounding +compounds +comprehend +comprehended +comprehending +comprehends +comprehensibility +comprehensible +comprehension +comprehensions +comprehensive +comprehensively +comprehensiveness +comprehensives +compress +compressed +compresses +compressing +compression +compressor +compressors +comprise +comprised +comprises +comprising +compromise +compromised +compromises +compromising +comptroller +comptrollers +compulsion +compulsions +compulsive +compulsively +compulsiveness +compulsories +compulsorily +compulsory +compunction +compunctions +computation +computational +computationally +computations +compute +computed +computer +computerization +computerize +computerized +computerizes +computerizing +computers +computes +computing +comrade +comrades +comradeship +con +concatenate +concatenated +concatenates +concatenating +concatenation +concatenations +concave +concavities +concavity +conceal +concealed +concealing +concealment +conceals +concede +conceded +concedes +conceding +conceit +conceited +conceits +conceivable +conceivably +conceive +conceived +conceives +conceiving +concentrate +concentrated +concentrates +concentrating +concentration +concentrations +concentric +concentrically +concept +conception +conceptions +concepts +conceptual +conceptualization +conceptualizations +conceptualize +conceptualized +conceptualizes +conceptualizing +conceptually +concern +concerned +concerning +concerns +concert +concerted +concertina +concertinaed +concertinaing +concertinas +concerting +concertmaster +concertmasters +concerto +concertos +concerts +concession +concessionaire +concessionaires +concessions +conch +conchs +concierge +concierges +conciliate +conciliated +conciliates +conciliating +conciliation +conciliator +conciliators +conciliatory +concise +concisely +conciseness +conciser +concisest +conclave +conclaves +conclude +concluded +concludes +concluding +conclusion +conclusions +conclusive +conclusively +concoct +concocted +concocting +concoction +concoctions +concocts +concomitant +concomitants +concord +concordance +concordances +concordant +concourse +concourses +concrete +concreted +concretely +concretes +concreting +concubine +concubines +concur +concurred +concurrence +concurrences +concurrency +concurrent +concurrently +concurring +concurs +concussion +concussions +condemn +condemnation +condemnations +condemnatory +condemned +condemning +condemns +condensation +condensations +condense +condensed +condenser +condensers +condenses +condensing +condescend +condescended +condescending +condescendingly +condescends +condescension +condiment +condiments +condition +conditional +conditionally +conditionals +conditioned +conditioner +conditioners +conditioning +conditions +condo +condole +condoled +condolence +condolences +condoles +condoling +condom +condominium +condominiums +condoms +condone +condoned +condones +condoning +condor +condors +condos +conduce +conduced +conduces +conducing +conducive +conduct +conducted +conducting +conduction +conductive +conductivity +conductor +conductors +conducts +conduit +conduits +cone +cones +confab +confabbed +confabbing +confabs +confection +confectioner +confectioneries +confectioners +confectionery +confections +confederacies +confederacy +confederate +confederated +confederates +confederating +confederation +confederations +confer +conference +conferences +conferencing +conferment +conferments +conferred +conferrer +conferring +confers +confess +confessed +confessedly +confesses +confessing +confession +confessional +confessionals +confessions +confessor +confessors +confetti +confidant +confidante +confidantes +confidants +confide +confided +confidence +confidences +confident +confidential +confidentiality +confidentially +confidently +confides +confiding +configurable +configuration +configurations +configure +configured +configures +configuring +confine +confined +confinement +confinements +confines +confining +confirm +confirmation +confirmations +confirmatory +confirmed +confirming +confirms +confiscate +confiscated +confiscates +confiscating +confiscation +confiscations +conflagration +conflagrations +conflict +conflicted +conflicting +conflicts +confluence +confluences +confluent +conform +conformance +conformation +conformations +conformed +conforming +conformist +conformists +conformity +conforms +confound +confounded +confounding +confounds +confront +confrontation +confrontational +confrontations +confronted +confronting +confronts +confrère +confrères +confuse +confused +confusedly +confuses +confusing +confusingly +confusion +confusions +confute +confuted +confutes +confuting +conga +congaed +congaing +congas +congeal +congealed +congealing +congeals +congenial +congeniality +congenially +congenital +congenitally +congest +congested +congesting +congestion +congestive +congests +conglomerate +conglomerated +conglomerates +conglomerating +conglomeration +conglomerations +congratulate +congratulated +congratulates +congratulating +congratulation +congratulations +congratulatory +congregate +congregated +congregates +congregating +congregation +congregational +congregations +congress +congresses +congressional +congressman +congressmen +congresswoman +congresswomen +congruence +congruent +congruities +congruity +congruous +conic +conical +conics +conifer +coniferous +conifers +conjectural +conjecture +conjectured +conjectures +conjecturing +conjoin +conjoined +conjoining +conjoins +conjoint +conjugal +conjugate +conjugated +conjugates +conjugating +conjugation +conjugations +conjunction +conjunctions +conjunctive +conjunctives +conjunctivitis +conjuncture +conjunctures +conjure +conjured +conjurer +conjurers +conjures +conjuring +conjuror +conjurors +conk +conked +conking +conks +connect +connected +connecting +connection +connections +connective +connectives +connectivity +connector +connectors +connects +conned +conning +connivance +connive +connived +conniver +connivers +connives +conniving +connoisseur +connoisseurs +connotation +connotations +connotative +connote +connoted +connotes +connoting +connubial +conquer +conquered +conquering +conqueror +conquerors +conquers +conquest +conquests +conquistador +conquistadors +cons +consanguinity +conscience +consciences +conscientious +conscientiously +conscientiousness +conscious +consciously +consciousness +consciousnesses +conscript +conscripted +conscripting +conscription +conscripts +consecrate +consecrated +consecrates +consecrating +consecration +consecrations +consecutive +consecutively +consensual +consensus +consensuses +consent +consented +consenting +consents +consequence +consequences +consequent +consequential +consequently +conservation +conservationist +conservationists +conservatism +conservative +conservatively +conservatives +conservator +conservatories +conservators +conservatory +conserve +conserved +conserves +conserving +consider +considerable +considerably +considerate +considerately +consideration +considerations +considered +considering +considers +consign +consigned +consigning +consignment +consignments +consigns +consist +consisted +consistencies +consistency +consistent +consistently +consisting +consists +consolation +consolations +console +consoled +consoles +consolidate +consolidated +consolidates +consolidating +consolidation +consolidations +consoling +consommé +consonance +consonances +consonant +consonants +consort +consorted +consortia +consorting +consortium +consorts +conspicuous +conspicuously +conspiracies +conspiracy +conspirator +conspiratorial +conspirators +conspire +conspired +conspires +conspiring +constable +constables +constabularies +constabulary +constancy +constant +constantly +constants +constellation +constellations +consternation +constipate +constipated +constipates +constipating +constipation +constituencies +constituency +constituent +constituents +constitute +constituted +constitutes +constituting +constitution +constitutional +constitutionality +constitutionally +constitutionals +constitutions +constrain +constrained +constraining +constrains +constraint +constraints +constrict +constricted +constricting +constriction +constrictions +constrictive +constrictor +constrictors +constricts +construct +constructed +constructing +construction +constructions +constructive +constructively +constructor +constructors +constructs +construe +construed +construes +construing +consul +consular +consulate +consulates +consuls +consult +consultancies +consultancy +consultant +consultants +consultation +consultations +consultative +consulted +consulting +consults +consumable +consumables +consume +consumed +consumer +consumerism +consumers +consumes +consuming +consummate +consummated +consummates +consummating +consummation +consummations +consumption +consumptive +consumptives +contact +contactable +contacted +contacting +contacts +contagion +contagions +contagious +contain +contained +container +containers +containing +containment +contains +contaminant +contaminants +contaminate +contaminated +contaminates +contaminating +contamination +contemplate +contemplated +contemplates +contemplating +contemplation +contemplative +contemplatives +contemporaneous +contemporaneously +contemporaries +contemporary +contempt +contemptible +contemptibly +contemptuous +contemptuously +contend +contended +contender +contenders +contending +contends +content +contented +contentedly +contentedness +contenting +contention +contentions +contentious +contentiously +contentment +contents +contest +contestant +contestants +contested +contesting +contests +context +contexts +contextual +contiguity +contiguous +continence +continent +continental +continentals +continents +contingencies +contingency +contingent +contingents +continua +continual +continually +continuance +continuances +continuation +continuations +continue +continued +continues +continuing +continuity +continuous +continuously +continuum +contort +contorted +contorting +contortion +contortionist +contortionists +contortions +contorts +contour +contoured +contouring +contours +contraband +contraception +contraceptive +contraceptives +contract +contracted +contractile +contracting +contraction +contractions +contractor +contractors +contracts +contractual +contractually +contradict +contradicted +contradicting +contradiction +contradictions +contradictory +contradicts +contradistinction +contradistinctions +contrail +contrails +contralto +contraltos +contraption +contraptions +contrapuntal +contraries +contrarily +contrariness +contrariwise +contrary +contrast +contrasted +contrasting +contrasts +contravene +contravened +contravenes +contravening +contravention +contraventions +contretemps +contribute +contributed +contributes +contributing +contribution +contributions +contributor +contributors +contributory +contrite +contritely +contrition +contrivance +contrivances +contrive +contrived +contrives +contriving +control +controllable +controlled +controller +controllers +controlling +controls +controversial +controversially +controversies +controversy +controvert +controverted +controverting +controverts +contumacious +contumelies +contumely +contuse +contused +contuses +contusing +contusion +contusions +conundrum +conundrums +conurbation +conurbations +convalesce +convalesced +convalescence +convalescences +convalescent +convalescents +convalesces +convalescing +convection +convene +convened +convenes +convenience +conveniences +convenient +conveniently +convening +convent +convention +conventional +conventionality +conventionally +conventions +convents +converge +converged +convergence +convergences +convergent +converges +converging +conversant +conversation +conversational +conversationalist +conversationalists +conversationally +conversations +converse +conversed +conversely +converses +conversing +conversion +conversions +convert +converted +converter +converters +convertible +convertibles +converting +convertor +convertors +converts +convex +convexity +convey +conveyance +conveyances +conveyed +conveying +conveyor +conveyors +conveys +convict +convicted +convicting +conviction +convictions +convicts +convince +convinced +convinces +convincing +convincingly +convivial +conviviality +convocation +convocations +convoke +convoked +convokes +convoking +convoluted +convolution +convolutions +convoy +convoyed +convoying +convoys +convulse +convulsed +convulses +convulsing +convulsion +convulsions +convulsive +convulsively +coo +cooed +cooing +cook +cookbook +cookbooks +cooked +cooker +cookeries +cookers +cookery +cookie +cookies +cooking +cookout +cookouts +cooks +cooky +cool +coolant +coolants +cooled +cooler +coolers +coolest +coolie +coolies +cooling +coolly +coolness +cools +coon +coons +coop +cooped +cooper +cooperate +cooperated +cooperates +cooperating +cooperation +cooperative +cooperatively +cooperatives +coopered +coopering +coopers +cooping +coops +coordinate +coordinated +coordinates +coordinating +coordination +coordinator +coordinators +coos +coot +cootie +cooties +coots +cop +cope +coped +copes +copied +copier +copiers +copies +copilot +copilots +coping +copings +copious +copiously +copped +copper +copperhead +copperheads +coppers +coppery +copping +copra +cops +copse +copses +copter +copters +copula +copulas +copulate +copulated +copulates +copulating +copulation +copy +copycat +copycats +copycatted +copycatting +copying +copyright +copyrighted +copyrighting +copyrights +copywriter +copywriters +coquette +coquetted +coquettes +coquetting +coquettish +coral +corals +cord +corded +cordial +cordiality +cordially +cordials +cording +cordite +cordless +cordon +cordoned +cordoning +cordons +cords +corduroy +corduroys +core +cored +cores +corespondent +corespondents +coriander +coring +cork +corked +corking +corks +corkscrew +corkscrewed +corkscrewing +corkscrews +corm +cormorant +cormorants +corms +corn +cornball +cornballs +cornbread +corncob +corncobs +cornea +corneal +corneas +corned +corner +cornered +cornering +corners +cornerstone +cornerstones +cornet +cornets +cornflakes +cornflower +cornflowers +cornice +cornices +cornier +corniest +corning +cornmeal +cornrow +cornrowed +cornrowing +cornrows +corns +cornstalk +cornstalks +cornstarch +cornucopia +cornucopias +corny +corolla +corollaries +corollary +corollas +corona +coronaries +coronary +coronas +coronation +coronations +coroner +coroners +coronet +coronets +corpora +corporal +corporals +corporate +corporation +corporations +corporeal +corps +corpse +corpses +corpulence +corpulent +corpus +corpuscle +corpuscles +corral +corralled +corralling +corrals +correct +correctable +corrected +correcter +correctest +correcting +correction +correctional +corrections +corrective +correctives +correctly +correctness +corrector +corrects +correlate +correlated +correlates +correlating +correlation +correlations +correlative +correlatives +correspond +corresponded +correspondence +correspondences +correspondent +correspondents +corresponding +correspondingly +corresponds +corridor +corridors +corroborate +corroborated +corroborates +corroborating +corroboration +corroborations +corroborative +corrode +corroded +corrodes +corroding +corrosion +corrosive +corrosives +corrugate +corrugated +corrugates +corrugating +corrugation +corrugations +corrupt +corrupted +corrupter +corruptest +corruptible +corrupting +corruption +corruptions +corruptly +corruptness +corrupts +corsage +corsages +corsair +corsairs +corset +corseted +corseting +corsets +cortex +cortical +cortices +cortisone +cortège +cortèges +coruscate +coruscated +coruscates +coruscating +cosign +cosignatories +cosignatory +cosigned +cosigner +cosigners +cosigning +cosigns +cosine +cosmetic +cosmetically +cosmetics +cosmetologist +cosmetologists +cosmetology +cosmic +cosmically +cosmogonies +cosmogony +cosmological +cosmologies +cosmologist +cosmologists +cosmology +cosmonaut +cosmonauts +cosmopolitan +cosmopolitans +cosmos +cosmoses +cosplay +cosponsor +cosponsored +cosponsoring +cosponsors +cost +costar +costarred +costarring +costars +costed +costing +costings +costlier +costliest +costliness +costly +costs +costume +costumed +costumes +costuming +cot +cote +coterie +coteries +cotes +cotillion +cotillions +cots +cottage +cottages +cotter +cotters +cotton +cottoned +cottoning +cottonmouth +cottonmouths +cottons +cottonseed +cottonseeds +cottontail +cottontails +cottonwood +cottonwoods +cotyledon +cotyledons +couch +couched +couches +couching +cougar +cougars +cough +coughed +coughing +coughs +could +council +councilman +councilmen +councilor +councilors +councils +councilwoman +councilwomen +counsel +counseled +counseling +counselings +counselor +counselors +counsels +count +countable +countably +countdown +countdowns +counted +countenance +countenanced +countenances +countenancing +counter +counteract +counteracted +counteracting +counteraction +counteractions +counteracts +counterattack +counterattacked +counterattacking +counterattacks +counterbalance +counterbalanced +counterbalances +counterbalancing +counterclaim +counterclaimed +counterclaiming +counterclaims +counterclockwise +counterculture +countered +counterespionage +counterexample +counterexamples +counterfeit +counterfeited +counterfeiter +counterfeiters +counterfeiting +counterfeits +countering +counterintelligence +countermand +countermanded +countermanding +countermands +counteroffer +counteroffers +counterpane +counterpanes +counterpart +counterparts +counterpoint +counterpoints +counterproductive +counterrevolution +counterrevolutionaries +counterrevolutionary +counterrevolutions +counters +countersign +countersigned +countersigning +countersigns +countersink +countersinking +countersinks +countersunk +countertenor +countertenors +counterweight +counterweights +countess +countesses +counties +counting +countless +countries +countrified +country +countryman +countrymen +countryside +countrysides +countrywoman +countrywomen +counts +county +coup +coupe +coupes +couple +coupled +couples +couplet +couplets +coupling +couplings +coupon +coupons +coups +courage +courageous +courageously +courier +couriers +course +coursed +courser +courses +coursing +court +courted +courteous +courteously +courteousness +courtesan +courtesans +courtesies +courtesy +courthouse +courthouses +courtier +courtiers +courting +courtlier +courtliest +courtliness +courtly +courtroom +courtrooms +courts +courtship +courtships +courtyard +courtyards +cousin +cousins +cove +coven +covenant +covenanted +covenanting +covenants +covens +cover +coverage +coverall +coveralls +covered +covering +coverings +coverlet +coverlets +covers +covert +covertly +coverts +coves +covet +coveted +coveting +covetous +covetously +covetousness +covets +covey +coveys +cow +coward +cowardice +cowardliness +cowardly +cowards +cowbird +cowbirds +cowboy +cowboys +cowed +cower +cowered +cowering +cowers +cowgirl +cowgirls +cowhand +cowhands +cowhide +cowhides +cowing +cowl +cowlick +cowlicks +cowling +cowlings +cowls +coworker +coworkers +cowpoke +cowpokes +cowpox +cowpuncher +cowpunchers +cows +cowslip +cowslips +cox +coxcomb +coxcombs +coxswain +coxswains +coy +coyer +coyest +coyly +coyness +coyote +coyotes +cozen +cozened +cozening +cozens +cozier +cozies +coziest +cozily +coziness +cozy +crab +crabbed +crabbier +crabbiest +crabbily +crabbiness +crabbing +crabby +crabs +crack +crackdown +crackdowns +cracked +cracker +crackerjack +crackerjacks +crackers +cracking +crackle +crackled +crackles +crackling +crackly +crackpot +crackpots +cracks +crackup +crackups +cradle +cradled +cradles +cradling +craft +crafted +craftier +craftiest +craftily +craftiness +crafting +crafts +craftsman +craftsmanship +craftsmen +crafty +crag +craggier +craggiest +craggy +crags +cram +crammed +cramming +cramp +cramped +cramping +cramps +crams +cranberries +cranberry +crane +craned +cranes +cranial +craning +cranium +craniums +crank +crankcase +crankcases +cranked +crankier +crankiest +crankiness +cranking +cranks +crankshaft +crankshafts +cranky +crannies +cranny +crap +crape +crapes +crapped +crappier +crappiest +crapping +crappy +craps +crash +crashed +crashes +crashing +crass +crasser +crassest +crassly +crassness +crate +crated +crater +cratered +cratering +craters +crates +crating +cravat +cravats +crave +craved +craven +cravenly +cravens +craves +craving +cravings +craw +crawl +crawled +crawling +crawls +crawlspace +crawlspaces +craws +crayfish +crayfishes +crayon +crayoned +crayoning +crayons +craze +crazed +crazes +crazier +crazies +craziest +crazily +craziness +crazing +crazy +creak +creaked +creakier +creakiest +creaking +creaks +creaky +cream +creamed +creamer +creameries +creamers +creamery +creamier +creamiest +creaminess +creaming +creams +creamy +crease +creased +creases +creasing +create +created +creates +creating +creation +creationism +creations +creative +creatively +creativeness +creatives +creativity +creator +creators +creature +creatures +credence +credential +credentials +credenza +credenzas +credibility +credible +credibly +credit +creditable +creditably +credited +crediting +creditor +creditors +credits +credo +credos +credulity +credulous +credulously +creed +creeds +creek +creeks +creel +creels +creep +creeper +creepers +creepier +creepiest +creepily +creepiness +creeping +creeps +creepy +cremate +cremated +cremates +cremating +cremation +cremations +crematoria +crematories +crematorium +crematoriums +crematory +creole +creoles +creosote +creosoted +creosotes +creosoting +crepe +crepes +crept +crescendo +crescendos +crescent +crescents +cress +crest +crested +crestfallen +cresting +crests +cretin +cretinous +cretins +crevasse +crevasses +crevice +crevices +crew +crewed +crewing +crewman +crewmen +crews +crib +cribbage +cribbed +cribbing +cribs +crick +cricked +cricket +cricketer +cricketers +crickets +cricking +cricks +cried +crier +criers +cries +crime +crimes +criminal +criminally +criminals +criminologist +criminologists +criminology +crimp +crimped +crimping +crimps +crimson +crimsoned +crimsoning +crimsons +cringe +cringed +cringes +cringing +crinkle +crinkled +crinkles +crinklier +crinkliest +crinkling +crinkly +crinoline +crinolines +cripple +crippled +cripples +crippling +crises +crisis +crisp +crisped +crisper +crispest +crispier +crispiest +crisping +crisply +crispness +crisps +crispy +crisscross +crisscrossed +crisscrosses +crisscrossing +criteria +criterion +critic +critical +critically +criticism +criticisms +criticize +criticized +criticizes +criticizing +critics +critique +critiqued +critiques +critiquing +critter +critters +croak +croaked +croaking +croaks +crochet +crocheted +crocheting +crochets +crock +crocked +crockery +crocks +crocodile +crocodiles +crocus +crocuses +crofts +croissant +croissants +crone +crones +cronies +crony +crook +crooked +crookeder +crookedest +crookedly +crookedness +crooking +crooks +croon +crooned +crooner +crooners +crooning +croons +crop +cropped +cropper +croppers +cropping +crops +croquet +croquette +croquettes +crosier +crosiers +cross +crossbar +crossbars +crossbeam +crossbeams +crossbones +crossbow +crossbows +crossbred +crossbreed +crossbreeding +crossbreeds +crosscheck +crosschecked +crosschecking +crosschecks +crossed +crosser +crosses +crossest +crossfire +crossfires +crossing +crossings +crossly +crossness +crossover +crossovers +crosspiece +crosspieces +crossroad +crossroads +crosstown +crosswalk +crosswalks +crosswise +crossword +crosswords +crotch +crotches +crotchet +crotchets +crotchety +crouch +crouched +crouches +crouching +croup +croupier +croupiers +croupiest +croupy +crow +crowbar +crowbars +crowd +crowded +crowdfund +crowdfunded +crowdfunding +crowdfunds +crowding +crowds +crowed +crowing +crown +crowned +crowning +crowns +crows +crozier +croziers +croûton +croûtons +crucial +crucially +crucible +crucibles +crucified +crucifies +crucifix +crucifixes +crucifixion +crucifixions +cruciform +cruciforms +crucify +crucifying +crud +cruddier +cruddiest +cruddy +crude +crudely +crudeness +cruder +crudest +crudities +crudity +crudités +cruel +crueler +cruelest +cruelly +cruelties +cruelty +cruet +cruets +cruise +cruised +cruiser +cruisers +cruises +cruising +cruller +crullers +crumb +crumbed +crumbier +crumbiest +crumbing +crumble +crumbled +crumbles +crumblier +crumbliest +crumbling +crumbly +crumbs +crumby +crummier +crummiest +crummy +crumpet +crumpets +crumple +crumpled +crumples +crumpling +crunch +crunched +cruncher +crunches +crunchier +crunchiest +crunching +crunchy +crusade +crusaded +crusader +crusaders +crusades +crusading +crush +crushed +crushes +crushing +crust +crustacean +crustaceans +crusted +crustier +crustiest +crusting +crusts +crusty +crutch +crutches +crux +cruxes +cry +crybabies +crybaby +crying +cryings +cryogenics +crypt +cryptic +cryptically +cryptogram +cryptograms +cryptographer +cryptographers +cryptography +crypts +crystal +crystalline +crystallization +crystallize +crystallized +crystallizes +crystallizing +crystallographic +crystallography +crystals +crèche +crèches +cs +cub +cubbyhole +cubbyholes +cube +cubed +cubes +cubic +cubical +cubicle +cubicles +cubing +cubism +cubist +cubists +cubit +cubits +cubs +cuckold +cuckolded +cuckolding +cuckolds +cuckoo +cuckoos +cucumber +cucumbers +cud +cuddle +cuddled +cuddles +cuddlier +cuddliest +cuddling +cuddly +cudgel +cudgeled +cudgeling +cudgels +cuds +cue +cued +cues +cuff +cuffed +cuffing +cuffs +cuing +cuisine +cuisines +culinary +cull +culled +cullender +cullenders +culling +culls +culminate +culminated +culminates +culminating +culmination +culminations +culotte +culottes +culpability +culpable +culprit +culprits +cult +cultivate +cultivated +cultivates +cultivating +cultivation +cultivator +cultivators +cults +cultural +culturally +culture +cultured +cultures +culturing +culvert +culverts +cumbersome +cumin +cummerbund +cummerbunds +cumming +cums +cumulative +cumulatively +cumuli +cumulus +cuneiform +cunnilingus +cunning +cunninger +cunningest +cunningly +cunt +cunts +cup +cupboard +cupboards +cupcake +cupcakes +cupful +cupfuls +cupid +cupidity +cupids +cupola +cupolas +cupped +cupping +cups +cur +curable +curacies +curacy +curate +curates +curative +curatives +curator +curators +curb +curbed +curbing +curbs +curd +curdle +curdled +curdles +curdling +curds +cure +cured +curer +cures +curfew +curfews +curie +curies +curing +curio +curios +curiosities +curiosity +curious +curiously +curl +curled +curler +curlers +curlew +curlews +curlicue +curlicued +curlicues +curlicuing +curlier +curliest +curliness +curling +curls +curly +curmudgeon +curmudgeons +currant +currants +currencies +currency +current +currently +currents +curricula +curriculum +curried +curries +curry +currycomb +currycombed +currycombing +currycombs +currying +curs +curse +cursed +curses +cursing +cursive +cursor +cursorily +cursors +cursory +curt +curtail +curtailed +curtailing +curtailment +curtailments +curtails +curtain +curtained +curtaining +curtains +curter +curtest +curtly +curtness +curtsied +curtsies +curtsy +curtsying +curvaceous +curvature +curvatures +curve +curved +curves +curvier +curviest +curving +curvy +cushier +cushiest +cushion +cushioned +cushioning +cushions +cushy +cusp +cuspid +cuspids +cusps +cuss +cussed +cusses +cussing +custard +custards +custodial +custodian +custodians +custody +custom +customarily +customary +customer +customers +customization +customize +customized +customizes +customizing +customs +cut +cutback +cutbacks +cute +cutely +cuteness +cuter +cutesier +cutesiest +cutest +cutesy +cuticle +cuticles +cutlass +cutlasses +cutlery +cutlet +cutlets +cutoff +cutoffs +cutout +cutouts +cuts +cutter +cutters +cutthroat +cutthroats +cutting +cuttings +cuttlefish +cuttlefishes +cutup +cutups +cyanide +cyberbullies +cyberbully +cybernetic +cybernetics +cyberpunk +cyberpunks +cybersex +cyberspace +cyclamen +cyclamens +cycle +cycled +cycles +cyclic +cyclical +cyclically +cycling +cyclist +cyclists +cyclone +cyclones +cyclonic +cyclotron +cyclotrons +cygnet +cygnets +cylinder +cylinders +cylindrical +cymbal +cymbals +cynic +cynical +cynically +cynicism +cynics +cynosure +cynosures +cypher +cypress +cypresses +cyst +cystic +cysts +cytology +cytoplasm +czar +czarina +czarinas +czars +d +dB +dab +dabbed +dabbing +dabble +dabbled +dabbler +dabblers +dabbles +dabbling +dabs +dacha +dachas +dachshund +dachshunds +dactyl +dactylic +dactylics +dactyls +dad +daddies +daddy +dado +dadoes +dados +dads +daemon +daemons +daffier +daffiest +daffodil +daffodils +daffy +daft +dafter +daftest +dagger +daggers +daguerreotype +daguerreotyped +daguerreotypes +daguerreotyping +dahlia +dahlias +dailies +daily +daintier +dainties +daintiest +daintily +daintiness +dainty +daiquiri +daiquiris +dairies +dairy +dairying +dairymaid +dairymaids +dairyman +dairymen +dais +daises +daisies +daisy +dale +dales +dalliance +dalliances +dallied +dallies +dally +dallying +dalmatian +dalmatians +dam +damage +damaged +damages +damaging +damask +damasked +damasking +damasks +dame +dames +dammed +damming +damn +damnable +damnably +damnation +damned +damnedest +damning +damns +damp +damped +dampen +dampened +dampening +dampens +damper +dampers +dampest +damping +damply +dampness +damps +dams +damsel +damsels +damson +damsons +dance +danced +dancer +dancers +dances +dancing +dandelion +dandelions +dander +dandier +dandies +dandiest +dandle +dandled +dandles +dandling +dandruff +dandy +danger +dangerous +dangerously +dangers +dangle +dangled +dangles +dangling +dank +danker +dankest +dankly +dankness +dapper +dapperer +dapperest +dapple +dappled +dapples +dappling +dare +dared +daredevil +daredevils +dares +daring +daringly +dark +darken +darkened +darkening +darkens +darker +darkest +darkly +darkness +darkroom +darkrooms +darling +darlings +darn +darned +darneder +darnedest +darning +darns +dart +dartboard +dartboards +darted +darting +darts +dash +dashboard +dashboards +dashed +dashes +dashiki +dashikis +dashing +dashingly +dastardly +data +database +databases +datatype +date +dated +dateline +datelined +datelines +datelining +dates +dating +dative +datives +datum +daub +daubed +dauber +daubers +daubing +daubs +daughter +daughters +daunt +daunted +daunting +dauntless +dauntlessly +dauntlessness +daunts +dauphin +dauphins +davenport +davenports +davit +davits +dawdle +dawdled +dawdler +dawdlers +dawdles +dawdling +dawn +dawned +dawning +dawns +day +daybed +daybeds +daybreak +daydream +daydreamed +daydreamer +daydreamers +daydreaming +daydreams +daylight +daylights +days +daytime +daze +dazed +dazes +dazing +dazzle +dazzled +dazzles +dazzling +deacon +deaconess +deaconesses +deacons +deactivate +deactivated +deactivates +deactivating +dead +deadbeat +deadbeats +deadbolt +deadbolts +deaden +deadened +deadening +deadens +deader +deadest +deadlier +deadliest +deadline +deadlines +deadliness +deadlock +deadlocked +deadlocking +deadlocks +deadly +deadpan +deadpanned +deadpanning +deadpans +deadwood +deaf +deafen +deafened +deafening +deafens +deafer +deafest +deafness +deal +dealer +dealers +dealership +dealerships +dealing +dealings +deals +dealt +dean +deans +dear +dearer +dearest +dearly +dearness +dears +dearth +dearths +death +deathbed +deathbeds +deathblow +deathblows +deathless +deathlike +deathly +deaths +deathtrap +deathtraps +deaves +deb +debacle +debacles +debar +debark +debarkation +debarked +debarking +debarks +debarment +debarred +debarring +debars +debase +debased +debasement +debasements +debases +debasing +debatable +debate +debated +debater +debaters +debates +debating +debauch +debauched +debaucheries +debauchery +debauches +debauching +debenture +debentures +debilitate +debilitated +debilitates +debilitating +debilitation +debilities +debility +debit +debited +debiting +debits +debonair +debonairly +debrief +debriefed +debriefing +debriefings +debriefs +debris +debs +debt +debtor +debtors +debts +debug +debugged +debugger +debuggers +debugging +debugs +debunk +debunked +debunking +debunks +debut +debuted +debuting +debuts +decade +decadence +decadent +decadently +decadents +decades +decaf +decaffeinate +decaffeinated +decaffeinates +decaffeinating +decal +decals +decamp +decamped +decamping +decamps +decant +decanted +decanter +decanters +decanting +decants +decapitate +decapitated +decapitates +decapitating +decapitation +decapitations +decathlon +decathlons +decay +decayed +decaying +decays +decease +deceased +deceases +deceasing +decedent +decedents +deceit +deceitful +deceitfully +deceitfulness +deceits +deceive +deceived +deceiver +deceivers +deceives +deceiving +decelerate +decelerated +decelerates +decelerating +deceleration +decencies +decency +decent +decently +decentralization +decentralize +decentralized +decentralizes +decentralizing +deception +deceptions +deceptive +deceptively +deceptiveness +decibel +decibels +decide +decided +decidedly +decides +deciding +deciduous +decimal +decimals +decimate +decimated +decimates +decimating +decimation +decipher +decipherable +deciphered +deciphering +deciphers +decision +decisions +decisive +decisively +decisiveness +deck +decked +deckhand +deckhands +decking +decks +declaim +declaimed +declaiming +declaims +declamation +declamations +declamatory +declaration +declarations +declarative +declare +declared +declares +declaring +declassified +declassifies +declassify +declassifying +declension +declensions +declination +decline +declined +declines +declining +declivities +declivity +decode +decoded +decoder +decodes +decoding +decolonization +decolonize +decolonized +decolonizes +decolonizing +decommission +decommissioned +decommissioning +decommissions +decompose +decomposed +decomposes +decomposing +decomposition +decompress +decompressed +decompresses +decompressing +decompression +decongestant +decongestants +deconstruction +deconstructions +decontaminate +decontaminated +decontaminates +decontaminating +decontamination +decor +decorate +decorated +decorates +decorating +decoration +decorations +decorative +decorator +decorators +decorous +decorously +decors +decorum +decoy +decoyed +decoying +decoys +decrease +decreased +decreases +decreasing +decree +decreed +decreeing +decrees +decremented +decrements +decrepit +decrepitude +decrescendo +decrescendos +decried +decries +decriminalization +decriminalize +decriminalized +decriminalizes +decriminalizing +decry +decrying +decryption +dedicate +dedicated +dedicates +dedicating +dedication +dedications +deduce +deduced +deduces +deducible +deducing +deduct +deducted +deductible +deductibles +deducting +deduction +deductions +deductive +deducts +deed +deeded +deeding +deeds +deejay +deejays +deem +deemed +deeming +deems +deep +deepen +deepened +deepening +deepens +deeper +deepest +deeply +deepness +deeps +deer +deerskin +deescalate +deescalated +deescalates +deescalating +deface +defaced +defacement +defaces +defacing +defamation +defamatory +defame +defamed +defames +defaming +default +defaulted +defaulter +defaulters +defaulting +defaults +defeat +defeated +defeating +defeatism +defeatist +defeatists +defeats +defecate +defecated +defecates +defecating +defecation +defect +defected +defecting +defection +defections +defective +defectives +defector +defectors +defects +defend +defendant +defendants +defended +defender +defenders +defending +defends +defense +defensed +defenseless +defenses +defensible +defensing +defensive +defensively +defensiveness +defer +deference +deferential +deferentially +deferment +deferments +deferred +deferring +defers +defiance +defiant +defiantly +deficiencies +deficiency +deficient +deficit +deficits +defied +defies +defile +defiled +defilement +defiles +defiling +definable +define +defined +definer +definers +defines +defining +definite +definitely +definiteness +definition +definitions +definitive +definitively +deflate +deflated +deflates +deflating +deflation +deflect +deflected +deflecting +deflection +deflections +deflector +deflectors +deflects +defogger +defoggers +defoliant +defoliants +defoliate +defoliated +defoliates +defoliating +defoliation +deforest +deforestation +deforested +deforesting +deforests +deform +deformation +deformations +deformed +deforming +deformities +deformity +deforms +defraud +defrauded +defrauding +defrauds +defray +defrayal +defrayed +defraying +defrays +defrost +defrosted +defroster +defrosters +defrosting +defrosts +deft +defter +deftest +deftly +deftness +defunct +defuse +defused +defuses +defusing +defy +defying +degeneracy +degenerate +degenerated +degenerates +degenerating +degeneration +degenerative +degradation +degrade +degraded +degrades +degrading +degree +degrees +dehumanization +dehumanize +dehumanized +dehumanizes +dehumanizing +dehumidified +dehumidifier +dehumidifiers +dehumidifies +dehumidify +dehumidifying +dehydrate +dehydrated +dehydrates +dehydrating +dehydration +deice +deiced +deicer +deicers +deices +deicing +deification +deified +deifies +deify +deifying +deign +deigned +deigning +deigns +deism +deities +deity +deject +dejected +dejectedly +dejecting +dejection +dejects +delay +delayed +delaying +delays +delectable +delectation +delegate +delegated +delegates +delegating +delegation +delegations +delete +deleted +deleterious +deletes +deleting +deletion +deletions +deleverage +deleveraged +deleverages +deleveraging +deli +deliberate +deliberated +deliberately +deliberates +deliberating +deliberation +deliberations +delicacies +delicacy +delicate +delicately +delicatessen +delicatessens +delicious +deliciously +deliciousness +delight +delighted +delightful +delightfully +delighting +delights +delimit +delimited +delimiter +delimiters +delimiting +delimits +delineate +delineated +delineates +delineating +delineation +delineations +delinquencies +delinquency +delinquent +delinquently +delinquents +deliquescent +delirious +deliriously +delirium +deliriums +delis +deliver +deliverance +delivered +deliverer +deliverers +deliveries +delivering +delivers +delivery +dell +dells +delphinium +delphiniums +delta +deltas +delude +deluded +deludes +deluding +deluge +deluged +deluges +deluging +delusion +delusions +delusive +deluxe +delve +delved +delves +delving +demagnetization +demagnetize +demagnetized +demagnetizes +demagnetizing +demagogic +demagogue +demagoguery +demagogues +demagogy +demand +demanded +demanding +demands +demarcate +demarcated +demarcates +demarcating +demarcation +demean +demeaned +demeaning +demeanor +demeans +demented +dementedly +dementia +demerit +demerits +demesne +demesnes +demigod +demigods +demijohn +demijohns +demilitarization +demilitarize +demilitarized +demilitarizes +demilitarizing +demise +demised +demises +demising +demitasse +demitasses +demo +demobilization +demobilize +demobilized +demobilizes +demobilizing +democracies +democracy +democrat +democratic +democratically +democratization +democratize +democratized +democratizes +democratizing +democrats +demoed +demographer +demographers +demographic +demographically +demographics +demography +demoing +demolish +demolished +demolishes +demolishing +demolition +demolitions +demon +demoniac +demoniacal +demonic +demons +demonstrable +demonstrably +demonstrate +demonstrated +demonstrates +demonstrating +demonstration +demonstrations +demonstrative +demonstratively +demonstratives +demonstrator +demonstrators +demoralization +demoralize +demoralized +demoralizes +demoralizing +demos +demote +demoted +demotes +demoting +demotion +demotions +demount +demur +demure +demurely +demurer +demurest +demurred +demurring +demurs +den +denature +denatured +denatures +denaturing +dendrite +dendrites +deniability +denial +denials +denied +denier +deniers +denies +denigrate +denigrated +denigrates +denigrating +denigration +denim +denims +denizen +denizens +denominate +denominated +denominates +denominating +denomination +denominational +denominations +denominator +denominators +denotation +denotations +denote +denoted +denotes +denoting +denouement +denouements +denounce +denounced +denouncement +denouncements +denounces +denouncing +dens +dense +densely +denseness +denser +densest +densities +density +dent +dental +dented +dentifrice +dentifrices +dentin +dentine +denting +dentist +dentistry +dentists +dents +denture +dentures +denude +denuded +denudes +denuding +denunciation +denunciations +deny +denying +deodorant +deodorants +deodorize +deodorized +deodorizer +deodorizers +deodorizes +deodorizing +depart +departed +departing +department +departmental +departmentalize +departmentalized +departmentalizes +departmentalizing +departments +departs +departure +departures +depend +dependability +dependable +dependably +depended +dependence +dependencies +dependency +dependent +dependents +depending +depends +depict +depicted +depicting +depiction +depictions +depicts +depilatories +depilatory +deplane +deplaned +deplanes +deplaning +deplete +depleted +depletes +depleting +depletion +deplorable +deplorably +deplore +deplored +deplores +deploring +deploy +deployed +deploying +deployment +deployments +deploys +depoliticize +depoliticized +depoliticizes +depoliticizing +depopulate +depopulated +depopulates +depopulating +depopulation +deport +deportation +deportations +deported +deporting +deportment +deports +depose +deposed +deposes +deposing +deposit +deposited +depositing +deposition +depositions +depositor +depositories +depositors +depository +deposits +depot +depots +deprave +depraved +depraves +depraving +depravities +depravity +deprecate +deprecated +deprecates +deprecating +deprecation +deprecatory +depreciate +depreciated +depreciates +depreciating +depreciation +depredation +depredations +depress +depressant +depressants +depressed +depresses +depressing +depressingly +depression +depressions +depressive +depressives +deprivation +deprivations +deprive +deprived +deprives +depriving +deprogram +deprogrammed +deprogramming +deprograms +depth +depths +deputation +deputations +depute +deputed +deputes +deputies +deputing +deputize +deputized +deputizes +deputizing +deputy +derail +derailed +derailing +derailment +derailments +derails +derange +deranged +derangement +deranges +deranging +derbies +derby +deregulate +deregulated +deregulates +deregulating +deregulation +derelict +dereliction +derelicts +deride +derided +derides +deriding +derision +derisive +derisively +derisory +derivable +derivation +derivations +derivative +derivatives +derive +derived +derives +deriving +dermatitis +dermatologist +dermatologists +dermatology +dermis +derogate +derogated +derogates +derogating +derogation +derogatory +derrick +derricks +derringer +derringers +derrière +derrières +dervish +dervishes +desalinate +desalinated +desalinates +desalinating +desalination +descant +descanted +descanting +descants +descend +descendant +descendants +descended +descender +descending +descends +descent +descents +describable +describe +described +describes +describing +descried +descries +description +descriptions +descriptive +descriptively +descriptor +descriptors +descry +descrying +desecrate +desecrated +desecrates +desecrating +desecration +desegregate +desegregated +desegregates +desegregating +desegregation +desensitization +desensitize +desensitized +desensitizes +desensitizing +desert +deserted +deserter +deserters +deserting +desertion +desertions +deserts +deserve +deserved +deservedly +deserves +deserving +desiccate +desiccated +desiccates +desiccating +desiccation +desiderata +desideratum +design +designate +designated +designates +designating +designation +designations +designed +designer +designers +designing +designs +desirability +desirable +desirably +desire +desired +desires +desiring +desirous +desist +desisted +desisting +desists +desk +desks +desktop +desktops +desolate +desolated +desolately +desolateness +desolates +desolating +desolation +despair +despaired +despairing +despairingly +despairs +desperado +desperadoes +desperate +desperately +desperation +despicable +despicably +despise +despised +despises +despising +despite +despoil +despoiled +despoiling +despoils +despondency +despondent +despondently +despot +despotic +despotism +despots +dessert +desserts +destabilize +destination +destinations +destine +destined +destines +destinies +destining +destiny +destitute +destitution +destroy +destroyed +destroyer +destroyers +destroying +destroys +destruct +destructed +destructible +destructing +destruction +destructive +destructively +destructiveness +destructs +desultory +detach +detachable +detached +detaches +detaching +detachment +detachments +detail +detailed +detailing +details +detain +detained +detainee +detainees +detaining +detainment +detains +detect +detectable +detected +detecting +detection +detective +detectives +detector +detectors +detects +detentes +detention +detentions +deter +detergent +detergents +deteriorate +deteriorated +deteriorates +deteriorating +deterioration +determinable +determinant +determinants +determinate +determination +determinations +determine +determined +determiner +determiners +determines +determining +determinism +deterministic +deterred +deterrence +deterrent +deterrents +deterring +deters +detest +detestable +detestation +detested +detesting +detests +dethrone +dethroned +dethronement +dethrones +dethroning +detonate +detonated +detonates +detonating +detonation +detonations +detonator +detonators +detour +detoured +detouring +detours +detox +detoxed +detoxes +detoxification +detoxified +detoxifies +detoxify +detoxifying +detoxing +detract +detracted +detracting +detraction +detractor +detractors +detracts +detriment +detrimental +detriments +detritus +deuce +deuces +deuterium +devaluation +devaluations +devalue +devalued +devalues +devaluing +devastate +devastated +devastates +devastating +devastation +develop +developed +developer +developers +developing +development +developmental +developments +develops +deviance +deviant +deviants +deviate +deviated +deviates +deviating +deviation +deviations +device +devices +devil +deviled +deviling +devilish +devilishly +devilment +devilries +devilry +devils +deviltries +deviltry +devious +deviously +deviousness +devise +devised +devises +devising +devoid +devolution +devolve +devolved +devolves +devolving +devote +devoted +devotedly +devotee +devotees +devotes +devoting +devotion +devotional +devotionals +devotions +devour +devoured +devouring +devours +devout +devouter +devoutest +devoutly +devoutness +dew +dewberries +dewberry +dewdrop +dewdrops +dewier +dewiest +dewlap +dewlaps +dewy +dexterity +dexterous +dexterously +dextrose +dharma +dhoti +dhotis +diabetes +diabetic +diabetics +diabolic +diabolical +diabolically +diacritic +diacritical +diacritics +diadem +diadems +diagnose +diagnosed +diagnoses +diagnosing +diagnosis +diagnostic +diagnostician +diagnosticians +diagnostics +diagonal +diagonally +diagonals +diagram +diagrammatic +diagrammed +diagramming +diagrams +dial +dialect +dialectal +dialectic +dialects +dialed +dialing +dialings +dialog +dialogue +dialogues +dials +dialyses +dialysis +dialyzes +diameter +diameters +diametrical +diametrically +diamond +diamonds +diaper +diapered +diapering +diapers +diaphanous +diaphragm +diaphragms +diaries +diarist +diarists +diarrhea +diary +diastolic +diatom +diatoms +diatribe +diatribes +dibble +dibbled +dibbles +dibbling +dice +diced +dices +dicey +dichotomies +dichotomy +dicier +diciest +dicing +dick +dicker +dickered +dickering +dickers +dickey +dickeys +dickies +dicks +dicky +dicta +dictate +dictated +dictates +dictating +dictation +dictations +dictator +dictatorial +dictators +dictatorship +dictatorships +diction +dictionaries +dictionary +dictum +did +didactic +diddle +diddled +diddles +diddling +die +died +diehard +diehards +diereses +dieresis +dies +diesel +dieseled +dieseling +diesels +diet +dietaries +dietary +dieted +dieter +dieters +dietetic +dietetics +dietician +dieticians +dieting +dietitian +dietitians +diets +differ +differed +difference +differences +different +differential +differentials +differentiate +differentiated +differentiates +differentiating +differentiation +differently +differing +differs +difficult +difficulties +difficulty +diffidence +diffident +diffidently +diffraction +diffuse +diffused +diffusely +diffuseness +diffuses +diffusing +diffusion +dig +digest +digested +digestible +digesting +digestion +digestions +digestive +digests +digger +diggers +digging +digit +digital +digitalis +digitally +digitization +digitize +digitized +digitizes +digitizing +digits +dignified +dignifies +dignify +dignifying +dignitaries +dignitary +dignities +dignity +digraph +digraphs +digress +digressed +digresses +digressing +digression +digressions +digressive +digs +dike +diked +dikes +diking +dilapidated +dilapidation +dilate +dilated +dilates +dilating +dilation +dilatory +dilemma +dilemmas +dilettante +dilettantes +dilettantism +diligence +diligent +diligently +dill +dillies +dills +dilly +dillydallied +dillydallies +dillydally +dillydallying +dilute +diluted +dilutes +diluting +dilution +dim +dime +dimension +dimensional +dimensionless +dimensions +dimer +dimes +diminish +diminished +diminishes +diminishing +diminuendo +diminuendoes +diminuendos +diminution +diminutions +diminutive +diminutives +dimly +dimmed +dimmer +dimmers +dimmest +dimming +dimness +dimple +dimpled +dimples +dimpling +dims +dimwit +dimwits +dimwitted +din +dine +dined +diner +diners +dines +dinette +dinettes +ding +dinged +dinghies +dinghy +dingier +dingiest +dinginess +dinging +dingo +dingoes +dings +dingy +dining +dinkier +dinkies +dinkiest +dinky +dinned +dinner +dinnered +dinnering +dinners +dinning +dinosaur +dinosaurs +dins +dint +diocesan +diocesans +diocese +dioceses +diode +diodes +diorama +dioramas +dioxide +dioxin +dioxins +dip +diphtheria +diphthong +diphthongs +diploma +diplomacy +diplomas +diplomat +diplomata +diplomatic +diplomatically +diplomats +dipole +dipped +dipper +dippers +dipping +dips +dipsomania +dipsomaniac +dipsomaniacs +dipstick +dipsticks +dire +direct +directed +directer +directest +directing +direction +directional +directions +directive +directives +directly +directness +director +directorate +directorates +directorial +directories +directors +directorship +directorships +directory +directs +direr +direst +dirge +dirges +dirigible +dirigibles +dirk +dirks +dirt +dirtied +dirtier +dirties +dirtiest +dirtiness +dirty +dirtying +dis +disabilities +disability +disable +disabled +disablement +disables +disabling +disabuse +disabused +disabuses +disabusing +disadvantage +disadvantaged +disadvantageous +disadvantageously +disadvantages +disadvantaging +disaffect +disaffected +disaffecting +disaffection +disaffects +disagree +disagreeable +disagreeably +disagreed +disagreeing +disagreement +disagreements +disagrees +disallow +disallowed +disallowing +disallows +disambiguate +disambiguation +disappear +disappearance +disappearances +disappeared +disappearing +disappears +disappoint +disappointed +disappointing +disappointingly +disappointment +disappointments +disappoints +disapprobation +disapproval +disapprove +disapproved +disapproves +disapproving +disapprovingly +disarm +disarmament +disarmed +disarming +disarms +disarrange +disarranged +disarrangement +disarranges +disarranging +disarray +disarrayed +disarraying +disarrays +disassemble +disassembled +disassembles +disassembling +disassociate +disassociated +disassociates +disassociating +disaster +disasters +disastrous +disastrously +disavow +disavowal +disavowals +disavowed +disavowing +disavows +disband +disbanded +disbanding +disbands +disbar +disbarment +disbarred +disbarring +disbars +disbelief +disbelieve +disbelieved +disbelieves +disbelieving +disburse +disbursed +disbursement +disbursements +disburses +disbursing +disc +discard +discarded +discarding +discards +discern +discerned +discernible +discerning +discernment +discerns +discharge +discharged +discharges +discharging +disciple +disciples +disciplinarian +disciplinarians +disciplinary +discipline +disciplined +disciplines +disciplining +disclaim +disclaimed +disclaimer +disclaimers +disclaiming +disclaims +disclose +disclosed +discloses +disclosing +disclosure +disclosures +disco +discoed +discoing +discolor +discoloration +discolorations +discolored +discoloring +discolors +discombobulate +discombobulated +discombobulates +discombobulating +discomfit +discomfited +discomfiting +discomfits +discomfiture +discomfort +discomforted +discomforting +discomforts +discommode +discommoded +discommodes +discommoding +discompose +discomposed +discomposes +discomposing +discomposure +disconcert +disconcerted +disconcerting +disconcerts +disconnect +disconnected +disconnectedly +disconnecting +disconnection +disconnections +disconnects +disconsolate +disconsolately +discontent +discontented +discontentedly +discontenting +discontentment +discontents +discontinuance +discontinuances +discontinuation +discontinuations +discontinue +discontinued +discontinues +discontinuing +discontinuities +discontinuity +discontinuous +discord +discordant +discorded +discording +discords +discos +discotheque +discotheques +discount +discounted +discountenance +discountenanced +discountenances +discountenancing +discounting +discounts +discourage +discouraged +discouragement +discouragements +discourages +discouraging +discouragingly +discourse +discoursed +discourses +discoursing +discourteous +discourteously +discourtesies +discourtesy +discover +discovered +discoverer +discoverers +discoveries +discovering +discovers +discovery +discredit +discreditable +discredited +discrediting +discredits +discreet +discreeter +discreetest +discreetly +discrepancies +discrepancy +discrete +discretion +discretionary +discriminant +discriminate +discriminated +discriminates +discriminating +discrimination +discriminatory +discs +discursive +discus +discuses +discuss +discussant +discussants +discussed +discusses +discussing +discussion +discussions +disdain +disdained +disdainful +disdainfully +disdaining +disdains +disease +diseased +diseases +disembark +disembarkation +disembarked +disembarking +disembarks +disembodied +disembodies +disembody +disembodying +disembowel +disemboweled +disemboweling +disembowels +disenchant +disenchanted +disenchanting +disenchantment +disenchants +disencumber +disencumbered +disencumbering +disencumbers +disenfranchise +disenfranchised +disenfranchisement +disenfranchises +disenfranchising +disengage +disengaged +disengagement +disengagements +disengages +disengaging +disentangle +disentangled +disentanglement +disentangles +disentangling +disestablish +disestablished +disestablishes +disestablishing +disfavor +disfavored +disfavoring +disfavors +disfigure +disfigured +disfigurement +disfigurements +disfigures +disfiguring +disfranchise +disfranchised +disfranchisement +disfranchises +disfranchising +disgorge +disgorged +disgorges +disgorging +disgrace +disgraced +disgraceful +disgracefully +disgraces +disgracing +disgruntle +disgruntled +disgruntles +disgruntling +disguise +disguised +disguises +disguising +disgust +disgusted +disgustedly +disgusting +disgustingly +disgusts +dish +disharmonious +disharmony +dishcloth +dishcloths +dishearten +disheartened +disheartening +disheartens +dished +dishes +dishevel +disheveled +disheveling +dishevels +dishing +dishonest +dishonestly +dishonesty +dishonor +dishonorable +dishonorably +dishonored +dishonoring +dishonors +dishpan +dishpans +dishrag +dishrags +dishtowel +dishtowels +dishwasher +dishwashers +dishwater +disillusion +disillusioned +disillusioning +disillusionment +disillusions +disincentive +disinclination +disincline +disinclined +disinclines +disinclining +disinfect +disinfectant +disinfectants +disinfected +disinfecting +disinfects +disinformation +disingenuous +disinherit +disinherited +disinheriting +disinherits +disintegrate +disintegrated +disintegrates +disintegrating +disintegration +disinter +disinterest +disinterested +disinterestedly +disinterests +disinterment +disinterred +disinterring +disinters +disjoint +disjointed +disjointedly +disjointing +disjoints +disk +diskette +diskettes +disks +dislike +disliked +dislikes +disliking +dislocate +dislocated +dislocates +dislocating +dislocation +dislocations +dislodge +dislodged +dislodges +dislodging +disloyal +disloyally +disloyalty +dismal +dismally +dismantle +dismantled +dismantles +dismantling +dismay +dismayed +dismaying +dismays +dismember +dismembered +dismembering +dismemberment +dismembers +dismiss +dismissal +dismissals +dismissed +dismisses +dismissing +dismissive +dismount +dismounted +dismounting +dismounts +disobedience +disobedient +disobediently +disobey +disobeyed +disobeying +disobeys +disoblige +disobliged +disobliges +disobliging +disorder +disordered +disordering +disorderliness +disorderly +disorders +disorganization +disorganize +disorganized +disorganizes +disorganizing +disorient +disorientation +disoriented +disorienting +disorients +disown +disowned +disowning +disowns +disparage +disparaged +disparagement +disparages +disparaging +disparate +disparities +disparity +dispassionate +dispassionately +dispatch +dispatched +dispatcher +dispatchers +dispatches +dispatching +dispel +dispelled +dispelling +dispels +dispensable +dispensaries +dispensary +dispensation +dispensations +dispense +dispensed +dispenser +dispensers +dispenses +dispensing +dispersal +disperse +dispersed +disperses +dispersing +dispersion +dispirit +dispirited +dispiriting +dispirits +displace +displaced +displacement +displacements +displaces +displacing +display +displayable +displayed +displaying +displays +displease +displeased +displeases +displeasing +displeasure +disport +disported +disporting +disports +disposable +disposables +disposal +disposals +dispose +disposed +disposes +disposing +disposition +dispositions +dispossess +dispossessed +dispossesses +dispossessing +dispossession +disproof +disproportion +disproportionate +disproportionately +disproportions +disprove +disproved +disproves +disproving +disputable +disputant +disputants +disputation +disputations +disputatious +dispute +disputed +disputes +disputing +disqualification +disqualifications +disqualified +disqualifies +disqualify +disqualifying +disquiet +disquieted +disquieting +disquiets +disquisition +disquisitions +disregard +disregarded +disregarding +disregards +disrepair +disreputable +disreputably +disrepute +disrespect +disrespected +disrespectful +disrespectfully +disrespecting +disrespects +disrobe +disrobed +disrobes +disrobing +disrupt +disrupted +disrupting +disruption +disruptions +disruptive +disrupts +dissatisfaction +dissatisfied +dissatisfies +dissatisfy +dissatisfying +dissect +dissected +dissecting +dissection +dissections +dissects +dissed +dissemble +dissembled +dissembles +dissembling +disseminate +disseminated +disseminates +disseminating +dissemination +dissension +dissensions +dissent +dissented +dissenter +dissenters +dissenting +dissents +dissertation +dissertations +disservice +disservices +dissidence +dissident +dissidents +dissimilar +dissimilarities +dissimilarity +dissimulate +dissimulated +dissimulates +dissimulating +dissimulation +dissing +dissipate +dissipated +dissipates +dissipating +dissipation +dissociate +dissociated +dissociates +dissociating +dissociation +dissolute +dissolutely +dissoluteness +dissolution +dissolve +dissolved +dissolves +dissolving +dissonance +dissonances +dissonant +dissuade +dissuaded +dissuades +dissuading +dissuasion +distaff +distaffs +distance +distanced +distances +distancing +distant +distantly +distaste +distasteful +distastefully +distastes +distemper +distend +distended +distending +distends +distension +distensions +distention +distentions +distill +distillate +distillates +distillation +distillations +distilled +distiller +distilleries +distillers +distillery +distilling +distills +distinct +distincter +distinctest +distinction +distinctions +distinctive +distinctively +distinctiveness +distinctly +distinguish +distinguishable +distinguished +distinguishes +distinguishing +distort +distorted +distorter +distorting +distortion +distortions +distorts +distract +distracted +distracting +distraction +distractions +distracts +distrait +distraught +distress +distressed +distresses +distressful +distressing +distressingly +distribute +distributed +distributes +distributing +distribution +distributions +distributive +distributor +distributors +district +districts +distrust +distrusted +distrustful +distrustfully +distrusting +distrusts +disturb +disturbance +disturbances +disturbed +disturbing +disturbingly +disturbs +disunite +disunited +disunites +disuniting +disunity +disuse +disused +disuses +disusing +ditch +ditched +ditches +ditching +dither +dithered +dithering +dithers +ditties +ditto +dittoed +dittoes +dittoing +dittos +ditty +diuretic +diuretics +diurnal +diurnally +diva +divan +divans +divas +dive +dived +diver +diverge +diverged +divergence +divergences +divergent +diverges +diverging +divers +diverse +diversely +diversification +diversified +diversifies +diversify +diversifying +diversion +diversionary +diversions +diversities +diversity +divert +diverted +diverting +diverts +dives +divest +divested +divesting +divests +divide +divided +dividend +dividends +divider +dividers +divides +dividing +divination +divine +divined +divinely +diviner +diviners +divines +divinest +diving +divining +divinities +divinity +divisibility +divisible +division +divisional +divisions +divisive +divisively +divisiveness +divisor +divisors +divorce +divorced +divorces +divorcing +divorcée +divorcées +divot +divots +divulge +divulged +divulges +divulging +divvied +divvies +divvy +divvying +dizzied +dizzier +dizzies +dizziest +dizzily +dizziness +dizzy +dizzying +do +doable +doc +docent +docents +docile +docilely +docility +dock +docked +docket +docketed +docketing +dockets +docking +docks +dockyard +dockyards +docs +doctor +doctoral +doctorate +doctorates +doctored +doctoring +doctors +doctrinaire +doctrinaires +doctrinal +doctrine +doctrines +docudrama +docudramas +document +documentaries +documentary +documentation +documented +documenting +documents +dodder +doddered +doddering +dodders +dodge +dodged +dodger +dodgers +dodges +dodging +dodo +dodos +doe +doer +doers +does +doff +doffed +doffing +doffs +dog +dogcatcher +dogcatchers +dogfight +dogfights +dogfish +dogfishes +dogged +doggedly +doggedness +doggerel +doggie +doggier +doggies +doggiest +dogging +doggone +doggoned +doggoneder +doggonedest +doggoner +doggones +doggonest +doggoning +doggy +doghouse +doghouses +dogie +dogies +dogma +dogmas +dogmatic +dogmatically +dogmatism +dogmatist +dogmatists +dogs +dogtrot +dogtrots +dogtrotted +dogtrotting +dogwood +dogwoods +doilies +doily +doing +doings +doldrums +dole +doled +doleful +dolefully +doles +doling +doll +dollar +dollars +dolled +dollhouse +dollhouses +dollies +dolling +dollop +dolloped +dolloping +dollops +dolls +dolly +dolmen +dolmens +dolorous +dolphin +dolphins +dolt +doltish +dolts +domain +domains +dome +domed +domes +domestic +domestically +domesticate +domesticated +domesticates +domesticating +domestication +domesticity +domestics +domicile +domiciled +domiciles +domiciling +dominance +dominant +dominantly +dominants +dominate +dominated +dominates +dominating +domination +domineer +domineered +domineering +domineers +doming +dominion +dominions +domino +dominoes +don +donate +donated +donates +donating +donation +donations +done +donkey +donkeys +donned +donning +donor +donors +dons +doodad +doodads +doodle +doodled +doodler +doodlers +doodles +doodling +doohickey +doohickeys +doom +doomed +dooming +dooms +doomsday +door +doorbell +doorbells +doorknob +doorknobs +doorman +doormat +doormats +doormen +doors +doorstep +doorsteps +doorway +doorways +dope +doped +dopes +dopey +dopier +dopiest +doping +dories +dork +dorkier +dorkiest +dorks +dorky +dorm +dormancy +dormant +dormer +dormers +dormice +dormitories +dormitory +dormouse +dorms +dorsal +dory +dos +dosage +dosages +dose +dosed +doses +dosing +dossier +dossiers +dot +dotage +dotcom +dotcoms +dote +doted +dotes +doth +doting +dotingly +dots +dotted +dotting +dotty +double +doubled +doubles +doublet +doublets +doubling +doubloon +doubloons +doubly +doubt +doubted +doubter +doubters +doubtful +doubtfully +doubting +doubtless +doubtlessly +doubts +douche +douched +douches +douching +dough +doughier +doughiest +doughnut +doughnuts +doughtier +doughtiest +doughty +doughy +dour +dourer +dourest +dourly +douse +doused +douses +dousing +dove +doves +dovetail +dovetailed +dovetailing +dovetails +dowager +dowagers +dowdier +dowdies +dowdiest +dowdily +dowdiness +dowdy +dowel +doweled +doweling +dowels +down +downbeat +downbeats +downcast +downed +downer +downers +downfall +downfalls +downgrade +downgraded +downgrades +downgrading +downhearted +downhill +downhills +downier +downiest +downing +download +downloadable +downloaded +downloading +downloads +downplay +downplayed +downplaying +downplays +downpour +downpours +downright +downs +downscale +downsize +downsized +downsizes +downsizing +downstage +downstairs +downstate +downstream +downswing +downswings +downtime +downtown +downtrodden +downturn +downturns +downward +downwards +downwind +downy +dowries +dowry +dowse +dowsed +dowses +dowsing +doxologies +doxology +doyen +doyens +doze +dozed +dozen +dozens +dozes +dozing +drab +drabber +drabbest +drably +drabness +drabs +drachma +drachmas +draconian +draft +drafted +draftee +draftees +draftier +draftiest +draftiness +drafting +drafts +draftsman +draftsmanship +draftsmen +drafty +drag +dragged +dragging +dragnet +dragnets +dragon +dragonflies +dragonfly +dragons +dragoon +dragooned +dragooning +dragoons +drags +drain +drainage +drained +drainer +drainers +draining +drainpipe +drainpipes +drains +drake +drakes +dram +drama +dramas +dramatic +dramatically +dramatics +dramatist +dramatists +dramatization +dramatizations +dramatize +dramatized +dramatizes +dramatizing +drams +drank +drape +draped +draperies +drapery +drapes +draping +drastic +drastically +draw +drawback +drawbacks +drawbridge +drawbridges +drawer +drawers +drawing +drawings +drawl +drawled +drawling +drawls +drawn +draws +drawstring +drawstrings +dray +drays +dread +dreaded +dreadful +dreadfully +dreading +dreadlocks +dreadnought +dreadnoughts +dreads +dream +dreamed +dreamer +dreamers +dreamier +dreamiest +dreamily +dreaming +dreamland +dreamless +dreamlike +dreams +dreamy +drearier +dreariest +drearily +dreariness +dreary +dredge +dredged +dredger +dredgers +dredges +dredging +dregs +drench +drenched +drenches +drenching +dress +dressage +dressed +dresser +dressers +dresses +dressier +dressiest +dressiness +dressing +dressings +dressmaker +dressmakers +dressmaking +dressy +drew +dribble +dribbled +dribbler +dribblers +dribbles +dribbling +driblet +driblets +dried +drier +driers +dries +driest +drift +drifted +drifter +drifters +drifting +drifts +driftwood +drill +drilled +drilling +drills +drily +drink +drinkable +drinker +drinkers +drinking +drinkings +drinks +drip +dripped +dripping +drippings +drips +drive +drivel +driveled +driveling +drivels +driven +driver +drivers +drives +driveway +driveways +driving +drivings +drizzle +drizzled +drizzles +drizzling +drizzly +droll +droller +drolleries +drollery +drollest +drollness +drolly +dromedaries +dromedary +drone +droned +drones +droning +drool +drooled +drooling +drools +droop +drooped +droopier +droopiest +drooping +droops +droopy +drop +droplet +droplets +dropout +dropouts +dropped +dropper +droppers +dropping +droppings +drops +dropsy +dross +drought +droughts +drove +drover +drovers +droves +drown +drowned +drowning +drownings +drowns +drowse +drowsed +drowses +drowsier +drowsiest +drowsily +drowsiness +drowsing +drowsy +drub +drubbed +drubbing +drubbings +drubs +drudge +drudged +drudgery +drudges +drudging +drug +drugged +drugging +druggist +druggists +drugs +drugstore +drugstores +druid +druids +drum +drummed +drummer +drummers +drumming +drums +drumstick +drumsticks +drunk +drunkard +drunkards +drunken +drunkenly +drunkenness +drunker +drunkest +drunks +dry +dryad +dryads +dryer +dryers +drying +dryly +dryness +drys +drywall +dual +dualism +duality +dub +dubbed +dubbing +dubiety +dubious +dubiously +dubiousness +dubs +ducal +ducat +ducats +duchess +duchesses +duchies +duchy +duck +duckbill +duckbills +ducked +ducking +duckling +ducklings +ducks +duct +ductile +ductility +ducting +ductless +ducts +dud +dude +duded +dudes +dudgeon +duding +duds +due +duel +dueled +dueling +duelist +duelists +duels +dues +duet +duets +duff +duffer +duffers +dug +dugout +dugouts +duh +duke +dukedom +dukedoms +dukes +dulcet +dulcimer +dulcimers +dull +dullard +dullards +dulled +duller +dullest +dulling +dullness +dulls +dully +duly +dumb +dumbbell +dumbbells +dumber +dumbest +dumbfound +dumbfounded +dumbfounding +dumbfounds +dumbly +dumbness +dumbwaiter +dumbwaiters +dummies +dummy +dump +dumped +dumpier +dumpiest +dumping +dumpling +dumplings +dumps +dumpster +dumpy +dun +dunce +dunces +dune +dunes +dung +dungaree +dungarees +dunged +dungeon +dungeons +dunging +dungs +dunk +dunked +dunking +dunks +dunned +dunner +dunnest +dunning +dunno +duns +duo +duodena +duodenal +duodenum +duos +dupe +duped +dupes +duping +duplex +duplexes +duplicate +duplicated +duplicates +duplicating +duplication +duplicator +duplicators +duplicity +durability +durable +durably +duration +duress +during +dusk +duskier +duskiest +dusky +dust +dustbin +dustbins +dusted +duster +dusters +dustier +dustiest +dustiness +dusting +dustless +dustman +dustmen +dustpan +dustpans +dusts +dusty +duteous +dutiable +duties +dutiful +dutifully +duty +duvet +dwarf +dwarfed +dwarfing +dwarfish +dwarfism +dwarfs +dweeb +dweebs +dwell +dweller +dwellers +dwelling +dwellings +dwells +dwelt +dwindle +dwindled +dwindles +dwindling +dyadic +dye +dyed +dyeing +dyer +dyers +dyes +dyestuff +dying +dyke +dykes +dynamic +dynamical +dynamically +dynamics +dynamism +dynamite +dynamited +dynamites +dynamiting +dynamo +dynamos +dynastic +dynasties +dynasty +dysentery +dysfunction +dysfunctional +dysfunctions +dyslexia +dyslexic +dyslexics +dyspepsia +dyspeptic +dyspeptics +débutante +débutantes +décolleté +dérailleur +dérailleurs +détente +e +eBay +eMusic +each +eager +eagerer +eagerest +eagerly +eagerness +eagle +eagles +eaglet +eaglets +ear +earache +earaches +earbud +earbuds +eardrum +eardrums +earful +earfuls +earl +earldom +earldoms +earlier +earliest +earliness +earlobe +earlobes +earls +early +earmark +earmarked +earmarking +earmarks +earmuff +earmuffs +earn +earned +earner +earners +earnest +earnestly +earnestness +earnests +earning +earnings +earns +earphone +earphones +earplug +earplugs +earring +earrings +ears +earshot +earsplitting +earth +earthed +earthen +earthenware +earthier +earthiest +earthiness +earthing +earthlier +earthliest +earthling +earthlings +earthly +earthquake +earthquakes +earths +earthshaking +earthward +earthwork +earthworks +earthworm +earthworms +earthy +earwax +earwig +earwigs +ease +eased +easel +easels +eases +easier +easiest +easily +easiness +easing +east +eastbound +easterlies +easterly +eastern +easterner +easterners +easternmost +eastward +eastwards +easy +easygoing +eat +eatable +eatables +eaten +eater +eateries +eaters +eatery +eating +eats +eave +eaves +eavesdrop +eavesdropped +eavesdropper +eavesdroppers +eavesdropping +eavesdrops +ebb +ebbed +ebbing +ebbs +ebonies +ebony +ebullience +ebullient +eccentric +eccentrically +eccentricities +eccentricity +eccentrics +ecclesiastic +ecclesiastical +ecclesiastics +echelon +echelons +echo +echoed +echoes +echoing +eclectic +eclectically +eclecticism +eclectics +eclipse +eclipsed +eclipses +eclipsing +ecliptic +ecological +ecologically +ecologist +ecologists +ecology +econometric +economic +economical +economically +economics +economies +economist +economists +economize +economized +economizes +economizing +economy +ecosystem +ecosystems +ecotourism +ecru +ecstasies +ecstasy +ecstatic +ecstatically +ecumenical +ecumenically +eczema +edamame +eddied +eddies +eddy +eddying +edelweiss +edema +edge +edged +edger +edges +edgeways +edgewise +edgier +edgiest +edginess +edging +edgings +edgy +edibility +edible +edibles +edict +edicts +edification +edifice +edifices +edified +edifies +edify +edifying +edit +editable +edited +editing +edition +editions +editor +editorial +editorialize +editorialized +editorializes +editorializing +editorially +editorials +editors +editorship +edits +educable +educate +educated +educates +educating +education +educational +educationally +educations +educator +educators +eel +eels +eerie +eerier +eeriest +eerily +eeriness +efface +effaced +effacement +effaces +effacing +effect +effected +effecting +effective +effectively +effectiveness +effects +effectual +effectually +effectuate +effectuated +effectuates +effectuating +effeminacy +effeminate +effervesce +effervesced +effervescence +effervescent +effervesces +effervescing +effete +efficacious +efficaciously +efficacy +efficiencies +efficiency +efficient +efficiently +effigies +effigy +effluent +effluents +effort +effortless +effortlessly +efforts +effrontery +effulgence +effulgent +effusion +effusions +effusive +effusively +effusiveness +egalitarian +egalitarianism +egalitarians +egg +eggbeater +eggbeaters +egged +egghead +eggheads +egging +eggnog +eggplant +eggplants +eggs +eggshell +eggshells +eglantine +eglantines +ego +egocentric +egocentrics +egoism +egoist +egoistic +egoists +egos +egotism +egotist +egotistic +egotistical +egotistically +egotists +egregious +egregiously +egress +egresses +egret +egrets +eh +eider +eiderdown +eiderdowns +eiders +eigenvalue +eigenvalues +eight +eighteen +eighteens +eighteenth +eighteenths +eighth +eighths +eighties +eightieth +eightieths +eights +eighty +either +ejaculate +ejaculated +ejaculates +ejaculating +ejaculation +ejaculations +eject +ejected +ejecting +ejection +ejections +ejects +eke +eked +ekes +eking +elaborate +elaborated +elaborately +elaborateness +elaborates +elaborating +elaboration +elaborations +elapse +elapsed +elapses +elapsing +elastic +elasticity +elastics +elate +elated +elates +elating +elation +elbow +elbowed +elbowing +elbowroom +elbows +elder +elderberries +elderberry +eldercare +elderly +elders +eldest +elect +elected +electing +election +electioneer +electioneered +electioneering +electioneers +elections +elective +electives +elector +electoral +electorate +electorates +electors +electric +electrical +electrically +electrician +electricians +electricity +electrification +electrified +electrifies +electrify +electrifying +electrocardiogram +electrocardiograms +electrocardiograph +electrocardiographs +electrocute +electrocuted +electrocutes +electrocuting +electrocution +electrocutions +electrode +electrodes +electrodynamics +electroencephalogram +electroencephalograms +electroencephalograph +electroencephalographs +electrolysis +electrolyte +electrolytes +electrolytic +electromagnet +electromagnetic +electromagnetism +electromagnets +electron +electronic +electronica +electronically +electronics +electrons +electroplate +electroplated +electroplates +electroplating +electrostatic +elects +elegance +elegant +elegantly +elegiac +elegiacs +elegies +elegy +element +elemental +elementary +elements +elephant +elephantine +elephants +elevate +elevated +elevates +elevating +elevation +elevations +elevator +elevators +eleven +elevens +eleventh +elevenths +elf +elfin +elfish +elicit +elicited +eliciting +elicits +elide +elided +elides +eliding +eligibility +eligible +eliminate +eliminated +eliminates +eliminating +elimination +eliminations +elision +elisions +elite +elites +elitism +elitist +elitists +elixir +elixirs +elk +elks +ell +ellipse +ellipses +ellipsis +elliptic +elliptical +elliptically +ells +elm +elms +elocution +elocutionist +elocutionists +elongate +elongated +elongates +elongating +elongation +elongations +elope +eloped +elopement +elopements +elopes +eloping +eloquence +eloquent +eloquently +else +elsewhere +elucidate +elucidated +elucidates +elucidating +elucidation +elucidations +elude +eluded +eludes +eluding +elusive +elusively +elusiveness +elves +em +emaciate +emaciated +emaciates +emaciating +emaciation +email +emailed +emailing +emails +emanate +emanated +emanates +emanating +emanation +emanations +emancipate +emancipated +emancipates +emancipating +emancipation +emancipator +emancipators +emasculate +emasculated +emasculates +emasculating +emasculation +embalm +embalmed +embalmer +embalmers +embalming +embalms +embankment +embankments +embargo +embargoed +embargoes +embargoing +embark +embarkation +embarkations +embarked +embarking +embarks +embarrass +embarrassed +embarrasses +embarrassing +embarrassingly +embarrassment +embarrassments +embassies +embassy +embattled +embed +embedded +embedding +embeds +embellish +embellished +embellishes +embellishing +embellishment +embellishments +ember +embers +embezzle +embezzled +embezzlement +embezzler +embezzlers +embezzles +embezzling +embitter +embittered +embittering +embitters +emblazon +emblazoned +emblazoning +emblazons +emblem +emblematic +emblems +embodied +embodies +embodiment +embody +embodying +embolden +emboldened +emboldening +emboldens +embolism +embolisms +emboss +embossed +embosses +embossing +embrace +embraced +embraces +embracing +embroider +embroidered +embroideries +embroidering +embroiders +embroidery +embroil +embroiled +embroiling +embroils +embryo +embryologist +embryologists +embryology +embryonic +embryos +emcee +emceed +emceeing +emcees +emend +emendation +emendations +emended +emending +emends +emerald +emeralds +emerge +emerged +emergence +emergencies +emergency +emergent +emerges +emerging +emeritus +emery +emetic +emetics +emigrant +emigrants +emigrate +emigrated +emigrates +emigrating +emigration +emigrations +eminence +eminences +eminent +eminently +emir +emirate +emirates +emirs +emissaries +emissary +emission +emissions +emit +emits +emitted +emitting +emo +emoji +emojis +emollient +emollients +emolument +emoluments +emos +emote +emoted +emotes +emoting +emotion +emotional +emotionalism +emotionally +emotions +emotive +empanel +empaneled +empaneling +empanels +empathetic +empathize +empathized +empathizes +empathizing +empathy +emperor +emperors +emphases +emphasis +emphasize +emphasized +emphasizes +emphasizing +emphatic +emphatically +emphysema +empire +empires +empirical +empirically +empiricism +emplacement +emplacements +employ +employable +employed +employee +employees +employer +employers +employing +employment +employments +employs +emporium +emporiums +empower +empowered +empowering +empowerment +empowers +empress +empresses +emptied +emptier +empties +emptiest +emptily +emptiness +empty +emptying +ems +emu +emulate +emulated +emulates +emulating +emulation +emulations +emulator +emulators +emulsification +emulsified +emulsifies +emulsify +emulsifying +emulsion +emulsions +emus +enable +enabled +enables +enabling +enact +enacted +enacting +enactment +enactments +enacts +enamel +enameled +enameling +enamels +enamor +enamored +enamoring +enamors +encamp +encamped +encamping +encampment +encampments +encamps +encapsulate +encapsulated +encapsulates +encapsulating +encapsulation +encapsulations +encase +encased +encases +encasing +encephalitis +enchant +enchanted +enchanter +enchanters +enchanting +enchantingly +enchantment +enchantments +enchantress +enchantresses +enchants +enchilada +enchiladas +encircle +encircled +encirclement +encircles +encircling +enclave +enclaves +enclose +enclosed +encloses +enclosing +enclosure +enclosures +encode +encoded +encoder +encoders +encodes +encoding +encompass +encompassed +encompasses +encompassing +encore +encored +encores +encoring +encounter +encountered +encountering +encounters +encourage +encouraged +encouragement +encouragements +encourages +encouraging +encouragingly +encroach +encroached +encroaches +encroaching +encroachment +encroachments +encrust +encrustation +encrustations +encrusted +encrusting +encrusts +encrypt +encrypted +encryption +encrypts +encumber +encumbered +encumbering +encumbers +encumbrance +encumbrances +encyclical +encyclicals +encyclopedia +encyclopedias +encyclopedic +end +endanger +endangered +endangering +endangers +endear +endeared +endearing +endearingly +endearment +endearments +endears +endeavor +endeavored +endeavoring +endeavors +ended +endemic +endemics +ending +endings +endive +endives +endless +endlessly +endlessness +endocrine +endocrines +endorse +endorsed +endorsement +endorsements +endorser +endorsers +endorses +endorsing +endow +endowed +endowing +endowment +endowments +endows +ends +endue +endued +endues +enduing +endurable +endurance +endure +endured +endures +enduring +endways +enema +enemas +enemies +enemy +energetic +energetically +energies +energize +energized +energizer +energizers +energizes +energizing +energy +enervate +enervated +enervates +enervating +enervation +enfeeble +enfeebled +enfeebles +enfeebling +enfold +enfolded +enfolding +enfolds +enforce +enforceable +enforced +enforcement +enforcer +enforcers +enforces +enforcing +enfranchise +enfranchised +enfranchisement +enfranchises +enfranchising +engage +engaged +engagement +engagements +engages +engaging +engagingly +engender +engendered +engendering +engenders +engine +engineer +engineered +engineering +engineers +engines +engorge +engorged +engorges +engorging +engrave +engraved +engraver +engravers +engraves +engraving +engravings +engross +engrossed +engrosses +engrossing +engulf +engulfed +engulfing +engulfs +enhance +enhanced +enhancement +enhancements +enhancer +enhances +enhancing +enigma +enigmas +enigmatic +enigmatically +enjoin +enjoined +enjoining +enjoins +enjoy +enjoyable +enjoyed +enjoying +enjoyment +enjoyments +enjoys +enlarge +enlarged +enlargement +enlargements +enlarger +enlargers +enlarges +enlarging +enlighten +enlightened +enlightening +enlightenment +enlightens +enlist +enlisted +enlistee +enlistees +enlisting +enlistment +enlistments +enlists +enliven +enlivened +enlivening +enlivens +enmesh +enmeshed +enmeshes +enmeshing +enmities +enmity +ennoble +ennobled +ennoblement +ennobles +ennobling +ennui +enormities +enormity +enormous +enormously +enormousness +enough +enrage +enraged +enrages +enraging +enrapture +enraptured +enraptures +enrapturing +enrich +enriched +enriches +enriching +enrichment +enroll +enrolled +enrolling +enrollment +enrollments +enrolls +ensconce +ensconced +ensconces +ensconcing +ensemble +ensembles +enshrine +enshrined +enshrines +enshrining +enshroud +enshrouded +enshrouding +enshrouds +ensign +ensigns +enslave +enslaved +enslavement +enslaves +enslaving +ensnare +ensnared +ensnares +ensnaring +ensue +ensued +ensues +ensuing +ensure +ensured +ensures +ensuring +entail +entailed +entailing +entails +entangle +entangled +entanglement +entanglements +entangles +entangling +entente +ententes +enter +entered +entering +enterprise +enterprises +enterprising +enters +entertain +entertained +entertainer +entertainers +entertaining +entertainingly +entertainment +entertainments +entertains +enthrall +enthralled +enthralling +enthralls +enthrone +enthroned +enthronement +enthronements +enthrones +enthroning +enthuse +enthused +enthuses +enthusiasm +enthusiasms +enthusiast +enthusiastic +enthusiastically +enthusiasts +enthusing +entice +enticed +enticement +enticements +entices +enticing +entire +entirely +entirety +entities +entitle +entitled +entitlement +entitlements +entitles +entitling +entity +entomb +entombed +entombing +entombment +entombs +entomological +entomologist +entomologists +entomology +entourage +entourages +entrails +entrance +entranced +entrances +entrancing +entrant +entrants +entrap +entrapment +entrapped +entrapping +entraps +entreat +entreated +entreaties +entreating +entreats +entreaty +entrench +entrenched +entrenches +entrenching +entrenchment +entrenchments +entrepreneur +entrepreneurial +entrepreneurs +entries +entropy +entrust +entrusted +entrusting +entrusts +entry +entryway +entryways +entrée +entrées +entwine +entwined +entwines +entwining +enumerable +enumerate +enumerated +enumerates +enumerating +enumeration +enumerations +enunciate +enunciated +enunciates +enunciating +enunciation +enure +enured +enures +enuring +envelop +envelope +enveloped +envelopes +enveloping +envelopment +envelops +enviable +enviably +envied +envies +envious +enviously +enviousness +environment +environmental +environmentalism +environmentalist +environmentalists +environmentally +environments +environs +envisage +envisaged +envisages +envisaging +envision +envisioned +envisioning +envisions +envoy +envoys +envy +envying +enzyme +enzymes +eon +eons +epaulet +epaulets +ephemeral +epic +epicenter +epicenters +epics +epicure +epicurean +epicureans +epicures +epidemic +epidemics +epidemiology +epidermal +epidermis +epidermises +epiglottis +epiglottises +epigram +epigrammatic +epigrams +epilepsy +epileptic +epileptics +epilogue +epilogues +episcopacy +episcopal +episcopate +episode +episodes +episodic +epistemology +epistle +epistles +epistolary +epitaph +epitaphs +epithet +epithets +epitome +epitomes +epitomize +epitomized +epitomizes +epitomizing +epoch +epochal +epochs +epoxied +epoxies +epoxy +epoxying +epsilon +equability +equable +equably +equal +equaled +equaling +equality +equalization +equalize +equalized +equalizer +equalizers +equalizes +equalizing +equally +equals +equanimity +equate +equated +equates +equating +equation +equations +equator +equatorial +equators +equestrian +equestrians +equestrienne +equestriennes +equidistant +equilateral +equilaterals +equilibrium +equine +equines +equinoctial +equinox +equinoxes +equip +equipage +equipages +equipment +equipoise +equipped +equipping +equips +equitable +equitably +equities +equity +equivalence +equivalences +equivalent +equivalently +equivalents +equivocal +equivocally +equivocate +equivocated +equivocates +equivocating +equivocation +equivocations +era +eradicate +eradicated +eradicates +eradicating +eradication +eras +erase +erased +eraser +erasers +erases +erasing +erasure +erasures +ere +erect +erected +erectile +erecting +erection +erections +erectly +erectness +erects +erg +ergo +ergonomic +ergonomics +ergs +ermine +ermines +erode +eroded +erodes +eroding +erogenous +erosion +erosive +erotic +erotica +erotically +eroticism +err +errand +errands +errant +errata +erratas +erratic +erratically +erratum +erred +erring +erroneous +erroneously +error +errors +errs +ersatz +ersatzes +erstwhile +erudite +eruditely +erudition +erupt +erupted +erupting +eruption +eruptions +erupts +erythrocyte +erythrocytes +es +escalate +escalated +escalates +escalating +escalation +escalations +escalator +escalators +escapade +escapades +escape +escaped +escapee +escapees +escapes +escaping +escapism +escapist +escapists +escarole +escaroles +escarpment +escarpments +eschatology +eschew +eschewed +eschewing +eschews +escort +escorted +escorting +escorts +escrow +escrows +escutcheon +escutcheons +esophagi +esophagus +esophaguses +esoteric +esoterically +espadrille +espadrilles +especial +especially +espied +espies +espionage +esplanade +esplanades +espousal +espouse +espoused +espouses +espousing +espresso +espressos +espy +espying +esquire +esquires +essay +essayed +essaying +essayist +essayists +essays +essence +essences +essential +essentially +essentials +establish +established +establishes +establishing +establishment +establishments +estate +estates +esteem +esteemed +esteeming +esteems +ester +esters +esthetic +esthetics +estimable +estimate +estimated +estimates +estimating +estimation +estimations +estimator +estimators +estrange +estranged +estrangement +estrangements +estranges +estranging +estrogen +estuaries +estuary +eta +etch +etched +etcher +etchers +etches +etching +etchings +eternal +eternally +eternities +eternity +ether +ethereal +ethereally +ethic +ethical +ethically +ethics +ethnic +ethnically +ethnicity +ethnics +ethnological +ethnologist +ethnologists +ethnology +ethos +etiologies +etiology +etiquette +etymological +etymologies +etymologist +etymologists +etymology +eucalypti +eucalyptus +eucalyptuses +eugenics +eulogies +eulogistic +eulogize +eulogized +eulogizes +eulogizing +eulogy +eunuch +eunuchs +euphemism +euphemisms +euphemistic +euphemistically +euphony +euphoria +euphoric +eureka +euro +euros +eutectic +euthanasia +evacuate +evacuated +evacuates +evacuating +evacuation +evacuations +evacuee +evacuees +evade +evaded +evades +evading +evaluate +evaluated +evaluates +evaluating +evaluation +evaluations +evanescent +evangelical +evangelicals +evangelism +evangelist +evangelistic +evangelists +evangelize +evangelized +evangelizes +evangelizing +evaporate +evaporated +evaporates +evaporating +evaporation +evasion +evasions +evasive +evasively +evasiveness +eve +even +evened +evener +evenest +evenhanded +evening +evenings +evenly +evenness +evens +event +eventful +eventfully +eventfulness +eventide +events +eventual +eventualities +eventuality +eventually +eventuate +eventuated +eventuates +eventuating +ever +everglade +everglades +evergreen +evergreens +everlasting +everlastings +evermore +every +everybody +everyday +everyone +everyplace +everything +everywhere +eves +evict +evicted +evicting +eviction +evictions +evicts +evidence +evidenced +evidences +evidencing +evident +evidently +evil +evildoer +evildoers +eviler +evilest +evilly +evils +evince +evinced +evinces +evincing +eviscerate +eviscerated +eviscerates +eviscerating +evisceration +evocation +evocations +evocative +evoke +evoked +evokes +evoking +evolution +evolutionary +evolve +evolved +evolves +evolving +ewe +ewer +ewers +ewes +ex +exacerbate +exacerbated +exacerbates +exacerbating +exacerbation +exact +exacted +exacter +exactest +exacting +exactingly +exactitude +exactly +exactness +exacts +exaggerate +exaggerated +exaggerates +exaggerating +exaggeration +exaggerations +exalt +exaltation +exalted +exalting +exalts +exam +examination +examinations +examine +examined +examiner +examiners +examines +examining +example +exampled +examples +exampling +exams +exasperate +exasperated +exasperates +exasperating +exasperation +excavate +excavated +excavates +excavating +excavation +excavations +excavator +excavators +exceed +exceeded +exceeding +exceedingly +exceeds +excel +excelled +excellence +excellent +excellently +excelling +excels +except +excepted +excepting +exception +exceptionable +exceptional +exceptionally +exceptions +excepts +excerpt +excerpted +excerpting +excerpts +excess +excesses +excessive +excessively +exchange +exchangeable +exchanged +exchanges +exchanging +exchequer +exchequers +excise +excised +excises +excising +excision +excisions +excitability +excitable +excitation +excite +excited +excitedly +excitement +excitements +excites +exciting +excitingly +exclaim +exclaimed +exclaiming +exclaims +exclamation +exclamations +exclamatory +exclude +excluded +excludes +excluding +exclusion +exclusive +exclusively +exclusiveness +exclusives +exclusivity +excommunicate +excommunicated +excommunicates +excommunicating +excommunication +excommunications +excoriate +excoriated +excoriates +excoriating +excoriation +excoriations +excrement +excrescence +excrescences +excreta +excrete +excreted +excretes +excreting +excretion +excretions +excretory +excruciating +excruciatingly +exculpate +exculpated +exculpates +exculpating +excursion +excursions +excusable +excuse +excused +excuses +excusing +exec +execrable +execrate +execrated +execrates +execrating +execs +executable +execute +executed +executes +executing +execution +executioner +executioners +executions +executive +executives +executor +executors +executrices +executrix +exegeses +exegesis +exemplar +exemplars +exemplary +exemplification +exemplifications +exemplified +exemplifies +exemplify +exemplifying +exempt +exempted +exempting +exemption +exemptions +exempts +exercise +exercised +exercises +exercising +exert +exerted +exerting +exertion +exertions +exerts +exes +exhalation +exhalations +exhale +exhaled +exhales +exhaling +exhaust +exhausted +exhaustible +exhausting +exhaustion +exhaustive +exhaustively +exhausts +exhibit +exhibited +exhibiting +exhibition +exhibitionism +exhibitionist +exhibitionists +exhibitions +exhibitor +exhibitors +exhibits +exhilarate +exhilarated +exhilarates +exhilarating +exhilaration +exhort +exhortation +exhortations +exhorted +exhorting +exhorts +exhumation +exhumations +exhume +exhumed +exhumes +exhuming +exigencies +exigency +exigent +exiguous +exile +exiled +exiles +exiling +exist +existed +existence +existences +existent +existential +existentialism +existentialist +existentialists +existentially +existing +exists +exit +exited +exiting +exits +exodus +exoduses +exonerate +exonerated +exonerates +exonerating +exoneration +exoplanet +exoplanets +exorbitance +exorbitant +exorbitantly +exorcise +exorcised +exorcises +exorcising +exorcism +exorcisms +exorcist +exorcists +exotic +exotically +exotics +expand +expandable +expanded +expanding +expands +expanse +expanses +expansion +expansionist +expansionists +expansions +expansive +expansively +expansiveness +expatiate +expatiated +expatiates +expatiating +expatriate +expatriated +expatriates +expatriating +expatriation +expect +expectancy +expectant +expectantly +expectation +expectations +expected +expecting +expectorant +expectorants +expectorate +expectorated +expectorates +expectorating +expectoration +expects +expedience +expediences +expediencies +expediency +expedient +expediently +expedients +expedite +expedited +expediter +expediters +expedites +expediting +expedition +expeditionary +expeditions +expeditious +expeditiously +expel +expelled +expelling +expels +expend +expendable +expendables +expended +expending +expenditure +expenditures +expends +expense +expenses +expensive +expensively +experience +experienced +experiences +experiencing +experiment +experimental +experimentally +experimentation +experimented +experimenter +experimenters +experimenting +experiments +expert +expertise +expertly +expertness +experts +expiate +expiated +expiates +expiating +expiation +expiration +expire +expired +expires +expiring +expiry +explain +explained +explaining +explains +explanation +explanations +explanatory +expletive +expletives +explicable +explicate +explicated +explicates +explicating +explication +explications +explicit +explicitly +explicitness +explode +exploded +explodes +exploding +exploit +exploitation +exploitative +exploited +exploiter +exploiters +exploiting +exploits +exploration +explorations +exploratory +explore +explored +explorer +explorers +explores +exploring +explosion +explosions +explosive +explosively +explosiveness +explosives +expo +exponent +exponential +exponentially +exponentiation +exponents +export +exportation +exported +exporter +exporters +exporting +exports +expos +expose +exposed +exposes +exposing +exposition +expositions +expository +expostulate +expostulated +expostulates +expostulating +expostulation +expostulations +exposure +exposures +expound +expounded +expounding +expounds +express +expressed +expresses +expressible +expressing +expression +expressionism +expressionist +expressionists +expressionless +expressions +expressive +expressively +expressiveness +expressly +expressway +expressways +expropriate +expropriated +expropriates +expropriating +expropriation +expropriations +expulsion +expulsions +expunge +expunged +expunges +expunging +expurgate +expurgated +expurgates +expurgating +expurgation +expurgations +exquisite +exquisitely +extant +extemporaneous +extemporaneously +extempore +extemporize +extemporized +extemporizes +extemporizing +extend +extendable +extended +extendible +extending +extends +extension +extensional +extensions +extensive +extensively +extensiveness +extent +extents +extenuate +extenuated +extenuates +extenuating +extenuation +exterior +exteriors +exterminate +exterminated +exterminates +exterminating +extermination +exterminations +exterminator +exterminators +external +externally +externals +extinct +extincted +extincting +extinction +extinctions +extincts +extinguish +extinguishable +extinguished +extinguisher +extinguishers +extinguishes +extinguishing +extirpate +extirpated +extirpates +extirpating +extirpation +extol +extolled +extolling +extols +extort +extorted +extorting +extortion +extortionate +extortionist +extortionists +extorts +extra +extract +extracted +extracting +extraction +extractions +extractor +extractors +extracts +extracurricular +extradite +extradited +extradites +extraditing +extradition +extraditions +extramarital +extraneous +extraneously +extraordinarily +extraordinary +extrapolate +extrapolated +extrapolates +extrapolating +extrapolation +extrapolations +extras +extrasensory +extraterrestrial +extraterrestrials +extravagance +extravagances +extravagant +extravagantly +extravaganza +extravaganzas +extreme +extremely +extremer +extremes +extremest +extremism +extremist +extremists +extremities +extremity +extricate +extricated +extricates +extricating +extrication +extrinsic +extrinsically +extroversion +extrovert +extroverted +extroverts +extrude +extruded +extrudes +extruding +extrusion +extrusions +exuberance +exuberant +exuberantly +exude +exuded +exudes +exuding +exult +exultant +exultantly +exultation +exulted +exulting +exults +eye +eyeball +eyeballed +eyeballing +eyeballs +eyebrow +eyebrows +eyed +eyeful +eyefuls +eyeglass +eyeglasses +eyeing +eyelash +eyelashes +eyelet +eyelets +eyelid +eyelids +eyeliner +eyeliners +eyepiece +eyepieces +eyes +eyesight +eyesore +eyesores +eyestrain +eyeteeth +eyetooth +eyewitness +eyewitnesses +eying +f +fa +fable +fabled +fables +fabric +fabricate +fabricated +fabricates +fabricating +fabrication +fabrications +fabrics +fabulous +fabulously +facade +facades +face +faced +faceless +facelift +facelifts +faces +facet +faceted +faceting +facetious +facetiously +facetiousness +facets +facial +facially +facials +facile +facilitate +facilitated +facilitates +facilitating +facilitation +facilities +facility +facing +facings +facsimile +facsimiled +facsimileing +facsimiles +fact +faction +factional +factionalism +factions +factitious +factor +factored +factorial +factories +factoring +factorization +factorize +factorizing +factors +factory +factotum +factotums +facts +factual +factually +faculties +faculty +fad +faddish +fade +faded +fades +fading +fads +fag +fagged +fagging +faggot +faggots +fagot +fagots +fags +fail +failed +failing +failings +fails +failure +failures +fain +fainer +fainest +faint +fainted +fainter +faintest +fainthearted +fainting +faintly +faintness +faints +fair +fairer +fairest +fairground +fairgrounds +fairies +fairly +fairness +fairs +fairway +fairways +fairy +fairyland +fairylands +faith +faithful +faithfully +faithfulness +faithfuls +faithless +faithlessly +faithlessness +faiths +fake +faked +faker +fakers +fakes +faking +fakir +fakirs +falcon +falconer +falconers +falconry +falcons +fall +fallacies +fallacious +fallaciously +fallacy +fallen +fallibility +fallible +fallibly +falling +falloff +falloffs +fallout +fallow +fallowed +fallowing +fallows +falls +false +falsehood +falsehoods +falsely +falseness +falser +falsest +falsetto +falsettos +falsifiable +falsification +falsifications +falsified +falsifies +falsify +falsifying +falsities +falsity +falter +faltered +faltering +falteringly +falterings +falters +fame +famed +familial +familiar +familiarity +familiarization +familiarize +familiarized +familiarizes +familiarizing +familiarly +familiars +families +family +famine +famines +famish +famished +famishes +famishing +famous +famously +fan +fanatic +fanatical +fanatically +fanaticism +fanatics +fanboy +fanboys +fancied +fancier +fanciers +fancies +fanciest +fanciful +fancifully +fancily +fanciness +fancy +fancying +fandom +fanfare +fanfares +fang +fangs +fanned +fannies +fanning +fanny +fans +fantasied +fantasies +fantasize +fantasized +fantasizes +fantasizing +fantastic +fantastically +fantasy +fantasying +fanzine +far +faraway +farce +farces +farcical +fare +fared +fares +farewell +farewells +farina +farinaceous +faring +farm +farmed +farmer +farmers +farmhand +farmhands +farmhouse +farmhouses +farming +farmland +farms +farmyard +farmyards +farrow +farrowed +farrowing +farrows +farsighted +farsightedness +fart +farted +farther +farthest +farthing +farthings +farting +farts +fascinate +fascinated +fascinates +fascinating +fascination +fascinations +fascism +fascist +fascists +fashion +fashionable +fashionably +fashioned +fashioning +fashionista +fashionistas +fashions +fast +fasted +fasten +fastened +fastener +fasteners +fastening +fastenings +fastens +faster +fastest +fastidious +fastidiously +fastidiousness +fasting +fastness +fastnesses +fasts +fat +fatal +fatalism +fatalist +fatalistic +fatalists +fatalities +fatality +fatally +fate +fated +fateful +fatefully +fates +fathead +fatheads +father +fathered +fatherhood +fathering +fatherland +fatherlands +fatherless +fatherly +fathers +fathom +fathomable +fathomed +fathoming +fathomless +fathoms +fatigue +fatigued +fatigues +fatiguing +fating +fatness +fats +fatten +fattened +fattening +fattens +fatter +fattest +fattier +fatties +fattiest +fatty +fatuous +fatuously +fatuousness +faucet +faucets +fault +faulted +faultfinding +faultier +faultiest +faultily +faultiness +faulting +faultless +faultlessly +faults +faulty +faun +fauna +faunas +fauns +favor +favorable +favorably +favored +favoring +favorite +favorites +favoritism +favors +fawn +fawned +fawning +fawns +fax +faxed +faxes +faxing +faze +fazed +fazes +fazing +fealty +fear +feared +fearful +fearfully +fearfulness +fearing +fearless +fearlessly +fearlessness +fears +fearsome +feasibility +feasible +feasibly +feast +feasted +feasting +feasts +feat +feather +featherbedding +feathered +featherier +featheriest +feathering +feathers +featherweight +featherweights +feathery +feats +feature +featured +featureless +features +featuring +febrile +fecal +feces +feckless +fecund +fecundity +fed +federal +federalism +federalist +federalists +federally +federals +federate +federated +federates +federating +federation +federations +fedora +fedoras +feds +fee +feeble +feebleness +feebler +feeblest +feebly +feed +feedback +feedbag +feedbags +feeder +feeders +feeding +feedings +feeds +feel +feeler +feelers +feeling +feelingly +feelings +feels +fees +feet +feign +feigned +feigning +feigns +feint +feinted +feinting +feints +feistier +feistiest +feisty +feldspar +felicities +felicitous +felicity +feline +felines +fell +fellatio +felled +feller +fellest +felling +fellow +fellows +fellowship +fellowships +fells +felon +felonies +felonious +felons +felony +felt +felted +felting +felts +female +females +feminine +feminines +femininity +feminism +feminist +feminists +femoral +femur +femurs +fen +fence +fenced +fencer +fencers +fences +fencing +fend +fended +fender +fenders +fending +fends +fennel +fens +fer +feral +ferment +fermentation +fermented +fermenting +ferments +fern +ferns +ferocious +ferociously +ferociousness +ferocity +ferret +ferreted +ferreting +ferrets +ferric +ferried +ferries +ferrous +ferrule +ferrules +ferry +ferryboat +ferryboats +ferrying +fertile +fertility +fertilization +fertilize +fertilized +fertilizer +fertilizers +fertilizes +fertilizing +fervency +fervent +fervently +fervid +fervidly +fervor +fest +festal +fester +festered +festering +festers +festival +festivals +festive +festively +festivities +festivity +festoon +festooned +festooning +festoons +fests +feta +fetal +fetch +fetched +fetches +fetching +fetchingly +feted +fetid +feting +fetish +fetishes +fetishism +fetishist +fetishistic +fetishists +fetlock +fetlocks +fetter +fettered +fettering +fetters +fettle +fetus +fetuses +feud +feudal +feudalism +feudalistic +feuded +feuding +feuds +fever +fevered +feverish +feverishly +fevers +few +fewer +fewest +fey +fez +fezzes +fiancé +fiancée +fiancées +fiancés +fiasco +fiascoes +fiascos +fiat +fiats +fib +fibbed +fibber +fibbers +fibbing +fiber +fiberboard +fiberglass +fibers +fibroid +fibrous +fibs +fibula +fibulae +fiche +fiches +fickle +fickleness +fickler +ficklest +fiction +fictional +fictionalize +fictionalized +fictionalizes +fictionalizing +fictions +fictitious +fiddle +fiddled +fiddler +fiddlers +fiddles +fiddlesticks +fiddling +fiddly +fidelity +fidget +fidgeted +fidgeting +fidgets +fidgety +fiduciaries +fiduciary +fie +fief +fiefs +field +fielded +fielder +fielders +fielding +fields +fieldwork +fiend +fiendish +fiendishly +fiends +fierce +fiercely +fierceness +fiercer +fiercest +fierier +fieriest +fieriness +fiery +fiesta +fiestas +fife +fifes +fifteen +fifteens +fifteenth +fifteenths +fifth +fifths +fifties +fiftieth +fiftieths +fifty +fig +fight +fighter +fighters +fighting +fights +figment +figments +figs +figurative +figuratively +figure +figured +figurehead +figureheads +figures +figurine +figurines +figuring +filament +filamentous +filaments +filbert +filberts +filch +filched +filches +filching +file +filed +files +filet +filial +filibuster +filibustered +filibustering +filibusters +filigree +filigreed +filigreeing +filigrees +filing +filings +fill +filled +filler +fillers +fillet +filleted +filleting +fillets +fillies +filling +fillings +fillip +filliped +filliping +fillips +fills +filly +film +filmed +filmier +filmiest +filming +filmmaker +filmmakers +films +filmstrip +filmstrips +filmy +filter +filterable +filtered +filtering +filters +filth +filthier +filthiest +filthiness +filthy +filtrate +filtrated +filtrates +filtrating +filtration +fin +finagle +finagled +finagler +finaglers +finagles +finagling +final +finale +finales +finalist +finalists +finality +finalize +finalized +finalizes +finalizing +finally +finals +finance +financed +finances +financial +financially +financier +financiers +financing +finch +finches +find +finder +finders +finding +findings +finds +fine +fined +finely +fineness +finer +finery +fines +finesse +finessed +finesses +finessing +finest +finger +fingerboard +fingerboards +fingered +fingering +fingerings +fingernail +fingernails +fingerprint +fingerprinted +fingerprinting +fingerprints +fingers +fingertip +fingertips +finickier +finickiest +finicky +fining +finis +finises +finish +finished +finisher +finishers +finishes +finishing +finite +finitely +fink +finked +finking +finks +finny +fins +fir +fire +firearm +firearms +fireball +fireballs +firebomb +firebombed +firebombing +firebombs +firebrand +firebrands +firebreak +firebreaks +firebug +firebugs +firecracker +firecrackers +fired +firefight +firefighter +firefighters +firefighting +firefights +fireflies +firefly +firehouse +firehouses +fireman +firemen +fireplace +fireplaces +fireplug +fireplugs +firepower +fireproof +fireproofed +fireproofing +fireproofs +fires +fireside +firesides +firestorm +firestorms +firetrap +firetraps +firewall +firewalls +firewater +firewood +firework +fireworks +firing +firm +firmament +firmaments +firmed +firmer +firmest +firming +firmly +firmness +firms +firmware +firs +first +firstborn +firstborns +firsthand +firstly +firsts +firth +firths +fiscal +fiscally +fiscals +fish +fishbowl +fishbowls +fished +fisher +fisheries +fisherman +fishermen +fishers +fishery +fishes +fishhook +fishhooks +fishier +fishiest +fishing +fishnet +fishnets +fishtail +fishtailed +fishtailing +fishtails +fishwife +fishwives +fishy +fission +fissure +fissures +fist +fistful +fistfuls +fisticuffs +fists +fit +fitful +fitfully +fitly +fitness +fits +fitted +fitter +fitters +fittest +fitting +fittingly +fittings +five +fiver +fives +fix +fixable +fixate +fixated +fixates +fixating +fixation +fixations +fixative +fixatives +fixed +fixedly +fixer +fixers +fixes +fixing +fixings +fixity +fixture +fixtures +fizz +fizzed +fizzes +fizzier +fizziest +fizzing +fizzle +fizzled +fizzles +fizzling +fizzy +fjord +fjords +flab +flabbergast +flabbergasted +flabbergasting +flabbergasts +flabbier +flabbiest +flabbiness +flabby +flaccid +flack +flacks +flag +flagella +flagellate +flagellated +flagellates +flagellating +flagellation +flagellum +flagged +flagging +flagon +flagons +flagpole +flagpoles +flagrant +flagrantly +flags +flagship +flagships +flagstaff +flagstaffs +flagstone +flagstones +flail +flailed +flailing +flails +flair +flairs +flak +flake +flaked +flakes +flakier +flakiest +flakiness +flaking +flaky +flambeing +flambes +flamboyance +flamboyant +flamboyantly +flambé +flambéed +flame +flamed +flamenco +flamencos +flames +flamethrower +flamethrowers +flaming +flamingo +flamingos +flamings +flammability +flammable +flammables +flan +flange +flanges +flank +flanked +flanking +flanks +flannel +flanneled +flannelette +flanneling +flannels +flap +flapjack +flapjacks +flapped +flapper +flappers +flapping +flaps +flare +flared +flares +flaring +flash +flashback +flashbacks +flashbulb +flashbulbs +flashed +flasher +flashers +flashes +flashest +flashgun +flashguns +flashier +flashiest +flashily +flashiness +flashing +flashlight +flashlights +flashy +flask +flasks +flat +flatbed +flatbeds +flatboat +flatboats +flatcar +flatcars +flatfeet +flatfish +flatfishes +flatfoot +flatfooted +flatfoots +flatiron +flatirons +flatly +flatness +flats +flatted +flatten +flattened +flattening +flattens +flatter +flattered +flatterer +flatterers +flattering +flatteringly +flatters +flattery +flattest +flatting +flattop +flattops +flatulence +flatulent +flatware +flaunt +flaunted +flaunting +flaunts +flavor +flavored +flavorful +flavoring +flavorings +flavorless +flavors +flaw +flawed +flawing +flawless +flawlessly +flaws +flax +flaxen +flay +flayed +flaying +flays +flea +fleas +fleck +flecked +flecking +flecks +fled +fledged +fledgling +fledglings +flee +fleece +fleeced +fleeces +fleecier +fleeciest +fleecing +fleecy +fleeing +flees +fleet +fleeted +fleeter +fleetest +fleeting +fleetingly +fleetness +fleets +flesh +fleshed +fleshes +fleshier +fleshiest +fleshing +fleshlier +fleshliest +fleshly +fleshy +flew +flex +flexed +flexes +flexibility +flexible +flexibly +flexing +flextime +flibbertigibbet +flibbertigibbets +flick +flicked +flicker +flickered +flickering +flickers +flicking +flicks +flied +flier +fliers +flies +fliest +flight +flightier +flightiest +flightiness +flightless +flights +flighty +flimflam +flimflammed +flimflamming +flimflams +flimsier +flimsiest +flimsily +flimsiness +flimsy +flinch +flinched +flinches +flinching +fling +flinging +flings +flint +flintier +flintiest +flintlock +flintlocks +flints +flinty +flip +flippancy +flippant +flippantly +flipped +flipper +flippers +flippest +flipping +flips +flirt +flirtation +flirtations +flirtatious +flirtatiously +flirted +flirting +flirts +flit +flits +flitted +flitting +float +floated +floater +floaters +floating +floats +flock +flocked +flocking +flocks +floe +floes +flog +flogged +flogging +floggings +flogs +flood +flooded +flooder +floodgate +floodgates +flooding +floodlight +floodlighted +floodlighting +floodlights +floodlit +floods +floor +floorboard +floorboards +floored +flooring +floors +floozie +floozies +floozy +flop +flophouse +flophouses +flopped +floppier +floppies +floppiest +floppiness +flopping +floppy +flops +flora +floral +floras +florid +floridly +florin +florins +florist +florists +floss +flossed +flosses +flossing +flotation +flotations +flotilla +flotillas +flotsam +flounce +flounced +flounces +flouncing +flounder +floundered +floundering +flounders +flour +floured +flouring +flourish +flourished +flourishes +flourishing +flours +floury +flout +flouted +flouting +flouts +flow +flowed +flower +flowerbed +flowerbeds +flowered +flowerier +floweriest +floweriness +flowering +flowerpot +flowerpots +flowers +flowery +flowing +flown +flows +flu +flub +flubbed +flubbing +flubs +fluctuate +fluctuated +fluctuates +fluctuating +fluctuation +fluctuations +flue +fluency +fluent +fluently +flues +fluff +fluffed +fluffier +fluffiest +fluffiness +fluffing +fluffs +fluffy +fluid +fluidity +fluidly +fluids +fluke +flukes +flukier +flukiest +fluky +flume +flumes +flummox +flummoxed +flummoxes +flummoxing +flung +flunk +flunked +flunkies +flunking +flunks +flunky +fluoresce +fluoresced +fluorescence +fluorescent +fluoresces +fluorescing +fluoridate +fluoridated +fluoridates +fluoridating +fluoridation +fluoride +fluorides +fluorine +fluorite +fluorocarbon +fluorocarbons +fluoroscope +fluoroscopes +flurried +flurries +flurry +flurrying +flush +flushed +flusher +flushes +flushest +flushing +fluster +flustered +flustering +flusters +flute +fluted +flutes +fluting +flutist +flutists +flutter +fluttered +fluttering +flutters +fluttery +flux +fluxed +fluxes +fluxing +fly +flyby +flybys +flycatcher +flycatchers +flying +flyleaf +flyleaves +flyover +flyovers +flypaper +flypapers +flysheet +flyspeck +flyspecked +flyspecking +flyspecks +flyswatter +flyswatters +flyweight +flyweights +flywheel +flywheels +foal +foaled +foaling +foals +foam +foamed +foamier +foamiest +foaming +foams +foamy +fob +fobbed +fobbing +fobs +focal +focus +focused +focuses +focusing +fodder +fodders +foe +foes +fog +fogbound +fogged +foggier +foggiest +fogginess +fogging +foggy +foghorn +foghorns +fogies +fogs +fogy +foible +foibles +foil +foiled +foiling +foils +foist +foisted +foisting +foists +fold +foldaway +folded +folder +folders +folding +folds +foliage +folio +folios +folk +folklore +folks +folksier +folksiest +folksy +follicle +follicles +follies +follow +followed +follower +followers +following +followings +follows +folly +foment +fomentation +fomented +fomenting +foments +fond +fondant +fondants +fonder +fondest +fondle +fondled +fondles +fondling +fondly +fondness +fondue +fondues +font +fonts +food +foods +foodstuff +foodstuffs +fool +fooled +fooleries +foolery +foolhardier +foolhardiest +foolhardiness +foolhardy +fooling +foolish +foolishly +foolishness +foolproof +fools +foolscap +foot +footage +football +footballer +footballers +footballs +footbridge +footbridges +footed +footfall +footfalls +foothill +foothills +foothold +footholds +footing +footings +footlights +footlocker +footlockers +footloose +footman +footmen +footnote +footnoted +footnotes +footnoting +footpath +footpaths +footprint +footprints +footrest +footrests +foots +footsie +footsies +footsore +footstep +footsteps +footstool +footstools +footwear +footwork +fop +foppish +fops +for +forage +foraged +forager +foragers +forages +foraging +foray +forayed +foraying +forays +forbade +forbear +forbearance +forbearing +forbears +forbid +forbidden +forbidding +forbiddingly +forbiddings +forbids +forbore +forborne +force +forced +forceful +forcefully +forcefulness +forceps +forces +forcible +forcibly +forcing +ford +forded +fording +fords +fore +forearm +forearmed +forearming +forearms +forebear +forebears +forebode +foreboded +forebodes +foreboding +forebodings +forecast +forecaster +forecasters +forecasting +forecastle +forecastles +forecasts +foreclose +foreclosed +forecloses +foreclosing +foreclosure +foreclosures +forefather +forefathers +forefeet +forefinger +forefingers +forefoot +forefront +forefronts +foregather +foregathered +foregathering +foregathers +forego +foregoes +foregoing +foregone +foreground +foregrounded +foregrounding +foregrounds +forehand +forehands +forehead +foreheads +foreign +foreigner +foreigners +foreknowledge +foreleg +forelegs +forelock +forelocks +foreman +foremast +foremasts +foremen +foremost +forename +forenames +forenoon +forenoons +forensic +forensics +foreordain +foreordained +foreordaining +foreordains +foreplay +forerunner +forerunners +fores +foresail +foresails +foresaw +foresee +foreseeable +foreseeing +foreseen +foresees +foreshadow +foreshadowed +foreshadowing +foreshadows +foreshorten +foreshortened +foreshortening +foreshortens +foresight +foreskin +foreskins +forest +forestall +forestalled +forestalling +forestalls +forestation +forested +forester +foresters +foresting +forestry +forests +foretaste +foretasted +foretastes +foretasting +foretell +foretelling +foretells +forethought +foretold +forever +forevermore +forewarn +forewarned +forewarning +forewarns +forewent +forewoman +forewomen +foreword +forewords +forfeit +forfeited +forfeiting +forfeits +forfeiture +forgather +forgathered +forgathering +forgathers +forgave +forge +forged +forger +forgeries +forgers +forgery +forges +forget +forgetful +forgetfully +forgetfulness +forgets +forgettable +forgetting +forging +forgivable +forgive +forgiven +forgiveness +forgives +forgiving +forgo +forgoes +forgoing +forgone +forgot +forgotten +fork +forked +forking +forklift +forklifts +forks +forlorn +forlornly +form +formal +formaldehyde +formalism +formalities +formality +formalization +formalize +formalized +formalizes +formalizing +formally +formals +format +formation +formations +formative +formats +formatted +formatting +formed +former +formerly +formidable +formidably +forming +formless +formlessly +formlessness +forms +formula +formulae +formulaic +formulas +formulate +formulated +formulates +formulating +formulation +formulations +fornicate +fornicated +fornicates +fornicating +fornication +forsake +forsaken +forsakes +forsaking +forsook +forsooth +forswear +forswearing +forswears +forswore +forsworn +forsythia +forsythias +fort +forte +fortes +forth +forthcoming +forthright +forthrightly +forthrightness +forthwith +forties +fortieth +fortieths +fortification +fortifications +fortified +fortifies +fortify +fortifying +fortissimo +fortitude +fortnight +fortnightly +fortnights +fortress +fortresses +forts +fortuitous +fortuitously +fortunate +fortunately +fortune +fortunes +forty +forum +forums +forward +forwarded +forwarder +forwardest +forwarding +forwardness +forwards +forwent +fossil +fossilization +fossilize +fossilized +fossilizes +fossilizing +fossils +foster +fostered +fostering +fosters +fought +foul +fouled +fouler +foulest +fouling +foully +foulness +fouls +found +foundation +foundations +founded +founder +foundered +foundering +founders +founding +foundling +foundlings +foundries +foundry +founds +fount +fountain +fountainhead +fountainheads +fountains +founts +four +fourfold +fours +fourscore +foursome +foursomes +foursquare +fourteen +fourteens +fourteenth +fourteenths +fourth +fourthly +fourths +fowl +fowled +fowling +fowls +fox +foxed +foxes +foxglove +foxgloves +foxhole +foxholes +foxhound +foxhounds +foxier +foxiest +foxing +foxtrot +foxtrots +foxtrotted +foxtrotting +foxy +foyer +foyers +fracas +fracases +frack +fracked +fracking +fracks +fractal +fractals +fraction +fractional +fractionally +fractions +fractious +fractiously +fracture +fractured +fractures +fracturing +fragile +fragility +fragment +fragmentary +fragmentation +fragmented +fragmenting +fragments +fragrance +fragrances +fragrant +fragrantly +frail +frailer +frailest +frailties +frailty +frame +framed +framer +framers +frames +framework +frameworks +framing +franc +franchise +franchised +franchisee +franchisees +franchiser +franchisers +franchises +franchising +francs +frank +franked +franker +frankest +frankfurter +frankfurters +frankincense +franking +frankly +frankness +franks +frantic +frantically +frappes +frappé +frat +fraternal +fraternally +fraternities +fraternity +fraternization +fraternize +fraternized +fraternizes +fraternizing +fratricide +fratricides +frats +fraud +frauds +fraudulence +fraudulent +fraudulently +fraught +fray +frayed +fraying +frays +frazzle +frazzled +frazzles +frazzling +freak +freaked +freakier +freakiest +freaking +freakish +freaks +freaky +freckle +freckled +freckles +freckling +free +freebase +freebased +freebases +freebasing +freebee +freebees +freebie +freebies +freebooter +freebooters +freed +freedman +freedmen +freedom +freedoms +freehand +freehold +freeholder +freeholders +freeholds +freeing +freelance +freelanced +freelancer +freelancers +freelances +freelancing +freeload +freeloaded +freeloader +freeloaders +freeloading +freeloads +freely +freeman +freemen +freer +frees +freest +freestanding +freestyle +freestyles +freethinker +freethinkers +freethinking +freeway +freeways +freewheel +freewheeled +freewheeling +freewheels +freewill +freeze +freezer +freezers +freezes +freezing +freight +freighted +freighter +freighters +freighting +freights +french +frenetic +frenetically +frenzied +frenziedly +frenzies +frenzy +frequencies +frequency +frequent +frequented +frequenter +frequentest +frequenting +frequently +frequents +fresco +frescoes +fresh +freshen +freshened +freshening +freshens +fresher +freshest +freshet +freshets +freshly +freshman +freshmen +freshness +freshwater +fret +fretful +fretfully +fretfulness +frets +fretted +fretting +fretwork +friable +friar +friars +fricassee +fricasseed +fricasseeing +fricassees +friction +fridge +fridges +fried +friend +friended +friending +friendless +friendlier +friendlies +friendliest +friendliness +friendly +friends +friendship +friendships +fries +frieze +friezes +frigate +frigates +fright +frighted +frighten +frightened +frightening +frighteningly +frightens +frightful +frightfully +frighting +frights +frigid +frigidity +frigidly +frill +frillier +frilliest +frills +frilly +fringe +fringed +fringes +fringing +fripperies +frippery +frisk +frisked +friskier +friskiest +friskily +friskiness +frisking +frisks +frisky +fritter +frittered +frittering +fritters +frivolities +frivolity +frivolous +frivolously +frizz +frizzed +frizzes +frizzier +frizziest +frizzing +frizzle +frizzled +frizzles +frizzling +frizzy +fro +frock +frocks +frog +frogman +frogmen +frogs +frolic +frolicked +frolicking +frolics +frolicsome +from +frond +fronds +front +frontage +frontages +frontal +frontally +fronted +frontier +frontiers +frontiersman +frontiersmen +fronting +frontispiece +frontispieces +fronts +frost +frostbit +frostbite +frostbites +frostbiting +frostbitten +frosted +frostier +frostiest +frostily +frostiness +frosting +frostings +frosts +frosty +froth +frothed +frothier +frothiest +frothing +froths +frothy +frown +frowned +frowning +frowns +frowsier +frowsiest +frowsy +frowzier +frowziest +frowzy +froze +frozen +fructified +fructifies +fructify +fructifying +fructose +frugal +frugality +frugally +fruit +fruitcake +fruitcakes +fruited +fruitful +fruitfully +fruitfulness +fruitier +fruitiest +fruiting +fruition +fruitless +fruitlessly +fruitlessness +fruits +fruity +frump +frumpier +frumpiest +frumps +frumpy +frustrate +frustrated +frustrates +frustrating +frustration +frustrations +fry +fryer +fryers +frying +fuchsia +fuchsias +fuck +fucked +fucker +fuckers +fucking +fucks +fuddle +fuddled +fuddles +fuddling +fudge +fudged +fudges +fudging +fuel +fueled +fueling +fuels +fugitive +fugitives +fugue +fugues +fulcrum +fulcrums +fulfill +fulfilled +fulfilling +fulfillment +fulfills +full +fullback +fullbacks +fulled +fuller +fullest +fulling +fullness +fulls +fully +fulminate +fulminated +fulminates +fulminating +fulmination +fulminations +fulsome +fumble +fumbled +fumbler +fumblers +fumbles +fumbling +fume +fumed +fumes +fumigate +fumigated +fumigates +fumigating +fumigation +fumigator +fumigators +fuming +fun +function +functional +functionality +functionally +functionaries +functionary +functioned +functioning +functions +fund +fundamental +fundamentalism +fundamentalist +fundamentalists +fundamentally +fundamentals +funded +funding +funds +funeral +funerals +funereal +funereally +fungal +fungi +fungicidal +fungicide +fungicides +fungous +fungus +funicular +funiculars +funk +funked +funkier +funkiest +funking +funks +funky +funnel +funneled +funneling +funnels +funner +funnest +funnier +funnies +funniest +funnily +funniness +funny +fur +furbelow +furbish +furbished +furbishes +furbishing +furies +furious +furiously +furl +furled +furling +furlong +furlongs +furlough +furloughed +furloughing +furloughs +furls +furnace +furnaces +furnish +furnished +furnishes +furnishing +furnishings +furniture +furor +furors +furred +furrier +furriers +furriest +furring +furrow +furrowed +furrowing +furrows +furry +furs +further +furtherance +furthered +furthering +furthermore +furthermost +furthers +furthest +furtive +furtively +furtiveness +fury +furze +fuse +fused +fuselage +fuselages +fuses +fusible +fusillade +fusillades +fusing +fusion +fusions +fuss +fussbudget +fussbudgets +fussed +fusses +fussier +fussiest +fussily +fussiness +fussing +fussy +fustian +fustier +fustiest +fusty +futile +futilely +futility +futon +futons +future +futures +futuristic +futurities +futurity +futz +futzed +futzes +futzing +fuzz +fuzzed +fuzzes +fuzzier +fuzziest +fuzzily +fuzziness +fuzzing +fuzzy +fête +fêtes +g +gab +gabardine +gabardines +gabbed +gabbier +gabbiest +gabbing +gabble +gabbled +gabbles +gabbling +gabby +gaberdine +gaberdines +gable +gabled +gables +gabs +gad +gadabout +gadabouts +gadded +gadding +gadflies +gadfly +gadget +gadgetry +gadgets +gads +gaff +gaffe +gaffed +gaffes +gaffing +gaffs +gag +gagged +gagging +gaggle +gaggles +gags +gaiety +gaily +gain +gained +gainful +gainfully +gaining +gains +gainsaid +gainsay +gainsaying +gainsays +gait +gaiter +gaiters +gaits +gal +gala +galactic +galas +galaxies +galaxy +gale +galena +gales +gall +gallant +gallantly +gallantry +gallants +gallbladder +gallbladders +galled +galleon +galleons +galleries +gallery +galley +galleys +galling +gallium +gallivant +gallivanted +gallivanting +gallivants +gallon +gallons +gallop +galloped +galloping +gallops +gallows +galls +gallstone +gallstones +galore +galosh +galoshes +gals +galvanic +galvanize +galvanized +galvanizes +galvanizing +galvanometer +galvanometers +gambit +gambits +gamble +gambled +gambler +gamblers +gambles +gambling +gambol +gamboled +gamboling +gambols +game +gamecock +gamecocks +gamed +gamekeeper +gamekeepers +gamely +gameness +gamer +games +gamesmanship +gamest +gamete +gametes +gamey +gamier +gamiest +gamin +gamine +gamines +gaming +gamins +gamma +gammas +gamut +gamuts +gamy +gander +ganders +gang +ganged +ganging +gangland +ganglia +gangling +ganglion +gangplank +gangplanks +gangrene +gangrened +gangrenes +gangrening +gangrenous +gangs +gangster +gangsters +gangway +gangways +gannet +gannets +gantlet +gantlets +gantries +gantry +gap +gape +gaped +gapes +gaping +gaps +garage +garaged +garages +garaging +garb +garbage +garbageman +garbanzo +garbanzos +garbed +garbing +garble +garbled +garbles +garbling +garbs +garden +gardened +gardener +gardeners +gardenia +gardenias +gardening +gardens +gargantuan +gargle +gargled +gargles +gargling +gargoyle +gargoyles +garish +garishly +garishness +garland +garlanded +garlanding +garlands +garlic +garlicky +garment +garments +garner +garnered +garnering +garners +garnet +garnets +garnish +garnished +garnishee +garnisheed +garnisheeing +garnishees +garnishes +garnishing +garotte +garotted +garottes +garotting +garret +garrets +garrison +garrisoned +garrisoning +garrisons +garrote +garroted +garrotes +garroting +garrotte +garrotted +garrottes +garrotting +garrulity +garrulous +garrulously +garrulousness +garter +garters +gas +gaseous +gases +gash +gashed +gashes +gashing +gasket +gaskets +gaslight +gaslights +gasohol +gasoline +gasp +gasped +gasping +gasps +gassed +gassier +gassiest +gassing +gassy +gastric +gastritis +gastrointestinal +gastronomic +gastronomical +gastronomy +gasworks +gate +gatecrasher +gatecrashers +gated +gatepost +gateposts +gates +gateway +gateways +gather +gathered +gatherer +gatherers +gathering +gatherings +gathers +gating +gauche +gaucher +gauchest +gaucho +gauchos +gaudier +gaudiest +gaudily +gaudiness +gaudy +gauge +gauged +gauges +gauging +gaunt +gaunter +gauntest +gauntlet +gauntlets +gauntness +gauze +gauzier +gauziest +gauzy +gave +gavel +gavels +gavotte +gavottes +gawk +gawked +gawkier +gawkiest +gawkily +gawkiness +gawking +gawks +gawky +gay +gayer +gayest +gayness +gays +gaze +gazebo +gazebos +gazed +gazelle +gazelles +gazer +gazers +gazes +gazette +gazetted +gazetteer +gazetteers +gazettes +gazetting +gazillion +gazillions +gazing +gazpacho +gear +gearbox +gearboxes +geared +gearing +gears +gearshift +gearshifts +gearwheel +gearwheels +gecko +geckos +gee +geed +geeing +geek +geekier +geekiest +geeks +geeky +gees +geese +geezer +geezers +geisha +gel +gelatin +gelatinous +geld +gelded +gelding +geldings +gelds +gelid +gelled +gelling +gels +gem +gems +gemstone +gemstones +gendarme +gendarmes +gender +genders +gene +genealogical +genealogies +genealogist +genealogists +genealogy +genera +general +generalissimo +generalissimos +generalities +generality +generalization +generalizations +generalize +generalized +generalizes +generalizing +generally +generals +generate +generated +generates +generating +generation +generations +generative +generator +generators +generic +generically +generics +generosities +generosity +generous +generously +genes +geneses +genesis +genetic +genetically +geneticist +geneticists +genetics +genial +geniality +genially +genie +genies +genii +genital +genitalia +genitals +genitive +genitives +genius +geniuses +genocide +genome +genomes +genre +genres +gent +genteel +gentian +gentians +gentile +gentiles +gentility +gentle +gentled +gentlefolk +gentleman +gentlemanly +gentlemen +gentleness +gentler +gentles +gentlest +gentlewoman +gentlewomen +gentling +gently +gentries +gentrification +gentrified +gentrifies +gentrify +gentrifying +gentry +gents +genuflect +genuflected +genuflecting +genuflection +genuflections +genuflects +genuine +genuinely +genuineness +genus +geocache +geocached +geocaches +geocaching +geocentric +geode +geodes +geodesic +geodesics +geoengineering +geographer +geographers +geographic +geographical +geographically +geographies +geography +geologic +geological +geologically +geologies +geologist +geologists +geology +geometer +geometric +geometrical +geometrically +geometries +geometry +geophysical +geophysics +geopolitical +geopolitics +geostationary +geothermal +geranium +geraniums +gerbil +gerbils +geriatric +geriatrics +germ +germane +germanium +germicidal +germicide +germicides +germinal +germinate +germinated +germinates +germinating +germination +germs +gerontologist +gerontologists +gerontology +gerrymander +gerrymandered +gerrymandering +gerrymanders +gerund +gerunds +gestate +gestated +gestates +gestating +gestation +gesticulate +gesticulated +gesticulates +gesticulating +gesticulation +gesticulations +gesture +gestured +gestures +gesturing +gesundheit +get +getaway +getaways +gets +getting +getup +gewgaw +gewgaws +geyser +geysers +ghastlier +ghastliest +ghastliness +ghastly +gherkin +gherkins +ghetto +ghettos +ghost +ghosted +ghosting +ghostlier +ghostliest +ghostliness +ghostly +ghosts +ghostwrite +ghostwriter +ghostwriters +ghostwrites +ghostwriting +ghostwritten +ghostwrote +ghoul +ghoulish +ghouls +giant +giantess +giantesses +giants +gibber +gibbered +gibbering +gibberish +gibbers +gibbet +gibbeted +gibbeting +gibbets +gibbon +gibbons +gibe +gibed +gibes +gibing +giblet +giblets +giddier +giddiest +giddily +giddiness +giddy +gift +gifted +gifting +gifts +gig +gigabit +gigabits +gigabyte +gigabytes +gigahertz +gigantic +gigapixel +gigapixels +gigged +gigging +giggle +giggled +giggler +gigglers +giggles +gigglier +giggliest +giggling +giggly +gigolo +gigolos +gigs +gild +gilded +gilding +gilds +gill +gills +gilt +gilts +gimcrack +gimcracks +gimlet +gimleted +gimleting +gimlets +gimme +gimmick +gimmickry +gimmicks +gimmicky +gimpy +gin +ginger +gingerbread +gingerly +gingersnap +gingersnaps +gingham +gingivitis +ginkgo +ginkgoes +ginkgos +ginned +ginning +gins +ginseng +giraffe +giraffes +gird +girded +girder +girders +girding +girdle +girdled +girdles +girdling +girds +girl +girlfriend +girlfriends +girlhood +girlhoods +girlish +girlishly +girls +girt +girted +girth +girths +girting +girts +gist +give +giveaway +giveaways +given +givens +gives +giving +gizmo +gizmos +gizzard +gizzards +glacial +glacially +glacier +glaciers +glad +gladden +gladdened +gladdening +gladdens +gladder +gladdest +glade +glades +gladiator +gladiatorial +gladiators +gladiola +gladiolas +gladioli +gladiolus +gladly +gladness +glads +glamor +glamored +glamoring +glamorize +glamorized +glamorizes +glamorizing +glamorous +glamorously +glamors +glamour +glamoured +glamouring +glamours +glance +glanced +glances +glancing +gland +glands +glandular +glare +glared +glares +glaring +glaringly +glass +glassed +glasses +glassful +glassfuls +glassier +glassiest +glassing +glassware +glassy +glaucoma +glaze +glazed +glazes +glazier +glaziers +glazing +gleam +gleamed +gleaming +gleamings +gleams +glean +gleaned +gleaning +gleans +glee +gleeful +gleefully +glen +glens +glib +glibber +glibbest +glibly +glibness +glide +glided +glider +gliders +glides +gliding +glimmer +glimmered +glimmering +glimmerings +glimmers +glimpse +glimpsed +glimpses +glimpsing +glint +glinted +glinting +glints +glissandi +glissando +glisten +glistened +glistening +glistens +glitch +glitches +glitter +glittered +glittering +glitters +glittery +glitz +glitzier +glitziest +glitzy +gloaming +gloamings +gloat +gloated +gloating +gloats +glob +global +globalization +globally +globe +globes +globetrotter +globetrotters +globs +globular +globule +globules +glockenspiel +glockenspiels +gloom +gloomier +gloomiest +gloomily +gloominess +gloomy +glop +gloried +glories +glorification +glorified +glorifies +glorify +glorifying +glorious +gloriously +glory +glorying +gloss +glossaries +glossary +glossed +glosses +glossier +glossies +glossiest +glossiness +glossing +glossy +glottis +glottises +glove +gloved +gloves +gloving +glow +glowed +glower +glowered +glowering +glowers +glowing +glowingly +glows +glowworm +glowworms +glucose +glue +glued +glues +gluey +gluier +gluiest +gluing +glum +glumly +glummer +glummest +glumness +glut +gluten +glutinous +gluts +glutted +glutting +glutton +gluttonous +gluttonously +gluttons +gluttony +glycerin +glycerine +glycerol +glycogen +glyph +gnarl +gnarled +gnarlier +gnarliest +gnarling +gnarls +gnarly +gnash +gnashed +gnashes +gnashing +gnat +gnats +gnaw +gnawed +gnawing +gnaws +gneiss +gnome +gnomes +gnomish +gnu +gnus +go +goad +goaded +goading +goads +goal +goalie +goalies +goalkeeper +goalkeepers +goalpost +goalposts +goals +goaltender +goaltenders +goat +goatee +goatees +goatherd +goatherds +goats +goatskin +goatskins +gob +gobbed +gobbing +gobble +gobbled +gobbledygook +gobbler +gobblers +gobbles +gobbling +goblet +goblets +goblin +goblins +gobs +god +godchild +godchildren +goddam +goddamed +goddamn +goddamned +goddaughter +goddaughters +goddess +goddesses +godfather +godfathers +godforsaken +godhood +godless +godlier +godliest +godlike +godliness +godly +godmother +godmothers +godparent +godparents +gods +godsend +godsends +godson +godsons +goes +gofer +gofers +goggle +goggled +goggles +goggling +going +goings +goiter +goiters +gold +goldbrick +goldbricked +goldbricking +goldbricks +golden +goldener +goldenest +goldenrod +goldfinch +goldfinches +goldfish +goldfishes +golds +goldsmith +goldsmiths +golf +golfed +golfer +golfers +golfing +golfs +gollies +golly +gonad +gonads +gondola +gondolas +gondolier +gondoliers +gone +goner +goners +gong +gonged +gonging +gongs +gonna +gonorrhea +goo +goober +goobers +good +goodby +goodbye +goodbyes +goodbys +goodie +goodies +goodlier +goodliest +goodly +goodness +goodnight +goods +goodwill +goody +gooey +goof +goofed +goofier +goofiest +goofing +goofs +goofy +google +googled +googles +googling +gooier +gooiest +gook +gooks +goon +goons +goop +goose +gooseberries +gooseberry +goosed +gooses +goosing +gopher +gophers +gore +gored +gores +gorge +gorged +gorgeous +gorgeously +gorges +gorging +gorier +goriest +gorilla +gorillas +goriness +goring +gorse +gory +gosh +gosling +goslings +gospel +gospels +gossamer +gossip +gossiped +gossiping +gossips +gossipy +got +gotta +gotten +gouge +gouged +gouger +gougers +gouges +gouging +goulash +goulashes +gourd +gourds +gourmand +gourmands +gourmet +gourmets +gout +goutier +goutiest +gouty +govern +governable +governance +governed +governess +governesses +governing +government +governmental +governments +governor +governors +governorship +governs +gown +gowned +gowning +gowns +grab +grabbed +grabber +grabbing +grabs +grace +graced +graceful +gracefully +gracefulness +graceless +gracelessly +gracelessness +graces +gracing +gracious +graciously +graciousness +grackle +grackles +grad +gradation +gradations +grade +graded +grader +graders +grades +gradient +gradients +grading +grads +gradual +gradually +graduate +graduated +graduates +graduating +graduation +graduations +graffiti +graffito +graft +grafted +grafter +grafters +grafting +grafts +grail +grain +grainier +grainiest +grains +grainy +gram +grammar +grammarian +grammarians +grammars +grammatical +grammatically +gramophone +grams +granaries +granary +grand +grandad +grandads +grandchild +grandchildren +granddad +granddads +granddaughter +granddaughters +grandee +grandees +grander +grandest +grandeur +grandfather +grandfathered +grandfathering +grandfathers +grandiloquence +grandiloquent +grandiose +grandly +grandma +grandmas +grandmother +grandmothers +grandness +grandpa +grandparent +grandparents +grandpas +grands +grandson +grandsons +grandstand +grandstanded +grandstanding +grandstands +grange +granges +granite +grannie +grannies +granny +granola +grant +granted +granting +grants +granular +granularity +granulate +granulated +granulates +granulating +granulation +granule +granules +grape +grapefruit +grapefruits +grapes +grapevine +grapevines +graph +graphed +graphic +graphical +graphically +graphics +graphing +graphite +graphologist +graphologists +graphology +graphs +grapnel +grapnels +grapple +grappled +grapples +grappling +grasp +grasped +grasping +grasps +grass +grassed +grasses +grasshopper +grasshoppers +grassier +grassiest +grassing +grassland +grassy +grate +grated +grateful +gratefully +gratefulness +grater +graters +grates +gratification +gratifications +gratified +gratifies +gratify +gratifying +grating +gratings +gratis +gratitude +gratuities +gratuitous +gratuitously +gratuity +grave +graved +gravel +graveled +graveling +gravelly +gravels +gravely +graven +graver +graves +gravest +gravestone +gravestones +graveyard +graveyards +gravies +graving +gravitate +gravitated +gravitates +gravitating +gravitation +gravitational +gravity +gravy +gray +graybeard +graybeards +grayed +grayer +grayest +graying +grayish +grayness +grays +graze +grazed +grazes +grazing +grease +greased +greasepaint +greases +greasier +greasiest +greasiness +greasing +greasy +great +greater +greatest +greatly +greatness +greats +grebe +grebes +greed +greedier +greediest +greedily +greediness +greedy +green +greenback +greenbacks +greened +greener +greenery +greenest +greengrocer +greengrocers +greenhorn +greenhorns +greenhouse +greenhouses +greening +greenish +greenness +greens +greensward +greet +greeted +greeting +greetings +greets +gregarious +gregariously +gregariousness +gremlin +gremlins +grenade +grenades +grenadier +grenadiers +grew +greyhound +greyhounds +grid +griddle +griddlecake +griddlecakes +griddles +gridiron +gridirons +gridlock +gridlocks +grids +grief +griefs +grievance +grievances +grieve +grieved +grieves +grieving +grievous +grievously +griffin +griffins +grill +grille +grilled +grilles +grilling +grills +grim +grimace +grimaced +grimaces +grimacing +grime +grimed +grimes +grimier +grimiest +griming +grimly +grimmer +grimmest +grimness +grimy +grin +grind +grinder +grinders +grinding +grinds +grindstone +grindstones +gringo +gringos +grinned +grinning +grins +grip +gripe +griped +gripes +griping +grippe +gripped +gripping +grips +grislier +grisliest +grisly +grist +gristle +gristly +grit +grits +gritted +grittier +grittiest +gritting +gritty +grizzled +grizzlier +grizzlies +grizzliest +grizzly +groan +groaned +groaning +groans +grocer +groceries +grocers +grocery +grog +groggier +groggiest +groggily +grogginess +groggy +groin +groins +grommet +grommets +groom +groomed +grooming +grooms +groove +grooved +grooves +groovier +grooviest +grooving +groovy +grope +groped +gropes +groping +grosbeak +grosbeaks +gross +grossed +grosser +grosses +grossest +grossing +grossly +grossness +grotesque +grotesquely +grotesques +grotto +grottoes +grouch +grouched +grouches +grouchier +grouchiest +grouchiness +grouching +grouchy +ground +groundbreaking +groundbreakings +grounded +grounder +grounders +groundhog +groundhogs +grounding +groundings +groundless +groundlessly +grounds +groundswell +groundswells +groundwork +group +grouped +grouper +groupers +groupie +groupies +grouping +groupings +groups +grouse +groused +grouses +grousing +grout +grouted +grouting +grouts +grove +grovel +groveled +groveler +grovelers +groveling +grovels +groves +grow +grower +growers +growing +growl +growled +growling +growls +grown +grownup +grownups +grows +growth +growths +grub +grubbed +grubbier +grubbiest +grubbiness +grubbing +grubby +grubs +grubstake +grudge +grudged +grudges +grudging +grudgingly +gruel +grueling +gruelings +gruesome +gruesomely +gruesomer +gruesomest +gruff +gruffer +gruffest +gruffly +gruffness +grumble +grumbled +grumbler +grumblers +grumbles +grumbling +grumpier +grumpiest +grumpily +grumpiness +grumpy +grunge +grungier +grungiest +grungy +grunt +grunted +grunting +grunts +gs +guacamole +guano +guarantee +guaranteed +guaranteeing +guarantees +guarantied +guaranties +guarantor +guarantors +guaranty +guarantying +guard +guarded +guardedly +guardhouse +guardhouses +guardian +guardians +guardianship +guarding +guardrail +guardrails +guardroom +guardrooms +guards +guardsman +guardsmen +guava +guavas +gubernatorial +guerilla +guerillas +guerrilla +guerrillas +guess +guessable +guessed +guesser +guessers +guesses +guessing +guesstimate +guesstimated +guesstimates +guesstimating +guesswork +guest +guested +guesting +guests +guff +guffaw +guffawed +guffawing +guffaws +guidance +guide +guidebook +guidebooks +guided +guideline +guidelines +guides +guiding +guild +guilder +guilders +guilds +guile +guileful +guileless +guillotine +guillotined +guillotines +guillotining +guilt +guiltier +guiltiest +guiltily +guiltiness +guiltless +guilty +guinea +guineas +guise +guises +guitar +guitarist +guitarists +guitars +gulag +gulags +gulch +gulches +gulf +gulfs +gull +gulled +gullet +gullets +gullibility +gullible +gullies +gulling +gulls +gully +gulp +gulped +gulping +gulps +gum +gumbo +gumbos +gumdrop +gumdrops +gummed +gummier +gummiest +gumming +gummy +gumption +gums +gun +gunboat +gunboats +gunfight +gunfights +gunfire +gunk +gunman +gunmen +gunned +gunner +gunners +gunnery +gunning +gunny +gunnysack +gunnysacks +gunpoint +gunpowder +gunrunner +gunrunners +gunrunning +guns +gunshot +gunshots +gunslinger +gunslingers +gunsmith +gunsmiths +gunwale +gunwales +guppies +guppy +gurgle +gurgled +gurgles +gurgling +gurney +gurneys +guru +gurus +gush +gushed +gusher +gushers +gushes +gushier +gushiest +gushing +gushy +gusset +gusseted +gusseting +gussets +gust +gustatory +gusted +gustier +gustiest +gusting +gusto +gusts +gusty +gut +gutless +guts +gutsier +gutsiest +gutsy +gutted +gutter +guttered +guttering +gutters +guttersnipe +guttersnipes +gutting +guttural +gutturals +guy +guyed +guying +guys +guzzle +guzzled +guzzler +guzzlers +guzzles +guzzling +gym +gymnasium +gymnasiums +gymnast +gymnastic +gymnastics +gymnasts +gymnosperm +gymnosperms +gyms +gynecological +gynecologist +gynecologists +gynecology +gyp +gypped +gypping +gyps +gypsies +gypsum +gypsy +gyrate +gyrated +gyrates +gyrating +gyration +gyrations +gyro +gyros +gyroscope +gyroscopes +h +ha +haberdasher +haberdasheries +haberdashers +haberdashery +habit +habitability +habitable +habitat +habitation +habitations +habitats +habits +habitual +habitually +habituate +habituated +habituates +habituating +habituation +habitué +habitués +hacienda +haciendas +hack +hacked +hacker +hackers +hacking +hackle +hackles +hackney +hackneyed +hackneying +hackneys +hacks +hacksaw +hacksaws +hacktivist +hacktivists +had +haddock +haddocks +hafnium +haft +hafts +hag +haggard +haggle +haggled +haggler +hagglers +haggles +haggling +hags +hah +haiku +hail +hailed +hailing +hails +hailstone +hailstones +hailstorm +hailstorms +hair +hairbreadth +hairbreadths +hairbrush +hairbrushes +haircut +haircuts +hairdo +hairdos +hairdresser +hairdressers +hairdressing +haired +hairier +hairiest +hairiness +hairless +hairline +hairlines +hairnet +hairnets +hairpiece +hairpieces +hairpin +hairpins +hairs +hairsbreadth +hairsbreadths +hairsplitting +hairspring +hairsprings +hairstyle +hairstyles +hairstylist +hairstylists +hairy +hake +hakes +halberd +halberds +halcyon +hale +haled +haler +hales +halest +half +halfback +halfbacks +halfhearted +halfheartedly +halfheartedness +halfpence +halfpennies +halfpenny +halftime +halftimes +halfway +halibut +halibuts +haling +halitosis +hall +hallelujah +hallelujahs +hallmark +hallmarked +hallmarking +hallmarks +hallow +hallowed +hallowing +hallows +halls +hallucinate +hallucinated +hallucinates +hallucinating +hallucination +hallucinations +hallucinatory +hallucinogen +hallucinogenic +hallucinogenics +hallucinogens +hallway +hallways +halo +haloed +halogen +halogens +haloing +halon +halos +halt +halted +halter +haltered +haltering +halters +halting +haltingly +halts +halve +halved +halves +halving +halyard +halyards +ham +hamburger +hamburgers +hamlet +hamlets +hammed +hammer +hammered +hammerhead +hammerheads +hammering +hammerings +hammers +hamming +hammock +hammocks +hamper +hampered +hampering +hampers +hams +hamster +hamsters +hamstring +hamstringing +hamstrings +hamstrung +hand +handbag +handbags +handball +handballs +handbill +handbills +handbook +handbooks +handcar +handcars +handcart +handcarts +handcraft +handcrafted +handcrafting +handcrafts +handcuff +handcuffed +handcuffing +handcuffs +handed +handedness +handful +handfuls +handgun +handguns +handheld +handhelds +handicap +handicapped +handicapper +handicappers +handicapping +handicaps +handicraft +handicrafts +handier +handiest +handily +handiness +handing +handiwork +handkerchief +handkerchiefs +handle +handlebar +handlebars +handled +handler +handlers +handles +handling +handmade +handmaid +handmaiden +handmaidens +handmaids +handout +handouts +handpick +handpicked +handpicking +handpicks +handrail +handrails +hands +handset +handsets +handshake +handshakes +handshaking +handsome +handsomely +handsomeness +handsomer +handsomest +handspring +handsprings +handstand +handstands +handwork +handwriting +handwritten +handy +handyman +handymen +hang +hangar +hangars +hangdog +hanged +hanger +hangers +hanging +hangings +hangman +hangmen +hangnail +hangnails +hangout +hangouts +hangover +hangovers +hangs +hank +hanker +hankered +hankering +hankerings +hankers +hankie +hankies +hanks +hanky +hansom +hansoms +haphazard +haphazardly +hapless +happen +happened +happening +happenings +happens +happenstance +happenstances +happier +happiest +happily +happiness +happy +harangue +harangued +harangues +haranguing +harass +harassed +harasses +harassing +harassment +harbinger +harbingers +harbor +harbored +harboring +harbors +hard +hardback +hardbacks +hardball +hardcover +hardcovers +harden +hardened +hardener +hardeners +hardening +hardens +harder +hardest +hardheaded +hardheadedly +hardheadedness +hardhearted +hardheartedly +hardheartedness +hardier +hardiest +hardily +hardiness +hardliner +hardliners +hardly +hardness +hardship +hardships +hardtack +hardtop +hardtops +hardware +hardwood +hardwoods +hardy +hare +harebrained +hared +harelip +harelips +harem +harems +hares +haring +hark +harked +harking +harks +harlequin +harlequins +harlot +harlots +harm +harmed +harmful +harmfully +harmfulness +harming +harmless +harmlessly +harmlessness +harmonic +harmonica +harmonically +harmonicas +harmonics +harmonies +harmonious +harmoniously +harmoniousness +harmonization +harmonize +harmonized +harmonizes +harmonizing +harmony +harms +harness +harnessed +harnesses +harnessing +harp +harped +harpies +harping +harpist +harpists +harpoon +harpooned +harpooning +harpoons +harps +harpsichord +harpsichords +harpy +harridan +harridans +harried +harries +harrow +harrowed +harrowing +harrows +harry +harrying +harsh +harsher +harshest +harshly +harshness +hart +harts +harvest +harvested +harvester +harvesters +harvesting +harvests +has +hash +hashed +hashes +hashing +hashish +hashtag +hashtags +hasp +hasps +hassle +hassled +hassles +hassling +hassock +hassocks +haste +hasted +hasten +hastened +hastening +hastens +hastes +hastier +hastiest +hastily +hastiness +hasting +hasty +hat +hatch +hatchback +hatchbacks +hatched +hatcheries +hatchery +hatches +hatchet +hatchets +hatching +hatchway +hatchways +hate +hated +hateful +hatefully +hatefulness +hater +haters +hates +hath +hating +hatred +hatreds +hats +hatted +hatter +hatters +hatting +haughtier +haughtiest +haughtily +haughtiness +haughty +haul +hauled +hauler +haulers +hauling +hauls +haunch +haunches +haunt +haunted +haunting +hauntingly +haunts +hauteur +have +haven +havens +haversack +haversacks +haves +having +havoc +haw +hawed +hawing +hawk +hawked +hawker +hawkers +hawking +hawkish +hawks +haws +hawser +hawsers +hawthorn +hawthorns +hay +haycock +haycocks +hayed +haying +hayloft +haylofts +haymow +haymows +hays +hayseed +hayseeds +haystack +haystacks +haywire +hazard +hazarded +hazarding +hazardous +hazards +haze +hazed +hazel +hazelnut +hazelnuts +hazels +hazes +hazier +haziest +hazily +haziness +hazing +hazings +hazmat +hazy +he +head +headache +headaches +headband +headbands +headboard +headboards +headdress +headdresses +headed +header +headers +headfirst +headgear +headhunter +headhunters +headier +headiest +heading +headings +headland +headlands +headless +headlight +headlights +headline +headlined +headlines +headlining +headlock +headlocks +headlong +headmaster +headmasters +headmistress +headmistresses +headphone +headphones +headquarter +headquarters +headrest +headrests +headroom +heads +headset +headsets +headstone +headstones +headstrong +headwaiter +headwaiters +headwaters +headway +headwind +headwinds +headword +headwords +heady +heal +healed +healer +healers +healing +heals +health +healthcare +healthful +healthfully +healthfulness +healthier +healthiest +healthily +healthiness +healthy +heap +heaped +heaping +heaps +hear +heard +hearer +hearers +hearing +hearings +hearken +hearkened +hearkening +hearkens +hears +hearsay +hearse +hearses +heart +heartache +heartaches +heartbeat +heartbeats +heartbreak +heartbreaking +heartbreaks +heartbroken +heartburn +hearten +heartened +heartening +heartens +heartfelt +hearth +hearths +heartier +hearties +heartiest +heartily +heartiness +heartland +heartlands +heartless +heartlessly +heartlessness +heartrending +hearts +heartsick +heartstrings +heartthrob +heartthrobs +heartwarming +hearty +heat +heated +heatedly +heater +heaters +heath +heathen +heathenish +heathens +heather +heaths +heating +heats +heatstroke +heave +heaved +heaven +heavenlier +heavenliest +heavenly +heavens +heavenward +heavenwards +heaves +heavier +heavies +heaviest +heavily +heaviness +heaving +heavy +heavyset +heavyweight +heavyweights +heck +heckle +heckled +heckler +hecklers +heckles +heckling +hectare +hectares +hectic +hectically +hector +hectored +hectoring +hectors +hedge +hedged +hedgehog +hedgehogs +hedgerow +hedgerows +hedges +hedging +hedonism +hedonist +hedonistic +hedonists +heed +heeded +heedful +heeding +heedless +heedlessly +heedlessness +heeds +heehaw +heehawed +heehawing +heehaws +heel +heeled +heeling +heels +heft +hefted +heftier +heftiest +hefting +hefts +hefty +hegemony +heifer +heifers +height +heighten +heightened +heightening +heightens +heights +heinous +heinously +heinousness +heir +heiress +heiresses +heirloom +heirlooms +heirs +heist +heisted +heisting +heists +held +helical +helices +helicopter +helicoptered +helicoptering +helicopters +heliotrope +heliotropes +heliport +heliports +helium +helix +hell +hellebore +hellhole +hellholes +hellion +hellions +hellish +hellishly +hello +hellos +helm +helmet +helmets +helms +helmsman +helmsmen +helot +helots +help +helped +helper +helpers +helpful +helpfully +helpfulness +helping +helpings +helpless +helplessly +helplessness +helpline +helplines +helpmate +helpmates +helps +hem +hematologist +hematologists +hematology +hemisphere +hemispheres +hemispheric +hemispherical +hemline +hemlines +hemlock +hemlocks +hemmed +hemming +hemoglobin +hemophilia +hemophiliac +hemophiliacs +hemorrhage +hemorrhaged +hemorrhages +hemorrhaging +hemorrhoid +hemorrhoids +hemp +hempen +hems +hemstitch +hemstitched +hemstitches +hemstitching +hen +hence +henceforth +henceforward +henchman +henchmen +henna +hennaed +hennaing +hennas +henpeck +henpecked +henpecking +henpecks +hens +hep +hepatic +hepatitis +hepper +heppest +heptagon +heptagons +her +herald +heralded +heraldic +heralding +heraldry +heralds +herb +herbaceous +herbage +herbal +herbalist +herbalists +herbicide +herbicides +herbivore +herbivores +herbivorous +herbs +herculean +herd +herded +herder +herders +herding +herds +herdsman +herdsmen +here +hereabout +hereabouts +hereafter +hereafters +hereby +hereditary +heredity +herein +hereof +heresies +heresy +heretic +heretical +heretics +hereto +heretofore +hereupon +herewith +heritage +heritages +hermaphrodite +hermaphrodites +hermaphroditic +hermetic +hermetically +hermit +hermitage +hermitages +hermits +hernia +hernias +hero +heroes +heroic +heroically +heroics +heroin +heroine +heroins +heroism +heron +herons +herpes +herring +herringbone +herrings +hers +herself +hertz +hes +hesitancy +hesitant +hesitantly +hesitate +hesitated +hesitates +hesitating +hesitatingly +hesitation +hesitations +heterodox +heterodoxy +heterogeneity +heterogeneous +heterosexual +heterosexuality +heterosexuals +heuristic +heuristics +hew +hewed +hewer +hewers +hewing +hews +hex +hexadecimal +hexagon +hexagonal +hexagons +hexameter +hexameters +hexed +hexes +hexing +hey +heyday +heydays +hi +hiatus +hiatuses +hibachi +hibachis +hibernate +hibernated +hibernates +hibernating +hibernation +hibiscus +hibiscuses +hiccough +hiccoughed +hiccoughing +hiccoughs +hiccup +hiccuped +hiccuping +hiccups +hick +hickey +hickeys +hickories +hickory +hicks +hid +hidden +hide +hideaway +hideaways +hidebound +hided +hideous +hideously +hideousness +hideout +hideouts +hides +hiding +hie +hied +hieing +hierarchical +hierarchically +hierarchies +hierarchy +hieroglyphic +hieroglyphics +hies +high +highball +highballs +highborn +highboy +highboys +highbrow +highbrows +highchair +highchairs +higher +highest +highfalutin +highland +highlands +highlight +highlighted +highlighter +highlighters +highlighting +highlights +highly +highness +highs +hightail +hightailed +hightailing +hightails +highway +highwayman +highwaymen +highways +hijack +hijacked +hijacker +hijackers +hijacking +hijackings +hijacks +hike +hiked +hiker +hikers +hikes +hiking +hilarious +hilariously +hilarity +hill +hillbillies +hillbilly +hillier +hilliest +hillock +hillocks +hills +hillside +hillsides +hilltop +hilltops +hilly +hilt +hilts +him +hims +himself +hind +hinder +hindered +hindering +hinders +hindmost +hindquarter +hindquarters +hindrance +hindrances +hinds +hindsight +hinge +hinged +hinges +hinging +hint +hinted +hinterland +hinterlands +hinting +hints +hip +hipped +hipper +hippest +hippie +hippies +hipping +hippo +hippopotamus +hippopotamuses +hippos +hippy +hips +hire +hired +hireling +hirelings +hires +hiring +hirsute +his +hiss +hissed +hisses +hissing +histamine +histamines +histogram +histograms +historian +historians +historic +historical +historically +histories +history +histrionic +histrionics +hit +hitch +hitched +hitches +hitchhike +hitchhiked +hitchhiker +hitchhikers +hitchhikes +hitchhiking +hitching +hither +hitherto +hits +hitter +hitters +hitting +hive +hived +hives +hiving +ho +hoagie +hoagies +hoard +hoarded +hoarder +hoarders +hoarding +hoards +hoarfrost +hoarier +hoariest +hoariness +hoarse +hoarsely +hoarseness +hoarser +hoarsest +hoary +hoax +hoaxed +hoaxer +hoaxers +hoaxes +hoaxing +hob +hobbies +hobbit +hobble +hobbled +hobbles +hobbling +hobby +hobbyhorse +hobbyhorses +hobbyist +hobbyists +hobgoblin +hobgoblins +hobnail +hobnailed +hobnailing +hobnails +hobnob +hobnobbed +hobnobbing +hobnobs +hobo +hoboes +hobos +hobs +hock +hocked +hockey +hocking +hocks +hockshop +hockshops +hod +hodgepodge +hodgepodges +hods +hoe +hoed +hoedown +hoedowns +hoeing +hoes +hog +hogan +hogans +hogged +hogging +hoggish +hogs +hogshead +hogsheads +hogwash +hoist +hoisted +hoisting +hoists +hokey +hokier +hokiest +hokum +hold +holder +holders +holding +holdings +holdout +holdouts +holdover +holdovers +holds +holdup +holdups +hole +holed +holes +holiday +holidayed +holidaying +holidays +holier +holiest +holiness +holing +holistic +holler +hollered +hollering +hollers +hollies +hollow +hollowed +hollower +hollowest +hollowing +hollowly +hollowness +hollows +holly +hollyhock +hollyhocks +holocaust +holocausts +hologram +holograms +holograph +holographic +holographs +holography +holster +holstered +holstering +holsters +holy +homage +homages +homburg +homburgs +home +homebodies +homebody +homeboy +homeboys +homecoming +homecomings +homed +homegrown +homeland +homelands +homeless +homelessness +homelier +homeliest +homeliness +homely +homemade +homemaker +homemakers +homeopathic +homeopathy +homeowner +homeowners +homepage +homepages +homer +homered +homering +homeroom +homerooms +homers +homes +homesick +homesickness +homespun +homestead +homesteaded +homesteader +homesteaders +homesteading +homesteads +homestretch +homestretches +hometown +hometowns +homeward +homewards +homework +homewrecker +homewreckers +homey +homeyness +homeys +homicidal +homicide +homicides +homie +homier +homies +homiest +homilies +homily +hominess +homing +hominy +homogeneity +homogeneous +homogeneously +homogenization +homogenize +homogenized +homogenizes +homogenizing +homograph +homographs +homonym +homonyms +homophobia +homophobic +homophone +homophones +homosexual +homosexuality +homosexuals +honcho +honchos +hone +honed +hones +honest +honester +honestest +honestly +honesty +honey +honeybee +honeybees +honeycomb +honeycombed +honeycombing +honeycombs +honeydew +honeydews +honeyed +honeying +honeymoon +honeymooned +honeymooner +honeymooners +honeymooning +honeymoons +honeys +honeysuckle +honeysuckles +honing +honk +honked +honking +honks +honor +honorable +honorably +honorarium +honorariums +honorary +honored +honorific +honorifics +honoring +honors +hooch +hood +hooded +hoodie +hoodies +hooding +hoodlum +hoodlums +hoodoo +hoodooed +hoodooing +hoodoos +hoods +hoodwink +hoodwinked +hoodwinking +hoodwinks +hooey +hoof +hoofed +hoofing +hoofs +hook +hookah +hookahs +hooked +hooker +hookers +hooking +hooks +hookup +hookups +hookworm +hookworms +hooky +hooligan +hooliganism +hooligans +hoop +hooped +hooping +hoopla +hoops +hooray +hoorays +hoot +hootch +hooted +hooter +hooters +hooting +hoots +hooves +hop +hope +hoped +hopeful +hopefully +hopefulness +hopefuls +hopeless +hopelessly +hopelessness +hopes +hoping +hopped +hopper +hoppers +hopping +hops +hopscotch +hopscotched +hopscotches +hopscotching +horde +horded +hordes +hording +horizon +horizons +horizontal +horizontally +horizontals +hormonal +hormone +hormones +horn +horned +hornet +hornets +hornier +horniest +hornless +hornpipe +hornpipes +horns +horny +horology +horoscope +horoscopes +horrendous +horrendously +horrible +horribly +horrid +horridly +horrific +horrified +horrifies +horrify +horrifying +horror +horrors +horse +horseback +horsed +horseflies +horsefly +horsehair +horsehide +horseman +horsemanship +horsemen +horseplay +horsepower +horseradish +horseradishes +horses +horseshoe +horseshoed +horseshoeing +horseshoes +horsetail +horsetails +horsewhip +horsewhipped +horsewhipping +horsewhips +horsewoman +horsewomen +horsey +horsier +horsiest +horsing +horsy +horticultural +horticulture +horticulturist +horticulturists +hos +hosanna +hosannas +hose +hosed +hoses +hosiery +hosing +hospice +hospices +hospitable +hospitably +hospital +hospitality +hospitalization +hospitalizations +hospitalize +hospitalized +hospitalizes +hospitalizing +hospitals +host +hostage +hostages +hosted +hostel +hosteled +hosteler +hostelers +hosteling +hostelries +hostelry +hostels +hostess +hostessed +hostesses +hostessing +hostile +hostilely +hostiles +hostilities +hostility +hosting +hostler +hostlers +hosts +hot +hotbed +hotbeds +hotcake +hotcakes +hotel +hotelier +hoteliers +hotels +hothead +hotheaded +hotheadedly +hotheadedness +hotheads +hothouse +hothouses +hotkey +hotkeys +hotly +hotness +hotshot +hotshots +hotter +hottest +hound +hounded +hounding +hounds +hour +hourglass +hourglasses +hourly +hours +house +houseboat +houseboats +housebound +housebreak +housebreaking +housebreaks +housebroke +housebroken +houseclean +housecleaned +housecleaning +housecleans +housecoat +housecoats +housed +houseflies +housefly +household +householder +householders +households +househusband +househusbands +housekeeper +housekeepers +housekeeping +housemaid +housemaids +housemother +housemothers +houseplant +houseplants +houses +housetop +housetops +housewares +housewarming +housewarmings +housewife +housewives +housework +housing +housings +hove +hovel +hovels +hover +hovercraft +hovercrafts +hovered +hovering +hovers +how +howdah +howdahs +howdy +however +howitzer +howitzers +howl +howled +howler +howlers +howling +howls +hows +howsoever +hub +hubbies +hubbub +hubbubs +hubby +hubcap +hubcaps +hubris +hubs +huckleberries +huckleberry +huckster +huckstered +huckstering +hucksters +huddle +huddled +huddles +huddling +hue +hued +hues +huff +huffed +huffier +huffiest +huffily +huffing +huffs +huffy +hug +huge +hugely +hugeness +huger +hugest +hugged +hugging +hugs +huh +hula +hulas +hulk +hulking +hulks +hull +hullabaloo +hullabaloos +hulled +hulling +hulls +hum +human +humane +humanely +humaneness +humaner +humanest +humanism +humanist +humanistic +humanists +humanitarian +humanitarianism +humanitarians +humanities +humanity +humanization +humanize +humanized +humanizer +humanizers +humanizes +humanizing +humankind +humanly +humanness +humanoid +humanoids +humans +humble +humbled +humbleness +humbler +humbles +humblest +humbling +humblings +humbly +humbug +humbugged +humbugging +humbugs +humdinger +humdingers +humdrum +humeri +humerus +humid +humidified +humidifier +humidifiers +humidifies +humidify +humidifying +humidity +humidor +humidors +humiliate +humiliated +humiliates +humiliating +humiliation +humiliations +humility +hummed +humming +hummingbird +hummingbirds +hummock +hummocks +hummus +humongous +humor +humored +humoring +humorist +humorists +humorless +humorlessness +humorous +humorously +humors +hump +humpback +humpbacked +humpbacks +humped +humping +humps +hums +humus +hunch +hunchback +hunchbacked +hunchbacks +hunched +hunches +hunching +hundred +hundredfold +hundreds +hundredth +hundredths +hundredweight +hundredweights +hung +hunger +hungered +hungering +hungers +hungover +hungrier +hungriest +hungrily +hungry +hunk +hunker +hunkered +hunkering +hunkers +hunks +hunt +hunted +hunter +hunters +hunting +huntress +huntresses +hunts +huntsman +huntsmen +hurdle +hurdled +hurdler +hurdlers +hurdles +hurdling +hurl +hurled +hurler +hurlers +hurling +hurls +hurrah +hurrahed +hurrahing +hurrahs +hurray +hurricane +hurricanes +hurried +hurriedly +hurries +hurry +hurrying +hurt +hurtful +hurting +hurtle +hurtled +hurtles +hurtling +hurts +husband +husbanded +husbanding +husbandry +husbands +hush +hushed +hushes +hushing +husk +husked +husker +huskers +huskier +huskies +huskiest +huskily +huskiness +husking +husks +husky +hussar +hussars +hussies +hussy +hustings +hustle +hustled +hustler +hustlers +hustles +hustling +hut +hutch +hutches +huts +hyacinth +hyacinths +hybrid +hybridize +hybridized +hybridizes +hybridizing +hybrids +hydra +hydrangea +hydrangeas +hydrant +hydrants +hydras +hydrate +hydrated +hydrates +hydrating +hydraulic +hydraulically +hydraulics +hydrocarbon +hydrocarbons +hydroelectric +hydroelectricity +hydrofoil +hydrofoils +hydrogen +hydrogenate +hydrogenated +hydrogenates +hydrogenating +hydrology +hydrolysis +hydrometer +hydrometers +hydrophobia +hydroplane +hydroplaned +hydroplanes +hydroplaning +hydroponic +hydroponics +hydrosphere +hydrotherapy +hyena +hyenas +hygiene +hygienic +hygienically +hygienist +hygienists +hygrometer +hygrometers +hying +hymen +hymens +hymn +hymnal +hymnals +hymned +hymning +hymns +hype +hyped +hyper +hyperactive +hyperactivity +hyperbola +hyperbolas +hyperbole +hyperbolic +hypercritical +hypercritically +hyperlink +hyperlinked +hyperlinking +hyperlinks +hypermarket +hypersensitive +hypersensitivities +hypersensitivity +hyperspace +hypertension +hypertext +hyperventilate +hyperventilated +hyperventilates +hyperventilating +hyperventilation +hypes +hyphen +hyphenate +hyphenated +hyphenates +hyphenating +hyphenation +hyphenations +hyphened +hyphening +hyphens +hyping +hypnoses +hypnosis +hypnotic +hypnotically +hypnotics +hypnotism +hypnotist +hypnotists +hypnotize +hypnotized +hypnotizes +hypnotizing +hypo +hypoallergenic +hypochondria +hypochondriac +hypochondriacs +hypocrisies +hypocrisy +hypocrite +hypocrites +hypocritical +hypocritically +hypodermic +hypodermics +hypoglycemia +hypoglycemic +hypoglycemics +hypos +hypotenuse +hypotenuses +hypothalami +hypothalamus +hypothermia +hypotheses +hypothesis +hypothesize +hypothesized +hypothesizes +hypothesizing +hypothetical +hypothetically +hysterectomies +hysterectomy +hysteresis +hysteria +hysteric +hysterical +hysterically +hysterics +i +iPad +iPhone +iPod +iTunes +iamb +iambic +iambics +iambs +ibex +ibexes +ibis +ibises +ibuprofen +ice +iceberg +icebergs +icebound +icebox +iceboxes +icebreaker +icebreakers +icecap +icecaps +iced +ices +icicle +icicles +icier +iciest +icily +iciness +icing +icings +ickier +ickiest +icky +icon +iconoclast +iconoclastic +iconoclasts +icons +icy +id +idea +ideal +idealism +idealist +idealistic +idealistically +idealists +idealization +idealize +idealized +idealizes +idealizing +ideally +ideals +ideas +identical +identically +identifiable +identification +identified +identifier +identifiers +identifies +identify +identifying +identities +identity +ideogram +ideograms +ideograph +ideographs +ideological +ideologically +ideologies +ideologist +ideologists +ideology +ides +idiocies +idiocy +idiom +idiomatic +idiomatically +idioms +idiosyncrasies +idiosyncrasy +idiosyncratic +idiot +idiotic +idiotically +idiots +idle +idled +idleness +idler +idlers +idles +idlest +idling +idly +idol +idolater +idolaters +idolatrous +idolatry +idolize +idolized +idolizes +idolizing +idols +ids +idyll +idyllic +idylls +if +iffier +iffiest +iffy +ifs +igloo +igloos +igneous +ignite +ignited +ignites +igniting +ignition +ignitions +ignoble +ignobly +ignominies +ignominious +ignominiously +ignominy +ignoramus +ignoramuses +ignorance +ignorant +ignorantly +ignore +ignored +ignores +ignoring +iguana +iguanas +ilk +ilks +ill +illegal +illegalities +illegality +illegally +illegals +illegibility +illegible +illegibly +illegitimacy +illegitimate +illegitimately +illiberal +illicit +illicitly +illicitness +illiteracy +illiterate +illiterates +illness +illnesses +illogical +illogically +ills +illuminate +illuminated +illuminates +illuminating +illumination +illuminations +illumine +illumined +illumines +illumining +illusion +illusions +illusive +illusory +illustrate +illustrated +illustrates +illustrating +illustration +illustrations +illustrative +illustrator +illustrators +illustrious +image +imaged +imagery +images +imaginable +imaginably +imaginary +imagination +imaginations +imaginative +imaginatively +imagine +imagined +imagines +imaging +imagining +imam +imams +imbalance +imbalanced +imbalances +imbecile +imbeciles +imbecilic +imbecilities +imbecility +imbibe +imbibed +imbibes +imbibing +imbroglio +imbroglios +imbue +imbued +imbues +imbuing +imitate +imitated +imitates +imitating +imitation +imitations +imitative +imitator +imitators +immaculate +immaculately +immaculateness +immanence +immanent +immaterial +immature +immaturely +immaturity +immeasurable +immeasurably +immediacy +immediate +immediately +immemorial +immense +immensely +immensities +immensity +immerse +immersed +immerses +immersing +immersion +immersions +immersive +immigrant +immigrants +immigrate +immigrated +immigrates +immigrating +immigration +imminence +imminent +imminently +immobile +immobility +immobilization +immobilize +immobilized +immobilizes +immobilizing +immoderate +immoderately +immodest +immodestly +immodesty +immolate +immolated +immolates +immolating +immolation +immoral +immoralities +immorality +immorally +immortal +immortality +immortalize +immortalized +immortalizes +immortalizing +immortally +immortals +immovable +immovably +immune +immunity +immunization +immunizations +immunize +immunized +immunizes +immunizing +immunology +immure +immured +immures +immuring +immutability +immutable +immutably +imp +impact +impacted +impacting +impacts +impair +impaired +impairing +impairment +impairments +impairs +impala +impalas +impale +impaled +impalement +impales +impaling +impalpable +impanel +impaneled +impaneling +impanels +impart +imparted +impartial +impartiality +impartially +imparting +imparts +impassable +impasse +impasses +impassioned +impassive +impassively +impassivity +impatience +impatiences +impatient +impatiently +impeach +impeached +impeaches +impeaching +impeachment +impeachments +impeccability +impeccable +impeccably +impecunious +impecuniousness +impedance +impede +impeded +impedes +impediment +impedimenta +impediments +impeding +impel +impelled +impelling +impels +impend +impended +impending +impends +impenetrability +impenetrable +impenetrably +impenitence +impenitent +imperative +imperatively +imperatives +imperceptible +imperceptibly +imperfect +imperfection +imperfections +imperfectly +imperfects +imperial +imperialism +imperialist +imperialistic +imperialists +imperially +imperials +imperil +imperiled +imperiling +imperils +imperious +imperiously +imperiousness +imperishable +impermanence +impermanent +impermeable +impermissible +impersonal +impersonally +impersonate +impersonated +impersonates +impersonating +impersonation +impersonations +impersonator +impersonators +impertinence +impertinent +impertinently +imperturbability +imperturbable +imperturbably +impervious +impetigo +impetuosity +impetuous +impetuously +impetus +impetuses +impieties +impiety +impinge +impinged +impingement +impinges +impinging +impious +impiously +impish +impishly +impishness +implacability +implacable +implacably +implant +implantation +implanted +implanting +implants +implausibilities +implausibility +implausible +implausibly +implement +implementable +implementation +implementations +implemented +implementer +implementing +implements +implicate +implicated +implicates +implicating +implication +implications +implicit +implicitly +implied +implies +implode +imploded +implodes +imploding +implore +implored +implores +imploring +implosion +implosions +imply +implying +impolite +impolitely +impoliteness +impolitenesses +impolitic +imponderable +imponderables +import +importance +important +importantly +importation +importations +imported +importer +importers +importing +imports +importunate +importune +importuned +importunes +importuning +importunity +impose +imposed +imposes +imposing +imposingly +imposition +impositions +impossibilities +impossibility +impossible +impossibles +impossibly +imposter +imposters +impostor +impostors +imposture +impostures +impotence +impotent +impotently +impound +impounded +impounding +impounds +impoverish +impoverished +impoverishes +impoverishing +impoverishment +impracticable +impracticably +impractical +impracticality +imprecation +imprecations +imprecise +imprecisely +imprecision +impregnability +impregnable +impregnably +impregnate +impregnated +impregnates +impregnating +impregnation +impresario +impresarios +impress +impressed +impresses +impressing +impression +impressionable +impressionism +impressionist +impressionistic +impressionists +impressions +impressive +impressively +impressiveness +imprimatur +imprimaturs +imprint +imprinted +imprinting +imprints +imprison +imprisoned +imprisoning +imprisonment +imprisonments +imprisons +improbabilities +improbability +improbable +improbably +impromptu +impromptus +improper +improperly +improprieties +impropriety +improvable +improve +improved +improvement +improvements +improves +improvidence +improvident +improvidently +improving +improvisation +improvisations +improvise +improvised +improvises +improvising +imprudence +imprudent +imps +impudence +impudent +impudently +impugn +impugned +impugning +impugns +impulse +impulsed +impulses +impulsing +impulsion +impulsive +impulsively +impulsiveness +impunity +impure +impurely +impurer +impurest +impurities +impurity +imputation +imputations +impute +imputed +imputes +imputing +in +inabilities +inability +inaccessibility +inaccessible +inaccuracies +inaccuracy +inaccurate +inaccurately +inaction +inactive +inactivity +inadequacies +inadequacy +inadequate +inadequately +inadmissible +inadvertence +inadvertent +inadvertently +inadvisable +inalienable +inamorata +inamoratas +inane +inanely +inaner +inanest +inanimate +inanities +inanity +inapplicable +inappropriate +inappropriately +inapt +inarticulate +inarticulately +inasmuch +inattention +inattentive +inaudible +inaudibly +inaugural +inaugurals +inaugurate +inaugurated +inaugurates +inaugurating +inauguration +inaugurations +inauspicious +inboard +inboards +inborn +inbound +inbox +inboxes +inbred +inbreed +inbreeding +inbreeds +inbuilt +incalculable +incalculably +incandescence +incandescent +incantation +incantations +incapability +incapable +incapacitate +incapacitated +incapacitates +incapacitating +incapacity +incarcerate +incarcerated +incarcerates +incarcerating +incarceration +incarcerations +incarnate +incarnated +incarnates +incarnating +incarnation +incarnations +incautious +incendiaries +incendiary +incense +incensed +incenses +incensing +incentive +incentives +inception +inceptions +incessant +incessantly +incest +incestuous +inch +inched +inches +inching +inchoate +incidence +incidences +incident +incidental +incidentally +incidentals +incidents +incinerate +incinerated +incinerates +incinerating +incineration +incinerator +incinerators +incipient +incise +incised +incises +incising +incision +incisions +incisive +incisively +incisiveness +incisor +incisors +incite +incited +incitement +incitements +incites +inciting +incivilities +incivility +inclemency +inclement +inclination +inclinations +incline +inclined +inclines +inclining +include +included +includes +including +inclusion +inclusions +inclusive +inclusively +incognito +incognitos +incoherence +incoherent +incoherently +incombustible +income +incomes +incoming +incommensurate +incommunicado +incomparable +incomparably +incompatibilities +incompatibility +incompatible +incompatibles +incompatibly +incompetence +incompetent +incompetently +incompetents +incomplete +incompletely +incompleteness +incomprehensible +incomprehensibly +inconceivable +inconceivably +inconclusive +inconclusively +incongruities +incongruity +incongruous +incongruously +inconsequential +inconsequentially +inconsiderable +inconsiderate +inconsiderately +inconsiderateness +inconsistencies +inconsistency +inconsistent +inconsistently +inconsolable +inconspicuous +inconspicuously +inconspicuousness +inconstancy +inconstant +incontestable +incontestably +incontinence +incontinent +incontrovertible +incontrovertibly +inconvenience +inconvenienced +inconveniences +inconveniencing +inconvenient +inconveniently +incorporate +incorporated +incorporates +incorporating +incorporation +incorporeal +incorrect +incorrectly +incorrectness +incorrigibility +incorrigible +incorrigibly +incorruptibility +incorruptible +increase +increased +increases +increasing +increasingly +incredibility +incredible +incredibly +incredulity +incredulous +incredulously +increment +incremental +incremented +increments +incriminate +incriminated +incriminates +incriminating +incrimination +incriminatory +incrustation +incrustations +incubate +incubated +incubates +incubating +incubation +incubator +incubators +incubus +incubuses +inculcate +inculcated +inculcates +inculcating +inculcation +inculpate +inculpated +inculpates +inculpating +incumbencies +incumbency +incumbent +incumbents +incur +incurable +incurables +incurably +incurious +incurred +incurring +incurs +incursion +incursions +indebted +indebtedness +indecencies +indecency +indecent +indecently +indecipherable +indecision +indecisive +indecisively +indecisiveness +indecorous +indeed +indefatigable +indefatigably +indefensible +indefensibly +indefinable +indefinably +indefinite +indefinitely +indelible +indelibly +indelicacies +indelicacy +indelicate +indelicately +indemnification +indemnifications +indemnified +indemnifies +indemnify +indemnifying +indemnities +indemnity +indent +indentation +indentations +indented +indenting +indents +indenture +indentured +indentures +indenturing +independence +independent +independently +independents +indescribable +indescribably +indestructible +indestructibly +indeterminable +indeterminacy +indeterminate +indeterminately +index +indexed +indexes +indexing +indicate +indicated +indicates +indicating +indication +indications +indicative +indicatives +indicator +indicators +indices +indict +indictable +indicted +indicting +indictment +indictments +indicts +indifference +indifferent +indifferently +indigence +indigenous +indigent +indigents +indigestible +indigestion +indignant +indignantly +indignation +indignities +indignity +indigo +indirect +indirection +indirectly +indirectness +indiscernible +indiscreet +indiscreetly +indiscretion +indiscretions +indiscriminate +indiscriminately +indispensable +indispensables +indispensably +indisposed +indisposition +indispositions +indisputable +indisputably +indissoluble +indistinct +indistinctly +indistinctness +indistinguishable +individual +individualism +individualist +individualistic +individualists +individuality +individualize +individualized +individualizes +individualizing +individually +individuals +indivisibility +indivisible +indivisibly +indoctrinate +indoctrinated +indoctrinates +indoctrinating +indoctrination +indolence +indolent +indolently +indomitable +indomitably +indoor +indoors +indubitable +indubitably +induce +induced +inducement +inducements +induces +inducing +induct +inductance +inducted +inductee +inductees +inducting +induction +inductions +inductive +inducts +indue +indued +indues +induing +indulge +indulged +indulgence +indulgences +indulgent +indulgently +indulges +indulging +industrial +industrialism +industrialist +industrialists +industrialization +industrialize +industrialized +industrializes +industrializing +industrially +industries +industrious +industriously +industriousness +industry +inebriate +inebriated +inebriates +inebriating +inebriation +inedible +ineducable +ineffable +ineffably +ineffective +ineffectively +ineffectiveness +ineffectual +ineffectually +inefficiencies +inefficiency +inefficient +inefficiently +inelastic +inelegance +inelegant +inelegantly +ineligibility +ineligible +ineligibles +ineluctable +ineluctably +inept +ineptitude +ineptly +ineptness +inequalities +inequality +inequitable +inequities +inequity +inert +inertia +inertial +inertly +inertness +inescapable +inescapably +inessential +inessentials +inestimable +inestimably +inevitability +inevitable +inevitably +inexact +inexcusable +inexcusably +inexhaustible +inexhaustibly +inexorable +inexorably +inexpedient +inexpensive +inexpensively +inexperience +inexperienced +inexpert +inexplicable +inexplicably +inexpressible +inextinguishable +inextricable +inextricably +infallibility +infallible +infallibly +infamies +infamous +infamously +infamy +infancy +infant +infanticide +infanticides +infantile +infantries +infantry +infantryman +infantrymen +infants +infarction +infatuate +infatuated +infatuates +infatuating +infatuation +infatuations +infeasible +infect +infected +infecting +infection +infections +infectious +infectiously +infectiousness +infects +infelicities +infelicitous +infelicity +infer +inference +inferences +inferential +inferior +inferiority +inferiors +infernal +inferno +infernos +inferred +inferring +infers +infertile +infertility +infest +infestation +infestations +infested +infesting +infests +infidel +infidelities +infidelity +infidels +infield +infielder +infielders +infields +infighting +infiltrate +infiltrated +infiltrates +infiltrating +infiltration +infiltrator +infiltrators +infinite +infinitely +infinitesimal +infinitesimally +infinitesimals +infinities +infinitive +infinitives +infinitude +infinity +infirm +infirmaries +infirmary +infirmities +infirmity +infix +inflame +inflamed +inflames +inflaming +inflammable +inflammation +inflammations +inflammatory +inflatable +inflatables +inflate +inflated +inflates +inflating +inflation +inflationary +inflect +inflected +inflecting +inflection +inflectional +inflections +inflects +inflexibility +inflexible +inflexibly +inflict +inflicted +inflicting +infliction +inflicts +inflorescence +inflow +influence +influenced +influences +influencing +influential +influentially +influenza +influx +influxes +info +infomercial +infomercials +inform +informal +informality +informally +informant +informants +information +informational +informative +informed +informer +informers +informing +informs +infotainment +infraction +infractions +infrared +infrastructure +infrastructures +infrequency +infrequent +infrequently +infringe +infringed +infringement +infringements +infringes +infringing +infuriate +infuriated +infuriates +infuriating +infuriatingly +infuse +infused +infuses +infusing +infusion +infusions +ingenious +ingeniously +ingenuity +ingenuous +ingenuously +ingenuousness +ingest +ingested +ingesting +ingestion +ingests +inglorious +ingot +ingots +ingrain +ingrained +ingraining +ingrains +ingrate +ingrates +ingratiate +ingratiated +ingratiates +ingratiating +ingratiatingly +ingratitude +ingredient +ingredients +ingress +ingresses +ingrown +ingénue +ingénues +inhabit +inhabitable +inhabitant +inhabitants +inhabited +inhabiting +inhabits +inhalant +inhalants +inhalation +inhalations +inhalator +inhalators +inhale +inhaled +inhaler +inhalers +inhales +inhaling +inhere +inhered +inherent +inherently +inheres +inhering +inherit +inheritance +inheritances +inherited +inheriting +inheritor +inheritors +inherits +inhibit +inhibited +inhibiting +inhibition +inhibitions +inhibits +inhospitable +inhuman +inhumane +inhumanely +inhumanities +inhumanity +inhumanly +inimical +inimically +inimitable +inimitably +iniquities +iniquitous +iniquity +initial +initialed +initialing +initialization +initialize +initialized +initializes +initializing +initially +initials +initiate +initiated +initiates +initiating +initiation +initiations +initiative +initiatives +initiator +initiators +inject +injected +injecting +injection +injections +injector +injectors +injects +injudicious +injunction +injunctions +injure +injured +injures +injuries +injuring +injurious +injury +injustice +injustices +ink +inkblot +inkblots +inked +inkier +inkiest +inkiness +inking +inkling +inklings +inks +inkwell +inkwells +inky +inlaid +inland +inlay +inlaying +inlays +inlet +inlets +inline +inmate +inmates +inmost +inn +innards +innate +innately +inner +innermost +inning +innings +innkeeper +innkeepers +innocence +innocent +innocently +innocents +innocuous +innocuously +innovate +innovated +innovates +innovating +innovation +innovations +innovative +innovator +innovators +inns +innuendo +innuendoes +innuendos +innumerable +inoculate +inoculated +inoculates +inoculating +inoculation +inoculations +inoffensive +inoffensively +inoperable +inoperative +inopportune +inordinate +inordinately +inorganic +inpatient +inpatients +input +inputs +inputted +inputting +inquest +inquests +inquietude +inquire +inquired +inquirer +inquirers +inquires +inquiries +inquiring +inquiringly +inquiry +inquisition +inquisitions +inquisitive +inquisitively +inquisitiveness +inquisitor +inquisitors +inroad +inroads +ins +insane +insanely +insaner +insanest +insanity +insatiable +insatiably +inscribe +inscribed +inscribes +inscribing +inscription +inscriptions +inscrutable +inscrutably +inseam +inseams +insect +insecticide +insecticides +insectivore +insectivores +insectivorous +insects +insecure +insecurely +insecurities +insecurity +inseminate +inseminated +inseminates +inseminating +insemination +insensate +insensibility +insensible +insensibly +insensitive +insensitively +insensitivity +insentience +insentient +inseparability +inseparable +inseparables +inseparably +insert +inserted +inserting +insertion +insertions +inserts +inset +insets +insetting +inshore +inside +insider +insiders +insides +insidious +insidiously +insidiousness +insight +insightful +insights +insignia +insignias +insignificance +insignificant +insignificantly +insincere +insincerely +insincerity +insinuate +insinuated +insinuates +insinuating +insinuation +insinuations +insipid +insist +insisted +insistence +insistent +insistently +insisting +insists +insofar +insole +insolence +insolent +insolently +insoles +insolubility +insoluble +insolvable +insolvency +insolvent +insolvents +insomnia +insomniac +insomniacs +insouciance +insouciant +inspect +inspected +inspecting +inspection +inspections +inspector +inspectors +inspects +inspiration +inspirational +inspirations +inspire +inspired +inspires +inspiring +instability +install +installation +installations +installed +installing +installment +installments +installs +instance +instanced +instances +instancing +instant +instantaneous +instantaneously +instantly +instants +instead +instep +insteps +instigate +instigated +instigates +instigating +instigation +instigator +instigators +instill +instilled +instilling +instills +instinct +instinctive +instinctively +instincts +institute +instituted +institutes +instituting +institution +institutional +institutionalize +institutionalized +institutionalizes +institutionalizing +institutions +instruct +instructed +instructing +instruction +instructional +instructions +instructive +instructively +instructor +instructors +instructs +instrument +instrumental +instrumentalist +instrumentalists +instrumentality +instrumentals +instrumentation +instrumented +instrumenting +instruments +insubordinate +insubordination +insubstantial +insufferable +insufferably +insufficiency +insufficient +insufficiently +insular +insularity +insulate +insulated +insulates +insulating +insulation +insulator +insulators +insulin +insult +insulted +insulting +insults +insuperable +insupportable +insurance +insurances +insure +insured +insureds +insurer +insurers +insures +insurgence +insurgences +insurgencies +insurgency +insurgent +insurgents +insuring +insurmountable +insurrection +insurrectionist +insurrectionists +insurrections +intact +intaglio +intaglios +intake +intakes +intangible +intangibles +intangibly +integer +integers +integral +integrals +integrate +integrated +integrates +integrating +integration +integrator +integrity +integument +integuments +intellect +intellects +intellectual +intellectualism +intellectualize +intellectualized +intellectualizes +intellectualizing +intellectually +intellectuals +intelligence +intelligent +intelligently +intelligentsia +intelligibility +intelligible +intelligibly +intemperance +intemperate +intend +intended +intendeds +intending +intends +intense +intensely +intenser +intensest +intensification +intensified +intensifier +intensifiers +intensifies +intensify +intensifying +intensities +intensity +intensive +intensively +intensives +intent +intention +intentional +intentionally +intentions +intently +intentness +intents +inter +interact +interacted +interacting +interaction +interactions +interactive +interactively +interacts +interbred +interbreed +interbreeding +interbreeds +intercede +interceded +intercedes +interceding +intercept +intercepted +intercepting +interception +interceptions +interceptor +interceptors +intercepts +intercession +intercessions +intercessor +intercessors +interchange +interchangeable +interchangeably +interchanged +interchanges +interchanging +intercollegiate +intercom +intercoms +interconnect +interconnected +interconnecting +interconnection +interconnections +interconnects +intercontinental +intercourse +interdenominational +interdepartmental +interdependence +interdependent +interdict +interdicted +interdicting +interdiction +interdicts +interdisciplinary +interest +interested +interesting +interestingly +interests +interface +interfaced +interfaces +interfacing +interfaith +interfere +interfered +interference +interferes +interfering +interferon +intergalactic +interim +interior +interiors +interject +interjected +interjecting +interjection +interjections +interjects +interlace +interlaced +interlaces +interlacing +interlard +interlarded +interlarding +interlards +interleave +interleaved +interleaves +interleaving +interleukin +interlink +interlinked +interlinking +interlinks +interlock +interlocked +interlocking +interlocks +interlocutory +interloper +interlopers +interlude +interluded +interludes +interluding +intermarriage +intermarriages +intermarried +intermarries +intermarry +intermarrying +intermediaries +intermediary +intermediate +intermediates +interment +interments +intermezzi +intermezzo +intermezzos +interminable +interminably +intermingle +intermingled +intermingles +intermingling +intermission +intermissions +intermittent +intermittently +intern +internal +internalize +internalized +internalizes +internalizing +internally +internals +international +internationalism +internationalize +internationalized +internationalizes +internationalizing +internationally +internationals +interne +internecine +interned +internee +internees +internement +interneship +interneships +internet +interning +internist +internists +internment +interns +internship +internships +interoffice +interpersonal +interplanetary +interplay +interpolate +interpolated +interpolates +interpolating +interpolation +interpolations +interpose +interposed +interposes +interposing +interposition +interpret +interpretation +interpretations +interpretative +interpreted +interpreter +interpreters +interpreting +interpretive +interprets +interracial +interred +interrelate +interrelated +interrelates +interrelating +interrelation +interrelations +interrelationship +interrelationships +interring +interrogate +interrogated +interrogates +interrogating +interrogation +interrogations +interrogative +interrogatives +interrogator +interrogatories +interrogators +interrogatory +interrupt +interrupted +interrupting +interruption +interruptions +interrupts +inters +interscholastic +intersect +intersected +intersecting +intersection +intersections +intersects +intersperse +interspersed +intersperses +interspersing +interstate +interstates +interstellar +interstice +interstices +intertwine +intertwined +intertwines +intertwining +interurban +interval +intervals +intervene +intervened +intervenes +intervening +intervention +interventions +interview +interviewed +interviewee +interviewees +interviewer +interviewers +interviewing +interviews +interweave +interweaves +interweaving +interwove +interwoven +intestate +intestinal +intestine +intestines +intimacies +intimacy +intimate +intimated +intimately +intimates +intimating +intimation +intimations +intimidate +intimidated +intimidates +intimidating +intimidation +into +intolerable +intolerably +intolerance +intolerant +intonation +intonations +intone +intoned +intones +intoning +intoxicant +intoxicants +intoxicate +intoxicated +intoxicates +intoxicating +intoxication +intractability +intractable +intramural +intranet +intranets +intransigence +intransigent +intransigents +intransitive +intransitively +intransitives +intravenous +intravenouses +intravenously +intrepid +intrepidly +intricacies +intricacy +intricate +intricately +intrigue +intrigued +intrigues +intriguing +intriguingly +intrinsic +intrinsically +introduce +introduced +introduces +introducing +introduction +introductions +introductory +intros +introspection +introspective +introversion +introvert +introverted +introverts +intrude +intruded +intruder +intruders +intrudes +intruding +intrusion +intrusions +intrusive +intuit +intuited +intuiting +intuition +intuitions +intuitive +intuitively +intuits +inundate +inundated +inundates +inundating +inundation +inundations +inure +inured +inures +inuring +invade +invaded +invader +invaders +invades +invading +invalid +invalidate +invalidated +invalidates +invalidating +invalidation +invalided +invaliding +invalidity +invalids +invaluable +invariable +invariables +invariably +invariant +invasion +invasions +invasive +invective +inveigh +inveighed +inveighing +inveighs +inveigle +inveigled +inveigles +inveigling +invent +invented +inventing +invention +inventions +inventive +inventiveness +inventor +inventoried +inventories +inventors +inventory +inventorying +invents +inverse +inversely +inverses +inversion +inversions +invert +invertebrate +invertebrates +inverted +inverting +inverts +invest +invested +investigate +investigated +investigates +investigating +investigation +investigations +investigative +investigator +investigators +investing +investiture +investitures +investment +investments +investor +investors +invests +inveterate +invidious +invidiously +invigorate +invigorated +invigorates +invigorating +invigoration +invincibility +invincible +invincibly +inviolability +inviolable +inviolate +invisibility +invisible +invisibly +invitation +invitational +invitationals +invitations +invite +invited +invites +inviting +invitingly +invocation +invocations +invoice +invoiced +invoices +invoicing +invoke +invoked +invokes +invoking +involuntarily +involuntary +involve +involved +involvement +involvements +involves +involving +invulnerability +invulnerable +invulnerably +inward +inwardly +inwards +iodine +iodize +iodized +iodizes +iodizing +ion +ionization +ionize +ionized +ionizer +ionizers +ionizes +ionizing +ionosphere +ionospheres +ions +iota +iotas +ipecac +ipecacs +irascibility +irascible +irate +irately +irateness +ire +iridescence +iridescent +iridium +iris +irises +irk +irked +irking +irks +irksome +iron +ironclad +ironclads +ironed +ironic +ironical +ironically +ironies +ironing +irons +ironware +ironwork +irony +irradiate +irradiated +irradiates +irradiating +irradiation +irrational +irrationality +irrationally +irrationals +irreconcilable +irrecoverable +irredeemable +irrefutable +irregardless +irregular +irregularities +irregularity +irregularly +irregulars +irrelevance +irrelevances +irrelevancies +irrelevancy +irrelevant +irrelevantly +irreligious +irremediable +irremediably +irreparable +irreparably +irreplaceable +irrepressible +irreproachable +irresistible +irresistibly +irresolute +irresolutely +irresolution +irrespective +irresponsibility +irresponsible +irresponsibly +irretrievable +irretrievably +irreverence +irreverent +irreverently +irreversible +irreversibly +irrevocable +irrevocably +irrigate +irrigated +irrigates +irrigating +irrigation +irritability +irritable +irritably +irritant +irritants +irritate +irritated +irritates +irritating +irritatingly +irritation +irritations +irruption +irruptions +is +isinglass +island +islander +islanders +islands +isle +isles +islet +islets +ism +isms +isobar +isobars +isolate +isolated +isolates +isolating +isolation +isolationism +isolationist +isolationists +isometric +isometrics +isomorphic +isosceles +isotope +isotopes +isotopic +isotropic +issuance +issue +issued +issues +issuing +isthmus +isthmuses +it +italic +italicize +italicized +italicizes +italicizing +italics +itch +itched +itches +itchier +itchiest +itchiness +itching +itchy +item +itemization +itemize +itemized +itemizes +itemizing +items +iterate +iterated +iterates +iterating +iteration +iterations +iterative +iterator +iterators +itinerant +itinerants +itineraries +itinerary +its +itself +ivies +ivories +ivory +ivy +j +jab +jabbed +jabber +jabbered +jabberer +jabberers +jabbering +jabbers +jabbing +jabot +jabots +jabs +jack +jackal +jackals +jackass +jackasses +jackboot +jackboots +jackdaw +jackdaws +jacked +jacket +jackets +jackhammer +jackhammers +jacking +jackknife +jackknifed +jackknifes +jackknifing +jackknives +jackpot +jackpots +jackrabbit +jackrabbits +jacks +jade +jaded +jades +jading +jag +jagged +jaggeder +jaggedest +jaggedly +jaggedness +jags +jaguar +jaguars +jail +jailbreak +jailbreaks +jailed +jailer +jailers +jailing +jails +jalapeño +jalapeños +jalopies +jalopy +jalousie +jalousies +jam +jamb +jamboree +jamborees +jambs +jammed +jamming +jams +jangle +jangled +jangles +jangling +janitor +janitorial +janitors +japan +japanned +japanning +japans +jape +japed +japes +japing +jar +jardinière +jardinières +jargon +jarred +jarring +jars +jasmine +jasmines +jasper +jaundice +jaundiced +jaundices +jaundicing +jaunt +jaunted +jauntier +jauntiest +jauntily +jauntiness +jaunting +jaunts +jaunty +javelin +javelins +jaw +jawbone +jawboned +jawbones +jawboning +jawbreaker +jawbreakers +jawed +jawing +jaws +jay +jays +jaywalk +jaywalked +jaywalker +jaywalkers +jaywalking +jaywalks +jazz +jazzed +jazzes +jazzier +jazziest +jazzing +jazzy +jealous +jealousies +jealously +jealousy +jeans +jeep +jeeps +jeer +jeered +jeering +jeeringly +jeers +jeez +jejune +jell +jelled +jellied +jellies +jelling +jello +jells +jelly +jellybean +jellybeans +jellyfish +jellyfishes +jellying +jeopardize +jeopardized +jeopardizes +jeopardizing +jeopardy +jeremiad +jeremiads +jerk +jerked +jerkier +jerkiest +jerkily +jerkin +jerking +jerkins +jerks +jerkwater +jerky +jersey +jerseys +jest +jested +jester +jesters +jesting +jests +jet +jets +jetsam +jetted +jetties +jetting +jettison +jettisoned +jettisoning +jettisons +jetty +jewel +jeweled +jeweler +jewelers +jeweling +jewelries +jewelry +jewels +jib +jibbed +jibbing +jibe +jibed +jibes +jibing +jibs +jiffies +jiffy +jig +jigged +jigger +jiggered +jiggering +jiggers +jigging +jiggle +jiggled +jiggles +jiggling +jigs +jigsaw +jigsawed +jigsawing +jigsaws +jihad +jihadist +jihadists +jihads +jilt +jilted +jilting +jilts +jimmied +jimmies +jimmy +jimmying +jingle +jingled +jingles +jingling +jingoism +jingoist +jingoistic +jingoists +jinn +jinni +jinns +jinricksha +jinrickshas +jinrikisha +jinrikishas +jinx +jinxed +jinxes +jinxing +jitney +jitneys +jitterbug +jitterbugged +jitterbugging +jitterbugs +jitterier +jitteriest +jitters +jittery +jive +jived +jives +jiving +job +jobbed +jobber +jobbers +jobbing +jobless +joblessness +jobs +jock +jockey +jockeyed +jockeying +jockeys +jocks +jockstrap +jockstraps +jocose +jocosely +jocosity +jocular +jocularity +jocularly +jocund +jocundity +jocundly +jodhpurs +jog +jogged +jogger +joggers +jogging +joggle +joggled +joggles +joggling +jogs +john +johns +join +joined +joiner +joiners +joining +joins +joint +jointed +jointing +jointly +joints +joist +joists +joke +joked +joker +jokers +jokes +joking +jokingly +jollied +jollier +jollies +jolliest +jolliness +jollity +jolly +jollying +jolt +jolted +jolting +jolts +jonquil +jonquils +josh +joshed +joshes +joshing +jostle +jostled +jostles +jostling +jot +jots +jotted +jotting +jottings +joule +joules +jounce +jounced +jounces +jouncing +journal +journalese +journalism +journalist +journalistic +journalists +journals +journey +journeyed +journeying +journeyman +journeymen +journeys +joust +jousted +jousting +jousts +jovial +joviality +jovially +jowl +jowls +joy +joyed +joyful +joyfuller +joyfullest +joyfully +joyfulness +joying +joyless +joyous +joyously +joyousness +joyridden +joyride +joyrider +joyriders +joyrides +joyriding +joyrode +joys +joystick +joysticks +jubilant +jubilantly +jubilation +jubilee +jubilees +judge +judged +judgemental +judges +judgeship +judging +judgment +judgmental +judgments +judicature +judicial +judicially +judiciaries +judiciary +judicious +judiciously +judiciousness +judo +jug +jugged +juggernaut +juggernauts +jugging +juggle +juggled +juggler +jugglers +juggles +juggling +jugs +jugular +jugulars +juice +juiced +juicer +juicers +juices +juicier +juiciest +juicily +juiciness +juicing +juicy +jujitsu +jujube +jujubes +jukebox +jukeboxes +julep +juleps +julienne +jumble +jumbled +jumbles +jumbling +jumbo +jumbos +jump +jumped +jumper +jumpers +jumpier +jumpiest +jumpiness +jumping +jumps +jumpsuit +jumpsuits +jumpy +junco +juncos +junction +junctions +juncture +junctures +jungle +jungles +junior +juniors +juniper +junipers +junk +junked +junker +junkers +junket +junketed +junketing +junkets +junkie +junkier +junkies +junkiest +junking +junks +junky +junkyard +junkyards +junta +juntas +juridical +juries +jurisdiction +jurisdictional +jurisprudence +jurist +jurists +juror +jurors +jury +just +juster +justest +justice +justices +justifiable +justifiably +justification +justifications +justified +justifies +justify +justifying +justly +justness +jut +jute +juts +jutted +jutting +juvenile +juveniles +juxtapose +juxtaposed +juxtaposes +juxtaposing +juxtaposition +juxtapositions +k +kHz +kW +kabob +kabobs +kaboom +kale +kaleidoscope +kaleidoscopes +kaleidoscopic +kamikaze +kamikazes +kangaroo +kangaroos +kaolin +kapok +kaput +karakul +karaoke +karaokes +karat +karate +karats +karma +katydid +katydids +kayak +kayaked +kayaking +kayaks +kazoo +kazoos +kebab +kebabs +kebob +kebobs +keel +keeled +keeling +keels +keen +keened +keener +keenest +keening +keenly +keenness +keens +keep +keeper +keepers +keeping +keeps +keepsake +keepsakes +keg +kegs +kelp +ken +kenned +kennel +kenneled +kenneling +kennels +kenning +kens +kept +keratin +kerchief +kerchiefs +kernel +kernels +kerosene +kestrel +kestrels +ketch +ketches +ketchup +kettle +kettledrum +kettledrums +kettles +key +keybinding +keybindings +keyboard +keyboarded +keyboarder +keyboarders +keyboarding +keyboards +keyed +keyhole +keyholes +keying +keynote +keynoted +keynotes +keynoting +keypunch +keypunched +keypunches +keypunching +keys +keystone +keystones +keystroke +keystrokes +keyword +keywords +khaki +khakis +khan +khans +kibbutz +kibbutzim +kibitz +kibitzed +kibitzer +kibitzers +kibitzes +kibitzing +kibosh +kick +kickback +kickbacks +kicked +kicker +kickers +kickier +kickiest +kicking +kickoff +kickoffs +kicks +kickstand +kickstands +kicky +kid +kidded +kidder +kidders +kiddie +kiddies +kidding +kiddo +kiddos +kidnap +kidnapped +kidnapper +kidnappers +kidnapping +kidnappings +kidnaps +kidney +kidneys +kids +kielbasa +kielbasas +kill +killdeer +killdeers +killed +killer +killers +killing +killings +killjoy +killjoys +kills +kiln +kilned +kilning +kilns +kilo +kilobyte +kilobytes +kilocycle +kilocycles +kilogram +kilograms +kilohertz +kilometer +kilometers +kilos +kiloton +kilotons +kilowatt +kilowatts +kilt +kilter +kilts +kimono +kimonos +kin +kind +kinda +kinder +kindergarten +kindergartens +kindergärtner +kindergärtners +kindest +kindhearted +kindle +kindled +kindles +kindlier +kindliest +kindliness +kindling +kindly +kindness +kindnesses +kindred +kinds +kinematic +kinematics +kinetic +kinfolk +kinfolks +king +kingdom +kingdoms +kingfisher +kingfishers +kinglier +kingliest +kingly +kingpin +kingpins +kings +kingship +kink +kinked +kinkier +kinkiest +kinking +kinks +kinky +kinship +kinsman +kinsmen +kinswoman +kinswomen +kiosk +kiosks +kipper +kippered +kippering +kippers +kismet +kiss +kissed +kisser +kissers +kisses +kissing +kit +kitchen +kitchenette +kitchenettes +kitchens +kitchenware +kite +kited +kites +kith +kiting +kits +kitsch +kitschy +kitten +kittenish +kittens +kitties +kitty +kiwi +kiwis +kleptomania +kleptomaniac +kleptomaniacs +klutz +klutzes +klutzier +klutziest +klutzy +knack +knacker +knacks +knackwurst +knackwursts +knapsack +knapsacks +knave +knavery +knaves +knavish +knead +kneaded +kneader +kneaders +kneading +kneads +knee +kneecap +kneecapped +kneecapping +kneecaps +kneed +kneeing +kneel +kneeling +kneels +knees +knell +knelled +knelling +knells +knelt +knew +knickers +knickknack +knickknacks +knife +knifed +knifes +knifing +knight +knighted +knighthood +knighthoods +knighting +knightly +knights +knit +knits +knitted +knitter +knitters +knitting +knitwear +knives +knob +knobbier +knobbiest +knobby +knobs +knock +knocked +knocker +knockers +knocking +knockout +knockouts +knocks +knockwurst +knockwursts +knoll +knolls +knot +knothole +knotholes +knots +knotted +knottier +knottiest +knotting +knotty +know +knowable +knowing +knowingly +knowings +knowledge +knowledgeable +knowledgeably +known +knows +knuckle +knuckled +knucklehead +knuckleheads +knuckles +knuckling +koala +koalas +kohlrabi +kohlrabies +kook +kookaburra +kookaburras +kookier +kookiest +kookiness +kooks +kooky +kopeck +kopecks +kopek +kopeks +kosher +koshered +koshering +koshers +kowtow +kowtowed +kowtowing +kowtows +krone +kroner +kronor +krypton +króna +krónur +ks +kudos +kudzu +kudzus +kumquat +kumquats +l +la +lab +label +labeled +labeling +labels +labia +labial +labials +labium +labor +laboratories +laboratory +labored +laborer +laborers +laboring +laborious +laboriously +labors +labs +laburnum +laburnums +labyrinth +labyrinthine +labyrinths +lace +laced +lacerate +lacerated +lacerates +lacerating +laceration +lacerations +laces +lachrymal +lachrymose +lacier +laciest +lacing +lack +lackadaisical +lackadaisically +lacked +lackey +lackeys +lacking +lackluster +lacks +laconic +laconically +lacquer +lacquered +lacquering +lacquers +lacrimal +lacrosse +lactate +lactated +lactates +lactating +lactation +lactic +lactose +lacuna +lacunae +lacy +lad +ladder +laddered +laddering +ladders +laddie +laddies +lade +laded +laden +lades +ladies +lading +ladings +ladle +ladled +ladles +ladling +lads +lady +ladybird +ladybirds +ladybug +ladybugs +ladyfinger +ladyfingers +ladylike +ladyship +lag +lager +lagers +laggard +laggards +lagged +lagging +lagniappe +lagniappes +lagoon +lagoons +lags +laid +lain +lair +lairs +laity +lake +lakes +lallygag +lallygagged +lallygagging +lallygags +lam +lama +lamas +lamaseries +lamasery +lamb +lambast +lambaste +lambasted +lambastes +lambasting +lambasts +lambda +lambed +lambent +lambing +lambkin +lambkins +lambs +lambskin +lambskins +lame +lamebrain +lamebrains +lamed +lamely +lameness +lament +lamentable +lamentably +lamentation +lamentations +lamented +lamenting +laments +lamer +lames +lamest +laminate +laminated +laminates +laminating +lamination +laming +lammed +lamming +lamp +lampblack +lampoon +lampooned +lampooning +lampoons +lamppost +lampposts +lamprey +lampreys +lamps +lampshade +lampshades +lams +lance +lanced +lancer +lancers +lances +lancet +lancets +lancing +land +landed +lander +landfall +landfalls +landfill +landfills +landholder +landholders +landing +landings +landladies +landlady +landline +landlines +landlocked +landlord +landlords +landlubber +landlubbers +landmark +landmarks +landmass +landmasses +landowner +landowners +lands +landscape +landscaped +landscaper +landscapers +landscapes +landscaping +landslid +landslide +landslides +landsliding +landward +landwards +lane +lanes +language +languages +languid +languidly +languish +languished +languishes +languishing +languor +languorous +languorously +languors +lank +lanker +lankest +lankier +lankiest +lankiness +lanky +lanolin +lantern +lanterns +lanyard +lanyards +lap +lapel +lapels +lapidaries +lapidary +lapped +lapping +laps +lapse +lapsed +lapses +lapsing +laptop +laptops +lapwing +lapwings +larboard +larboards +larcenies +larcenous +larceny +larch +larches +lard +larded +larder +larders +larding +lards +large +largely +largeness +larger +larges +largess +largest +largo +largos +lariat +lariats +lark +larked +larking +larks +larkspur +larkspurs +larva +larvae +larval +larynges +laryngitis +larynx +lasagna +lasagnas +lascivious +lasciviously +lasciviousness +laser +lasers +lash +lashed +lashes +lashing +lass +lasses +lassie +lassies +lassitude +lasso +lassoed +lassoing +lassos +last +lasted +lasting +lastingly +lastly +lasts +latch +latched +latches +latching +late +latecomer +latecomers +lately +latency +lateness +latent +later +lateral +lateraled +lateraling +laterally +laterals +latest +latex +lath +lathe +lathed +lather +lathered +lathering +lathers +lathes +lathing +laths +latitude +latitudes +latitudinal +latrine +latrines +lats +latte +latter +latterly +lattes +lattice +latticed +lattices +latticework +latticeworks +laud +laudable +laudably +laudanum +laudatory +lauded +lauding +lauds +laugh +laughable +laughably +laughed +laughing +laughingly +laughingstock +laughingstocks +laughs +laughter +launch +launched +launcher +launchers +launches +launching +launder +laundered +launderer +launderers +laundering +launders +laundress +laundresses +laundries +laundry +laundryman +laundrymen +laureate +laureates +laurel +laurels +lava +lavatories +lavatory +lavender +lavenders +lavish +lavished +lavisher +lavishes +lavishest +lavishing +lavishly +lavishness +law +lawbreaker +lawbreakers +lawful +lawfully +lawfulness +lawgiver +lawgivers +lawless +lawlessly +lawlessness +lawmaker +lawmakers +lawn +lawns +lawrencium +laws +lawsuit +lawsuits +lawyer +lawyers +lax +laxative +laxatives +laxer +laxest +laxity +laxly +laxness +lay +layaway +layer +layered +layering +layers +layette +layettes +laying +layman +laymen +layoff +layoffs +layout +layouts +layover +layovers +laypeople +layperson +laypersons +lays +laywoman +laywomen +laze +lazed +lazes +lazied +lazier +lazies +laziest +lazily +laziness +lazing +lazy +lazybones +lazying +lea +leach +leached +leaches +leaching +lead +leaded +leaden +leader +leaders +leadership +leading +leads +leaf +leafed +leafier +leafiest +leafing +leafless +leaflet +leafleted +leafleting +leaflets +leafs +leafy +league +leagued +leagues +leaguing +leak +leakage +leakages +leaked +leakier +leakiest +leaking +leaks +leaky +lean +leaned +leaner +leanest +leaning +leanings +leanness +leans +leap +leaped +leapfrog +leapfrogged +leapfrogging +leapfrogs +leaping +leaps +leapt +learn +learned +learner +learners +learning +learns +leas +lease +leased +leasehold +leaseholder +leaseholders +leaseholds +leases +leash +leashed +leashes +leashing +leasing +least +leastwise +leather +leatherneck +leathernecks +leathers +leathery +leave +leaved +leaven +leavened +leavening +leavens +leaves +leaving +leavings +lecher +lecherous +lecherously +lechers +lechery +lecithin +lectern +lecterns +lecture +lectured +lecturer +lecturers +lectures +lecturing +led +ledge +ledger +ledgers +ledges +lee +leech +leeched +leeches +leeching +leek +leeks +leer +leered +leerier +leeriest +leering +leers +leery +lees +leeward +leewards +leeway +left +lefter +leftest +lefties +leftism +leftist +leftists +leftmost +leftover +leftovers +lefts +leftwards +lefty +leg +legacies +legacy +legal +legalese +legalism +legalisms +legalistic +legality +legalization +legalize +legalized +legalizes +legalizing +legally +legals +legate +legatee +legatees +legates +legation +legations +legato +legatos +legend +legendary +legends +legerdemain +legged +leggier +leggiest +leggin +legging +leggings +leggins +leggy +legibility +legible +legibly +legion +legionnaire +legionnaires +legions +legislate +legislated +legislates +legislating +legislation +legislative +legislator +legislators +legislature +legislatures +legit +legitimacy +legitimate +legitimated +legitimately +legitimates +legitimating +legitimize +legitimized +legitimizes +legitimizing +legless +legman +legmen +legroom +legrooms +legs +legume +legumes +leguminous +legwork +lei +leis +leisure +leisurely +leitmotif +leitmotifs +lemma +lemmas +lemme +lemming +lemmings +lemon +lemonade +lemons +lemony +lemur +lemurs +lend +lender +lenders +lending +lends +length +lengthen +lengthened +lengthening +lengthens +lengthier +lengthiest +lengthily +lengths +lengthwise +lengthy +leniency +lenient +leniently +lens +lenses +lent +lentil +lentils +leonine +leopard +leopards +leotard +leotards +leper +lepers +leprechaun +leprechauns +leprosy +leprous +lesbian +lesbianism +lesbians +lesion +lesions +less +lessee +lessees +lessen +lessened +lessening +lessens +lesser +lesson +lessons +lessor +lessors +lest +let +letdown +letdowns +lethal +lethally +lethargic +lethargically +lethargy +lets +letter +letterbox +lettered +letterhead +letterheads +lettering +letters +letting +lettuce +lettuces +letup +letups +leukemia +leukocyte +leukocytes +levee +levees +level +leveled +leveler +levelers +levelheaded +levelheadedness +leveling +levelness +levels +lever +leverage +leveraged +leverages +leveraging +levered +levering +levers +leviathan +leviathans +levied +levies +levitate +levitated +levitates +levitating +levitation +levity +levy +levying +lewd +lewder +lewdest +lewdly +lewdness +lexical +lexicographer +lexicographers +lexicography +lexicon +lexicons +liabilities +liability +liable +liaise +liaised +liaises +liaising +liaison +liaisons +liar +liars +lib +libation +libations +libel +libeled +libeler +libelers +libeling +libelous +libels +liberal +liberalism +liberality +liberalization +liberalizations +liberalize +liberalized +liberalizes +liberalizing +liberally +liberals +liberate +liberated +liberates +liberating +liberation +liberator +liberators +libertarian +libertarians +liberties +libertine +libertines +liberty +libidinous +libido +libidos +librarian +librarians +libraries +library +librettist +librettists +libretto +librettos +lice +license +licensed +licensee +licensees +licenses +licensing +licentiate +licentiates +licentious +licentiously +licentiousness +lichen +lichens +licit +lick +licked +licking +lickings +licks +licorice +licorices +lid +lidded +lids +lie +lied +lief +liefer +liefest +liege +lieges +lien +liens +lies +lieu +lieutenancy +lieutenant +lieutenants +life +lifeblood +lifeboat +lifeboats +lifeforms +lifeguard +lifeguards +lifeless +lifelike +lifeline +lifelines +lifelong +lifer +lifers +lifesaver +lifesavers +lifesaving +lifespan +lifespans +lifestyle +lifestyles +lifetime +lifetimes +lifework +lifeworks +lift +lifted +lifting +liftoff +liftoffs +lifts +ligament +ligaments +ligature +ligatured +ligatures +ligaturing +light +lighted +lighten +lightened +lightening +lightens +lighter +lighters +lightest +lightheaded +lighthearted +lightheartedly +lightheartedness +lighthouse +lighthouses +lighting +lightly +lightness +lightning +lightninged +lightnings +lights +lightweight +lightweights +lignite +likable +likableness +like +likeable +likeableness +liked +likelier +likeliest +likelihood +likelihoods +likely +liken +likened +likeness +likenesses +likening +likens +liker +likes +likest +likewise +liking +lilac +lilacs +lilies +lilt +lilted +lilting +lilts +lily +limb +limber +limbered +limbering +limbers +limbless +limbo +limbos +limbs +lime +limeade +limeades +limed +limelight +limerick +limericks +limes +limestone +limier +limiest +liming +limit +limitation +limitations +limited +limiting +limitings +limitless +limits +limn +limned +limning +limns +limo +limos +limousine +limousines +limp +limped +limper +limpest +limpet +limpets +limpid +limpidity +limpidly +limping +limply +limpness +limps +limy +linage +linchpin +linchpins +linden +lindens +line +lineage +lineages +lineal +lineally +lineament +lineaments +linear +linearly +linebacker +linebackers +lined +linefeed +lineman +linemen +linen +linens +liner +liners +lines +linesman +linesmen +lineup +lineups +linger +lingered +lingerer +lingerers +lingerie +lingering +lingeringly +lingerings +lingers +lingo +lingoes +lingual +linguist +linguistic +linguistics +linguists +liniment +liniments +lining +linings +link +linkage +linkages +linked +linker +linking +links +linkup +linkups +linnet +linnets +linoleum +linseed +lint +lintel +lintels +lion +lioness +lionesses +lionhearted +lionize +lionized +lionizes +lionizing +lions +lip +lipid +lipids +liposuction +lipread +lipreading +lipreads +lips +lipstick +lipsticked +lipsticking +lipsticks +liquefaction +liquefied +liquefies +liquefy +liquefying +liqueur +liqueurs +liquid +liquidate +liquidated +liquidates +liquidating +liquidation +liquidations +liquidator +liquidators +liquidity +liquidize +liquidized +liquidizes +liquidizing +liquids +liquor +liquored +liquoring +liquors +lira +lire +lisle +lisp +lisped +lisping +lisps +lissome +list +listed +listen +listened +listener +listeners +listening +listens +listing +listings +listless +listlessly +listlessness +lists +lit +litanies +litany +litchi +litchis +lite +liter +literacy +literal +literally +literals +literary +literate +literates +literati +literature +liters +lithe +lither +lithest +lithium +lithograph +lithographed +lithographer +lithographers +lithographic +lithographing +lithographs +lithography +lithosphere +lithospheres +litigant +litigants +litigate +litigated +litigates +litigating +litigation +litigious +litigiousness +litmus +litter +litterbug +litterbugs +littered +littering +litters +little +littleness +littler +littlest +littoral +littorals +liturgical +liturgies +liturgy +livability +livable +live +lived +livelier +liveliest +livelihood +livelihoods +liveliness +livelong +livelongs +lively +liven +livened +livening +livens +liver +liveried +liveries +livers +liverwurst +livery +lives +livest +livestock +livid +lividly +living +livings +lizard +lizards +llama +llamas +llano +llanos +lo +load +loadable +loaded +loader +loaders +loading +loads +loaf +loafed +loafer +loafers +loafing +loafs +loam +loamier +loamiest +loamy +loan +loaned +loaner +loaners +loaning +loans +loanword +loanwords +loath +loathe +loathed +loathes +loathing +loathings +loathsome +loathsomeness +loaves +lob +lobbed +lobbied +lobbies +lobbing +lobby +lobbying +lobbyist +lobbyists +lobe +lobed +lobes +lobotomies +lobotomy +lobs +lobster +lobsters +local +locale +locales +localities +locality +localization +localize +localized +localizes +localizing +locally +locals +locate +located +locates +locating +location +locations +locavore +locavores +loci +lock +lockable +locked +locker +lockers +locket +lockets +locking +lockjaw +lockout +lockouts +locks +locksmith +locksmiths +lockstep +lockup +lockups +loco +locomotion +locomotive +locomotives +locoweed +locoweeds +locus +locust +locusts +locution +locutions +lode +lodes +lodestar +lodestars +lodestone +lodestones +lodge +lodged +lodger +lodgers +lodges +lodging +lodgings +loft +lofted +loftier +loftiest +loftily +loftiness +lofting +lofts +lofty +log +loganberries +loganberry +logarithm +logarithmic +logarithms +logbook +logbooks +loge +loges +logged +logger +loggerhead +loggerheads +loggers +logging +logic +logical +logically +logician +logicians +login +logins +logistic +logistical +logistically +logistics +logjam +logjams +logo +logoff +logoffs +logon +logons +logos +logotype +logotypes +logout +logouts +logrolling +logs +loin +loincloth +loincloths +loins +loiter +loitered +loiterer +loiterers +loitering +loiters +lolcat +lolcats +loll +lolled +lolling +lollipop +lollipops +lolls +lollygag +lollygagged +lollygagging +lollygags +lollypop +lollypops +lone +lonelier +loneliest +loneliness +lonely +loner +loners +lonesome +long +longboat +longboats +longed +longer +longest +longevity +longhair +longhairs +longhand +longhorn +longhorns +longing +longingly +longings +longish +longitude +longitudes +longitudinal +longitudinally +longs +longshoreman +longshoremen +longtime +loofah +look +lookalike +lookalikes +looked +looking +lookout +lookouts +looks +lookup +loom +loomed +looming +looms +loon +loonie +loonier +loonies +looniest +loons +loony +loop +looped +loophole +loopholes +loopier +loopiest +looping +loops +loopy +loose +loosed +loosely +loosen +loosened +looseness +loosening +loosens +looser +looses +loosest +loosing +loot +looted +looter +looters +looting +loots +lop +lope +loped +lopes +loping +lopped +lopping +lops +lopsided +lopsidedly +lopsidedness +loquacious +loquacity +lord +lorded +lording +lordlier +lordliest +lordly +lords +lordship +lordships +lore +lorgnette +lorgnettes +lorn +lorries +lorry +lose +loser +losers +loses +losing +loss +losses +lost +lot +lotion +lotions +lots +lotteries +lottery +lotto +lotus +lotuses +loud +louder +loudest +loudly +loudmouth +loudmouthed +loudmouths +loudness +loudspeaker +loudspeakers +lounge +lounged +lounges +lounging +louse +louses +lousier +lousiest +lousiness +lousy +lout +loutish +louts +louver +louvered +louvers +lovable +love +lovebird +lovebirds +loved +loveless +lovelier +lovelies +loveliest +loveliness +lovelorn +lovely +lovemaking +lover +lovers +loves +lovesick +loving +lovingly +low +lowbrow +lowbrows +lowdown +lowed +lower +lowercase +lowered +lowering +lowers +lowest +lowing +lowish +lowland +lowlands +lowlier +lowliest +lowliness +lowly +lowness +lows +lox +loyal +loyaler +loyalest +loyalist +loyalists +loyally +loyalties +loyalty +lozenge +lozenges +ls +luau +luaus +lubber +lubbers +lube +lubed +lubes +lubing +lubricant +lubricants +lubricate +lubricated +lubricates +lubricating +lubrication +lubricator +lubricators +lucid +lucidity +lucidly +lucidness +luck +lucked +luckier +luckiest +luckily +luckiness +lucking +luckless +lucks +lucky +lucrative +lucratively +lucre +ludicrous +ludicrously +ludicrousness +lug +luggage +lugged +lugging +lugs +lugubrious +lugubriously +lugubriousness +lukewarm +lull +lullabies +lullaby +lulled +lulling +lulls +lumbago +lumbar +lumber +lumbered +lumbering +lumberjack +lumberjacks +lumberman +lumbermen +lumbers +lumberyard +lumberyards +luminaries +luminary +luminescence +luminescent +luminosity +luminous +luminously +lummox +lummoxes +lump +lumped +lumpier +lumpiest +lumpiness +lumping +lumpish +lumps +lumpy +lunacies +lunacy +lunar +lunatic +lunatics +lunch +lunchbox +lunched +luncheon +luncheonette +luncheonettes +luncheons +lunches +lunching +lunchroom +lunchrooms +lunchtime +lunchtimes +lung +lunge +lunged +lunges +lunging +lungs +lupine +lupines +lupus +lurch +lurched +lurches +lurching +lure +lured +lures +lurid +luridly +luridness +luring +lurk +lurked +lurking +lurks +luscious +lusciously +lusciousness +lush +lusher +lushes +lushest +lushness +lust +lusted +luster +lustful +lustfully +lustier +lustiest +lustily +lustiness +lusting +lustrous +lusts +lusty +lute +lutes +luxuriance +luxuriant +luxuriantly +luxuriate +luxuriated +luxuriates +luxuriating +luxuries +luxurious +luxuriously +luxuriousness +luxury +lyceum +lyceums +lychee +lychees +lye +lying +lymph +lymphatic +lymphatics +lymphoma +lymphomas +lynch +lynched +lynches +lynching +lynchings +lynx +lynxes +lyre +lyres +lyric +lyrical +lyrically +lyricist +lyricists +lyrics +m +ma +macabre +macadam +macaroni +macaronis +macaroon +macaroons +macaw +macaws +mace +maced +macerate +macerated +macerates +macerating +maceration +maces +machete +machetes +machination +machinations +machine +machined +machinery +machines +machining +machinist +machinists +machismo +macho +macing +mackerel +mackerels +mackinaw +mackinaws +mackintosh +mackintoshes +macramé +macro +macrobiotic +macrobiotics +macrocosm +macrocosms +macron +macrons +macros +macroscopic +mad +madam +madame +madams +madcap +madcaps +madden +maddened +maddening +maddeningly +maddens +madder +madders +maddest +made +mademoiselle +mademoiselles +madhouse +madhouses +madly +madman +madmen +madness +madras +madrasa +madrasah +madrasahs +madrasas +madrases +madrassa +madrassas +madrigal +madrigals +mads +madwoman +madwomen +maelstrom +maelstroms +maestro +maestros +magazine +magazines +magenta +maggot +maggots +magic +magical +magically +magician +magicians +magisterial +magisterially +magistrate +magistrates +magma +magnanimity +magnanimous +magnanimously +magnate +magnates +magnesia +magnesium +magnet +magnetic +magnetically +magnetism +magnetization +magnetize +magnetized +magnetizes +magnetizing +magneto +magnetos +magnetosphere +magnets +magnification +magnifications +magnificence +magnificent +magnificently +magnified +magnifier +magnifiers +magnifies +magnify +magnifying +magnitude +magnitudes +magnolia +magnolias +magnum +magnums +magpie +magpies +maharaja +maharajah +maharajahs +maharajas +maharanee +maharanees +maharani +maharanis +maharishi +maharishis +mahatma +mahatmas +mahjong +mahoganies +mahogany +maid +maiden +maidenhair +maidenhead +maidenheads +maidenhood +maidenly +maidens +maids +maidservant +maidservants +mail +mailbox +mailboxes +mailed +mailer +mailers +mailing +mailings +mailman +mailmen +mails +maim +maimed +maiming +maims +main +mainframe +mainframes +mainland +mainlands +mainline +mainlined +mainlines +mainlining +mainly +mainmast +mainmasts +mains +mainsail +mainsails +mainspring +mainsprings +mainstay +mainstays +mainstream +mainstreamed +mainstreaming +mainstreams +maintain +maintainability +maintainable +maintained +maintainer +maintainers +maintaining +maintains +maintenance +maize +maizes +majestic +majestically +majesties +majesty +major +majored +majorette +majorettes +majoring +majorities +majority +majorly +majors +make +maker +makers +makes +makeshift +makeshifts +makeup +makeups +making +makings +maladies +maladjusted +maladjustment +maladroit +malady +malaise +malapropism +malapropisms +malaria +malarial +malarkey +malcontent +malcontents +male +malediction +maledictions +malefactor +malefactors +maleness +males +malevolence +malevolent +malevolently +malfeasance +malformation +malformations +malformed +malfunction +malfunctioned +malfunctioning +malfunctions +malice +malicious +maliciously +malign +malignancies +malignancy +malignant +malignantly +maligned +maligning +malignity +maligns +malinger +malingered +malingerer +malingerers +malingering +malingers +mall +mallard +mallards +malleability +malleable +mallet +mallets +mallow +mallows +malls +malnourished +malnutrition +malodorous +malpractice +malpractices +malt +malted +malteds +malting +maltreat +maltreated +maltreating +maltreatment +maltreats +malts +malware +mama +mamas +mambo +mamboed +mamboing +mambos +mamma +mammal +mammalian +mammalians +mammals +mammary +mammogram +mammograms +mammography +mammon +mammoth +mammoths +man +manacle +manacled +manacles +manacling +manage +manageability +manageable +managed +management +manager +managerial +managers +manages +managing +manatee +manatees +mandarin +mandarins +mandate +mandated +mandates +mandating +mandatory +mandible +mandibles +mandolin +mandolins +mandrake +mandrakes +mandrill +mandrills +mane +manes +maneuver +maneuverability +maneuverable +maneuvered +maneuvering +maneuvers +manful +manfully +manga +manganese +mange +manger +mangers +mangier +mangiest +mangle +mangled +mangles +mangling +mango +mangoes +mangrove +mangroves +mangy +manhandle +manhandled +manhandles +manhandling +manhole +manholes +manhood +manhunt +manhunts +mania +maniac +maniacal +maniacs +manias +manic +manics +manicure +manicured +manicures +manicuring +manicurist +manicurists +manifest +manifestation +manifestations +manifested +manifesting +manifestly +manifesto +manifestos +manifests +manifold +manifolded +manifolding +manifolds +manikin +manikins +manipulate +manipulated +manipulates +manipulating +manipulation +manipulations +manipulative +manipulator +manipulators +mankind +manlier +manliest +manliness +manly +manna +manned +mannequin +mannequins +manner +mannered +mannerism +mannerisms +mannerly +manners +manning +mannish +mannishly +mannishness +manor +manorial +manors +manpower +manqué +mans +mansard +mansards +manse +manservant +manses +mansion +mansions +manslaughter +mantel +mantelpiece +mantelpieces +mantels +mantes +mantilla +mantillas +mantis +mantises +mantissa +mantle +mantled +mantles +mantling +mantra +mantras +manual +manually +manuals +manufacture +manufactured +manufacturer +manufacturers +manufactures +manufacturing +manumit +manumits +manumitted +manumitting +manure +manured +manures +manuring +manuscript +manuscripts +many +manège +map +maple +maples +mapped +mapper +mapping +mappings +maps +mar +marabou +marabous +maraca +maracas +marathon +marathoner +marathoners +marathons +maraud +marauded +marauder +marauders +marauding +marauds +marble +marbled +marbles +marbling +march +marched +marcher +marchers +marches +marching +marchioness +marchionesses +mare +mares +margarine +margarita +margaritas +margin +marginal +marginalia +marginally +margins +maria +mariachi +mariachis +marigold +marigolds +marijuana +marimba +marimbas +marina +marinade +marinaded +marinades +marinading +marinas +marinate +marinated +marinates +marinating +marine +mariner +mariners +marines +marionette +marionettes +marital +maritime +marjoram +mark +markdown +markdowns +marked +markedly +marker +markers +market +marketability +marketable +marketed +marketer +marketers +marketing +marketplace +marketplaces +markets +marking +markings +marks +marksman +marksmanship +marksmen +markup +markups +marlin +marlins +marmalade +marmoset +marmosets +marmot +marmots +maroon +marooned +marooning +maroons +marquee +marquees +marquess +marquesses +marquetry +marquis +marquise +marquises +marred +marriage +marriageable +marriages +married +marrieds +marries +marring +marrow +marrows +marry +marrying +mars +marsh +marshal +marshaled +marshaling +marshals +marshes +marshier +marshiest +marshmallow +marshmallows +marshy +marsupial +marsupials +mart +marten +martens +martial +martin +martinet +martinets +martini +martinis +martins +marts +martyr +martyrdom +martyred +martyring +martyrs +marvel +marveled +marveling +marvelous +marvelously +marvels +marzipan +mas +mascara +mascaraed +mascaraing +mascaras +mascot +mascots +masculine +masculines +masculinity +mash +mashed +masher +mashers +mashes +mashing +mashup +mashups +mask +masked +masking +masks +masochism +masochist +masochistic +masochists +mason +masonic +masonry +masons +masque +masquerade +masqueraded +masquerader +masqueraders +masquerades +masquerading +masques +mass +massacre +massacred +massacres +massacring +massage +massaged +massages +massaging +massed +masses +masseur +masseurs +masseuse +masseuses +massing +massive +massively +massiveness +mast +mastectomies +mastectomy +master +mastered +masterful +masterfully +mastering +masterly +mastermind +masterminded +masterminding +masterminds +masterpiece +masterpieces +masters +masterstroke +masterstrokes +masterwork +masterworks +mastery +masthead +mastheads +masticate +masticated +masticates +masticating +mastication +mastiff +mastiffs +mastodon +mastodons +mastoid +mastoids +masts +masturbate +masturbated +masturbates +masturbating +masturbation +mat +matador +matadors +match +matchbook +matchbooks +matchbox +matchboxes +matched +matches +matching +matchless +matchmaker +matchmakers +matchmaking +matchstick +matchsticks +mate +mated +material +materialism +materialist +materialistic +materialistically +materialists +materialization +materialize +materialized +materializes +materializing +materially +materials +maternal +maternally +maternity +mates +math +mathematical +mathematically +mathematician +mathematicians +mathematics +mating +matins +matinée +matinées +matriarch +matriarchal +matriarchies +matriarchs +matriarchy +matrices +matricide +matricides +matriculate +matriculated +matriculates +matriculating +matriculation +matrimonial +matrimony +matrix +matron +matronly +matrons +mats +matte +matted +matter +mattered +mattering +matters +mattes +matting +mattock +mattocks +mattress +mattresses +maturation +mature +matured +maturely +maturer +matures +maturest +maturing +maturities +maturity +matzo +matzoh +matzohs +matzos +matzot +matzoth +matériel +maudlin +maul +mauled +mauling +mauls +maunder +maundered +maundering +maunders +mausoleum +mausoleums +mauve +maven +mavens +maverick +mavericks +maw +mawkish +mawkishly +maws +maxed +maxes +maxilla +maxillae +maxillary +maxim +maximal +maximally +maximization +maximize +maximized +maximizes +maximizing +maxims +maximum +maximums +maxing +may +maybe +maybes +mayday +maydays +mayflies +mayflower +mayflowers +mayfly +mayhem +mayo +mayonnaise +mayor +mayoral +mayoralty +mayors +maypole +maypoles +maze +mazes +mazurka +mazurkas +me +mead +meadow +meadowlark +meadowlarks +meadows +meager +meagerly +meagerness +meal +mealier +mealiest +meals +mealtime +mealtimes +mealy +mean +meander +meandered +meandering +meanders +meaner +meanest +meaning +meaningful +meaningfully +meaningless +meanings +meanly +meanness +means +meant +meantime +meanwhile +measles +measlier +measliest +measly +measurable +measurably +measure +measured +measureless +measurement +measurements +measures +measuring +meat +meatball +meatballs +meatier +meatiest +meatloaf +meatloaves +meats +meaty +mecca +meccas +mechanic +mechanical +mechanically +mechanics +mechanism +mechanisms +mechanistic +mechanization +mechanize +mechanized +mechanizes +mechanizing +medal +medalist +medalists +medallion +medallions +medals +meddle +meddled +meddler +meddlers +meddles +meddlesome +meddling +media +medial +median +medians +medias +mediate +mediated +mediates +mediating +mediation +mediator +mediators +medic +medical +medically +medicals +medicate +medicated +medicates +medicating +medication +medications +medicinal +medicinally +medicine +medicines +medics +medieval +mediocre +mediocrities +mediocrity +meditate +meditated +meditates +meditating +meditation +meditations +meditative +meditatively +medium +mediums +medley +medleys +medulla +medullas +meek +meeker +meekest +meekly +meekness +meet +meeting +meetinghouse +meetinghouses +meetings +meets +meg +megabyte +megabytes +megachurch +megachurches +megacycle +megacycles +megahertz +megalith +megaliths +megalomania +megalomaniac +megalomaniacs +megalopolis +megalopolises +megaphone +megaphoned +megaphones +megaphoning +megapixel +megapixels +megaton +megatons +megs +meh +melancholia +melancholic +melancholics +melancholy +melange +melanges +melanin +melanoma +melanomas +meld +melded +melding +melds +mellifluous +mellifluously +mellow +mellowed +mellower +mellowest +mellowing +mellowness +mellows +melodic +melodically +melodies +melodious +melodiously +melodiousness +melodrama +melodramas +melodramatic +melodramatically +melody +melon +melons +melt +meltdown +meltdowns +melted +melting +melts +member +members +membership +memberships +membrane +membranes +membranous +meme +memento +mementos +memes +memo +memoir +memoirs +memorabilia +memorable +memorably +memorandum +memorandums +memorial +memorialize +memorialized +memorializes +memorializing +memorials +memories +memorization +memorize +memorized +memorizes +memorizing +memory +memos +men +menace +menaced +menaces +menacing +menacingly +menage +menagerie +menageries +menages +mend +mendacious +mendacity +mended +mender +menders +mendicant +mendicants +mending +mends +menfolk +menhaden +menial +menially +menials +meningitis +menopausal +menopause +menorah +menorahs +menservants +menses +menstrual +menstruate +menstruated +menstruates +menstruating +menstruation +menswear +mental +mentalities +mentality +mentally +menthol +mentholated +mention +mentioned +mentioning +mentions +mentor +mentored +mentoring +mentors +menu +menus +meow +meowed +meowing +meows +mercantile +mercenaries +mercenary +mercerize +mercerized +mercerizes +mercerizing +merchandise +merchandised +merchandises +merchandising +merchant +merchantman +merchantmen +merchants +mercies +merciful +mercifully +merciless +mercilessly +mercurial +mercuric +mercury +mercy +mere +merely +meres +merest +meretricious +merganser +mergansers +merge +merged +merger +mergers +merges +merging +meridian +meridians +meringue +meringues +merino +merinos +merit +merited +meriting +meritocracies +meritocracy +meritorious +meritoriously +merits +mermaid +mermaids +merman +mermen +merrier +merriest +merrily +merriment +merriness +merry +merrymaker +merrymakers +merrymaking +mes +mesa +mesas +mescal +mescaline +mescals +mesdames +mesdemoiselles +mesh +meshed +meshes +meshing +mesmerism +mesmerize +mesmerized +mesmerizes +mesmerizing +mesquite +mesquites +mess +message +messages +messed +messenger +messengers +messes +messiah +messiahs +messier +messiest +messieurs +messily +messiness +messing +messy +mestizo +mestizos +met +metabolic +metabolism +metabolisms +metabolize +metabolized +metabolizes +metabolizing +metacarpal +metacarpals +metacarpi +metacarpus +metal +metallic +metallurgical +metallurgist +metallurgists +metallurgy +metals +metamorphic +metamorphism +metamorphose +metamorphosed +metamorphoses +metamorphosing +metamorphosis +metaphor +metaphorical +metaphorically +metaphors +metaphysical +metaphysics +metastases +metastasis +metastasize +metastasized +metastasizes +metastasizing +metatarsal +metatarsals +mete +meted +meteor +meteoric +meteorite +meteorites +meteoroid +meteoroids +meteorological +meteorologist +meteorologists +meteorology +meteors +meter +metered +metering +meters +metes +methadone +methane +methanol +methinks +method +methodical +methodically +methodological +methodologies +methodology +methods +methought +meticulous +meticulously +meticulousness +meting +metric +metrical +metrically +metrication +metrics +metro +metronome +metronomes +metropolis +metropolises +metropolitan +metros +mettle +mettlesome +mew +mewed +mewing +mewl +mewled +mewling +mewls +mews +mezzanine +mezzanines +mi +miasma +miasmas +mica +mice +microaggression +microaggressions +microbe +microbes +microbiologist +microbiologists +microbiology +microchip +microchips +microcode +microcomputer +microcomputers +microcosm +microcosms +microeconomics +microfiche +microfilm +microfilmed +microfilming +microfilms +microloan +microloans +micrometer +micrometers +micron +microns +microorganism +microorganisms +microphone +microphones +microprocessor +microprocessors +microscope +microscopes +microscopic +microscopically +microscopy +microsecond +microseconds +microsurgery +microwave +microwaved +microwaves +microwaving +mid +midair +midday +middies +middle +middlebrow +middlebrows +middleman +middlemen +middles +middleweight +middleweights +middling +middy +midge +midges +midget +midgets +midland +midlands +midmost +midnight +midpoint +midpoints +midriff +midriffs +midshipman +midshipmen +midst +midstream +midsummer +midterm +midterms +midtown +midway +midways +midweek +midweeks +midwife +midwifed +midwiferies +midwifery +midwifes +midwifing +midwinter +midwived +midwives +midwiving +midyear +midyears +mien +miens +miff +miffed +miffing +miffs +might +mightier +mightiest +mightily +mightiness +mighty +migraine +migraines +migrant +migrants +migrate +migrated +migrates +migrating +migration +migrations +migratory +mike +miked +mikes +miking +mil +milch +mild +milder +mildest +mildew +mildewed +mildewing +mildews +mildly +mildness +mile +mileage +mileages +milepost +mileposts +miler +milers +miles +milestone +milestones +milf +milfs +milieu +milieus +militancy +militant +militantly +militants +militarily +militarism +militarist +militaristic +militarists +militarization +militarize +militarized +militarizes +militarizing +military +militate +militated +militates +militating +militia +militiaman +militiamen +militias +milk +milked +milker +milkier +milkiest +milkiness +milking +milkmaid +milkmaids +milkman +milkmen +milks +milkshake +milkshakes +milksop +milksops +milkweed +milkweeds +milky +mill +millage +milled +millennia +millennial +millennium +millenniums +miller +millers +millet +milligram +milligrams +milliliter +milliliters +millimeter +millimeters +milliner +milliners +millinery +milling +million +millionaire +millionaires +millions +millionth +millionths +millipede +millipedes +millisecond +milliseconds +millrace +millraces +mills +millstone +millstones +milquetoast +milquetoasts +mils +mime +mimed +mimeograph +mimeographed +mimeographing +mimeographs +mimes +mimetic +mimic +mimicked +mimicking +mimicries +mimicry +mimics +miming +mimosa +mimosas +minaret +minarets +minatory +mince +minced +mincemeat +minces +mincing +mind +mindbogglingly +minded +mindedness +mindful +mindfully +mindfulness +minding +mindless +mindlessly +mindlessness +minds +mine +mined +minefield +minefields +miner +mineral +mineralogist +mineralogists +mineralogy +minerals +miners +mines +minestrone +minesweeper +minesweepers +mingle +mingled +mingles +mingling +mini +miniature +miniatures +miniaturist +miniaturists +miniaturization +miniaturize +miniaturized +miniaturizes +miniaturizing +minibike +minibikes +minibus +minibuses +minicam +minicams +minicomputer +minicomputers +minim +minimal +minimalism +minimalist +minimalists +minimally +minimization +minimize +minimized +minimizes +minimizing +minims +minimum +minimums +mining +minion +minions +minis +miniseries +miniskirt +miniskirts +minister +ministered +ministerial +ministering +ministers +ministrant +ministrants +ministration +ministrations +ministries +ministry +minivan +minivans +mink +minks +minnow +minnows +minor +minored +minoring +minorities +minority +minors +minster +minstrel +minstrels +mint +minted +mintier +mintiest +minting +mints +minty +minuend +minuends +minuet +minuets +minus +minuscule +minuscules +minuses +minute +minuted +minutely +minuteman +minutemen +minuteness +minuter +minutes +minutest +minutia +minutiae +minuting +minx +minxes +miracle +miracles +miraculous +miraculously +mirage +mirages +mire +mired +mires +miring +mirror +mirrored +mirroring +mirrors +mirth +mirthful +mirthfully +mirthless +misadventure +misadventures +misalignment +misalliance +misalliances +misanthrope +misanthropes +misanthropic +misanthropist +misanthropists +misanthropy +misapplication +misapplied +misapplies +misapply +misapplying +misapprehend +misapprehended +misapprehending +misapprehends +misapprehension +misapprehensions +misappropriate +misappropriated +misappropriates +misappropriating +misappropriation +misappropriations +misbegotten +misbehave +misbehaved +misbehaves +misbehaving +misbehavior +miscalculate +miscalculated +miscalculates +miscalculating +miscalculation +miscalculations +miscall +miscalled +miscalling +miscalls +miscarriage +miscarriages +miscarried +miscarries +miscarry +miscarrying +miscast +miscasting +miscasts +miscegenation +miscellaneous +miscellanies +miscellany +mischance +mischances +mischief +mischievous +mischievously +mischievousness +miscommunication +misconceive +misconceived +misconceives +misconceiving +misconception +misconceptions +misconduct +misconducted +misconducting +misconducts +misconstruction +misconstructions +misconstrue +misconstrued +misconstrues +misconstruing +miscount +miscounted +miscounting +miscounts +miscreant +miscreants +miscue +miscued +miscues +miscuing +misdeal +misdealing +misdeals +misdealt +misdeed +misdeeds +misdemeanor +misdemeanors +misdiagnose +misdiagnosed +misdiagnoses +misdiagnosing +misdiagnosis +misdid +misdirect +misdirected +misdirecting +misdirection +misdirects +misdo +misdoes +misdoing +misdoings +misdone +miser +miserable +miserably +miseries +miserliness +miserly +misers +misery +misfeasance +misfire +misfired +misfires +misfiring +misfit +misfits +misfitted +misfitting +misfortune +misfortunes +misgiving +misgivings +misgovern +misgoverned +misgoverning +misgoverns +misguide +misguided +misguidedly +misguides +misguiding +mishandle +mishandled +mishandles +mishandling +mishap +mishaps +mishmash +mishmashes +misidentified +misidentifies +misidentify +misidentifying +misinform +misinformation +misinformed +misinforming +misinforms +misinterpret +misinterpretation +misinterpretations +misinterpreted +misinterpreting +misinterprets +misjudge +misjudged +misjudges +misjudging +misjudgment +misjudgments +mislaid +mislay +mislaying +mislays +mislead +misleading +misleads +misled +mismanage +mismanaged +mismanagement +mismanages +mismanaging +mismatch +mismatched +mismatches +mismatching +misnomer +misnomers +misogynist +misogynistic +misogynists +misogyny +misplace +misplaced +misplaces +misplacing +misplay +misplayed +misplaying +misplays +misprint +misprinted +misprinting +misprints +mispronounce +mispronounced +mispronounces +mispronouncing +mispronunciation +mispronunciations +misquotation +misquotations +misquote +misquoted +misquotes +misquoting +misread +misreading +misreadings +misreads +misrepresent +misrepresentation +misrepresentations +misrepresented +misrepresenting +misrepresents +misrule +misruled +misrules +misruling +miss +missal +missals +missed +misses +misshapen +missile +missilery +missiles +missing +mission +missionaries +missionary +missions +missive +missives +misspell +misspelled +misspelling +misspellings +misspells +misspend +misspending +misspends +misspent +misstate +misstated +misstatement +misstatements +misstates +misstating +misstep +missteps +mist +mistake +mistaken +mistakenly +mistakes +mistaking +misted +mister +misters +mistier +mistiest +mistily +mistime +mistimed +mistimes +mistiming +mistiness +misting +mistletoe +mistook +mistranslated +mistreat +mistreated +mistreating +mistreatment +mistreats +mistress +mistresses +mistrial +mistrials +mistrust +mistrusted +mistrustful +mistrusting +mistrusts +mists +misty +mistype +mistypes +mistyping +misunderstand +misunderstanding +misunderstandings +misunderstands +misunderstood +misuse +misused +misuses +misusing +mite +miter +mitered +mitering +miters +mites +mitigate +mitigated +mitigates +mitigating +mitigation +mitosis +mitt +mitten +mittens +mitts +mix +mixed +mixer +mixers +mixes +mixing +mixture +mixtures +mizzen +mizzenmast +mizzenmasts +mizzens +mkay +mnemonic +mnemonics +moan +moaned +moaning +moans +moat +moats +mob +mobbed +mobbing +mobile +mobiles +mobility +mobilization +mobilizations +mobilize +mobilized +mobilizes +mobilizing +mobs +mobster +mobsters +moccasin +moccasins +mocha +mochas +mock +mocked +mocker +mockeries +mockers +mockery +mocking +mockingbird +mockingbirds +mockingly +mocks +mod +modal +modals +mode +model +modeled +modeling +modelings +models +modem +modems +moderate +moderated +moderately +moderates +moderating +moderation +moderator +moderators +modern +modernism +modernist +modernistic +modernists +modernity +modernization +modernize +modernized +modernizes +modernizing +moderns +modes +modest +modestly +modesty +modicum +modicums +modifiable +modification +modifications +modified +modifier +modifiers +modifies +modify +modifying +modish +modishly +modishness +mods +modular +modulate +modulated +modulates +modulating +modulation +modulations +modulator +modulators +module +modules +modulus +mogul +moguls +mohair +moieties +moiety +moire +moires +moist +moisten +moistened +moistening +moistens +moister +moistest +moistly +moistness +moisture +moisturize +moisturized +moisturizer +moisturizers +moisturizes +moisturizing +molar +molars +molasses +mold +molded +molder +moldered +moldering +molders +moldier +moldiest +moldiness +molding +moldings +molds +moldy +mole +molecular +molecule +molecules +molehill +molehills +moles +moleskin +molest +molestation +molested +molester +molesters +molesting +molests +moll +mollification +mollified +mollifies +mollify +mollifying +molls +mollusc +molluscs +mollusk +mollusks +mollycoddle +mollycoddled +mollycoddles +mollycoddling +molt +molted +molten +molting +molts +molybdenum +mom +moment +momentarily +momentary +momentous +momentousness +moments +momentum +mommies +mommy +moms +monarch +monarchic +monarchical +monarchies +monarchism +monarchist +monarchists +monarchs +monarchy +monasteries +monastery +monastic +monasticism +monastics +monaural +monetarily +monetarism +monetary +monetize +monetized +monetizes +monetizing +money +moneybag +moneybags +moneyed +moneymaker +moneymakers +moneymaking +monger +mongered +mongering +mongers +mongolism +mongoose +mongooses +mongrel +mongrels +monies +moniker +monikers +monitor +monitored +monitoring +monitors +monk +monkey +monkeyed +monkeying +monkeys +monkeyshine +monkeyshines +monks +mono +monochromatic +monochrome +monochromes +monocle +monocles +monocotyledon +monocotyledons +monogamous +monogamy +monogram +monogrammed +monogramming +monograms +monograph +monographs +monolingual +monolinguals +monolith +monolithic +monoliths +monologue +monologues +monomania +monomaniac +monomaniacs +mononucleosis +monophonic +monopolies +monopolist +monopolistic +monopolists +monopolization +monopolize +monopolized +monopolizes +monopolizing +monopoly +monorail +monorails +monosyllabic +monosyllable +monosyllables +monotheism +monotheist +monotheistic +monotheists +monotone +monotones +monotonic +monotonically +monotonous +monotonously +monotony +monoxide +monoxides +monsieur +monsignor +monsignors +monsoon +monsoons +monster +monsters +monstrance +monstrances +monstrosities +monstrosity +monstrous +monstrously +montage +montages +month +monthlies +monthly +months +monument +monumental +monumentally +monuments +moo +mooch +mooched +moocher +moochers +mooches +mooching +mood +moodier +moodiest +moodily +moodiness +moods +moody +mooed +mooing +moon +moonbeam +moonbeams +mooned +mooning +moonlight +moonlighted +moonlighter +moonlighters +moonlighting +moonlights +moonlit +moons +moonscape +moonscapes +moonshine +moonshines +moonshot +moonshots +moonstone +moonstones +moonstruck +moor +moored +mooring +moorings +moorland +moors +moos +moose +moot +mooted +mooting +moots +mop +mope +moped +mopeds +mopes +moping +mopped +moppet +moppets +mopping +mops +moraine +moraines +moral +morale +moralist +moralistic +moralists +moralities +morality +moralize +moralized +moralizes +moralizing +morally +morals +morass +morasses +moratorium +moratoriums +moray +morays +morbid +morbidity +morbidly +mordant +mordants +more +moreover +mores +morgue +morgues +moribund +morn +morning +mornings +morns +morocco +moron +moronic +morons +morose +morosely +moroseness +morpheme +morphemes +morphine +morphological +morphology +morrow +morrows +morsel +morsels +mortal +mortality +mortally +mortals +mortar +mortarboard +mortarboards +mortared +mortaring +mortars +mortgage +mortgaged +mortgagee +mortgagees +mortgages +mortgaging +mortgagor +mortgagors +mortician +morticians +mortification +mortified +mortifies +mortify +mortifying +mortise +mortised +mortises +mortising +mortuaries +mortuary +mosaic +mosaics +mosey +moseyed +moseying +moseys +mosque +mosques +mosquito +mosquitoes +moss +mosses +mossier +mossiest +mossy +most +mostly +mote +motel +motels +motes +moth +mothball +mothballed +mothballing +mothballs +mother +motherboard +motherboards +mothered +motherfucker +motherfuckers +motherfucking +motherhood +mothering +motherland +motherlands +motherless +motherliness +motherly +mothers +moths +motif +motifs +motile +motiles +motility +motion +motioned +motioning +motionless +motions +motivate +motivated +motivates +motivating +motivation +motivational +motivations +motivator +motivators +motive +motives +motley +motleys +motlier +motliest +motocross +motocrosses +motor +motorbike +motorbiked +motorbikes +motorbiking +motorboat +motorboats +motorcade +motorcades +motorcar +motorcars +motorcycle +motorcycled +motorcycles +motorcycling +motorcyclist +motorcyclists +motored +motoring +motorist +motorists +motorize +motorized +motorizes +motorizing +motorman +motormen +motormouth +motormouths +motors +motorway +motorways +mottle +mottled +mottles +mottling +motto +mottoes +mound +mounded +mounding +mounds +mount +mountain +mountaineer +mountaineered +mountaineering +mountaineers +mountainous +mountains +mountainside +mountainsides +mountaintop +mountaintops +mountebank +mountebanks +mounted +mounting +mountings +mounts +mourn +mourned +mourner +mourners +mournful +mournfully +mournfulness +mourning +mourns +mouse +moused +mouser +mousers +mouses +mousetrap +mousetrapped +mousetrapping +mousetraps +mousey +mousier +mousiest +mousiness +mousing +mousse +moussed +mousses +moussing +mousy +mouth +mouthed +mouthful +mouthfuls +mouthing +mouthpiece +mouthpieces +mouths +mouthwash +mouthwashes +mouthwatering +movable +movables +move +moveable +moveables +moved +movement +movements +mover +movers +moves +movie +movies +moving +movingly +mow +mowed +mower +mowers +mowing +mows +mozzarella +ms +mu +much +mucilage +muck +mucked +muckier +muckiest +mucking +muckrake +muckraked +muckraker +muckrakers +muckrakes +muckraking +mucks +mucky +mucous +mucus +mud +muddied +muddier +muddies +muddiest +muddiness +muddle +muddled +muddles +muddling +muddy +muddying +mudguard +mudguards +mudslide +mudslides +mudslinger +mudslingers +mudslinging +muesli +muezzin +muezzins +muff +muffed +muffin +muffing +muffins +muffle +muffled +muffler +mufflers +muffles +muffling +muffs +mufti +muftis +mug +mugged +mugger +muggers +muggier +muggiest +mugginess +mugging +muggings +muggle +muggles +muggy +mugs +mukluk +mukluks +mulatto +mulattoes +mulberries +mulberry +mulch +mulched +mulches +mulching +mule +mules +muleteer +muleteers +mulish +mulishly +mulishness +mull +mullah +mullahs +mulled +mullet +mullets +mulligatawny +mulling +mullion +mullions +mulls +multi +multicolored +multicultural +multiculturalism +multidimensional +multifaceted +multifarious +multifariousness +multilateral +multilingual +multimedia +multimillionaire +multimillionaires +multinational +multinationals +multiplayer +multiple +multiples +multiplex +multiplexed +multiplexer +multiplexers +multiplexes +multiplexing +multiplicand +multiplicands +multiplication +multiplications +multiplicative +multiplicities +multiplicity +multiplied +multiplier +multipliers +multiplies +multiply +multiplying +multiprocessing +multipurpose +multiracial +multitasking +multitude +multitudes +multitudinous +multivariate +multiverse +multiverses +multivitamin +multivitamins +mum +mumble +mumbled +mumbler +mumblers +mumbles +mumbling +mummer +mummers +mummery +mummies +mummification +mummified +mummifies +mummify +mummifying +mummy +mumps +munch +munched +munches +munchies +munching +mundane +mundanely +municipal +municipalities +municipality +municipally +municipals +munificence +munificent +munition +munitions +mural +muralist +muralists +murals +murder +murdered +murderer +murderers +murderess +murderesses +murdering +murderous +murderously +murders +murk +murkier +murkiest +murkily +murkiness +murks +murky +murmur +murmured +murmuring +murmurs +muscat +muscatel +muscatels +muscle +muscled +muscles +muscling +muscular +muscularity +musculature +muse +mused +muses +museum +museums +mush +mushed +mushes +mushier +mushiest +mushiness +mushing +mushroom +mushroomed +mushrooming +mushrooms +mushy +music +musical +musicale +musicales +musically +musicals +musician +musicians +musicianship +musicologist +musicologists +musicology +musing +musings +musk +muskellunge +muskellunges +musket +musketeer +musketeers +musketry +muskets +muskier +muskiest +muskiness +muskmelon +muskmelons +muskrat +muskrats +musky +muslin +muss +mussed +mussel +mussels +musses +mussier +mussiest +mussing +mussy +must +mustache +mustaches +mustang +mustangs +mustard +muster +mustered +mustering +musters +mustier +mustiest +mustiness +musts +musty +mutability +mutable +mutant +mutants +mutate +mutated +mutates +mutating +mutation +mutations +mute +muted +mutely +muteness +muter +mutes +mutest +mutilate +mutilated +mutilates +mutilating +mutilation +mutilations +mutineer +mutineers +muting +mutinied +mutinies +mutinous +mutinously +mutiny +mutinying +mutt +mutter +muttered +muttering +mutters +mutton +mutts +mutual +mutuality +mutually +muumuu +muumuus +muzzle +muzzled +muzzles +muzzling +my +myna +mynah +mynahes +mynas +myopia +myopic +myriad +myriads +myrrh +myrtle +myrtles +myself +mysteries +mysterious +mysteriously +mysteriousness +mystery +mystic +mystical +mystically +mysticism +mystics +mystification +mystified +mystifies +mystify +mystifying +mystique +myth +mythic +mythical +mythological +mythologies +mythologist +mythologists +mythology +myths +métier +métiers +mêlée +mêlées +n +nab +nabbed +nabbing +nabob +nabobs +nabs +nacho +nachos +nacre +nadir +nadirs +nag +nagged +nagging +nags +naiad +naiads +nail +nailbrush +nailbrushes +nailed +nailing +nails +naive +naively +naiver +naivest +naivety +naiveté +naked +nakedly +nakedness +name +named +nameless +namely +names +namesake +namesakes +naming +nannies +nanny +nanosecond +nanoseconds +nanotechnology +nap +napalm +napalmed +napalming +napalms +nape +napes +naphtha +naphthalene +napkin +napkins +napped +nappier +nappies +nappiest +napping +nappy +naps +narc +narcissism +narcissist +narcissistic +narcissists +narcissus +narcosis +narcotic +narcotics +narcs +nark +narrate +narrated +narrates +narrating +narration +narrations +narrative +narratives +narrator +narrators +narrow +narrowed +narrower +narrowest +narrowing +narrowly +narrowness +narrows +narwhal +narwhals +nary +nasal +nasalize +nasalized +nasalizes +nasalizing +nasally +nasals +nascent +nastier +nastiest +nastily +nastiness +nasturtium +nasturtiums +nasty +natal +nation +national +nationalism +nationalist +nationalistic +nationalists +nationalities +nationality +nationalization +nationalizations +nationalize +nationalized +nationalizes +nationalizing +nationally +nationals +nations +nationwide +native +natives +nativities +nativity +nattier +nattiest +nattily +natty +natural +naturalism +naturalist +naturalistic +naturalists +naturalization +naturalize +naturalized +naturalizes +naturalizing +naturally +naturalness +naturals +nature +natures +naught +naughtier +naughtiest +naughtily +naughtiness +naughts +naughty +nausea +nauseate +nauseated +nauseates +nauseating +nauseatingly +nauseous +nautical +nautically +nautilus +nautiluses +naval +nave +navel +navels +naves +navies +navigability +navigable +navigate +navigated +navigates +navigating +navigation +navigational +navigator +navigators +navy +nay +nays +naysayer +naysayers +near +nearby +neared +nearer +nearest +nearing +nearly +nearness +nears +nearsighted +nearsightedness +neat +neater +neatest +neath +neatly +neatness +nebula +nebulae +nebular +nebulous +necessaries +necessarily +necessary +necessitate +necessitated +necessitates +necessitating +necessities +necessity +neck +necked +neckerchief +neckerchiefs +necking +necklace +necklaces +neckline +necklines +necks +necktie +neckties +necromancer +necromancers +necromancy +necrophilia +necrosis +nectar +nectarine +nectarines +need +needed +needful +needier +neediest +neediness +needing +needle +needled +needlepoint +needles +needless +needlessly +needlework +needling +needs +needy +nefarious +nefariously +nefariousness +negate +negated +negates +negating +negation +negations +negative +negatived +negatively +negatives +negativing +negativity +neglect +neglected +neglectful +neglectfully +neglecting +neglects +negligee +negligees +negligence +negligent +negligently +negligible +negligibly +negotiable +negotiate +negotiated +negotiates +negotiating +negotiation +negotiations +negotiator +negotiators +neigh +neighbor +neighbored +neighborhood +neighborhoods +neighboring +neighborliness +neighborly +neighbors +neighed +neighing +neighs +neither +nematode +nematodes +nemeses +nemesis +neoclassic +neoclassical +neoclassicism +neocolonialism +neocon +neocons +neoconservative +neoconservatives +neodymium +neologism +neologisms +neon +neonatal +neonate +neonates +neophyte +neophytes +neoprene +nephew +nephews +nephritis +nepotism +neptunium +nerd +nerdier +nerdiest +nerds +nerdy +nerve +nerved +nerveless +nervelessly +nerves +nervier +nerviest +nerving +nervous +nervously +nervousness +nervy +nest +nested +nesting +nestle +nestled +nestles +nestling +nestlings +nests +net +netbook +netbooks +nether +nethermost +nets +netted +netting +nettle +nettled +nettles +nettlesome +nettling +network +networked +networking +networks +neural +neuralgia +neuralgic +neuritis +neurological +neurologist +neurologists +neurology +neuron +neurons +neuroses +neurosis +neurosurgery +neurotic +neurotically +neurotics +neurotransmitter +neurotransmitters +neuter +neutered +neutering +neuters +neutral +neutrality +neutralization +neutralize +neutralized +neutralizer +neutralizers +neutralizes +neutralizing +neutrally +neutrals +neutrino +neutrinos +neutron +neutrons +never +nevermore +nevertheless +new +newbie +newbies +newborn +newborns +newcomer +newcomers +newel +newels +newer +newest +newfangled +newly +newlywed +newlyweds +newness +news +newsagents +newsboy +newsboys +newscast +newscaster +newscasters +newscasts +newsflash +newsier +newsiest +newsletter +newsletters +newsman +newsmen +newspaper +newspaperman +newspapermen +newspapers +newspaperwoman +newspaperwomen +newsprint +newsreel +newsreels +newsstand +newsstands +newsworthy +newsy +newt +newton +newtons +newts +next +nexus +nexuses +niacin +nib +nibble +nibbled +nibbler +nibblers +nibbles +nibbling +nibs +nice +nicely +niceness +nicer +nicest +niceties +nicety +niche +niches +nick +nicked +nickel +nickelodeon +nickelodeons +nickels +nicking +nickname +nicknamed +nicknames +nicknaming +nicks +nicotine +niece +nieces +niftier +niftiest +nifty +nigga +niggard +niggardliness +niggardly +niggards +niggas +niggaz +nigger +niggers +niggle +niggled +niggles +niggling +nigh +nigher +nighest +night +nightcap +nightcaps +nightclothes +nightclub +nightclubbed +nightclubbing +nightclubs +nightfall +nightgown +nightgowns +nighthawk +nighthawks +nightie +nighties +nightingale +nightingales +nightlife +nightly +nightmare +nightmares +nightmarish +nights +nightshade +nightshades +nightshirt +nightshirts +nightstick +nightsticks +nighttime +nighty +nihilism +nihilist +nihilistic +nihilists +nil +nimbi +nimble +nimbleness +nimbler +nimblest +nimbly +nimbus +nincompoop +nincompoops +nine +ninepin +ninepins +nines +nineteen +nineteens +nineteenth +nineteenths +nineties +ninetieth +ninetieths +ninety +ninja +ninjas +ninnies +ninny +ninth +ninths +nip +nipped +nipper +nippers +nippier +nippiest +nipping +nipple +nipples +nippy +nips +nirvana +nit +nite +niter +nites +nitpick +nitpicked +nitpicker +nitpickers +nitpicking +nitpicks +nitrate +nitrated +nitrates +nitrating +nitrogen +nitrogenous +nitroglycerin +nitroglycerine +nits +nitwit +nitwits +nix +nixed +nixes +nixing +no +nobility +noble +nobleman +noblemen +nobleness +nobler +nobles +noblest +noblewoman +noblewomen +nobly +nobodies +nobody +nocturnal +nocturnally +nocturne +nocturnes +nod +nodal +nodded +nodding +noddy +node +nodes +nods +nodular +nodule +nodules +noel +noels +noes +noggin +noggins +noise +noised +noiseless +noiselessly +noiselessness +noisemaker +noisemakers +noises +noisier +noisiest +noisily +noisiness +noising +noisome +noisy +nomad +nomadic +nomads +nomenclature +nomenclatures +nominal +nominally +nominate +nominated +nominates +nominating +nomination +nominations +nominative +nominatives +nominee +nominees +non +nonabrasive +nonabsorbent +nonabsorbents +nonagenarian +nonagenarians +nonalcoholic +nonaligned +nonbeliever +nonbelievers +nonbreakable +nonce +nonchalance +nonchalant +nonchalantly +noncom +noncombatant +noncombatants +noncommercial +noncommercials +noncommittal +noncommittally +noncompetitive +noncompliance +noncoms +nonconductor +nonconductors +nonconformist +nonconformists +nonconformity +noncontagious +noncooperation +nondairy +nondeductible +nondenominational +nondescript +nondrinker +nondrinkers +none +nonempty +nonentities +nonentity +nonessential +nonesuch +nonesuches +nonetheless +nonevent +nonevents +nonexempt +nonexistence +nonexistent +nonfat +nonfatal +nonfiction +nonflammable +nongovernmental +nonhazardous +nonhuman +nonindustrial +noninterference +nonintervention +nonjudgmental +nonliving +nonmalignant +nonmember +nonmembers +nonnegotiable +nonobjective +nonpareil +nonpareils +nonpartisan +nonpartisans +nonpayment +nonpayments +nonphysical +nonplus +nonpluses +nonplussed +nonplussing +nonpoisonous +nonpolitical +nonpolluting +nonprescription +nonproductive +nonprofessional +nonprofessionals +nonprofit +nonprofits +nonproliferation +nonrefillable +nonrefundable +nonrenewable +nonrepresentational +nonresident +nonresidents +nonrestrictive +nonreturnable +nonreturnables +nonrigid +nonscheduled +nonseasonal +nonsectarian +nonsense +nonsensical +nonsensically +nonsexist +nonskid +nonsmoker +nonsmokers +nonsmoking +nonstandard +nonstick +nonstop +nonsupport +nontaxable +nontechnical +nontoxic +nontransferable +nontrivial +nonunion +nonuser +nonusers +nonverbal +nonviolence +nonviolent +nonvoting +nonwhite +nonwhites +nonzero +noodle +noodled +noodles +noodling +nook +nooks +noon +noonday +noontime +noose +nooses +nope +nor +norm +normal +normalcy +normality +normalization +normalize +normalized +normalizes +normalizing +normally +normative +norms +north +northbound +northeast +northeaster +northeasterly +northeastern +northeasters +northeastward +northerlies +northerly +northern +northerner +northerners +northernmost +northward +northwards +northwest +northwesterly +northwestern +northwestward +nose +nosebleed +nosebleeds +nosed +nosedive +nosedived +nosedives +nosediving +nosegay +nosegays +noses +nosh +noshed +noshes +noshing +nosier +nosiest +nosiness +nosing +nostalgia +nostalgic +nostalgically +nostril +nostrils +nostrum +nostrums +nosy +not +notable +notables +notably +notaries +notarize +notarized +notarizes +notarizing +notary +notation +notations +notch +notched +notches +notching +note +notebook +notebooks +noted +notepad +notepaper +notes +noteworthy +nothing +nothingness +nothings +notice +noticeable +noticeably +noticeboard +noticeboards +noticed +notices +noticing +notification +notifications +notified +notifies +notify +notifying +noting +notion +notional +notionally +notions +notoriety +notorious +notoriously +notwithstanding +nougat +nougats +nought +noughts +noun +nouns +nourish +nourished +nourishes +nourishing +nourishment +nous +nova +novae +novas +novel +novelette +novelettes +novelist +novelists +novella +novellas +novels +novelties +novelty +novice +novices +novitiate +novitiates +now +nowadays +noway +nowhere +nowise +noxious +nozzle +nozzles +nth +nu +nuance +nuanced +nuances +nub +nubile +nubs +nuclear +nuclei +nucleic +nucleus +nude +nuder +nudes +nudest +nudge +nudged +nudges +nudging +nudism +nudist +nudists +nudity +nugget +nuggets +nuisance +nuisances +nuke +nuked +nukes +nuking +null +nullification +nullified +nullifies +nullify +nullifying +nullity +nulls +numb +numbed +number +numbered +numbering +numberless +numbers +numbest +numbing +numbly +numbness +numbs +numbskull +numbskulls +numeracy +numeral +numerals +numerate +numerated +numerates +numerating +numeration +numerations +numerator +numerators +numeric +numerical +numerically +numerology +numerous +numismatic +numismatics +numismatist +numismatists +numskull +numskulls +nun +nuncio +nuncios +nunneries +nunnery +nuns +nuptial +nuptials +nurse +nursed +nursemaid +nursemaids +nurseries +nursery +nurseryman +nurserymen +nurses +nursing +nurture +nurtured +nurtures +nurturing +nut +nutcracker +nutcrackers +nuthatch +nuthatches +nutmeat +nutmeats +nutmeg +nutmegs +nutria +nutrias +nutrient +nutrients +nutriment +nutriments +nutrition +nutritional +nutritionally +nutritionist +nutritionists +nutritious +nutritive +nuts +nutshell +nutshells +nutted +nuttier +nuttiest +nuttiness +nutting +nutty +nuzzle +nuzzled +nuzzles +nuzzling +nylon +nylons +nymph +nymphomania +nymphomaniac +nymphomaniacs +nymphs +née +o +oaf +oafish +oafs +oak +oaken +oaks +oakum +oar +oared +oaring +oarlock +oarlocks +oars +oarsman +oarsmen +oases +oasis +oat +oaten +oath +oaths +oatmeal +oats +obduracy +obdurate +obdurately +obedience +obedient +obediently +obeisance +obeisances +obeisant +obelisk +obelisks +obese +obesity +obey +obeyed +obeying +obeys +obfuscate +obfuscated +obfuscates +obfuscating +obfuscation +obit +obits +obituaries +obituary +object +objected +objecting +objection +objectionable +objectionably +objections +objective +objectively +objectiveness +objectives +objectivity +objector +objectors +objects +oblate +oblation +oblations +obligate +obligated +obligates +obligating +obligation +obligations +obligatory +oblige +obliged +obliges +obliging +obligingly +oblique +obliquely +obliqueness +obliques +obliterate +obliterated +obliterates +obliterating +obliteration +oblivion +oblivious +obliviously +obliviousness +oblong +oblongs +obloquy +obnoxious +obnoxiously +oboe +oboes +oboist +oboists +obscene +obscenely +obscener +obscenest +obscenities +obscenity +obscure +obscured +obscurely +obscurer +obscures +obscurest +obscuring +obscurities +obscurity +obsequies +obsequious +obsequiously +obsequiousness +obsequy +observable +observably +observance +observances +observant +observantly +observation +observational +observations +observatories +observatory +observe +observed +observer +observers +observes +observing +obsess +obsessed +obsesses +obsessing +obsession +obsessions +obsessive +obsessively +obsessives +obsidian +obsolescence +obsolescent +obsolete +obsoleted +obsoletes +obsoleting +obstacle +obstacles +obstetric +obstetrical +obstetrician +obstetricians +obstetrics +obstinacy +obstinate +obstinately +obstreperous +obstruct +obstructed +obstructing +obstruction +obstructionist +obstructionists +obstructions +obstructive +obstructively +obstructiveness +obstructs +obtain +obtainable +obtained +obtaining +obtains +obtrude +obtruded +obtrudes +obtruding +obtrusive +obtrusively +obtrusiveness +obtuse +obtusely +obtuseness +obtuser +obtusest +obverse +obverses +obviate +obviated +obviates +obviating +obvious +obviously +obviousness +ocarina +ocarinas +occasion +occasional +occasionally +occasioned +occasioning +occasions +occidental +occidentals +occlude +occluded +occludes +occluding +occlusion +occlusions +occlusive +occult +occupancy +occupant +occupants +occupation +occupational +occupations +occupied +occupies +occupy +occupying +occur +occurred +occurrence +occurrences +occurring +occurs +ocean +oceangoing +oceanic +oceanographer +oceanographers +oceanographic +oceanography +oceans +ocelot +ocelots +ocher +octagon +octagonal +octagons +octal +octane +octave +octaves +octet +octets +octogenarian +octogenarians +octopus +octopuses +ocular +oculars +oculist +oculists +odd +oddball +oddballs +odder +oddest +oddities +oddity +oddly +oddness +odds +ode +odes +odious +odiously +odium +odometer +odometers +odor +odoriferous +odorless +odorous +odors +odyssey +odysseys +of +off +offal +offbeat +offbeats +offed +offend +offended +offender +offenders +offending +offends +offense +offenses +offensive +offensively +offensiveness +offensives +offer +offered +offering +offerings +offers +offertories +offertory +offhand +offhandedly +office +officeholder +officeholders +officer +officers +offices +official +officialdom +officially +officials +officiate +officiated +officiates +officiating +officious +officiously +officiousness +offing +offings +offload +offloaded +offloading +offloads +offs +offset +offsets +offsetting +offshoot +offshoots +offshore +offshoring +offside +offspring +offstage +offstages +oft +often +oftener +oftenest +oftentimes +ogle +ogled +ogles +ogling +ogre +ogres +oh +ohm +ohms +oho +ohs +oil +oilcloth +oilcloths +oiled +oilfield +oilfields +oilier +oiliest +oiliness +oiling +oils +oilskin +oily +oink +oinked +oinking +oinks +ointment +ointments +okay +okayed +okaying +okays +okra +okras +old +olden +older +oldest +oldie +oldies +oleaginous +oleander +oleanders +oleo +oleomargarine +olfactories +olfactory +oligarch +oligarchic +oligarchies +oligarchs +oligarchy +olive +olives +ombudsman +ombudsmen +omega +omegas +omelet +omelets +omelette +omelettes +omen +omens +ominous +ominously +omission +omissions +omit +omits +omitted +omitting +omnibus +omnibuses +omnipotence +omnipotent +omnipresence +omnipresent +omniscience +omniscient +omnivore +omnivores +omnivorous +on +once +oncology +oncoming +one +oneness +onerous +ones +oneself +onetime +ongoing +onion +onions +onionskin +online +onlooker +onlookers +only +onomatopoeia +onomatopoeic +onrush +onrushes +onrushing +onset +onsets +onshore +onslaught +onslaughts +onto +onus +onuses +onward +onyx +onyxes +oodles +oops +ooze +oozed +oozes +oozing +opacity +opal +opalescence +opalescent +opals +opaque +opaqued +opaquely +opaqueness +opaquer +opaques +opaquest +opaquing +open +opened +opener +openers +openest +openhanded +opening +openings +openly +openness +opens +openwork +opera +operable +operand +operands +operas +operate +operated +operates +operatic +operating +operation +operational +operationally +operations +operative +operatives +operator +operators +operetta +operettas +ophthalmic +ophthalmologist +ophthalmologists +ophthalmology +opiate +opiates +opine +opined +opines +opining +opinion +opinionated +opinions +opium +opossum +opossums +opponent +opponents +opportune +opportunism +opportunist +opportunistic +opportunists +opportunities +opportunity +oppose +opposed +opposes +opposing +opposite +opposites +opposition +oppress +oppressed +oppresses +oppressing +oppression +oppressive +oppressively +oppressor +oppressors +opprobrious +opprobrium +opt +opted +optic +optical +optically +optician +opticians +optics +optima +optimal +optimism +optimist +optimistic +optimistically +optimists +optimization +optimizations +optimize +optimized +optimizer +optimizes +optimizing +optimum +optimums +opting +option +optional +optionally +optioned +optioning +options +optometrist +optometrists +optometry +opts +opulence +opulent +opus +opuses +or +oracle +oracles +oracular +oral +orally +orals +orange +orangeade +orangeades +oranges +orangutan +orangutans +orate +orated +orates +orating +oration +orations +orator +oratorical +oratories +oratorio +oratorios +orators +oratory +orb +orbit +orbital +orbitals +orbited +orbiting +orbits +orbs +orc +orchard +orchards +orchestra +orchestral +orchestras +orchestrate +orchestrated +orchestrates +orchestrating +orchestration +orchestrations +orchid +orchids +orcs +ordain +ordained +ordaining +ordains +ordeal +ordeals +order +ordered +ordering +orderings +orderlies +orderliness +orderly +orders +ordinal +ordinals +ordinance +ordinances +ordinaries +ordinarily +ordinariness +ordinary +ordination +ordinations +ordnance +ordure +ore +oregano +ores +organ +organdy +organelle +organelles +organic +organically +organics +organism +organisms +organist +organists +organization +organizational +organizations +organize +organized +organizer +organizers +organizes +organizing +organs +orgasm +orgasmic +orgasms +orgiastic +orgies +orgy +orient +oriental +orientals +orientate +orientated +orientates +orientating +orientation +orientations +oriented +orienting +orients +orifice +orifices +origami +origin +original +originality +originally +originals +originate +originated +originates +originating +origination +originator +originators +origins +oriole +orioles +ormolu +ornament +ornamental +ornamentation +ornamented +ornamenting +ornaments +ornate +ornately +ornateness +ornerier +orneriest +ornery +ornithologist +ornithologists +ornithology +orotund +orphan +orphanage +orphanages +orphaned +orphaning +orphans +orthodontia +orthodontic +orthodontics +orthodontist +orthodontists +orthodox +orthodoxies +orthodoxy +orthogonal +orthogonality +orthographic +orthographies +orthography +orthopedic +orthopedics +orthopedist +orthopedists +oscillate +oscillated +oscillates +oscillating +oscillation +oscillations +oscillator +oscillators +oscilloscope +oscilloscopes +osier +osiers +osmosis +osmotic +osprey +ospreys +ossification +ossified +ossifies +ossify +ossifying +ostensible +ostensibly +ostentation +ostentatious +ostentatiously +osteopath +osteopaths +osteopathy +osteoporosis +ostracism +ostracize +ostracized +ostracizes +ostracizing +ostrich +ostriches +other +others +otherwise +otherworldly +otiose +otter +otters +ottoman +ottomans +ouch +ought +ounce +ounces +our +ours +ourselves +oust +ousted +ouster +ousters +ousting +ousts +out +outage +outages +outback +outbacks +outbalance +outbalanced +outbalances +outbalancing +outbid +outbidding +outbids +outbound +outbreak +outbreaks +outbuilding +outbuildings +outburst +outbursts +outcast +outcasts +outclass +outclassed +outclasses +outclassing +outcome +outcomes +outcries +outcrop +outcropped +outcropping +outcroppings +outcrops +outcry +outdated +outdid +outdistance +outdistanced +outdistances +outdistancing +outdo +outdoes +outdoing +outdone +outdoor +outdoors +outed +outer +outermost +outfield +outfielder +outfielders +outfields +outfit +outfits +outfitted +outfitter +outfitters +outfitting +outflank +outflanked +outflanking +outflanks +outfox +outfoxed +outfoxes +outfoxing +outgo +outgoes +outgoing +outgrew +outgrow +outgrowing +outgrown +outgrows +outgrowth +outgrowths +outhouse +outhouses +outing +outings +outlaid +outlandish +outlandishly +outlast +outlasted +outlasting +outlasts +outlaw +outlawed +outlawing +outlaws +outlay +outlaying +outlays +outlet +outlets +outline +outlined +outlines +outlining +outlive +outlived +outlives +outliving +outlook +outlooks +outlying +outmaneuver +outmaneuvered +outmaneuvering +outmaneuvers +outmoded +outnumber +outnumbered +outnumbering +outnumbers +outpatient +outpatients +outperform +outperformed +outperforming +outperforms +outplacement +outplay +outplayed +outplaying +outplays +outpost +outposts +outpouring +outpourings +output +outputs +outputted +outputting +outrage +outraged +outrageous +outrageously +outrages +outraging +outran +outrank +outranked +outranking +outranks +outreach +outreached +outreaches +outreaching +outrider +outriders +outrigger +outriggers +outright +outrun +outrunning +outruns +outré +outs +outsell +outselling +outsells +outset +outsets +outshine +outshines +outshining +outshone +outside +outsider +outsiders +outsides +outsize +outsizes +outskirt +outskirts +outsmart +outsmarted +outsmarting +outsmarts +outsold +outsource +outsourced +outsources +outsourcing +outspoken +outspokenly +outspokenness +outspread +outspreading +outspreads +outstanding +outstandingly +outstation +outstations +outstay +outstayed +outstaying +outstays +outstretch +outstretched +outstretches +outstretching +outstrip +outstripped +outstripping +outstrips +outtake +outtakes +outvote +outvoted +outvotes +outvoting +outward +outwardly +outwards +outwear +outwearing +outwears +outweigh +outweighed +outweighing +outweighs +outwit +outwits +outwitted +outwitting +outwore +outworn +ova +oval +ovals +ovarian +ovaries +ovary +ovation +ovations +oven +ovens +over +overabundance +overabundant +overachieve +overachieved +overachiever +overachievers +overachieves +overachieving +overact +overacted +overacting +overactive +overacts +overage +overages +overall +overalls +overambitious +overanxious +overate +overawe +overawed +overawes +overawing +overbalance +overbalanced +overbalances +overbalancing +overbear +overbearing +overbears +overbite +overbites +overblown +overboard +overbook +overbooked +overbooking +overbooks +overbore +overborne +overburden +overburdened +overburdening +overburdens +overcame +overcast +overcasting +overcasts +overcautious +overcharge +overcharged +overcharges +overcharging +overcoat +overcoats +overcome +overcomes +overcoming +overcompensate +overcompensated +overcompensates +overcompensating +overcompensation +overconfident +overcook +overcooked +overcooking +overcooks +overcrowd +overcrowded +overcrowding +overcrowds +overdid +overdo +overdoes +overdoing +overdone +overdose +overdosed +overdoses +overdosing +overdraft +overdrafts +overdraw +overdrawing +overdrawn +overdraws +overdress +overdressed +overdresses +overdressing +overdrew +overdrive +overdue +overeager +overeat +overeaten +overeating +overeats +overemphasize +overemphasized +overemphasizes +overemphasizing +overenthusiastic +overestimate +overestimated +overestimates +overestimating +overexpose +overexposed +overexposes +overexposing +overexposure +overextend +overextended +overextending +overextends +overflow +overflowed +overflowing +overflows +overfull +overgenerous +overgrew +overgrow +overgrowing +overgrown +overgrows +overgrowth +overhand +overhands +overhang +overhanging +overhangs +overhaul +overhauled +overhauling +overhauls +overhead +overheads +overhear +overheard +overhearing +overhears +overheat +overheated +overheating +overheats +overhung +overindulge +overindulged +overindulgence +overindulges +overindulging +overjoy +overjoyed +overjoying +overjoys +overkill +overlaid +overlain +overland +overlap +overlapped +overlapping +overlaps +overlay +overlaying +overlays +overlie +overlies +overload +overloaded +overloading +overloads +overlong +overlook +overlooked +overlooking +overlooks +overlord +overlords +overly +overlying +overmuch +overmuches +overnight +overnights +overpaid +overpass +overpasses +overpay +overpaying +overpays +overplay +overplayed +overplaying +overplays +overpopulate +overpopulated +overpopulates +overpopulating +overpopulation +overpower +overpowered +overpowering +overpowers +overprice +overpriced +overprices +overpricing +overprint +overprinted +overprinting +overprints +overproduce +overproduced +overproduces +overproducing +overproduction +overprotective +overqualified +overran +overrate +overrated +overrates +overrating +overreach +overreached +overreaches +overreaching +overreact +overreacted +overreacting +overreaction +overreactions +overreacts +overridden +override +overrides +overriding +overripe +overrode +overrule +overruled +overrules +overruling +overrun +overrunning +overruns +overs +oversampling +oversaw +overseas +oversee +overseeing +overseen +overseer +overseers +oversees +oversell +overselling +oversells +oversensitive +oversexed +overshadow +overshadowed +overshadowing +overshadows +overshare +overshared +overshares +oversharing +overshoe +overshoes +overshoot +overshooting +overshoots +overshot +oversight +oversights +oversimplification +oversimplifications +oversimplified +oversimplifies +oversimplify +oversimplifying +oversize +oversized +oversleep +oversleeping +oversleeps +overslept +oversold +overspecialize +overspecialized +overspecializes +overspecializing +overspend +overspending +overspends +overspent +overspread +overspreading +overspreads +overstate +overstated +overstatement +overstatements +overstates +overstating +overstay +overstayed +overstaying +overstays +overstep +overstepped +overstepping +oversteps +overstock +overstocked +overstocking +overstocks +overstuffed +oversupplied +oversupplies +oversupply +oversupplying +overt +overtake +overtaken +overtakes +overtaking +overtax +overtaxed +overtaxes +overtaxing +overthink +overthinking +overthinks +overthought +overthrew +overthrow +overthrowing +overthrown +overthrows +overtime +overtimes +overtly +overtone +overtones +overtook +overture +overtures +overturn +overturned +overturning +overturns +overuse +overused +overuses +overusing +overview +overviews +overweening +overweight +overwhelm +overwhelmed +overwhelming +overwhelmingly +overwhelms +overwork +overworked +overworking +overworks +overwrite +overwrites +overwriting +overwritten +overwrought +overzealous +oviduct +oviducts +oviparous +ovoid +ovoids +ovulate +ovulated +ovulates +ovulating +ovulation +ovule +ovules +ovum +ow +owe +owed +owes +owing +owl +owlet +owlets +owlish +owls +own +owned +owner +owners +ownership +owning +owns +ox +oxbow +oxbows +oxen +oxford +oxfords +oxidation +oxide +oxides +oxidize +oxidized +oxidizer +oxidizers +oxidizes +oxidizing +oxyacetylene +oxygen +oxygenate +oxygenated +oxygenates +oxygenating +oxygenation +oxymora +oxymoron +oyster +oysters +ozone +p +pH +pa +pace +paced +pacemaker +pacemakers +paces +pacesetter +pacesetters +pachyderm +pachyderms +pacific +pacifically +pacification +pacified +pacifier +pacifiers +pacifies +pacifism +pacifist +pacifists +pacify +pacifying +pacing +pack +package +packaged +packages +packaging +packed +packer +packers +packet +packets +packing +packs +pact +pacts +pad +padded +paddies +padding +paddle +paddled +paddles +paddling +paddock +paddocked +paddocking +paddocks +paddy +padlock +padlocked +padlocking +padlocks +padre +padres +pads +paean +paeans +pagan +paganism +pagans +page +pageant +pageantry +pageants +paged +pager +pagers +pages +paginate +paginated +paginates +paginating +pagination +paging +pagoda +pagodas +paid +pail +pailful +pailfuls +pails +pain +pained +painful +painfuller +painfullest +painfully +paining +painkiller +painkillers +painless +painlessly +pains +painstaking +painstakingly +paint +paintbrush +paintbrushes +painted +painter +painters +painting +paintings +paints +paintwork +pair +paired +pairing +pairs +pairwise +paisley +paisleys +pajamas +pal +palace +palaces +palatable +palatal +palatals +palate +palates +palatial +palaver +palavered +palavering +palavers +palazzi +palazzo +pale +paled +paleface +palefaces +paleness +paleontologist +paleontologists +paleontology +paler +pales +palest +palette +palettes +palimony +palimpsest +palimpsests +palindrome +palindromes +palindromic +paling +palings +palisade +palisades +pall +palladium +pallbearer +pallbearers +palled +pallet +pallets +palliate +palliated +palliates +palliating +palliation +palliative +palliatives +pallid +palling +pallor +palls +palm +palmed +palmetto +palmettos +palmier +palmiest +palming +palmist +palmistry +palmists +palms +palmy +palomino +palominos +palpable +palpably +palpate +palpated +palpates +palpating +palpation +palpitate +palpitated +palpitates +palpitating +palpitation +palpitations +pals +palsied +palsies +palsy +palsying +paltrier +paltriest +paltriness +paltry +pampas +pamper +pampered +pampering +pampers +pamphlet +pamphleteer +pamphleteers +pamphlets +pan +panacea +panaceas +panache +pancake +pancaked +pancakes +pancaking +panchromatic +pancreas +pancreases +pancreatic +panda +pandas +pandemic +pandemics +pandemonium +pander +pandered +panderer +panderers +pandering +panders +pane +panegyric +panegyrics +panel +paneled +paneling +panelings +panelist +panelists +panels +panes +pang +pangs +panhandle +panhandled +panhandler +panhandlers +panhandles +panhandling +panic +panicked +panicking +panicky +panics +panned +pannier +panniers +panning +panoplies +panoply +panorama +panoramas +panoramic +pans +pansies +pansy +pant +pantaloons +panted +pantheism +pantheist +pantheistic +pantheists +pantheon +pantheons +panther +panthers +pantie +panties +panting +pantomime +pantomimed +pantomimes +pantomiming +pantries +pantry +pants +pantsuit +pantsuits +panty +pantyhose +pap +papa +papacies +papacy +papal +papas +papaw +papaws +papaya +papayas +paper +paperback +paperbacks +paperboy +paperboys +papered +papergirl +papergirls +paperhanger +paperhangers +papering +papers +paperweight +paperweights +paperwork +papery +papilla +papillae +papoose +papooses +paprika +paps +papyri +papyrus +par +parable +parables +parabola +parabolas +parabolic +parachute +parachuted +parachutes +parachuting +parachutist +parachutists +parade +paraded +parades +paradigm +paradigmatic +paradigms +parading +paradise +paradises +paradox +paradoxes +paradoxical +paradoxically +paraffin +paragliding +paragon +paragons +paragraph +paragraphed +paragraphing +paragraphs +parakeet +parakeets +paralegal +paralegals +parallax +parallaxes +parallel +paralleled +paralleling +parallelism +parallelisms +parallelogram +parallelograms +parallels +paralyses +paralysis +paralytic +paralytics +paralyze +paralyzed +paralyzes +paralyzing +paramecia +paramecium +paramedic +paramedical +paramedicals +paramedics +parameter +parameters +paramilitaries +paramilitary +paramount +paramour +paramours +paranoia +paranoid +paranoids +paranormal +parapet +parapets +paraphernalia +paraphrase +paraphrased +paraphrases +paraphrasing +paraplegia +paraplegic +paraplegics +paraprofessional +paraprofessionals +parapsychology +parasailing +parasite +parasites +parasitic +parasol +parasols +paratrooper +paratroopers +paratroops +parboil +parboiled +parboiling +parboils +parcel +parceled +parceling +parcels +parch +parched +parches +parching +parchment +parchments +pardon +pardonable +pardoned +pardoning +pardons +pare +pared +parent +parentage +parental +parented +parentheses +parenthesis +parenthesize +parenthesized +parenthesizes +parenthesizing +parenthetic +parenthetical +parenthetically +parenthood +parenting +parents +pares +parfait +parfaits +pariah +pariahs +paring +parings +parish +parishes +parishioner +parishioners +parity +park +parka +parkas +parked +parking +parkour +parks +parkway +parkways +parlance +parlay +parlayed +parlaying +parlays +parley +parleyed +parleying +parleys +parliament +parliamentarian +parliamentarians +parliamentary +parliaments +parlor +parlors +parochial +parochialism +parodied +parodies +parody +parodying +parole +paroled +parolee +parolees +paroles +paroling +paroxysm +paroxysms +parquet +parqueted +parqueting +parquetry +parquets +parred +parricide +parricides +parried +parries +parring +parrot +parroted +parroting +parrots +parry +parrying +pars +parse +parsec +parsecs +parsed +parser +parses +parsimonious +parsimony +parsing +parsley +parsnip +parsnips +parson +parsonage +parsonages +parsons +part +partake +partaken +partaker +partakers +partakes +partaking +parted +parterre +parterres +parthenogenesis +partial +partiality +partially +partials +participant +participants +participate +participated +participates +participating +participation +participator +participators +participatory +participial +participle +participles +particle +particles +particular +particularities +particularity +particularization +particularize +particularized +particularizes +particularizing +particularly +particulars +particulate +particulates +partied +parties +parting +partings +partisan +partisans +partisanship +partition +partitioned +partitioning +partitions +partizan +partizans +partly +partner +partnered +partnering +partners +partnership +partnerships +partook +partridge +partridges +parts +parturition +partway +party +partying +parvenu +parvenus +pas +paschal +pasha +pashas +pass +passable +passably +passage +passages +passageway +passageways +passbook +passbooks +passed +passel +passels +passenger +passengers +passer +passerby +passersby +passes +passing +passion +passionate +passionately +passionless +passions +passive +passively +passives +passivity +passkey +passkeys +passport +passports +password +passwords +passé +past +pasta +pastas +paste +pasteboard +pasted +pastel +pastels +pastern +pasterns +pastes +pasteurization +pasteurize +pasteurized +pasteurizes +pasteurizing +pastiche +pastiches +pastier +pasties +pastiest +pastime +pastimes +pasting +pastor +pastoral +pastorals +pastorate +pastorates +pastors +pastrami +pastries +pastry +pasts +pasturage +pasture +pastured +pastures +pasturing +pasty +pat +patch +patched +patches +patchier +patchiest +patchiness +patching +patchwork +patchworks +patchy +pate +patella +patellae +patellas +patent +patented +patenting +patently +patents +paternal +paternalism +paternalistic +paternally +paternity +pates +path +pathetic +pathetically +pathogen +pathogenic +pathogens +pathological +pathologically +pathologist +pathologists +pathology +pathos +paths +pathway +pathways +patience +patient +patienter +patientest +patiently +patients +patina +patinas +patine +patio +patios +patois +patriarch +patriarchal +patriarchies +patriarchs +patriarchy +patrician +patricians +patricide +patricides +patrimonial +patrimonies +patrimony +patriot +patriotic +patriotically +patriotism +patriots +patrol +patrolled +patrolling +patrolman +patrolmen +patrols +patrolwoman +patrolwomen +patron +patronage +patronages +patronize +patronized +patronizes +patronizing +patronizingly +patrons +patronymic +patronymics +pats +patsies +patsy +patted +patter +pattered +pattering +pattern +patterned +patterning +patterns +patters +patties +patting +patty +paucity +paunch +paunches +paunchier +paunchiest +paunchy +pauper +pauperism +pauperize +pauperized +pauperizes +pauperizing +paupers +pause +paused +pauses +pausing +pave +paved +pavement +pavements +paves +pavilion +pavilions +paving +pavings +paw +pawed +pawing +pawl +pawls +pawn +pawnbroker +pawnbrokers +pawned +pawning +pawns +pawnshop +pawnshops +pawpaw +pawpaws +paws +pay +payable +paycheck +paychecks +payday +paydays +payed +payee +payees +payer +payers +paying +payload +payloads +paymaster +paymasters +payment +payments +payoff +payoffs +payroll +payrolls +pays +paywall +paywalls +pea +peace +peaceable +peaceably +peaceful +peacefully +peacefulness +peacekeeping +peacemaker +peacemakers +peaces +peacetime +peach +peaches +peacock +peacocks +peafowl +peafowls +peahen +peahens +peak +peaked +peaking +peaks +peal +pealed +pealing +peals +peanut +peanuts +pear +pearl +pearled +pearlier +pearliest +pearling +pearls +pearly +pears +peas +peasant +peasantry +peasants +peat +pebble +pebbled +pebbles +pebbling +pebbly +pecan +pecans +peccadillo +peccadilloes +peccaries +peccary +peck +pecked +pecking +pecks +pecs +pectin +pectoral +pectorals +peculiar +peculiarities +peculiarity +peculiarly +pecuniary +pedagogic +pedagogical +pedagogue +pedagogues +pedagogy +pedal +pedaled +pedaling +pedals +pedant +pedantic +pedantically +pedantry +pedants +peddle +peddled +peddler +peddlers +peddles +peddling +pederast +pederasts +pederasty +pedestal +pedestals +pedestrian +pedestrianize +pedestrianized +pedestrianizes +pedestrianizing +pedestrians +pediatric +pediatrician +pediatricians +pediatrics +pedicure +pedicured +pedicures +pedicuring +pedigree +pedigreed +pedigrees +pediment +pediments +pedometer +pedometers +pee +peed +peeing +peek +peekaboo +peeked +peeking +peeks +peel +peeled +peeling +peelings +peels +peep +peeped +peeper +peepers +peephole +peepholes +peeping +peeps +peer +peerage +peerages +peered +peering +peerless +peers +pees +peeve +peeved +peeves +peeving +peevish +peevishly +peevishness +peewee +peewees +peg +pegged +pegging +pegs +pejorative +pejoratives +pekoe +pelagic +pelican +pelicans +pellagra +pellet +pelleted +pelleting +pellets +pellucid +pelt +pelted +pelting +pelts +pelvic +pelvis +pelvises +pen +penal +penalize +penalized +penalizes +penalizing +penalties +penalty +penance +penances +pence +penchant +penchants +pencil +penciled +penciling +pencils +pendant +pendants +pended +pendent +pendents +pending +pends +pendulous +pendulum +pendulums +penetrable +penetrate +penetrated +penetrates +penetrating +penetration +penetrations +penetrative +penguin +penguins +penicillin +penile +peninsula +peninsular +peninsulas +penis +penises +penitence +penitent +penitential +penitentiaries +penitentiary +penitently +penitents +penknife +penknives +penlight +penlights +penmanship +pennant +pennants +penned +pennies +penniless +penning +pennon +pennons +penny +pennyweight +pennyweights +penologist +penologists +penology +pens +pension +pensioned +pensioner +pensioners +pensioning +pensions +pensive +pensively +pensiveness +pent +pentagon +pentagonal +pentagons +pentameter +pentameters +pentathlon +pentathlons +penthouse +penthouses +penultimate +penultimates +penurious +penury +peon +peonage +peonies +peons +peony +people +peopled +peoples +peopling +pep +pepped +pepper +peppercorn +peppercorns +peppered +peppering +peppermint +peppermints +pepperoni +pepperonis +peppers +peppery +peppier +peppiest +pepping +peppy +peps +pepsin +peptic +peptics +per +perambulate +perambulated +perambulates +perambulating +perambulator +perambulators +percale +percales +perceivable +perceive +perceived +perceives +perceiving +percent +percentage +percentages +percentile +percentiles +percents +perceptible +perceptibly +perception +perceptions +perceptive +perceptively +perceptiveness +perceptual +perch +perchance +perched +perches +perching +percolate +percolated +percolates +percolating +percolation +percolator +percolators +percussion +percussionist +percussionists +perdition +peregrination +peregrinations +peremptorily +peremptory +perennial +perennially +perennials +perfect +perfected +perfecter +perfectest +perfectible +perfecting +perfection +perfectionism +perfectionist +perfectionists +perfections +perfectly +perfects +perfidies +perfidious +perfidy +perforate +perforated +perforates +perforating +perforation +perforations +perforce +perform +performance +performances +performed +performer +performers +performing +performs +perfume +perfumed +perfumeries +perfumery +perfumes +perfuming +perfunctorily +perfunctory +perhaps +pericardia +pericardium +perigee +perigees +perihelia +perihelion +peril +periled +periling +perilous +perilously +perils +perimeter +perimeters +period +periodic +periodical +periodically +periodicals +periodicity +periodontal +periods +peripatetic +peripatetics +peripheral +peripherals +peripheries +periphery +periphrases +periphrasis +periscope +periscopes +perish +perishable +perishables +perished +perishes +perishing +peritoneum +peritoneums +peritonitis +periwig +periwigs +periwinkle +periwinkles +perjure +perjured +perjurer +perjurers +perjures +perjuries +perjuring +perjury +perk +perked +perkier +perkiest +perkiness +perking +perks +perky +perm +permafrost +permanence +permanent +permanently +permanents +permeability +permeable +permeate +permeated +permeates +permeating +permed +perming +permissible +permissibly +permission +permissions +permissive +permissively +permissiveness +permit +permits +permitted +permitting +perms +permutation +permutations +permute +permuted +permutes +permuting +pernicious +perniciously +peroration +perorations +peroxide +peroxided +peroxides +peroxiding +perpendicular +perpendiculars +perpetrate +perpetrated +perpetrates +perpetrating +perpetration +perpetrator +perpetrators +perpetual +perpetually +perpetuals +perpetuate +perpetuated +perpetuates +perpetuating +perpetuation +perpetuity +perplex +perplexed +perplexes +perplexing +perplexities +perplexity +perquisite +perquisites +persecute +persecuted +persecutes +persecuting +persecution +persecutions +persecutor +persecutors +perseverance +persevere +persevered +perseveres +persevering +persiflage +persimmon +persimmons +persist +persisted +persistence +persistent +persistently +persisting +persists +persnickety +person +persona +personable +personae +personage +personages +personal +personalities +personality +personalize +personalized +personalizes +personalizing +personally +personals +personification +personifications +personified +personifies +personify +personifying +personnel +persons +perspective +perspectives +perspicacious +perspicacity +perspicuity +perspicuous +perspiration +perspire +perspired +perspires +perspiring +persuade +persuaded +persuades +persuading +persuasion +persuasions +persuasive +persuasively +persuasiveness +pert +pertain +pertained +pertaining +pertains +perter +pertest +pertinacious +pertinacity +pertinence +pertinent +pertly +pertness +perturb +perturbation +perturbations +perturbed +perturbing +perturbs +perusal +perusals +peruse +perused +peruses +perusing +pervade +pervaded +pervades +pervading +pervasive +perverse +perversely +perverseness +perversion +perversions +perversity +pervert +perverted +perverting +perverts +peseta +pesetas +peskier +peskiest +pesky +peso +pesos +pessimism +pessimist +pessimistic +pessimistically +pessimists +pest +pester +pestered +pestering +pesters +pesticide +pesticides +pestilence +pestilences +pestilent +pestle +pestled +pestles +pestling +pests +pet +petal +petals +petard +petards +peter +petered +petering +peters +petiole +petioles +petite +petites +petition +petitioned +petitioner +petitioners +petitioning +petitions +petrel +petrels +petrifaction +petrified +petrifies +petrify +petrifying +petrochemical +petrochemicals +petrol +petrolatum +petroleum +pets +petted +petticoat +petticoats +pettier +pettiest +pettifog +pettifogged +pettifogger +pettifoggers +pettifogging +pettifogs +pettily +pettiness +petting +petty +petulance +petulant +petulantly +petunia +petunias +pew +pewee +pewees +pews +pewter +pewters +peyote +phalanges +phalanx +phalanxes +phalli +phallic +phallus +phantasm +phantasmagoria +phantasmagorias +phantasms +phantom +phantoms +pharaoh +pharaohs +pharmaceutical +pharmaceuticals +pharmacies +pharmacist +pharmacists +pharmacologist +pharmacologists +pharmacology +pharmacopeia +pharmacopeias +pharmacopoeia +pharmacopoeias +pharmacy +pharyngeal +pharynges +pharynx +phase +phased +phases +phasing +pheasant +pheasants +phenobarbital +phenomena +phenomenal +phenomenally +phenomenon +phenomenons +phenotype +pheromone +pheromones +phial +phials +philander +philandered +philanderer +philanderers +philandering +philanders +philanthropic +philanthropically +philanthropies +philanthropist +philanthropists +philanthropy +philatelic +philatelist +philatelists +philately +philharmonic +philharmonics +philippic +philippics +philistine +philistines +philodendron +philodendrons +philological +philologist +philologists +philology +philosopher +philosophers +philosophic +philosophical +philosophically +philosophies +philosophize +philosophized +philosophizes +philosophizing +philosophy +philter +philters +phish +phished +phisher +phishers +phishing +phlebitis +phlegm +phlegmatic +phlegmatically +phloem +phlox +phobia +phobias +phobic +phobics +phoebe +phoebes +phoenix +phoenixes +phone +phoned +phoneme +phonemes +phonemic +phones +phonetic +phonetically +phonetician +phoneticians +phonetics +phoneyed +phoneying +phonic +phonically +phonics +phonied +phonier +phonies +phoniest +phoniness +phoning +phonograph +phonographs +phonological +phonologist +phonologists +phonology +phony +phonying +phooey +phosphate +phosphates +phosphor +phosphorescence +phosphorescent +phosphoric +phosphors +phosphorus +photo +photocopied +photocopier +photocopiers +photocopies +photocopy +photocopying +photoed +photoelectric +photogenic +photograph +photographed +photographer +photographers +photographic +photographically +photographing +photographs +photography +photoing +photojournalism +photojournalist +photojournalists +photon +photons +photos +photosensitive +photosynthesis +phototypesetter +phototypesetting +phrasal +phrase +phrased +phraseology +phrases +phrasing +phrasings +phrenology +phyla +phylum +physic +physical +physically +physicals +physician +physicians +physicist +physicists +physicked +physicking +physics +physiognomies +physiognomy +physiological +physiologist +physiologists +physiology +physiotherapist +physiotherapists +physiotherapy +physique +physiques +pi +pianissimo +pianissimos +pianist +pianists +piano +pianoforte +pianofortes +pianos +piazza +piazzas +pica +picante +picaresque +picayune +piccalilli +piccolo +piccolos +pick +pickax +pickaxed +pickaxes +pickaxing +picked +picker +pickerel +pickerels +pickers +picket +picketed +picketing +pickets +pickier +pickiest +picking +pickings +pickle +pickled +pickles +pickling +pickpocket +pickpockets +picks +pickup +pickups +picky +picnic +picnicked +picnicker +picnickers +picnicking +picnics +pictograph +pictographs +pictorial +pictorially +pictorials +picture +pictured +pictures +picturesque +picturing +piddle +piddled +piddles +piddling +pidgin +pidgins +pie +piebald +piebalds +piece +pieced +piecemeal +pieces +piecework +piecing +pied +pieing +pier +pierce +pierced +pierces +piercing +piercingly +piercings +piers +pies +piety +piffle +pig +pigeon +pigeonhole +pigeonholed +pigeonholes +pigeonholing +pigeons +pigged +piggier +piggies +piggiest +pigging +piggish +piggishness +piggy +piggyback +piggybacked +piggybacking +piggybacks +pigheaded +piglet +piglets +pigment +pigmentation +pigments +pigpen +pigpens +pigs +pigskin +pigskins +pigsties +pigsty +pigtail +pigtails +piing +pike +piked +piker +pikers +pikes +piking +pilaf +pilafs +pilaster +pilasters +pilchard +pilchards +pile +piled +piles +pileup +pileups +pilfer +pilfered +pilferer +pilferers +pilfering +pilfers +pilgrim +pilgrimage +pilgrimages +pilgrims +piling +pilings +pill +pillage +pillaged +pillages +pillaging +pillar +pillars +pillbox +pillboxes +pilled +pilling +pillion +pillions +pilloried +pillories +pillory +pillorying +pillow +pillowcase +pillowcases +pillowed +pillowing +pillows +pills +pilot +piloted +pilothouse +pilothouses +piloting +pilots +pimento +pimentos +pimiento +pimientos +pimp +pimped +pimpernel +pimpernels +pimping +pimple +pimples +pimplier +pimpliest +pimply +pimps +pin +pinafore +pinafores +pinball +pincer +pincers +pinch +pinched +pinches +pinching +pincushion +pincushions +pine +pineapple +pineapples +pined +pines +pinfeather +pinfeathers +ping +pinged +pinging +pings +pinhead +pinheads +pinhole +pinholes +pining +pinion +pinioned +pinioning +pinions +pink +pinked +pinker +pinkest +pinkeye +pinkie +pinkies +pinking +pinkish +pinks +pinky +pinnacle +pinnacles +pinnate +pinned +pinning +pinochle +pinpoint +pinpointed +pinpointing +pinpoints +pinprick +pinpricks +pins +pinstripe +pinstriped +pinstripes +pint +pinto +pintos +pints +pinup +pinups +pinwheel +pinwheeled +pinwheeling +pinwheels +pioneer +pioneered +pioneering +pioneers +pious +piously +pip +pipe +piped +pipeline +pipelines +piper +pipers +pipes +piping +pipit +pipits +pipped +pippin +pipping +pippins +pips +pipsqueak +pipsqueaks +piquancy +piquant +pique +piqued +piques +piquing +piracy +piranha +piranhas +pirate +pirated +pirates +piratical +pirating +pirouette +pirouetted +pirouettes +pirouetting +pis +piscatorial +piss +pissed +pisses +pissing +pistachio +pistachios +pistil +pistillate +pistils +pistol +pistols +piston +pistons +pit +pita +pitch +pitchblende +pitched +pitcher +pitchers +pitches +pitchfork +pitchforked +pitchforking +pitchforks +pitching +pitchman +pitchmen +piteous +piteously +pitfall +pitfalls +pith +pithier +pithiest +pithily +pithy +pitiable +pitiably +pitied +pities +pitiful +pitifully +pitiless +pitilessly +piton +pitons +pits +pittance +pittances +pitted +pitting +pituitaries +pituitary +pity +pitying +pivot +pivotal +pivoted +pivoting +pivots +pixel +pixels +pixie +pixies +pizazz +pizza +pizzas +pizzazz +pizzeria +pizzerias +pizzicati +pizzicato +placard +placarded +placarding +placards +placate +placated +placates +placating +placation +place +placebo +placebos +placed +placeholder +placement +placements +placenta +placental +placentals +placentas +placer +placers +places +placid +placidity +placidly +placing +placket +plackets +plagiarism +plagiarisms +plagiarist +plagiarists +plagiarize +plagiarized +plagiarizes +plagiarizing +plague +plagued +plagues +plaguing +plaice +plaid +plaids +plain +plainclothes +plainclothesman +plainclothesmen +plainer +plainest +plainly +plainness +plains +plaint +plaintiff +plaintiffs +plaintive +plaintively +plaints +plait +plaited +plaiting +plaits +plan +planar +plane +planed +planes +planet +planetarium +planetariums +planetary +planets +plangent +planing +plank +planked +planking +planks +plankton +planned +planner +planners +planning +plannings +plans +plant +plantain +plantains +plantation +plantations +planted +planter +planters +planting +plantings +plants +plaque +plaques +plasma +plaster +plasterboard +plastered +plasterer +plasterers +plastering +plasters +plastic +plasticity +plastics +plastique +plate +plateau +plateaued +plateauing +plateaus +plated +plateful +platefuls +platelet +platelets +platen +platens +plates +platform +platformed +platforming +platforms +plating +platinum +platitude +platitudes +platitudinous +platonic +platoon +platooned +platooning +platoons +platter +platters +platypus +platypuses +plaudit +plaudits +plausibility +plausible +plausibly +play +playable +playact +playacted +playacting +playacts +playback +playbacks +playbill +playbills +playboy +playboys +played +player +players +playful +playfully +playfulness +playgoer +playgoers +playground +playgrounds +playhouse +playhouses +playing +playlist +playlists +playmate +playmates +playoff +playoffs +playpen +playpens +playroom +playrooms +plays +plaything +playthings +playwright +playwrights +plaza +plazas +plea +plead +pleaded +pleader +pleaders +pleading +pleads +pleas +pleasant +pleasanter +pleasantest +pleasantly +pleasantness +pleasantries +pleasantry +please +pleased +pleases +pleasing +pleasingly +pleasings +pleasurable +pleasurably +pleasure +pleasured +pleasures +pleasuring +pleat +pleated +pleating +pleats +plebeian +plebeians +plebiscite +plebiscites +plectra +plectrum +plectrums +pledge +pledged +pledges +pledging +plenaries +plenary +plenipotentiaries +plenipotentiary +plenitude +plenitudes +plenteous +plentiful +plentifully +plenty +plethora +pleurisy +plexus +plexuses +pliability +pliable +pliancy +pliant +plied +pliers +plies +plight +plighted +plighting +plights +plinth +plinths +plod +plodded +plodder +plodders +plodding +ploddings +plods +plop +plopped +plopping +plops +plot +plots +plotted +plotter +plotters +plotting +plover +plovers +plow +plowed +plowing +plowman +plowmen +plows +plowshare +plowshares +ploy +ploys +pluck +plucked +pluckier +pluckiest +pluckiness +plucking +plucks +plucky +plug +plugged +plugging +plugin +plugins +plugs +plum +plumage +plumb +plumbed +plumber +plumbers +plumbing +plumbs +plume +plumed +plumes +pluming +plummet +plummeted +plummeting +plummets +plump +plumped +plumper +plumpest +plumping +plumpness +plumps +plums +plunder +plundered +plunderer +plunderers +plundering +plunders +plunge +plunged +plunger +plungers +plunges +plunging +plunk +plunked +plunking +plunks +pluperfect +pluperfects +plural +pluralism +pluralistic +pluralities +plurality +pluralize +pluralized +pluralizes +pluralizing +plurals +plus +pluses +plush +plusher +plushest +plushier +plushiest +plushy +plutocracies +plutocracy +plutocrat +plutocratic +plutocrats +plutonium +ply +plying +plywood +pneumatic +pneumatically +pneumonia +poach +poached +poacher +poachers +poaches +poaching +pock +pocked +pocket +pocketbook +pocketbooks +pocketed +pocketful +pocketfuls +pocketing +pocketknife +pocketknives +pockets +pocking +pockmark +pockmarked +pockmarking +pockmarks +pocks +pod +podcast +podcasting +podcasts +podded +podding +podiatrist +podiatrists +podiatry +podium +podiums +pods +poem +poems +poesy +poet +poetess +poetesses +poetic +poetical +poetically +poetry +poets +pogrom +pogroms +poi +poignancy +poignant +poignantly +poinsettia +poinsettias +point +pointed +pointedly +pointer +pointers +pointier +pointiest +pointillism +pointillist +pointillists +pointing +pointless +pointlessly +pointlessness +points +pointy +poise +poised +poises +poising +poison +poisoned +poisoner +poisoners +poisoning +poisonings +poisonous +poisonously +poisons +poke +poked +poker +pokers +pokes +pokey +pokeys +pokier +pokiest +poking +poky +pol +polar +polarities +polarity +polarization +polarize +polarized +polarizes +polarizing +pole +polecat +polecats +poled +polemic +polemical +polemics +poles +polestar +polestars +police +policed +policeman +policemen +polices +policewoman +policewomen +policies +policing +policy +policyholder +policyholders +poling +polio +poliomyelitis +polios +polish +polished +polisher +polishers +polishes +polishing +polite +politely +politeness +politer +politesse +politest +politic +political +politically +politician +politicians +politicize +politicized +politicizes +politicizing +politico +politicos +politics +polities +polity +polka +polkaed +polkaing +polkas +poll +polled +pollen +pollinate +pollinated +pollinates +pollinating +pollination +polling +polliwog +polliwogs +polls +pollster +pollsters +pollutant +pollutants +pollute +polluted +polluter +polluters +pollutes +polluting +pollution +pollywog +pollywogs +polo +polonaise +polonaises +polonium +pols +poltergeist +poltergeists +poltroon +poltroons +polyamories +polyamory +polyester +polyesters +polyethylene +polygamist +polygamists +polygamous +polygamy +polyglot +polyglots +polygon +polygonal +polygons +polygraph +polygraphed +polygraphing +polygraphs +polyhedron +polyhedrons +polymath +polymaths +polymer +polymeric +polymerization +polymers +polymorphic +polynomial +polynomials +polyp +polyphonic +polyphony +polyps +polystyrene +polysyllabic +polysyllable +polysyllables +polytechnic +polytechnics +polytheism +polytheist +polytheistic +polytheists +polythene +polyunsaturated +pomade +pomaded +pomades +pomading +pomegranate +pomegranates +pommel +pommeled +pommeling +pommels +pomp +pompadour +pompadoured +pompadours +pompom +pompoms +pomposity +pompous +pompously +pompousness +poncho +ponchos +pond +ponder +pondered +pondering +ponderous +ponderously +ponders +ponds +pone +pones +poniard +poniards +ponies +pontiff +pontiffs +pontifical +pontificate +pontificated +pontificates +pontificating +pontoon +pontoons +pony +ponytail +ponytails +pooch +pooched +pooches +pooching +poodle +poodles +pooh +poohed +poohing +poohs +pool +pooled +pooling +pools +poop +pooped +pooping +poops +poor +poorer +poorest +poorhouse +poorhouses +poorly +pop +popcorn +pope +popes +popgun +popguns +popinjay +popinjays +poplar +poplars +poplin +popover +popovers +poppa +poppas +popped +poppies +popping +poppy +poppycock +pops +populace +populaces +popular +popularity +popularization +popularize +popularized +popularizes +popularizing +popularly +populate +populated +populates +populating +population +populations +populism +populist +populists +populous +porcelain +porch +porches +porcine +porcupine +porcupines +pore +pored +pores +poring +pork +porn +porno +pornographer +pornographers +pornographic +pornography +porosity +porous +porphyry +porpoise +porpoised +porpoises +porpoising +porridge +porringer +porringers +port +portability +portable +portables +portage +portaged +portages +portaging +portal +portals +portcullis +portcullises +ported +portend +portended +portending +portends +portent +portentous +portentously +portents +porter +porterhouse +porterhouses +porters +portfolio +portfolios +porthole +portholes +portico +porticoes +porting +portion +portioned +portioning +portions +portlier +portliest +portliness +portly +portmanteau +portmanteaus +portrait +portraitist +portraitists +portraits +portraiture +portray +portrayal +portrayals +portrayed +portraying +portrays +ports +pose +posed +poser +posers +poses +poseur +poseurs +posh +posher +poshest +posies +posing +posit +posited +positing +position +positional +positioned +positioning +positions +positive +positively +positives +positivism +positron +positrons +posits +posse +posses +possess +possessed +possesses +possessing +possession +possessions +possessive +possessively +possessiveness +possessives +possessor +possessors +possibilities +possibility +possible +possibles +possibly +possum +possums +post +postage +postal +postbox +postcard +postcards +postcode +postcodes +postdate +postdated +postdates +postdating +postdoc +postdoctoral +posted +poster +posterior +posteriors +posterity +posters +postgraduate +postgraduates +posthaste +posthumous +posthumously +posting +postlude +postludes +postman +postmark +postmarked +postmarking +postmarks +postmaster +postmasters +postmen +postmistress +postmistresses +postmodern +postmortem +postmortems +postnatal +postoperative +postpaid +postpartum +postpone +postponed +postponement +postponements +postpones +postponing +posts +postscript +postscripts +postulate +postulated +postulates +postulating +posture +postured +postures +posturing +postwar +posy +pot +potable +potables +potash +potassium +potato +potatoes +potbellied +potbellies +potbelly +potboiler +potboilers +potency +potent +potentate +potentates +potential +potentialities +potentiality +potentially +potentials +potful +potfuls +potholder +potholders +pothole +potholes +pothook +pothooks +potion +potions +potluck +potlucks +potpie +potpies +potpourri +potpourris +pots +potsherd +potsherds +potshot +potshots +pottage +potted +potter +pottered +potteries +pottering +potters +pottery +pottier +potties +pottiest +potting +potty +pouch +pouched +pouches +pouching +poultice +poulticed +poultices +poulticing +poultry +pounce +pounced +pounces +pouncing +pound +pounded +pounding +pounds +pour +poured +pouring +pours +pout +pouted +pouting +pouts +poverty +powder +powdered +powdering +powders +powdery +power +powerboat +powerboats +powered +powerful +powerfully +powerhouse +powerhouses +powering +powerless +powerlessly +powerlessness +powers +powwow +powwowed +powwowing +powwows +pox +poxes +practicability +practicable +practicably +practical +practicalities +practicality +practically +practicals +practice +practiced +practices +practicing +practitioner +practitioners +pragmatic +pragmatically +pragmatics +pragmatism +pragmatist +pragmatists +prairie +prairies +praise +praised +praises +praiseworthiness +praiseworthy +praising +praline +pralines +pram +prance +pranced +prancer +prancers +prances +prancing +prank +pranks +prankster +pranksters +prate +prated +prates +pratfall +pratfalls +prating +prattle +prattled +prattles +prattling +prawn +prawned +prawning +prawns +pray +prayed +prayer +prayers +praying +prays +preach +preached +preacher +preachers +preaches +preachier +preachiest +preaching +preachy +preamble +preambled +preambles +preambling +prearrange +prearranged +prearrangement +prearranges +prearranging +precarious +precariously +precaution +precautionary +precautions +precede +preceded +precedence +precedent +precedents +precedes +preceding +precept +preceptor +preceptors +precepts +precinct +precincts +preciosity +precious +preciously +preciousness +precipice +precipices +precipitant +precipitants +precipitate +precipitated +precipitately +precipitates +precipitating +precipitation +precipitations +precipitous +precipitously +precise +precisely +preciseness +preciser +precises +precisest +precision +preclude +precluded +precludes +precluding +preclusion +precocious +precociously +precociousness +precocity +precognition +preconceive +preconceived +preconceives +preconceiving +preconception +preconceptions +precondition +preconditioned +preconditioning +preconditions +precursor +precursors +predate +predated +predates +predating +predator +predators +predatory +predecease +predeceased +predeceases +predeceasing +predecessor +predecessors +predefined +predestination +predestine +predestined +predestines +predestining +predetermination +predetermine +predetermined +predetermines +predetermining +predicament +predicaments +predicate +predicated +predicates +predicating +predication +predicative +predict +predictability +predictable +predictably +predicted +predicting +prediction +predictions +predictive +predictor +predicts +predilection +predilections +predispose +predisposed +predisposes +predisposing +predisposition +predispositions +predominance +predominant +predominantly +predominate +predominated +predominates +predominating +preeminence +preeminent +preeminently +preempt +preempted +preempting +preemption +preemptive +preemptively +preempts +preen +preened +preening +preens +preexist +preexisted +preexisting +preexists +prefab +prefabbed +prefabbing +prefabricate +prefabricated +prefabricates +prefabricating +prefabrication +prefabs +preface +prefaced +prefaces +prefacing +prefatory +prefect +prefects +prefecture +prefectures +prefer +preferable +preferably +preference +preferences +preferential +preferentially +preferment +preferred +preferring +prefers +prefigure +prefigured +prefigures +prefiguring +prefix +prefixed +prefixes +prefixing +pregnancies +pregnancy +pregnant +preheat +preheated +preheating +preheats +prehensile +prehistoric +prehistory +prejudge +prejudged +prejudges +prejudging +prejudgment +prejudgments +prejudice +prejudiced +prejudices +prejudicial +prejudicing +prelate +prelates +preliminaries +preliminary +prelude +preludes +premarital +premature +prematurely +premeditate +premeditated +premeditates +premeditating +premeditation +premenstrual +premier +premiere +premiered +premieres +premiering +premiers +premise +premised +premises +premising +premium +premiums +premonition +premonitions +premonitory +prenatal +prenup +prenups +preoccupation +preoccupations +preoccupied +preoccupies +preoccupy +preoccupying +preordain +preordained +preordaining +preordains +prep +prepackage +prepackaged +prepackages +prepackaging +prepaid +preparation +preparations +preparatory +prepare +prepared +preparedness +prepares +preparing +prepay +prepaying +prepayment +prepayments +prepays +preponderance +preponderances +preponderant +preponderate +preponderated +preponderates +preponderating +preposition +prepositional +prepositions +prepossess +prepossessed +prepossesses +prepossessing +preposterous +preposterously +prepped +preppie +preppier +preppies +preppiest +prepping +preppy +preps +prequel +prequels +prerecord +prerecorded +prerecording +prerecords +preregister +preregistered +preregistering +preregisters +preregistration +prerequisite +prerequisites +prerogative +prerogatives +presage +presaged +presages +presaging +preschool +preschooler +preschoolers +preschools +prescience +prescient +prescribe +prescribed +prescribes +prescribing +prescription +prescriptions +prescriptive +presence +presences +present +presentable +presentation +presentations +presented +presenter +presentiment +presentiments +presenting +presently +presents +preservation +preservative +preservatives +preserve +preserved +preserver +preservers +preserves +preserving +preset +presets +presetting +preshrank +preshrink +preshrinking +preshrinks +preshrunk +preside +presided +presidencies +presidency +president +presidential +presidents +presides +presiding +press +pressed +presses +pressing +pressings +pressman +pressmen +pressure +pressured +pressures +pressuring +pressurization +pressurize +pressurized +pressurizes +pressurizing +prestige +prestigious +presto +prestos +presumable +presumably +presume +presumed +presumes +presuming +presumption +presumptions +presumptive +presumptuous +presumptuously +presumptuousness +presuppose +presupposed +presupposes +presupposing +presupposition +presuppositions +preteen +preteens +pretend +pretended +pretender +pretenders +pretending +pretends +pretense +pretenses +pretension +pretensions +pretentious +pretentiously +pretentiousness +preterit +preterite +preterites +preterits +preternatural +pretext +pretexts +prettied +prettier +pretties +prettiest +prettified +prettifies +prettify +prettifying +prettily +prettiness +pretty +prettying +pretzel +pretzels +prevail +prevailed +prevailing +prevails +prevalence +prevalent +prevaricate +prevaricated +prevaricates +prevaricating +prevarication +prevarications +prevaricator +prevaricators +prevent +preventable +preventative +preventatives +prevented +preventing +prevention +preventive +preventives +prevents +preview +previewed +previewer +previewers +previewing +previews +previous +previously +prewar +prey +preyed +preying +preys +price +priced +priceless +prices +pricey +pricier +priciest +pricing +prick +pricked +pricking +prickle +prickled +prickles +pricklier +prickliest +prickling +prickly +pricks +pride +prided +prides +priding +pried +pries +priest +priestess +priestesses +priesthood +priesthoods +priestlier +priestliest +priestly +priests +prig +priggish +prigs +prim +primacy +primal +primaries +primarily +primary +primate +primates +prime +primed +primer +primers +primes +primeval +priming +primitive +primitively +primitives +primly +primmer +primmest +primness +primogeniture +primordial +primp +primped +primping +primps +primrose +primroses +prince +princelier +princeliest +princely +princes +princess +princesses +principal +principalities +principality +principally +principals +principle +principled +principles +print +printable +printed +printer +printers +printing +printings +printout +printouts +prints +prior +prioress +prioresses +priories +priorities +prioritize +prioritized +prioritizes +prioritizing +priority +priors +priory +prism +prismatic +prisms +prison +prisoner +prisoners +prisons +prissier +prissiest +prissiness +prissy +pristine +prithee +privacy +private +privateer +privateers +privately +privater +privates +privatest +privation +privations +privatization +privatizations +privatize +privatized +privatizes +privatizing +privet +privets +privier +privies +priviest +privilege +privileged +privileges +privileging +privy +prize +prized +prizefight +prizefighter +prizefighters +prizefighting +prizefights +prizes +prizing +pro +proactive +probabilistic +probabilities +probability +probable +probables +probably +probate +probated +probates +probating +probation +probationary +probationer +probationers +probe +probed +probes +probing +probity +problem +problematic +problematical +problematically +problems +proboscis +proboscises +procedural +procedure +procedures +proceed +proceeded +proceeding +proceedings +proceeds +process +processed +processes +processing +procession +processional +processionals +processioned +processioning +processions +processor +processors +proclaim +proclaimed +proclaiming +proclaims +proclamation +proclamations +proclivities +proclivity +procrastinate +procrastinated +procrastinates +procrastinating +procrastination +procrastinator +procrastinators +procreate +procreated +procreates +procreating +procreation +procreative +proctor +proctored +proctoring +proctors +procurator +procurators +procure +procured +procurement +procurer +procurers +procures +procuring +prod +prodded +prodding +prodigal +prodigality +prodigals +prodigies +prodigious +prodigiously +prodigy +prods +produce +produced +producer +producers +produces +producing +product +production +productions +productive +productively +productiveness +productivity +products +prof +profanation +profanations +profane +profaned +profanely +profanes +profaning +profanities +profanity +profess +professed +professes +professing +profession +professional +professionalism +professionally +professionals +professions +professor +professorial +professors +professorship +professorships +proffer +proffered +proffering +proffers +proficiency +proficient +proficiently +proficients +profile +profiled +profiles +profiling +profit +profitability +profitable +profitably +profited +profiteer +profiteered +profiteering +profiteers +profiting +profits +profligacy +profligate +profligates +proforma +profound +profounder +profoundest +profoundly +profs +profundities +profundity +profuse +profusely +profusion +profusions +progenitor +progenitors +progeny +progesterone +prognoses +prognosis +prognostic +prognosticate +prognosticated +prognosticates +prognosticating +prognostication +prognostications +prognosticator +prognosticators +prognostics +program +programed +programing +programmable +programmables +programmed +programmer +programmers +programming +programs +progress +progressed +progresses +progressing +progression +progressions +progressive +progressively +progressives +prohibit +prohibited +prohibiting +prohibition +prohibitionist +prohibitionists +prohibitions +prohibitive +prohibitively +prohibitory +prohibits +project +projected +projectile +projectiles +projecting +projection +projectionist +projectionists +projections +projector +projectors +projects +proletarian +proletarians +proletariat +proliferate +proliferated +proliferates +proliferating +proliferation +prolific +prolifically +prolix +prolixity +prologue +prologues +prolong +prolongation +prolongations +prolonged +prolonging +prolongs +prom +promenade +promenaded +promenades +promenading +prominence +prominent +prominently +promiscuity +promiscuous +promiscuously +promise +promised +promises +promising +promisingly +promissory +promo +promontories +promontory +promos +promote +promoted +promoter +promoters +promotes +promoting +promotion +promotional +promotions +prompt +prompted +prompter +prompters +promptest +prompting +promptings +promptly +promptness +prompts +proms +promulgate +promulgated +promulgates +promulgating +promulgation +prone +proneness +prong +pronged +pronghorn +pronghorns +prongs +pronoun +pronounce +pronounceable +pronounced +pronouncement +pronouncements +pronounces +pronouncing +pronouns +pronto +pronunciation +pronunciations +proof +proofed +proofing +proofread +proofreader +proofreaders +proofreading +proofreads +proofs +prop +propaganda +propagandist +propagandists +propagandize +propagandized +propagandizes +propagandizing +propagate +propagated +propagates +propagating +propagation +propane +propel +propellant +propellants +propelled +propeller +propellers +propelling +propels +propensities +propensity +proper +properer +properest +properly +propertied +properties +property +prophecies +prophecy +prophesied +prophesies +prophesy +prophesying +prophet +prophetess +prophetesses +prophetic +prophetically +prophets +prophylactic +prophylactics +prophylaxis +propinquity +propitiate +propitiated +propitiates +propitiating +propitiation +propitiatory +propitious +proponent +proponents +proportion +proportional +proportionality +proportionally +proportionals +proportionate +proportionately +proportioned +proportioning +proportions +proposal +proposals +propose +proposed +proposer +proposes +proposing +proposition +propositional +propositioned +propositioning +propositions +propound +propounded +propounding +propounds +propped +propping +proprietaries +proprietary +proprietor +proprietors +proprietorship +proprietress +proprietresses +propriety +props +propulsion +propulsive +prorate +prorated +prorates +prorating +pros +prosaic +prosaically +proscenium +prosceniums +proscribe +proscribed +proscribes +proscribing +proscription +proscriptions +prose +prosecute +prosecuted +prosecutes +prosecuting +prosecution +prosecutions +prosecutor +prosecutors +proselyte +proselyted +proselytes +proselyting +proselytize +proselytized +proselytizes +proselytizing +prosier +prosiest +prosodies +prosody +prospect +prospected +prospecting +prospective +prospector +prospectors +prospects +prospectus +prospectuses +prosper +prospered +prospering +prosperity +prosperous +prosperously +prospers +prostate +prostates +prostheses +prosthesis +prosthetic +prostitute +prostituted +prostitutes +prostituting +prostitution +prostrate +prostrated +prostrates +prostrating +prostration +prostrations +prosy +protagonist +protagonists +protean +protect +protected +protecting +protection +protections +protective +protectively +protectiveness +protector +protectorate +protectorates +protectors +protects +protein +proteins +protest +protestant +protestants +protestation +protestations +protested +protester +protesters +protesting +protestor +protestors +protests +protocol +protocols +proton +protons +protoplasm +protoplasmic +prototype +prototypes +prototyping +protozoa +protozoan +protozoans +protract +protracted +protracting +protraction +protractor +protractors +protracts +protrude +protruded +protrudes +protruding +protrusion +protrusions +protuberance +protuberances +protuberant +protégé +protégés +proud +prouder +proudest +proudly +provable +provably +prove +proved +proven +provenance +provender +proverb +proverbial +proverbially +proverbs +proves +provide +provided +providence +provident +providential +providentially +providently +provider +providers +provides +providing +province +provinces +provincial +provincialism +provincials +proving +provision +provisional +provisionally +provisioned +provisioning +provisions +proviso +provisos +provocation +provocations +provocative +provocatively +provoke +provoked +provokes +provoking +provost +provosts +prow +prowess +prowl +prowled +prowler +prowlers +prowling +prowls +prows +proxies +proximity +proxy +prude +prudence +prudent +prudential +prudently +prudery +prudes +prudish +prudishly +prune +pruned +prunes +pruning +prurience +prurient +pry +prying +précis +précised +précising +psalm +psalmist +psalmists +psalms +pseudo +pseudonym +pseudonyms +pshaw +pshaws +psoriasis +psst +psych +psyche +psyched +psychedelic +psychedelics +psyches +psychiatric +psychiatrist +psychiatrists +psychiatry +psychic +psychical +psychically +psychics +psyching +psycho +psychoanalysis +psychoanalyst +psychoanalysts +psychoanalyze +psychoanalyzed +psychoanalyzes +psychoanalyzing +psychobabble +psychogenic +psychokinesis +psychological +psychologically +psychologies +psychologist +psychologists +psychology +psychopath +psychopathic +psychopaths +psychos +psychoses +psychosis +psychosomatic +psychotherapies +psychotherapist +psychotherapists +psychotherapy +psychotic +psychotics +psychs +ptarmigan +ptarmigans +pterodactyl +pterodactyls +ptomaine +ptomaines +pub +puberty +pubescence +pubescent +pubic +public +publican +publicans +publication +publications +publicist +publicists +publicity +publicize +publicized +publicizes +publicizing +publicly +publish +publishable +published +publisher +publishers +publishes +publishing +pubs +puck +pucker +puckered +puckering +puckers +puckish +pucks +pudding +puddings +puddle +puddled +puddles +puddling +pudgier +pudgiest +pudgy +pueblo +pueblos +puerile +puerility +puff +puffball +puffballs +puffed +puffer +puffier +puffiest +puffin +puffiness +puffing +puffins +puffs +puffy +pug +pugilism +pugilist +pugilistic +pugilists +pugnacious +pugnaciously +pugnacity +pugs +puke +puked +pukes +puking +pulchritude +pull +pullback +pullbacks +pulled +puller +pullers +pullet +pullets +pulley +pulleys +pulling +pullout +pullouts +pullover +pullovers +pulls +pulmonary +pulp +pulped +pulpier +pulpiest +pulping +pulpit +pulpits +pulps +pulpy +pulsar +pulsars +pulsate +pulsated +pulsates +pulsating +pulsation +pulsations +pulse +pulsed +pulses +pulsing +pulverization +pulverize +pulverized +pulverizes +pulverizing +puma +pumas +pumice +pumices +pummel +pummeled +pummeling +pummels +pump +pumped +pumper +pumpernickel +pumpers +pumping +pumpkin +pumpkins +pumps +pun +punch +punched +punches +punchier +punchiest +punching +punchline +punchy +punctilious +punctiliously +punctual +punctuality +punctually +punctuate +punctuated +punctuates +punctuating +punctuation +puncture +punctured +punctures +puncturing +pundit +pundits +pungency +pungent +pungently +punier +puniest +punish +punishable +punished +punishes +punishing +punishment +punishments +punitive +punk +punker +punkest +punks +punned +punning +puns +punster +punsters +punt +punted +punter +punters +punting +punts +puny +pup +pupa +pupae +pupal +pupil +pupils +pupped +puppet +puppeteer +puppeteers +puppetry +puppets +puppies +pupping +puppy +pups +purblind +purchasable +purchase +purchased +purchaser +purchasers +purchases +purchasing +pure +purebred +purebreds +puree +pureed +pureeing +purees +purely +pureness +purer +purest +purgative +purgatives +purgatorial +purgatories +purgatory +purge +purged +purges +purging +purification +purified +purifier +purifiers +purifies +purify +purifying +purism +purist +purists +puritan +puritanical +puritanically +puritanism +puritans +purity +purl +purled +purling +purloin +purloined +purloining +purloins +purls +purple +purpler +purples +purplest +purplish +purport +purported +purportedly +purporting +purports +purpose +purposed +purposeful +purposefully +purposeless +purposely +purposes +purposing +purr +purred +purring +purrs +purse +pursed +purser +pursers +purses +pursing +pursuance +pursuant +pursue +pursued +pursuer +pursuers +pursues +pursuing +pursuit +pursuits +purulence +purulent +purvey +purveyed +purveying +purveyor +purveyors +purveys +purview +pus +push +pushcart +pushcarts +pushed +pusher +pushers +pushes +pushier +pushiest +pushiness +pushing +pushover +pushovers +pushup +pushups +pushy +pusillanimity +pusillanimous +puss +pusses +pussier +pussies +pussiest +pussy +pussycat +pussycats +pussyfoot +pussyfooted +pussyfooting +pussyfoots +pustule +pustules +put +putative +putrefaction +putrefied +putrefies +putrefy +putrefying +putrescence +putrescent +putrid +puts +putsch +putsches +putt +putted +putter +puttered +puttering +putters +puttied +putties +putting +putts +putty +puttying +puzzle +puzzled +puzzlement +puzzler +puzzlers +puzzles +puzzling +pwn +pwned +pwning +pwns +pygmies +pygmy +pylon +pylons +pyorrhea +pyramid +pyramidal +pyramided +pyramiding +pyramids +pyre +pyres +pyrite +pyromania +pyromaniac +pyromaniacs +pyrotechnic +pyrotechnics +python +pythons +pyx +pyxes +q +qua +quack +quacked +quackery +quacking +quacks +quad +quadrangle +quadrangles +quadrangular +quadrant +quadrants +quadraphonic +quadratic +quadrature +quadrennial +quadriceps +quadricepses +quadrilateral +quadrilaterals +quadrille +quadrilles +quadriplegia +quadriplegic +quadriplegics +quadruped +quadrupeds +quadruple +quadrupled +quadruples +quadruplet +quadruplets +quadruplicate +quadruplicated +quadruplicates +quadruplicating +quadrupling +quads +quaff +quaffed +quaffing +quaffs +quagmire +quagmires +quahog +quahogs +quail +quailed +quailing +quails +quaint +quainter +quaintest +quaintly +quaintness +quake +quaked +quakes +quaking +qualification +qualifications +qualified +qualifier +qualifiers +qualifies +qualify +qualifying +qualitative +qualitatively +qualities +quality +qualm +qualms +quandaries +quandary +quanta +quantified +quantifier +quantifiers +quantifies +quantify +quantifying +quantitative +quantities +quantity +quantum +quarantine +quarantined +quarantines +quarantining +quark +quarks +quarrel +quarreled +quarreling +quarrels +quarrelsome +quarried +quarries +quarry +quarrying +quart +quarter +quarterback +quarterbacked +quarterbacking +quarterbacks +quarterdeck +quarterdecks +quartered +quarterfinal +quarterfinals +quartering +quarterlies +quarterly +quartermaster +quartermasters +quarters +quartet +quartets +quarto +quartos +quarts +quartz +quasar +quasars +quash +quashed +quashes +quashing +quasi +quatrain +quatrains +quaver +quavered +quavering +quavers +quavery +quay +quays +queasier +queasiest +queasily +queasiness +queasy +queen +queened +queening +queenlier +queenliest +queenly +queens +queer +queered +queerer +queerest +queering +queerly +queerness +queers +quell +quelled +quelling +quells +quench +quenched +quenches +quenching +queried +queries +querulous +querulously +query +querying +quesadilla +quesadillas +quest +quested +questing +question +questionable +questionably +questioned +questioner +questioners +questioning +questioningly +questionnaire +questionnaires +questions +quests +queue +queued +queues +queuing +quibble +quibbled +quibbler +quibblers +quibbles +quibbling +quiche +quiches +quick +quicken +quickened +quickening +quickens +quicker +quickest +quickie +quickies +quicklime +quickly +quickness +quicksand +quicksands +quicksilver +quid +quids +quiescence +quiescent +quiet +quieted +quieter +quietest +quieting +quietly +quietness +quiets +quietude +quietus +quietuses +quill +quills +quilt +quilted +quilter +quilters +quilting +quilts +quince +quinces +quinine +quintessence +quintessences +quintessential +quintet +quintets +quintuple +quintupled +quintuples +quintuplet +quintuplets +quintupling +quip +quipped +quipping +quips +quire +quires +quirk +quirked +quirkier +quirkiest +quirking +quirks +quirky +quisling +quislings +quit +quite +quits +quitter +quitters +quitting +quiver +quivered +quivering +quivers +quixotic +quiz +quizzed +quizzes +quizzical +quizzically +quizzing +quoit +quoited +quoiting +quoits +quondam +quorum +quorums +quota +quotable +quotas +quotation +quotations +quote +quoted +quotes +quoth +quotidian +quotient +quotients +quoting +r +rabbi +rabbinate +rabbinical +rabbis +rabbit +rabbited +rabbiting +rabbits +rabble +rabbles +rabid +rabies +raccoon +raccoons +race +racecourse +racecourses +raced +racehorse +racehorses +raceme +racemes +racer +racers +races +racetrack +racetracks +raceway +raceways +racial +racially +racier +raciest +racily +raciness +racing +racism +racist +racists +rack +racked +racket +racketed +racketeer +racketeered +racketeering +racketeers +racketing +rackets +racking +racks +raconteur +raconteurs +racquet +racquetball +racquetballs +racquets +racy +radar +radars +radial +radially +radials +radiance +radiant +radiantly +radiate +radiated +radiates +radiating +radiation +radiations +radiator +radiators +radical +radicalism +radically +radicals +radii +radio +radioactive +radioactivity +radioed +radiogram +radiograms +radioing +radioisotope +radioisotopes +radiologist +radiologists +radiology +radios +radiotelephone +radiotelephones +radiotherapist +radiotherapists +radiotherapy +radish +radishes +radium +radius +radon +raffia +raffish +raffle +raffled +raffles +raffling +raft +rafted +rafter +rafters +rafting +rafts +rag +raga +ragamuffin +ragamuffins +ragas +rage +raged +rages +ragged +raggeder +raggedest +raggedier +raggediest +raggedly +raggedness +raggedy +ragging +raging +raglan +raglans +ragout +ragouts +rags +ragtag +ragtags +ragtime +ragweed +raid +raided +raider +raiders +raiding +raids +rail +railed +railing +railings +railleries +raillery +railroad +railroaded +railroading +railroads +rails +railway +railways +raiment +rain +rainbow +rainbows +raincoat +raincoats +raindrop +raindrops +rained +rainfall +rainfalls +rainier +rainiest +raining +rainmaker +rainmakers +rains +rainstorm +rainstorms +rainwater +rainy +raise +raised +raises +raisin +raising +raisins +raja +rajah +rajahs +rajas +rake +raked +rakes +raking +rakish +rakishly +rakishness +rallied +rallies +rally +rallying +ram +ramble +rambled +rambler +ramblers +rambles +rambling +rambunctious +rambunctiousness +ramification +ramifications +ramified +ramifies +ramify +ramifying +rammed +ramming +ramp +rampage +rampaged +rampages +rampaging +rampant +rampantly +rampart +ramparts +ramps +ramrod +ramrodded +ramrodding +ramrods +rams +ramshackle +ran +ranch +ranched +rancher +ranchers +ranches +ranching +rancid +rancidity +rancor +rancorous +rancorously +randier +randiest +random +randomize +randomized +randomizes +randomizing +randomly +randomness +randy +rang +range +ranged +ranger +rangers +ranges +rangier +rangiest +ranginess +ranging +rangy +rank +ranked +ranker +rankest +ranking +rankings +rankle +rankled +rankles +rankling +rankness +ranks +ransack +ransacked +ransacking +ransacks +ransom +ransomed +ransoming +ransoms +rant +ranted +ranter +ranting +rants +rap +rapacious +rapaciously +rapaciousness +rapacity +rape +raped +rapes +rapid +rapider +rapidest +rapidity +rapidly +rapids +rapier +rapiers +rapine +raping +rapist +rapists +rapped +rapper +rappers +rapping +rapport +rapports +rapprochement +rapprochements +raps +rapscallion +rapscallions +rapt +rapture +raptures +rapturous +rare +rared +rarefied +rarefies +rarefy +rarefying +rarely +rareness +rarer +rares +rarest +raring +rarities +rarity +rascal +rascally +rascals +rash +rasher +rashers +rashes +rashest +rashly +rashness +rasp +raspberries +raspberry +rasped +raspier +raspiest +rasping +rasps +raspy +raster +rat +ratchet +ratcheted +ratcheting +ratchets +rate +rated +rates +rather +rathskeller +rathskellers +ratification +ratified +ratifies +ratify +ratifying +rating +ratings +ratio +ration +rational +rationale +rationales +rationalism +rationalist +rationalistic +rationalists +rationality +rationalization +rationalizations +rationalize +rationalized +rationalizes +rationalizing +rationally +rationals +rationed +rationing +rations +ratios +rats +rattan +rattans +ratted +rattier +rattiest +ratting +rattle +rattled +rattler +rattlers +rattles +rattlesnake +rattlesnakes +rattletrap +rattletraps +rattling +rattlings +rattrap +rattraps +ratty +raucous +raucously +raucousness +raunchier +raunchiest +raunchiness +raunchy +ravage +ravaged +ravages +ravaging +rave +raved +ravel +raveled +raveling +ravels +raven +ravened +ravening +ravenous +ravenously +ravens +raves +ravine +ravines +raving +ravings +ravioli +raviolis +ravish +ravished +ravishes +ravishing +ravishingly +ravishment +raw +rawboned +rawer +rawest +rawhide +rawness +ray +rayon +rays +raze +razed +razes +razing +razor +razors +razz +razzed +razzes +razzing +re +reach +reachable +reached +reaches +reaching +react +reacted +reacting +reaction +reactionaries +reactionary +reactions +reactivate +reactivated +reactivates +reactivating +reactivation +reactive +reactor +reactors +reacts +read +readabilities +readability +readable +reader +readers +readership +readerships +readied +readier +readies +readiest +readily +readiness +reading +readings +readjust +readjusted +readjusting +readjustment +readjustments +readjusts +readmit +readmits +readmitted +readmitting +readout +readouts +reads +ready +readying +reaffirm +reaffirmed +reaffirming +reaffirms +reagent +reagents +real +realer +realest +realign +realism +realist +realistic +realistically +realists +realities +reality +realizable +realization +realize +realized +realizes +realizing +reallocate +reallocated +reallocates +reallocating +reallocation +really +realm +realms +reals +realtor +realtors +realty +ream +reamed +reamer +reamers +reaming +reams +reanimate +reanimated +reanimates +reanimating +reap +reaped +reaper +reapers +reaping +reappear +reappearance +reappearances +reappeared +reappearing +reappears +reapplied +reapplies +reapply +reapplying +reappoint +reappointed +reappointing +reappointment +reappoints +reapportion +reapportioned +reapportioning +reapportionment +reapportions +reappraisal +reappraisals +reappraise +reappraised +reappraises +reappraising +reaps +rear +reared +rearing +rearm +rearmament +rearmed +rearming +rearmost +rearms +rearrange +rearranged +rearrangement +rearrangements +rearranges +rearranging +rears +rearward +rearwards +reason +reasonable +reasonableness +reasonably +reasoned +reasoning +reasons +reassemble +reassembled +reassembles +reassembling +reassert +reasserted +reasserting +reasserts +reassess +reassessed +reassesses +reassessing +reassessment +reassessments +reassign +reassigned +reassigning +reassigns +reassurance +reassurances +reassure +reassured +reassures +reassuring +reassuringly +reawaken +reawakened +reawakening +reawakens +rebate +rebated +rebates +rebating +rebel +rebelled +rebelling +rebellion +rebellions +rebellious +rebelliously +rebelliousness +rebels +rebind +rebinding +rebinds +rebirth +rebirths +reborn +rebound +rebounded +rebounding +rebounds +rebroadcast +rebroadcasting +rebroadcasts +rebuff +rebuffed +rebuffing +rebuffs +rebuild +rebuilding +rebuilds +rebuilt +rebuke +rebuked +rebukes +rebuking +rebus +rebuses +rebut +rebuts +rebuttal +rebuttals +rebutted +rebutting +recalcitrance +recalcitrant +recall +recalled +recalling +recalls +recant +recantation +recantations +recanted +recanting +recants +recap +recapitulate +recapitulated +recapitulates +recapitulating +recapitulation +recapitulations +recapped +recapping +recaps +recapture +recaptured +recaptures +recapturing +recast +recasting +recasts +recede +receded +recedes +receding +receipt +receipted +receipting +receipts +receivable +receive +received +receiver +receivers +receivership +receives +receiving +recent +recenter +recentest +recently +receptacle +receptacles +reception +receptionist +receptionists +receptions +receptive +receptively +receptiveness +receptivity +receptor +receptors +recess +recessed +recesses +recessing +recession +recessional +recessionals +recessions +recessive +recessives +recharge +rechargeable +recharged +recharges +recharging +recheck +rechecked +rechecking +rechecks +recherché +recidivism +recidivist +recidivists +recipe +recipes +recipient +recipients +reciprocal +reciprocally +reciprocals +reciprocate +reciprocated +reciprocates +reciprocating +reciprocation +reciprocity +recital +recitals +recitation +recitations +recitative +recitatives +recite +recited +recites +reciting +reckless +recklessly +recklessness +reckon +reckoned +reckoning +reckonings +reckons +reclaim +reclaimed +reclaiming +reclaims +reclamation +reclassified +reclassifies +reclassify +reclassifying +recline +reclined +recliner +recliners +reclines +reclining +recluse +recluses +reclusive +recognition +recognizable +recognizably +recognizance +recognize +recognized +recognizer +recognizes +recognizing +recoil +recoiled +recoiling +recoils +recollect +recollected +recollecting +recollection +recollections +recollects +recombination +recombine +recombined +recombines +recombining +recommence +recommenced +recommences +recommencing +recommend +recommendation +recommendations +recommended +recommending +recommends +recompense +recompensed +recompenses +recompensing +recompilation +recompile +recompiled +recompiling +reconcilable +reconcile +reconciled +reconciles +reconciliation +reconciliations +reconciling +recondite +recondition +reconditioned +reconditioning +reconditions +reconfiguration +reconfigure +reconfigured +reconnaissance +reconnaissances +reconnect +reconnected +reconnecting +reconnects +reconnoiter +reconnoitered +reconnoitering +reconnoiters +reconquer +reconquered +reconquering +reconquers +reconsider +reconsideration +reconsidered +reconsidering +reconsiders +reconstitute +reconstituted +reconstitutes +reconstituting +reconstruct +reconstructed +reconstructing +reconstruction +reconstructions +reconstructs +reconvene +reconvened +reconvenes +reconvening +recopied +recopies +recopy +recopying +record +recorded +recorder +recorders +recording +recordings +records +recount +recounted +recounting +recounts +recoup +recouped +recouping +recoups +recourse +recover +recoverable +recovered +recoveries +recovering +recovers +recovery +recreant +recreants +recreate +recreated +recreates +recreating +recreation +recreational +recreations +recriminate +recriminated +recriminates +recriminating +recrimination +recriminations +recrudescence +recruit +recruited +recruiter +recruiters +recruiting +recruitment +recruits +rectal +rectangle +rectangles +rectangular +rectifiable +rectification +rectifications +rectified +rectifier +rectifiers +rectifies +rectify +rectifying +rectilinear +rectitude +rector +rectories +rectors +rectory +rectum +rectums +recumbent +recuperate +recuperated +recuperates +recuperating +recuperation +recuperative +recur +recurred +recurrence +recurrences +recurrent +recurring +recurs +recursion +recursive +recursively +recyclable +recyclables +recycle +recycled +recycles +recycling +red +redbreast +redbreasts +redcap +redcaps +redcoat +redcoats +redden +reddened +reddening +reddens +redder +reddest +reddish +redecorate +redecorated +redecorates +redecorating +rededicate +rededicated +rededicates +rededicating +redeem +redeemable +redeemed +redeemer +redeemers +redeeming +redeems +redefine +redefined +redefines +redefining +redefinition +redemption +redeploy +redeployed +redeploying +redeployment +redeploys +redesign +redesigned +redesigning +redesigns +redevelop +redeveloped +redeveloping +redevelopment +redevelopments +redevelops +redhead +redheaded +redheads +redid +redirect +redirected +redirecting +redirection +redirects +rediscover +rediscovered +rediscovering +rediscovers +rediscovery +redistribute +redistributed +redistributes +redistributing +redistribution +redistributor +redistributors +redistrict +redistricted +redistricting +redistricts +redneck +rednecks +redness +redo +redoes +redoing +redolence +redolent +redone +redouble +redoubled +redoubles +redoubling +redoubt +redoubtable +redoubts +redound +redounded +redounding +redounds +redraft +redrafted +redrafting +redrafts +redraw +redrawing +redrawn +redraws +redress +redressed +redresses +redressing +redrew +reds +redskin +redskins +reduce +reduced +reduces +reducing +reduction +reductions +redundancies +redundancy +redundant +redundantly +redwood +redwoods +reed +reedier +reediest +reeds +reeducate +reeducated +reeducates +reeducating +reeducation +reedy +reef +reefed +reefer +reefers +reefing +reefs +reek +reeked +reeking +reeks +reel +reelect +reelected +reelecting +reelection +reelections +reelects +reeled +reeling +reels +reemerge +reemerged +reemerges +reemerging +reemphasize +reemphasized +reemphasizes +reemphasizing +reenact +reenacted +reenacting +reenactment +reenactments +reenacts +reenlist +reenlisted +reenlisting +reenlists +reenter +reentered +reentering +reenters +reentries +reentry +reestablish +reestablished +reestablishes +reestablishing +reevaluate +reevaluated +reevaluates +reevaluating +reeve +reeves +reeving +reexamine +reexamined +reexamines +reexamining +ref +refashion +refashioned +refashioning +refashions +refectories +refectory +refer +referee +refereed +refereeing +referees +reference +referenced +references +referencing +referendum +referendums +referent +referential +referral +referrals +referred +referring +refers +reffed +reffing +refile +refiled +refiles +refiling +refill +refillable +refilled +refilling +refills +refinance +refinanced +refinances +refinancing +refine +refined +refinement +refinements +refiner +refineries +refiners +refinery +refines +refining +refinish +refinished +refinishes +refinishing +refit +refits +refitted +refitting +reflect +reflected +reflecting +reflection +reflections +reflective +reflector +reflectors +reflects +reflex +reflexes +reflexive +reflexively +reflexives +refocus +refocused +refocuses +refocusing +reforest +reforestation +reforested +reforesting +reforests +reform +reformat +reformation +reformations +reformatories +reformatory +reformatted +reformatting +reformed +reformer +reformers +reforming +reforms +reformulate +reformulated +reformulates +reformulating +refract +refracted +refracting +refraction +refractories +refractory +refracts +refrain +refrained +refraining +refrains +refresh +refreshed +refresher +refreshers +refreshes +refreshing +refreshingly +refreshment +refreshments +refrigerant +refrigerants +refrigerate +refrigerated +refrigerates +refrigerating +refrigeration +refrigerator +refrigerators +refs +refuel +refueled +refueling +refuels +refuge +refugee +refugees +refuges +refulgence +refulgent +refund +refundable +refunded +refunding +refunds +refurbish +refurbished +refurbishes +refurbishing +refurbishment +refurbishments +refurnish +refurnished +refurnishes +refurnishing +refusal +refusals +refuse +refused +refuses +refusing +refutation +refutations +refute +refuted +refutes +refuting +regain +regained +regaining +regains +regal +regale +regaled +regales +regalia +regaling +regally +regard +regarded +regarding +regardless +regards +regatta +regattas +regencies +regency +regenerate +regenerated +regenerates +regenerating +regeneration +regenerative +regent +regents +reggae +regicide +regicides +regime +regimen +regimens +regiment +regimental +regimentation +regimented +regimenting +regiments +regimes +region +regional +regionalism +regionalisms +regionally +regions +register +registered +registering +registers +registrant +registrants +registrar +registrars +registration +registrations +registries +registry +regress +regressed +regresses +regressing +regression +regressions +regressive +regret +regretful +regretfully +regrets +regrettable +regrettably +regretted +regretting +regroup +regrouped +regrouping +regroups +regular +regularity +regularize +regularized +regularizes +regularizing +regularly +regulars +regulate +regulated +regulates +regulating +regulation +regulations +regulator +regulators +regulatory +regurgitate +regurgitated +regurgitates +regurgitating +regurgitation +rehab +rehabbed +rehabbing +rehabilitate +rehabilitated +rehabilitates +rehabilitating +rehabilitation +rehabs +rehash +rehashed +rehashes +rehashing +rehearsal +rehearsals +rehearse +rehearsed +rehearses +rehearsing +reheat +reheated +reheating +reheats +rehire +rehired +rehires +rehiring +reign +reigned +reigning +reigns +reimburse +reimbursed +reimbursement +reimbursements +reimburses +reimbursing +reimpose +reimposed +reimposes +reimposing +rein +reincarnate +reincarnated +reincarnates +reincarnating +reincarnation +reincarnations +reindeer +reined +reinforce +reinforced +reinforcement +reinforcements +reinforces +reinforcing +reining +reinitialize +reinitialized +reins +reinsert +reinserted +reinserting +reinserts +reinstall +reinstalled +reinstalling +reinstate +reinstated +reinstatement +reinstates +reinstating +reinterpret +reinterpretation +reinterpretations +reinterpreted +reinterpreting +reinterprets +reinvent +reinvented +reinventing +reinvents +reinvest +reinvested +reinvesting +reinvests +reissue +reissued +reissues +reissuing +reiterate +reiterated +reiterates +reiterating +reiteration +reiterations +reject +rejected +rejecting +rejection +rejections +rejects +rejoice +rejoiced +rejoices +rejoicing +rejoicings +rejoin +rejoinder +rejoinders +rejoined +rejoining +rejoins +rejuvenate +rejuvenated +rejuvenates +rejuvenating +rejuvenation +rekindle +rekindled +rekindles +rekindling +relabel +relabeled +relabeling +relabels +relaid +relapse +relapsed +relapses +relapsing +relate +related +relates +relating +relation +relational +relations +relationship +relationships +relative +relatively +relatives +relativistic +relativity +relax +relaxant +relaxants +relaxation +relaxations +relaxed +relaxes +relaxing +relay +relayed +relaying +relays +relearn +relearned +relearning +relearns +releasable +release +released +releases +releasing +relegate +relegated +relegates +relegating +relegation +relent +relented +relenting +relentless +relentlessly +relentlessness +relents +relevance +relevancy +relevant +relevantly +reliability +reliable +reliably +reliance +reliant +relic +relics +relied +relief +reliefs +relies +relieve +relieved +relieves +relieving +religion +religions +religious +religiously +relinquish +relinquished +relinquishes +relinquishing +relinquishment +relish +relished +relishes +relishing +relive +relived +relives +reliving +reload +reloaded +reloading +reloads +relocatable +relocate +relocated +relocates +relocating +relocation +reluctance +reluctant +reluctantly +rely +relying +remade +remain +remainder +remaindered +remainders +remained +remaining +remains +remake +remakes +remaking +remand +remanded +remanding +remands +remark +remarkable +remarkably +remarked +remarking +remarks +remarriage +remarriages +remarried +remarries +remarry +remarrying +rematch +rematches +remediable +remedial +remedied +remedies +remedy +remedying +remember +remembered +remembering +remembers +remembrance +remembrances +remind +reminded +reminder +reminders +reminding +reminds +reminisce +reminisced +reminiscence +reminiscences +reminiscent +reminisces +reminiscing +remiss +remission +remissions +remissness +remit +remits +remittance +remittances +remitted +remitting +remnant +remnants +remodel +remodeled +remodeling +remodels +remonstrance +remonstrances +remonstrate +remonstrated +remonstrates +remonstrating +remorse +remorseful +remorsefully +remorseless +remorselessly +remortgage +remortgaged +remortgages +remortgaging +remote +remotely +remoteness +remoter +remotes +remotest +remount +remounted +remounting +remounts +removable +removal +removals +remove +removed +remover +removers +removes +removing +remunerate +remunerated +remunerates +remunerating +remuneration +remunerations +remunerative +renaissance +renaissances +renal +rename +renamed +renames +renaming +renascence +renascences +renascent +rend +render +rendered +rendering +renderings +renders +rendezvous +rendezvoused +rendezvouses +rendezvousing +rending +rendition +renditions +rends +renegade +renegaded +renegades +renegading +renege +reneged +reneges +reneging +renegotiate +renegotiated +renegotiates +renegotiating +renew +renewable +renewal +renewals +renewed +renewing +renews +rennet +renounce +renounced +renounces +renouncing +renovate +renovated +renovates +renovating +renovation +renovations +renovator +renovators +renown +renowned +rent +rental +rentals +rented +renter +renters +renting +rents +renumber +renumbered +renumbering +renumbers +renunciation +renunciations +reoccupied +reoccupies +reoccupy +reoccupying +reoccur +reoccurred +reoccurring +reoccurs +reopen +reopened +reopening +reopens +reorder +reordered +reordering +reorders +reorg +reorganization +reorganizations +reorganize +reorganized +reorganizes +reorganizing +reorged +reorging +reorgs +rep +repackage +repackaged +repackages +repackaging +repaid +repaint +repainted +repainting +repaints +repair +repairable +repaired +repairing +repairman +repairmen +repairs +reparation +reparations +repartee +repast +repasts +repatriate +repatriated +repatriates +repatriating +repatriation +repay +repayable +repaying +repayment +repayments +repays +repeal +repealed +repealing +repeals +repeat +repeatable +repeatably +repeated +repeatedly +repeater +repeaters +repeating +repeats +repel +repelled +repellent +repellents +repelling +repels +repent +repentance +repentant +repented +repenting +repents +repercussion +repercussions +repertoire +repertoires +repertories +repertory +repetition +repetitions +repetitious +repetitive +rephrase +rephrased +rephrases +rephrasing +replace +replaceable +replaced +replacement +replacements +replaces +replacing +replay +replayed +replaying +replays +replenish +replenished +replenishes +replenishing +replenishment +replete +repleted +repletes +repleting +repletion +replica +replicas +replicate +replicated +replicates +replicating +replication +replications +replied +replies +reply +replying +report +reportage +reported +reportedly +reporter +reporters +reporting +reports +repose +reposed +reposeful +reposes +reposing +repositories +repository +repossess +repossessed +repossesses +repossessing +repossession +repossessions +reprehend +reprehended +reprehending +reprehends +reprehensible +reprehensibly +represent +representation +representational +representations +representative +representatives +represented +representing +represents +repress +repressed +represses +repressing +repression +repressions +repressive +reprieve +reprieved +reprieves +reprieving +reprimand +reprimanded +reprimanding +reprimands +reprint +reprinted +reprinting +reprints +reprisal +reprisals +reprise +reprises +reprising +reprized +reproach +reproached +reproaches +reproachful +reproachfully +reproaching +reprobate +reprobates +reprocess +reprocessed +reprocesses +reprocessing +reproduce +reproduced +reproduces +reproducible +reproducing +reproduction +reproductions +reproductive +reprogram +reprogrammed +reprogramming +reprograms +reproof +reproofed +reproofing +reproofs +reprove +reproved +reproves +reproving +reps +reptile +reptiles +reptilian +reptilians +republic +republican +republicanism +republicans +republics +republish +republished +republishes +republishing +repudiate +repudiated +repudiates +repudiating +repudiation +repudiations +repugnance +repugnant +repulse +repulsed +repulses +repulsing +repulsion +repulsive +repulsively +repulsiveness +reputable +reputably +reputation +reputations +repute +reputed +reputedly +reputes +reputing +request +requested +requester +requesting +requests +requiem +requiems +require +required +requirement +requirements +requires +requiring +requisite +requisites +requisition +requisitioned +requisitioning +requisitions +requital +requite +requited +requites +requiting +reran +reread +rereading +rereads +reroute +rerouted +reroutes +rerouting +rerun +rerunning +reruns +resale +resales +reschedule +rescheduled +reschedules +rescheduling +rescind +rescinded +rescinding +rescinds +rescission +rescue +rescued +rescuer +rescuers +rescues +rescuing +research +researched +researcher +researchers +researches +researching +resell +reselling +resells +resemblance +resemblances +resemble +resembled +resembles +resembling +resend +resent +resented +resentful +resentfully +resenting +resentment +resentments +resents +reservation +reservations +reserve +reserved +reservedly +reserves +reserving +reservist +reservists +reservoir +reservoirs +reset +resets +resetting +resettle +resettled +resettles +resettling +reshuffle +reshuffled +reshuffles +reshuffling +reside +resided +residence +residences +residencies +residency +resident +residential +residents +resides +residing +residual +residuals +residue +residues +resign +resignation +resignations +resigned +resignedly +resigning +resigns +resilience +resiliency +resilient +resin +resinous +resins +resist +resistance +resistances +resistant +resisted +resister +resisters +resisting +resistor +resistors +resists +resold +resolute +resolutely +resoluteness +resolution +resolutions +resolve +resolved +resolver +resolves +resolving +resonance +resonances +resonant +resonantly +resonate +resonated +resonates +resonating +resonator +resonators +resort +resorted +resorting +resorts +resound +resounded +resounding +resoundingly +resounds +resource +resourceful +resourcefully +resourcefulness +resources +respect +respectability +respectable +respectably +respected +respectful +respectfully +respecting +respective +respectively +respects +respell +respelled +respelling +respells +respiration +respirator +respirators +respiratory +respire +respired +respires +respiring +respite +respites +resplendence +resplendent +resplendently +respond +responded +respondent +respondents +responding +responds +response +responses +responsibilities +responsibility +responsible +responsibly +responsive +responsively +responsiveness +rest +restart +restarted +restarting +restarts +restate +restated +restatement +restatements +restates +restating +restaurant +restaurants +restaurateur +restaurateurs +rested +restful +restfuller +restfullest +restfully +restfulness +resting +restitution +restive +restively +restiveness +restless +restlessly +restlessness +restock +restocked +restocking +restocks +restoration +restorations +restorative +restoratives +restore +restored +restorer +restorers +restores +restoring +restrain +restrained +restraining +restrains +restraint +restraints +restrict +restricted +restricting +restriction +restrictions +restrictive +restrictively +restricts +restroom +restrooms +restructure +restructured +restructures +restructuring +restructurings +rests +restudied +restudies +restudy +restudying +resubmit +resubmits +resubmitted +resubmitting +result +resultant +resultants +resulted +resulting +results +resume +resumed +resumes +resuming +resumption +resumptions +resupplied +resupplies +resupply +resupplying +resurface +resurfaced +resurfaces +resurfacing +resurgence +resurgences +resurgent +resurrect +resurrected +resurrecting +resurrection +resurrections +resurrects +resuscitate +resuscitated +resuscitates +resuscitating +resuscitation +resuscitator +resuscitators +retail +retailed +retailer +retailers +retailing +retails +retain +retained +retainer +retainers +retaining +retains +retake +retaken +retakes +retaking +retaliate +retaliated +retaliates +retaliating +retaliation +retaliations +retaliatory +retard +retardant +retardants +retardation +retarded +retarding +retards +retch +retched +retches +retching +retell +retelling +retells +retention +retentive +retentiveness +rethink +rethinking +rethinks +rethought +reticence +reticent +retina +retinal +retinas +retinue +retinues +retire +retired +retiree +retirees +retirement +retirements +retires +retiring +retold +retook +retool +retooled +retooling +retools +retort +retorted +retorting +retorts +retouch +retouched +retouches +retouching +retrace +retraced +retraces +retracing +retract +retractable +retracted +retracting +retraction +retractions +retracts +retrain +retrained +retraining +retrains +retread +retreaded +retreading +retreads +retreat +retreated +retreating +retreats +retrench +retrenched +retrenches +retrenching +retrenchment +retrenchments +retrial +retrials +retribution +retributions +retributive +retried +retries +retrievable +retrieval +retrievals +retrieve +retrieved +retriever +retrievers +retrieves +retrieving +retroactive +retroactively +retrod +retrodden +retrofit +retrofits +retrofitted +retrofitting +retrograde +retrograded +retrogrades +retrograding +retrogress +retrogressed +retrogresses +retrogressing +retrogression +retrogressive +retrorocket +retrorockets +retrospect +retrospected +retrospecting +retrospection +retrospective +retrospectively +retrospectives +retrospects +retry +retrying +return +returnable +returnables +returned +returnee +returnees +returning +returns +retweet +retweeted +retweeting +retweets +retype +retyped +retypes +retyping +reunification +reunified +reunifies +reunify +reunifying +reunion +reunions +reunite +reunited +reunites +reuniting +reupholster +reupholstered +reupholstering +reupholsters +reusable +reuse +reused +reuses +reusing +rev +revaluation +revaluations +revalue +revalued +revalues +revaluing +revamp +revamped +revamping +revamps +reveal +revealed +revealing +revealings +reveals +reveille +revel +revelation +revelations +reveled +reveler +revelers +reveling +revelries +revelry +revels +revenge +revenged +revengeful +revenges +revenging +revenue +revenues +reverberate +reverberated +reverberates +reverberating +reverberation +reverberations +revere +revered +reverence +reverenced +reverences +reverencing +reverend +reverends +reverent +reverential +reverently +reveres +reverie +reveries +revering +reversal +reversals +reverse +reversed +reverses +reversible +reversing +reversion +revert +reverted +reverting +reverts +review +reviewed +reviewer +reviewers +reviewing +reviews +revile +reviled +revilement +reviler +revilers +reviles +reviling +revise +revised +revises +revising +revision +revisions +revisit +revisited +revisiting +revisits +revitalization +revitalize +revitalized +revitalizes +revitalizing +revival +revivalist +revivalists +revivals +revive +revived +revives +revivification +revivified +revivifies +revivify +revivifying +reviving +revocable +revocation +revocations +revoke +revoked +revokes +revoking +revolt +revolted +revolting +revoltingly +revolts +revolution +revolutionaries +revolutionary +revolutionist +revolutionists +revolutionize +revolutionized +revolutionizes +revolutionizing +revolutions +revolve +revolved +revolver +revolvers +revolves +revolving +revs +revue +revues +revulsion +revved +revving +reward +rewarded +rewarding +rewards +rewind +rewindable +rewinding +rewinds +rewire +rewired +rewires +rewiring +reword +reworded +rewording +rewords +rework +reworked +reworking +reworks +rewound +rewrite +rewrites +rewriting +rewritten +rewrote +rhapsodic +rhapsodies +rhapsodize +rhapsodized +rhapsodizes +rhapsodizing +rhapsody +rhea +rheas +rheostat +rheostats +rhetoric +rhetorical +rhetorically +rhetorician +rhetoricians +rheum +rheumatic +rheumatics +rheumatism +rheumy +rhinestone +rhinestones +rhino +rhinoceros +rhinoceroses +rhinos +rhizome +rhizomes +rho +rhodium +rhododendron +rhododendrons +rhomboid +rhomboids +rhombus +rhombuses +rhubarb +rhubarbs +rhyme +rhymed +rhymes +rhyming +rhythm +rhythmic +rhythmical +rhythmically +rhythms +rib +ribald +ribaldry +ribbed +ribbing +ribbon +ribbons +riboflavin +ribs +rice +riced +rices +rich +richer +riches +richest +richly +richness +ricing +rick +ricked +ricketier +ricketiest +rickets +rickety +ricking +ricks +rickshaw +rickshaws +ricochet +ricocheted +ricocheting +ricochets +ricotta +rid +riddance +ridden +ridding +riddle +riddled +riddles +riddling +ride +rider +riders +rides +ridge +ridged +ridgepole +ridgepoles +ridges +ridging +ridicule +ridiculed +ridicules +ridiculing +ridiculous +ridiculously +ridiculousness +riding +rids +rife +rifer +rifest +riff +riffed +riffing +riffle +riffled +riffles +riffling +riffraff +riffs +rifle +rifled +rifleman +riflemen +rifles +rifling +rift +rifted +rifting +rifts +rig +rigamarole +rigamaroles +rigged +rigging +right +righted +righteous +righteously +righteousness +righter +rightest +rightful +rightfully +rightfulness +righting +rightist +rightists +rightly +rightmost +rightness +rights +rigid +rigidity +rigidly +rigidness +rigmarole +rigmaroles +rigor +rigorous +rigorously +rigors +rigs +rile +riled +riles +riling +rill +rills +rim +rime +rimed +rimes +riming +rimmed +rimming +rims +rind +rinds +ring +ringed +ringer +ringers +ringing +ringleader +ringleaders +ringlet +ringlets +ringmaster +ringmasters +rings +ringside +ringtone +ringtones +ringworm +rink +rinks +rinse +rinsed +rinses +rinsing +riot +rioted +rioter +rioters +rioting +riotous +riots +rip +ripe +ripely +ripen +ripened +ripeness +ripening +ripens +riper +ripest +riposte +riposted +ripostes +riposting +ripped +ripper +rippers +ripping +ripple +rippled +ripples +rippling +rips +ripsaw +ripsaws +rise +risen +riser +risers +rises +risible +rising +risk +risked +riskier +riskiest +riskiness +risking +risks +risky +risqué +rite +rites +ritual +ritualism +ritualistic +ritually +rituals +ritzier +ritziest +ritzy +rival +rivaled +rivaling +rivalries +rivalry +rivals +riven +river +riverbed +riverbeds +riverfront +rivers +riverside +riversides +rivet +riveted +riveter +riveters +riveting +rivets +rivulet +rivulets +roach +roaches +road +roadbed +roadbeds +roadblock +roadblocked +roadblocking +roadblocks +roadhouse +roadhouses +roadkill +roadrunner +roadrunners +roads +roadshow +roadside +roadsides +roadster +roadsters +roadway +roadways +roadwork +roadworthy +roam +roamed +roamer +roamers +roaming +roams +roan +roans +roar +roared +roaring +roars +roast +roasted +roaster +roasters +roasting +roasts +rob +robbed +robber +robberies +robbers +robbery +robbing +robe +robed +robes +robin +robing +robins +robocall +robocalled +robocalling +robocalls +robot +robotic +robotics +robots +robs +robust +robuster +robustest +robustly +robustness +rock +rocked +rocker +rockers +rocket +rocketed +rocketing +rocketry +rockets +rockier +rockiest +rockiness +rocking +rocks +rocky +rococo +rod +rode +rodent +rodents +rodeo +rodeos +rods +roe +roebuck +roebucks +roentgen +roentgens +roes +roger +rogered +rogering +rogers +rogue +roguery +rogues +roguish +roguishly +roil +roiled +roiling +roils +roister +roistered +roisterer +roisterers +roistering +roisters +role +roles +roll +rollback +rollbacks +rolled +roller +rollers +rollerskating +rollick +rollicked +rollicking +rollicks +rolling +rolls +romaine +roman +romance +romanced +romances +romancing +romantic +romantically +romanticism +romanticist +romanticists +romanticize +romanticized +romanticizes +romanticizing +romantics +romp +romped +romper +rompers +romping +romps +rood +roods +roof +roofed +roofer +roofers +roofing +roofs +rooftop +rooftops +rook +rooked +rookeries +rookery +rookie +rookies +rooking +rooks +room +roomed +roomer +roomers +roomful +roomfuls +roomier +roomiest +roominess +rooming +roommate +roommates +rooms +roomy +roost +roosted +rooster +roosters +roosting +roosts +root +rooted +rooter +rooting +rootless +roots +rope +roped +ropes +roping +rosaries +rosary +rose +roseate +rosebud +rosebuds +rosebush +rosebushes +rosemary +roses +rosette +rosettes +rosewood +rosewoods +rosier +rosiest +rosily +rosin +rosined +rosiness +rosining +rosins +roster +rosters +rostrum +rostrums +rosy +rot +rotaries +rotary +rotate +rotated +rotates +rotating +rotation +rotational +rotations +rote +rotisserie +rotisseries +rotogravure +rotogravures +rotor +rotors +rots +rotted +rotten +rottener +rottenest +rottenness +rotting +rotund +rotunda +rotundas +rotundity +rotundness +rouge +rouged +rouges +rough +roughage +roughed +roughen +roughened +roughening +roughens +rougher +roughest +roughhouse +roughhoused +roughhouses +roughhousing +roughing +roughly +roughneck +roughnecked +roughnecking +roughnecks +roughness +roughs +roughshod +rouging +roulette +round +roundabout +roundabouts +rounded +roundelay +roundelays +rounder +roundest +roundhouse +roundhouses +rounding +roundish +roundly +roundness +rounds +roundup +roundups +roundworm +roundworms +rouse +roused +rouses +rousing +roustabout +roustabouts +rout +route +routed +routeing +router +routes +routine +routinely +routines +routing +routinize +routinized +routinizes +routinizing +routs +roué +roués +rove +roved +rover +rovers +roves +roving +row +rowboat +rowboats +rowdier +rowdies +rowdiest +rowdiness +rowdy +rowdyism +rowed +rowel +roweled +roweling +rowels +rower +rowers +rowing +rows +royal +royalist +royalists +royally +royals +royalties +royalty +rs +rub +rubbed +rubber +rubberize +rubberized +rubberizes +rubberizing +rubberneck +rubbernecked +rubbernecking +rubbernecks +rubbers +rubbery +rubbing +rubbish +rubbished +rubbishes +rubbishing +rubbishy +rubble +rubdown +rubdowns +rube +rubella +rubes +rubicund +rubier +rubies +rubiest +ruble +rubles +rubric +rubrics +rubs +ruby +rucksack +rucksacks +ruckus +ruckuses +rudder +rudders +ruddier +ruddiest +ruddiness +ruddy +rude +rudely +rudeness +ruder +rudest +rudiment +rudimentary +rudiments +rue +rued +rueful +ruefully +rues +ruff +ruffed +ruffian +ruffians +ruffing +ruffle +ruffled +ruffles +ruffling +ruffs +rug +rugby +rugged +ruggeder +ruggedest +ruggedly +ruggedness +rugrat +rugrats +rugs +ruin +ruination +ruined +ruing +ruining +ruinous +ruinously +ruins +rule +ruled +ruler +rulers +rules +ruling +rulings +rum +rumba +rumbaed +rumbaing +rumbas +rumble +rumbled +rumbles +rumbling +rumblings +ruminant +ruminants +ruminate +ruminated +ruminates +ruminating +rumination +ruminations +rummage +rummaged +rummages +rummaging +rummer +rummest +rummy +rumor +rumored +rumoring +rumors +rump +rumple +rumpled +rumples +rumpling +rumps +rumpus +rumpuses +rums +run +runabout +runabouts +runaround +runarounds +runaway +runaways +rundown +rundowns +rune +runes +rung +rungs +runnel +runnels +runner +runners +runnier +runniest +running +runny +runoff +runoffs +runs +runt +runts +runway +runways +rupee +rupees +rupture +ruptured +ruptures +rupturing +rural +ruse +ruses +rush +rushed +rushes +rushing +rusk +rusks +russet +russets +rust +rusted +rustic +rustically +rusticity +rustics +rustier +rustiest +rustiness +rusting +rustle +rustled +rustler +rustlers +rustles +rustling +rustproof +rustproofed +rustproofing +rustproofs +rusts +rusty +rut +rutabaga +rutabagas +ruthless +ruthlessly +ruthlessness +ruts +rutted +rutting +rye +s +sabbatical +sabbaticals +saber +sabers +sable +sables +sabotage +sabotaged +sabotages +sabotaging +saboteur +saboteurs +sabre +sabres +sac +saccharin +saccharine +sacerdotal +sachem +sachems +sachet +sachets +sack +sackcloth +sacked +sackful +sackfuls +sacking +sacks +sacrament +sacramental +sacraments +sacred +sacredly +sacredness +sacrifice +sacrificed +sacrifices +sacrificial +sacrificing +sacrilege +sacrileges +sacrilegious +sacristan +sacristans +sacristies +sacristy +sacrosanct +sacs +sad +sadden +saddened +saddening +saddens +sadder +saddest +saddle +saddlebag +saddlebags +saddled +saddles +saddling +sades +sadism +sadist +sadistic +sadistically +sadists +sadly +sadness +safari +safaried +safariing +safaris +safe +safeguard +safeguarded +safeguarding +safeguards +safekeeping +safely +safeness +safer +safes +safest +safeties +safety +safflower +safflowers +saffron +saffrons +sag +saga +sagacious +sagacity +sagas +sage +sagebrush +sager +sages +sagest +sagged +sagging +sago +sags +saguaro +saguaros +sahib +sahibs +said +sail +sailboard +sailboards +sailboat +sailboats +sailcloth +sailed +sailfish +sailfishes +sailing +sailings +sailor +sailors +sails +saint +sainthood +saintlier +saintliest +saintliness +saintly +saints +saith +sake +saki +salaam +salaamed +salaaming +salaams +salable +salacious +salaciously +salaciousness +salad +salads +salamander +salamanders +salami +salamis +salaried +salaries +salary +sale +saleable +sales +salesclerk +salesclerks +salesgirl +salesgirls +salesman +salesmanship +salesmen +salespeople +salesperson +salespersons +saleswoman +saleswomen +salience +salient +salients +saline +salines +salinity +saliva +salivary +salivate +salivated +salivates +salivating +salivation +sallied +sallies +sallow +sallower +sallowest +sally +sallying +salmon +salmonella +salmonellae +salmons +salon +salons +saloon +saloons +salsa +salsas +salt +saltcellar +saltcellars +salted +salter +saltest +saltier +saltiest +saltine +saltines +saltiness +salting +saltpeter +salts +saltshaker +saltshakers +saltwater +salty +salubrious +salutary +salutation +salutations +salute +saluted +salutes +saluting +salvage +salvageable +salvaged +salvages +salvaging +salvation +salve +salved +salver +salvers +salves +salving +salvo +salvos +samba +sambaed +sambaing +sambas +same +sameness +sames +samovar +samovars +sampan +sampans +sample +sampled +sampler +samplers +samples +sampling +samplings +samurai +sanatorium +sanatoriums +sanctification +sanctified +sanctifies +sanctify +sanctifying +sanctimonious +sanctimoniously +sanction +sanctioned +sanctioning +sanctions +sanctity +sanctuaries +sanctuary +sanctum +sanctums +sand +sandal +sandals +sandalwood +sandbag +sandbagged +sandbagging +sandbags +sandbank +sandbanks +sandbar +sandbars +sandblast +sandblasted +sandblaster +sandblasters +sandblasting +sandblasts +sandbox +sandboxes +sandcastle +sandcastles +sanded +sander +sanders +sandhog +sandhogs +sandier +sandiest +sandiness +sanding +sandlot +sandlots +sandman +sandmen +sandpaper +sandpapered +sandpapering +sandpapers +sandpiper +sandpipers +sands +sandstone +sandstorm +sandstorms +sandwich +sandwiched +sandwiches +sandwiching +sandy +sane +sanely +saner +sanest +sang +sangfroid +sangs +sanguinary +sanguine +sanitarium +sanitariums +sanitary +sanitation +sanitize +sanitized +sanitizes +sanitizing +sanity +sank +sans +sanserif +sap +sapience +sapient +sapling +saplings +sapped +sapphire +sapphires +sappier +sappiest +sapping +sappy +saprophyte +saprophytes +saps +sapsucker +sapsuckers +sarcasm +sarcasms +sarcastic +sarcastically +sarcoma +sarcomas +sarcophagi +sarcophagus +sardine +sardines +sardonic +sardonically +sari +saris +sarong +sarongs +sarsaparilla +sarsaparillas +sartorial +sartorially +sash +sashay +sashayed +sashaying +sashays +sashes +sass +sassafras +sassafrases +sassed +sasses +sassier +sassiest +sassing +sassy +sat +satanic +satanically +satanism +satay +satchel +satchels +sate +sated +sateen +satellite +satellited +satellites +satelliting +sates +satiate +satiated +satiates +satiating +satiety +satin +sating +satinwood +satinwoods +satiny +satire +satires +satirical +satirically +satirist +satirists +satirize +satirized +satirizes +satirizing +satisfaction +satisfactions +satisfactorily +satisfactory +satisfied +satisfies +satisfy +satisfying +satrap +satraps +saturate +saturated +saturates +saturating +saturation +saturnine +satyr +satyrs +sauce +sauced +saucepan +saucepans +saucer +saucers +sauces +saucier +sauciest +saucily +sauciness +saucing +saucy +sauerkraut +sauna +saunaed +saunaing +saunas +saunter +sauntered +sauntering +saunters +sausage +sausages +sauté +sautéed +sautéing +sautés +savage +savaged +savagely +savageness +savager +savageries +savagery +savages +savagest +savaging +savanna +savannas +savant +savants +save +saved +saver +savers +saves +saving +savings +savior +saviors +savor +savored +savorier +savories +savoriest +savoring +savors +savory +savvied +savvier +savvies +savviest +savvy +savvying +saw +sawdust +sawed +sawhorse +sawhorses +sawing +sawmill +sawmills +saws +sawyer +sawyers +sax +saxes +saxophone +saxophones +saxophonist +saxophonists +say +saying +sayings +says +scab +scabbard +scabbards +scabbed +scabbier +scabbiest +scabbing +scabby +scabies +scabrous +scabs +scad +scads +scaffold +scaffolding +scaffolds +scalar +scalars +scalawag +scalawags +scald +scalded +scalding +scalds +scale +scaled +scalene +scales +scalier +scaliest +scaling +scallion +scallions +scallop +scalloped +scalloping +scallops +scallywag +scallywags +scalp +scalped +scalpel +scalpels +scalper +scalpers +scalping +scalps +scaly +scam +scammed +scammer +scammers +scamming +scamp +scamper +scampered +scampering +scampers +scampi +scamps +scams +scan +scandal +scandalize +scandalized +scandalizes +scandalizing +scandalmonger +scandalmongers +scandalous +scandalously +scandals +scanned +scanner +scanners +scanning +scans +scansion +scant +scanted +scanter +scantest +scantier +scanties +scantiest +scantily +scantiness +scanting +scants +scanty +scapegoat +scapegoated +scapegoating +scapegoats +scapula +scapulae +scar +scarab +scarabs +scarce +scarcely +scarceness +scarcer +scarcest +scarcity +scare +scarecrow +scarecrows +scared +scares +scarf +scarfed +scarfing +scarfs +scarier +scariest +scarified +scarifies +scarify +scarifying +scaring +scarlet +scarred +scarring +scars +scarves +scary +scat +scathing +scathingly +scatological +scats +scatted +scatter +scatterbrain +scatterbrained +scatterbrains +scattered +scattering +scatters +scatting +scavenge +scavenged +scavenger +scavengers +scavenges +scavenging +scenario +scenarios +scene +scenery +scenes +scenic +scenically +scent +scented +scenting +scents +scepter +scepters +schedule +scheduled +scheduler +schedulers +schedules +scheduling +schema +schematic +schematically +schematics +scheme +schemed +schemer +schemers +schemes +scheming +scherzo +scherzos +schism +schismatic +schismatics +schisms +schist +schizoid +schizoids +schizophrenia +schizophrenic +schizophrenics +schlemiel +schlemiels +schlep +schlepp +schlepped +schlepping +schlepps +schleps +schlock +schlocky +schmaltz +schmaltzier +schmaltziest +schmaltzy +schmooze +schmoozed +schmoozes +schmoozing +schmuck +schmucks +schnapps +schnauzer +schnauzers +scholar +scholarly +scholars +scholarship +scholarships +scholastic +scholastically +school +schoolbook +schoolbooks +schoolboy +schoolboys +schoolchild +schoolchildren +schooldays +schooled +schoolgirl +schoolgirls +schoolhouse +schoolhouses +schooling +schoolmarm +schoolmarms +schoolmaster +schoolmasters +schoolmate +schoolmates +schoolmistress +schoolmistresses +schoolroom +schoolrooms +schools +schoolteacher +schoolteachers +schoolwork +schoolyard +schoolyards +schooner +schooners +schuss +schussed +schusses +schussing +schwa +schwas +sciatic +sciatica +science +sciences +scientific +scientifically +scientist +scientists +scimitar +scimitars +scintilla +scintillas +scintillate +scintillated +scintillates +scintillating +scintillation +scion +scions +scissor +scissors +sclerosis +sclerotic +scoff +scoffed +scoffing +scofflaw +scofflaws +scoffs +scold +scolded +scolding +scoldings +scolds +scoliosis +sconce +sconces +scone +scones +scoop +scooped +scooping +scoops +scoot +scooted +scooter +scooters +scooting +scoots +scope +scoped +scopes +scoping +scorch +scorched +scorcher +scorchers +scorches +scorching +score +scoreboard +scoreboards +scorecard +scorecards +scored +scoreless +scorer +scorers +scores +scoring +scorn +scorned +scornful +scornfully +scorning +scorns +scorpion +scorpions +scotch +scotched +scotches +scotching +scotchs +scoundrel +scoundrels +scour +scoured +scourge +scourged +scourges +scourging +scouring +scours +scout +scouted +scouting +scoutmaster +scoutmasters +scouts +scow +scowl +scowled +scowling +scowls +scows +scrabble +scrabbled +scrabbles +scrabbling +scragglier +scraggliest +scraggly +scram +scramble +scrambled +scrambler +scramblers +scrambles +scrambling +scrammed +scramming +scrams +scrap +scrapbook +scrapbooks +scrape +scraped +scraper +scrapers +scrapes +scraping +scrapped +scrappier +scrappiest +scrapping +scrappy +scraps +scratch +scratched +scratches +scratchier +scratchiest +scratchiness +scratching +scratchy +scrawl +scrawled +scrawling +scrawls +scrawnier +scrawniest +scrawny +scream +screamed +screaming +screams +screech +screeched +screeches +screechier +screechiest +screeching +screechy +screen +screened +screening +screenings +screenplay +screenplays +screens +screenshot +screenshots +screenwriter +screenwriters +screw +screwball +screwballs +screwdriver +screwdrivers +screwed +screwier +screwiest +screwing +screws +screwy +scribble +scribbled +scribbler +scribblers +scribbles +scribbling +scribe +scribes +scrimmage +scrimmaged +scrimmages +scrimmaging +scrimp +scrimped +scrimping +scrimps +scrimshaw +scrimshawed +scrimshawing +scrimshaws +scrip +scrips +script +scripted +scripting +scripts +scriptural +scripture +scriptures +scriptwriter +scriptwriters +scrod +scrofula +scroll +scrolled +scrolling +scrolls +scrooge +scrooges +scrota +scrotum +scrounge +scrounged +scrounger +scroungers +scrounges +scrounging +scrub +scrubbed +scrubber +scrubbers +scrubbier +scrubbiest +scrubbing +scrubby +scrubs +scruff +scruffier +scruffiest +scruffs +scruffy +scrumptious +scrunch +scrunched +scrunches +scrunchie +scrunchies +scrunching +scrunchy +scruple +scrupled +scruples +scrupling +scrupulous +scrupulously +scrutinize +scrutinized +scrutinizes +scrutinizing +scrutiny +scuba +scubaed +scubaing +scubas +scud +scudded +scudding +scuds +scuff +scuffed +scuffing +scuffle +scuffled +scuffles +scuffling +scuffs +scull +sculled +sculleries +scullery +sculling +scullion +scullions +sculls +sculpt +sculpted +sculpting +sculptor +sculptors +sculpts +sculptural +sculpture +sculptured +sculptures +sculpturing +scum +scumbag +scumbags +scummed +scummier +scummiest +scumming +scummy +scums +scupper +scuppered +scuppering +scuppers +scurf +scurfy +scurried +scurries +scurrilous +scurrilously +scurry +scurrying +scurvier +scurviest +scurvy +scuttle +scuttlebutt +scuttled +scuttles +scuttling +scuzzier +scuzziest +scuzzy +scythe +scythed +scythes +scything +sea +seabed +seabeds +seabird +seabirds +seaboard +seaboards +seacoast +seacoasts +seafarer +seafarers +seafaring +seafood +seagoing +seal +sealant +sealants +sealed +sealer +sealers +sealing +seals +sealskin +seam +seaman +seamanship +seamed +seamen +seamier +seamiest +seaming +seamless +seams +seamstress +seamstresses +seamy +seaplane +seaplanes +seaport +seaports +sear +search +searched +searcher +searchers +searches +searching +searchingly +searchlight +searchlights +seared +searing +sears +seas +seascape +seascapes +seashell +seashells +seashore +seashores +seasick +seasickness +seaside +seasides +season +seasonable +seasonal +seasonally +seasoned +seasoning +seasonings +seasons +seat +seated +seating +seats +seaward +seawards +seaway +seaways +seaweed +seaworthy +sebaceous +secede +seceded +secedes +seceding +secession +secessionist +secessionists +seclude +secluded +secludes +secluding +seclusion +seclusive +second +secondaries +secondarily +secondary +seconded +secondhand +seconding +secondly +seconds +secrecy +secret +secretarial +secretariat +secretariats +secretaries +secretary +secrete +secreted +secretes +secreting +secretion +secretions +secretive +secretively +secretiveness +secretly +secrets +secs +sect +sectarian +sectarianism +sectarians +section +sectional +sectionalism +sectionals +sectioned +sectioning +sections +sector +sectors +sects +secular +secularism +secularization +secularize +secularized +secularizes +secularizing +secure +secured +securely +securer +secures +securest +securing +securities +security +sedan +sedans +sedate +sedated +sedately +sedater +sedates +sedatest +sedating +sedation +sedative +sedatives +sedentary +sedge +sediment +sedimentary +sedimentation +sediments +sedition +seditious +seduce +seduced +seducer +seducers +seduces +seducing +seduction +seductions +seductive +seductively +sedulous +see +seed +seeded +seedier +seediest +seediness +seeding +seedless +seedling +seedlings +seeds +seedy +seeing +seeings +seek +seeker +seekers +seeking +seeks +seem +seemed +seeming +seemingly +seemlier +seemliest +seemliness +seemly +seems +seen +seep +seepage +seeped +seeping +seeps +seer +seers +seersucker +sees +seesaw +seesawed +seesawing +seesaws +seethe +seethed +seethes +seething +segment +segmentation +segmented +segmenting +segments +segregate +segregated +segregates +segregating +segregation +segregationist +segregationists +segue +segued +segueing +segues +seismic +seismically +seismograph +seismographic +seismographs +seismologist +seismologists +seismology +seize +seized +seizes +seizing +seizure +seizures +seldom +select +selected +selecting +selection +selections +selective +selectively +selectivity +selectman +selectmen +selector +selectors +selects +selenium +self +selfie +selfies +selfish +selfishly +selfishness +selfless +selflessly +selflessness +selfsame +sell +seller +sellers +selling +selloff +selloffs +sellout +sellouts +sells +seltzer +selvage +selvages +selvedge +selvedges +selves +semantic +semantically +semantics +semaphore +semaphored +semaphores +semaphoring +semblance +semblances +semen +semester +semesters +semi +semiannual +semiautomatic +semiautomatics +semicircle +semicircles +semicircular +semicolon +semicolons +semiconductor +semiconductors +semiconscious +semifinal +semifinalist +semifinalists +semifinals +semimonthlies +semimonthly +seminal +seminar +seminarian +seminarians +seminaries +seminars +seminary +semiotics +semipermeable +semiprecious +semiprivate +semiprofessional +semiprofessionals +semiretired +semis +semiskilled +semitone +semitones +semitrailer +semitrailers +semitropical +semiweeklies +semiweekly +senate +senates +senator +senatorial +senators +send +sender +senders +sending +sends +senile +senility +senior +seniority +seniors +senna +sensation +sensational +sensationalism +sensationalist +sensationalists +sensationally +sensations +sense +sensed +senseless +senselessly +senselessness +senses +sensibilities +sensibility +sensible +sensibly +sensing +sensitive +sensitively +sensitiveness +sensitives +sensitivities +sensitivity +sensitization +sensitize +sensitized +sensitizes +sensitizing +sensor +sensors +sensory +sensual +sensuality +sensually +sensuous +sensuously +sensuousness +sent +sentence +sentenced +sentences +sentencing +sententious +sentience +sentient +sentiment +sentimental +sentimentalism +sentimentalist +sentimentalists +sentimentality +sentimentalize +sentimentalized +sentimentalizes +sentimentalizing +sentimentally +sentiments +sentinel +sentinels +sentries +sentry +sepal +sepals +separable +separate +separated +separately +separates +separating +separation +separations +separatism +separatist +separatists +separator +separators +sepia +sepsis +septa +septet +septets +septic +septicemia +septuagenarian +septuagenarians +septum +sepulcher +sepulchered +sepulchering +sepulchers +sepulchral +sequel +sequels +sequence +sequenced +sequencer +sequencers +sequences +sequencing +sequential +sequentially +sequester +sequestered +sequestering +sequesters +sequestration +sequestrations +sequin +sequined +sequins +sequitur +sequoia +sequoias +seraglio +seraglios +serape +serapes +seraph +seraphic +seraphs +sere +serenade +serenaded +serenades +serenading +serendipitous +serendipity +serene +serenely +sereneness +serener +serenest +serenity +serer +serest +serf +serfdom +serfs +serge +sergeant +sergeants +serial +serialization +serialize +serialized +serializes +serializing +serially +serials +series +serious +seriously +seriousness +sermon +sermonize +sermonized +sermonizes +sermonizing +sermons +serous +serpent +serpentine +serpents +serrated +serried +serum +serums +servant +servants +serve +served +server +servers +serves +service +serviceable +serviced +serviceman +servicemen +services +servicewoman +servicewomen +servicing +serviette +serviettes +servile +servility +serving +servings +servitude +servo +servomechanism +servomechanisms +servos +sesame +sesames +session +sessions +set +setback +setbacks +sets +settable +settee +settees +setter +setters +setting +settings +settle +settled +settlement +settlements +settler +settlers +settles +settling +setup +setups +seven +sevens +seventeen +seventeens +seventeenth +seventeenths +seventh +sevenths +seventies +seventieth +seventieths +seventy +sever +several +severally +severance +severances +severe +severed +severely +severer +severest +severing +severity +severs +sew +sewage +sewed +sewer +sewerage +sewers +sewing +sewn +sews +sex +sexagenarian +sexagenarians +sexed +sexes +sexier +sexiest +sexily +sexiness +sexing +sexism +sexist +sexists +sexless +sexpot +sexpots +sextant +sextants +sextet +sextets +sexting +sexton +sextons +sexual +sexuality +sexually +sexy +sh +shabbier +shabbiest +shabbily +shabbiness +shabby +shack +shackle +shackled +shackles +shackling +shacks +shad +shade +shaded +shades +shadier +shadiest +shadiness +shading +shadings +shadow +shadowbox +shadowboxed +shadowboxes +shadowboxing +shadowed +shadowier +shadowiest +shadowing +shadows +shadowy +shads +shady +shaft +shafted +shafting +shafts +shag +shagged +shaggier +shaggiest +shagginess +shagging +shaggy +shags +shah +shahs +shake +shakedown +shakedowns +shaken +shaker +shakers +shakes +shakeup +shakeups +shakier +shakiest +shakily +shakiness +shaking +shaky +shale +shall +shallot +shallots +shallow +shallower +shallowest +shallowness +shallows +shalt +sham +shaman +shamans +shamble +shambled +shambles +shambling +shame +shamed +shamefaced +shameful +shamefully +shamefulness +shameless +shamelessly +shames +shaming +shammed +shammies +shamming +shampoo +shampooed +shampooing +shampoos +shamrock +shamrocks +shams +shandy +shanghai +shanghaied +shanghaiing +shanghais +shank +shanks +shanties +shantung +shanty +shantytown +shantytowns +shape +shaped +shapeless +shapelessly +shapelessness +shapelier +shapeliest +shapeliness +shapely +shapes +shaping +sharable +shard +shards +share +shareable +sharecropper +sharecroppers +shared +shareholder +shareholders +shares +sharia +shariah +sharing +shark +sharked +sharking +sharks +sharkskin +sharp +sharped +sharpen +sharpened +sharpener +sharpeners +sharpening +sharpens +sharper +sharpers +sharpest +sharping +sharply +sharpness +sharps +sharpshooter +sharpshooters +shat +shatter +shattered +shattering +shatterproof +shatters +shave +shaved +shaven +shaver +shavers +shaves +shaving +shavings +shawl +shawls +she +sheaf +shear +sheared +shearer +shearers +shearing +shears +sheath +sheathe +sheathed +sheathes +sheathing +sheathings +sheaths +sheave +sheaves +shebang +shebangs +shed +shedding +sheds +sheen +sheep +sheepdog +sheepdogs +sheepfold +sheepfolds +sheepish +sheepishly +sheepishness +sheepskin +sheepskins +sheer +sheered +sheerer +sheerest +sheering +sheers +sheet +sheeting +sheets +sheik +sheikdom +sheikdoms +sheikh +sheikhdom +sheikhdoms +sheikhs +sheiks +shekel +shekels +shelf +shell +shellac +shellacked +shellacking +shellacs +shelled +sheller +shellfish +shellfishes +shelling +shells +shelter +sheltered +sheltering +shelters +shelve +shelved +shelves +shelving +shenanigan +shenanigans +shepherd +shepherded +shepherdess +shepherdesses +shepherding +shepherds +sherbet +sherbets +sherd +sherds +sheriff +sheriffs +sherries +sherry +shes +shibboleth +shibboleths +shied +shield +shielded +shielding +shields +shies +shift +shifted +shiftier +shiftiest +shiftily +shiftiness +shifting +shiftless +shiftlessness +shifts +shifty +shiitake +shiitakes +shill +shilled +shillelagh +shillelaghs +shilling +shillings +shills +shim +shimmed +shimmer +shimmered +shimmering +shimmers +shimmery +shimmied +shimmies +shimming +shimmy +shimmying +shims +shin +shinbone +shinbones +shindig +shindigs +shine +shined +shiner +shiners +shines +shingle +shingled +shingles +shingling +shinier +shiniest +shininess +shining +shinned +shinnied +shinnies +shinning +shinny +shinnying +shins +shiny +ship +shipboard +shipboards +shipbuilder +shipbuilders +shipbuilding +shipload +shiploads +shipmate +shipmates +shipment +shipments +shipped +shipper +shippers +shipping +ships +shipshape +shipwreck +shipwrecked +shipwrecking +shipwrecks +shipwright +shipwrights +shipyard +shipyards +shire +shires +shirk +shirked +shirker +shirkers +shirking +shirks +shirr +shirred +shirring +shirrings +shirrs +shirt +shirted +shirting +shirts +shirtsleeve +shirtsleeves +shirttail +shirttails +shirtwaist +shirtwaists +shit +shits +shittier +shittiest +shitting +shitty +shiver +shivered +shivering +shivers +shivery +shoal +shoaled +shoaling +shoals +shock +shocked +shocker +shockers +shocking +shockingly +shockproof +shocks +shod +shoddier +shoddiest +shoddily +shoddiness +shoddy +shoe +shoehorn +shoehorned +shoehorning +shoehorns +shoeing +shoelace +shoelaces +shoemaker +shoemakers +shoes +shoeshine +shoeshines +shoestring +shoestrings +shogun +shoguns +shone +shoo +shooed +shooing +shook +shoos +shoot +shooter +shooters +shooting +shootings +shootout +shootouts +shoots +shop +shopaholic +shopaholics +shopkeeper +shopkeepers +shoplift +shoplifted +shoplifter +shoplifters +shoplifting +shoplifts +shopped +shopper +shoppers +shopping +shops +shoptalk +shopworn +shore +shored +shoreline +shorelines +shores +shoring +short +shortage +shortages +shortbread +shortcake +shortcakes +shortchange +shortchanged +shortchanges +shortchanging +shortcoming +shortcomings +shortcut +shortcuts +shorted +shorten +shortened +shortening +shortenings +shortens +shorter +shortest +shortfall +shortfalls +shorthand +shorthorn +shorthorns +shorting +shortish +shortlist +shortly +shortness +shorts +shortsighted +shortsightedly +shortsightedness +shortstop +shortstops +shortwave +shortwaves +shot +shotgun +shotgunned +shotgunning +shotguns +shots +should +shoulder +shouldered +shouldering +shoulders +shout +shouted +shouting +shouts +shove +shoved +shovel +shoveled +shovelful +shovelfuls +shoveling +shovels +shoves +shoving +show +showbiz +showboat +showboated +showboating +showboats +showcase +showcased +showcases +showcasing +showdown +showdowns +showed +shower +showered +showering +showers +showery +showgirl +showgirls +showier +showiest +showily +showiness +showing +showings +showman +showmanship +showmen +shown +showoff +showoffs +showpiece +showpieces +showplace +showplaces +showroom +showrooms +shows +showy +shrank +shrapnel +shred +shredded +shredder +shredders +shredding +shreds +shrew +shrewd +shrewder +shrewdest +shrewdly +shrewdness +shrewish +shrews +shriek +shrieked +shrieking +shrieks +shrift +shrike +shrikes +shrill +shrilled +shriller +shrillest +shrilling +shrillness +shrills +shrilly +shrimp +shrimped +shrimping +shrimps +shrine +shrines +shrink +shrinkable +shrinkage +shrinking +shrinks +shrive +shrived +shrivel +shriveled +shriveling +shrivels +shriven +shrives +shriving +shroud +shrouded +shrouding +shrouds +shrub +shrubberies +shrubbery +shrubbier +shrubbiest +shrubby +shrubs +shrug +shrugged +shrugging +shrugs +shrunk +shrunken +shtick +shticks +shuck +shucked +shucking +shucks +shuckses +shudder +shuddered +shuddering +shudders +shuffle +shuffleboard +shuffleboards +shuffled +shuffler +shufflers +shuffles +shuffling +shun +shunned +shunning +shuns +shunt +shunted +shunting +shunts +shush +shushed +shushes +shushing +shut +shutdown +shutdowns +shuteye +shutout +shutouts +shuts +shutter +shutterbug +shutterbugs +shuttered +shuttering +shutters +shutting +shuttle +shuttlecock +shuttlecocked +shuttlecocking +shuttlecocks +shuttled +shuttles +shuttling +shy +shyer +shyest +shying +shyly +shyness +shyster +shysters +sibilant +sibilants +sibling +siblings +sibyl +sibyls +sic +sick +sickbed +sickbeds +sicked +sicken +sickened +sickening +sickeningly +sickens +sicker +sickest +sicking +sickle +sickles +sicklier +sickliest +sickly +sickness +sicknesses +sicks +sics +side +sidearm +sidearms +sidebar +sidebars +sideboard +sideboards +sideburns +sidecar +sidecars +sided +sidekick +sidekicks +sidelight +sidelights +sideline +sidelined +sidelines +sidelining +sidelong +sidereal +sides +sidesaddle +sidesaddles +sideshow +sideshows +sidesplitting +sidestep +sidestepped +sidestepping +sidesteps +sidestroke +sidestroked +sidestrokes +sidestroking +sideswipe +sideswiped +sideswipes +sideswiping +sidetrack +sidetracked +sidetracking +sidetracks +sidewalk +sidewalks +sidewall +sidewalls +sideways +siding +sidings +sidle +sidled +sidles +sidling +siege +sieges +sierra +sierras +siesta +siestas +sieve +sieved +sieves +sieving +sift +sifted +sifter +sifters +sifting +sifts +sigh +sighed +sighing +sighs +sight +sighted +sighting +sightings +sightless +sightread +sights +sightseeing +sightseer +sightseers +sigma +sign +signal +signaled +signaling +signalize +signalized +signalizes +signalizing +signally +signals +signatories +signatory +signature +signatures +signboard +signboards +signed +signer +signers +signet +signets +significance +significant +significantly +signification +significations +signified +signifies +signify +signifying +signing +signings +signpost +signposted +signposting +signposts +signs +silage +silence +silenced +silencer +silencers +silences +silencing +silent +silenter +silentest +silently +silents +silhouette +silhouetted +silhouettes +silhouetting +silica +silicate +silicates +siliceous +silicon +silicone +silicosis +silk +silken +silkier +silkiest +silks +silkworm +silkworms +silky +sill +sillier +sillies +silliest +silliness +sills +silly +silo +silos +silt +silted +silting +silts +silvan +silver +silvered +silverfish +silverfishes +silvering +silvers +silversmith +silversmiths +silverware +silvery +sim +simian +simians +similar +similarities +similarity +similarly +simile +similes +simmer +simmered +simmering +simmers +simpatico +simper +simpered +simpering +simpers +simple +simpleness +simpler +simplest +simpleton +simpletons +simplex +simplicity +simplification +simplifications +simplified +simplifies +simplify +simplifying +simplistic +simply +sims +simulate +simulated +simulates +simulating +simulation +simulations +simulator +simulators +simulcast +simulcasted +simulcasting +simulcasts +simultaneous +simultaneously +sin +since +sincere +sincerely +sincerer +sincerest +sincerity +sine +sinecure +sinecures +sinew +sinews +sinewy +sinful +sinfully +sinfulness +sing +singe +singed +singeing +singer +singers +singes +singing +single +singled +singles +singleton +singletons +singling +singly +sings +singsong +singsonged +singsonging +singsongs +singular +singularities +singularity +singularly +singulars +sinister +sink +sinkable +sinker +sinkers +sinkhole +sinkholes +sinking +sinks +sinned +sinner +sinners +sinning +sins +sinuous +sinus +sinuses +sinusitis +sinusoidal +sip +siphon +siphoned +siphoning +siphons +sipped +sipping +sips +sir +sire +sired +siren +sirens +sires +siring +sirloin +sirloins +sirocco +siroccos +sirs +sis +sisal +sises +sissier +sissies +sissiest +sissy +sister +sisterhood +sisterhoods +sisterly +sisters +sit +sitar +sitars +sitcom +sitcoms +site +sited +sites +siting +sits +sitter +sitters +sitting +sittings +situate +situated +situates +situating +situation +situations +six +sixes +sixpence +sixpences +sixteen +sixteens +sixteenth +sixteenths +sixth +sixths +sixties +sixtieth +sixtieths +sixty +sizable +size +sizeable +sized +sizer +sizes +sizing +sizzle +sizzled +sizzles +sizzling +skate +skateboard +skateboarded +skateboarder +skateboarders +skateboarding +skateboards +skated +skater +skaters +skates +skating +skedaddle +skedaddled +skedaddles +skedaddling +skeet +skein +skeins +skeletal +skeleton +skeletons +skeptic +skeptical +skeptically +skepticism +skeptics +sketch +sketched +sketches +sketchier +sketchiest +sketching +sketchy +skew +skewed +skewer +skewered +skewering +skewers +skewing +skews +ski +skid +skidded +skidding +skids +skied +skier +skiers +skies +skiff +skiffs +skiing +skill +skilled +skillet +skillets +skillful +skillfully +skills +skim +skimmed +skimming +skimp +skimped +skimpier +skimpiest +skimpiness +skimping +skimps +skimpy +skims +skin +skinflint +skinflints +skinhead +skinheads +skinless +skinned +skinnier +skinniest +skinniness +skinning +skinny +skins +skintight +skip +skipped +skipper +skippered +skippering +skippers +skipping +skips +skirmish +skirmished +skirmishes +skirmishing +skirt +skirted +skirting +skirts +skis +skit +skits +skitter +skittered +skittering +skitters +skittish +skivvied +skivvies +skivvy +skivvying +skulduggery +skulk +skulked +skulking +skulks +skull +skullcap +skullcaps +skullduggery +skulls +skunk +skunked +skunking +skunks +sky +skycap +skycaps +skydive +skydived +skydiver +skydivers +skydives +skydiving +skying +skyjack +skyjacked +skyjacker +skyjackers +skyjacking +skyjacks +skylark +skylarked +skylarking +skylarks +skylight +skylights +skyline +skylines +skyrocket +skyrocketed +skyrocketing +skyrockets +skyscraper +skyscrapers +skyward +skywards +skywriter +skywriters +skywriting +slab +slabbed +slabbing +slabs +slack +slacked +slacken +slackened +slackening +slackens +slacker +slackers +slackest +slacking +slackly +slackness +slacks +slag +slags +slain +slake +slaked +slakes +slaking +slalom +slalomed +slaloming +slaloms +slam +slammed +slammer +slammers +slamming +slams +slander +slandered +slanderer +slanderers +slandering +slanderous +slanders +slang +slangier +slangiest +slangy +slant +slanted +slanting +slants +slantwise +slap +slapdash +slaphappy +slapped +slapping +slaps +slapstick +slash +slashed +slashes +slashing +slat +slate +slated +slates +slather +slathered +slathering +slathers +slating +slats +slattern +slatternly +slatterns +slaughter +slaughtered +slaughterer +slaughterers +slaughterhouse +slaughterhouses +slaughtering +slaughters +slave +slaved +slaver +slavered +slavering +slavers +slavery +slaves +slaving +slavish +slavishly +slaw +slay +slayer +slayers +slaying +slayings +slays +sleaze +sleazes +sleazier +sleaziest +sleazily +sleaziness +sleazy +sled +sledded +sledding +sledge +sledged +sledgehammer +sledgehammered +sledgehammering +sledgehammers +sledges +sledging +sleds +sleek +sleeked +sleeker +sleekest +sleeking +sleekly +sleekness +sleeks +sleep +sleeper +sleepers +sleepier +sleepiest +sleepily +sleepiness +sleeping +sleepless +sleeplessness +sleeps +sleepwalk +sleepwalked +sleepwalker +sleepwalkers +sleepwalking +sleepwalks +sleepwear +sleepy +sleepyhead +sleepyheads +sleet +sleeted +sleeting +sleets +sleety +sleeve +sleeveless +sleeves +sleigh +sleighed +sleighing +sleighs +slender +slenderer +slenderest +slenderize +slenderized +slenderizes +slenderizing +slenderness +slept +sleuth +sleuths +slew +slewed +slewing +slews +slice +sliced +slicer +slicers +slices +slicing +slick +slicked +slicker +slickers +slickest +slicking +slickly +slickness +slicks +slid +slide +slider +sliders +slides +slideshow +slideshows +sliding +slier +sliest +slight +slighted +slighter +slightest +slighting +slightly +slightness +slights +slim +slime +slimier +slimiest +slimmed +slimmer +slimmest +slimming +slimness +slims +slimy +sling +slinging +slings +slingshot +slingshots +slink +slinkier +slinkiest +slinking +slinks +slinky +slip +slipcover +slipcovers +slipknot +slipknots +slippage +slippages +slipped +slipper +slipperier +slipperiest +slipperiness +slippers +slippery +slipping +slips +slipshod +slit +slither +slithered +slithering +slithers +slithery +slits +slitter +slitting +sliver +slivered +slivering +slivers +slob +slobber +slobbered +slobbering +slobbers +slobs +sloe +sloes +slog +slogan +slogans +slogged +slogging +slogs +sloop +sloops +slop +slope +sloped +slopes +sloping +slopped +sloppier +sloppiest +sloppily +sloppiness +slopping +sloppy +slops +slosh +sloshed +sloshes +sloshing +slot +sloth +slothful +slothfulness +sloths +slots +slotted +slotting +slouch +slouched +slouches +slouchier +slouchiest +slouching +slouchy +slough +sloughed +sloughing +sloughs +sloven +slovenlier +slovenliest +slovenliness +slovenly +slovens +slow +slowdown +slowdowns +slowed +slower +slowest +slowing +slowly +slowness +slowpoke +slowpokes +slows +sludge +slue +slued +slues +slug +sluggard +sluggards +slugged +slugger +sluggers +slugging +sluggish +sluggishly +sluggishness +slugs +sluice +sluiced +sluices +sluicing +sluing +slum +slumber +slumbered +slumbering +slumberous +slumbers +slumbrous +slumdog +slumdogs +slumlord +slumlords +slummed +slummer +slumming +slump +slumped +slumping +slumps +slums +slung +slunk +slur +slurp +slurped +slurping +slurps +slurred +slurring +slurs +slush +slushier +slushiest +slushy +slut +sluts +sluttish +sly +slyer +slyest +slyly +slyness +smack +smacked +smacker +smackers +smacking +smacks +small +smaller +smallest +smallish +smallness +smallpox +smalls +smarmier +smarmiest +smarmy +smart +smarted +smarten +smartened +smartening +smartens +smarter +smartest +smarting +smartly +smartness +smartphone +smartphones +smarts +smartwatch +smartwatches +smash +smashed +smashes +smashing +smattering +smatterings +smear +smeared +smearing +smears +smell +smelled +smellier +smelliest +smelling +smells +smelly +smelt +smelted +smelter +smelters +smelting +smelts +smidgen +smidgens +smile +smiled +smiles +smiling +smilingly +smirch +smirched +smirches +smirching +smirk +smirked +smirking +smirks +smite +smites +smith +smithereens +smithies +smiths +smithy +smiting +smitten +smock +smocked +smocking +smocks +smog +smoggier +smoggiest +smoggy +smoke +smoked +smokehouse +smokehouses +smokeless +smoker +smokers +smokes +smokestack +smokestacks +smokier +smokiest +smokiness +smoking +smoky +smolder +smoldered +smoldering +smolders +smooch +smooched +smooches +smooching +smooth +smoothed +smoother +smoothest +smoothie +smoothies +smoothing +smoothly +smoothness +smooths +smoothy +smote +smother +smothered +smothering +smothers +smoulder +smouldered +smouldering +smoulders +smudge +smudged +smudges +smudgier +smudgiest +smudging +smudgy +smug +smugger +smuggest +smuggle +smuggled +smuggler +smugglers +smuggles +smuggling +smugly +smugness +smut +smuts +smuttier +smuttiest +smutty +smörgÃ¥sbord +smörgÃ¥sbords +snack +snacked +snacking +snacks +snaffle +snaffled +snaffles +snaffling +snafu +snafus +snag +snagged +snagging +snags +snail +snailed +snailing +snails +snake +snakebite +snakebites +snaked +snakes +snakier +snakiest +snaking +snaky +snap +snapdragon +snapdragons +snapped +snapper +snappers +snappier +snappiest +snapping +snappish +snappy +snaps +snapshot +snapshots +snare +snared +snares +snaring +snarkier +snarkiest +snarky +snarl +snarled +snarling +snarls +snatch +snatched +snatches +snatching +snazzier +snazziest +snazzy +sneak +sneaked +sneaker +sneakers +sneakier +sneakiest +sneaking +sneaks +sneaky +sneer +sneered +sneering +sneeringly +sneers +sneeze +sneezed +sneezes +sneezing +snicker +snickered +snickering +snickers +snide +snider +snidest +sniff +sniffed +sniffing +sniffle +sniffled +sniffles +sniffling +sniffs +snifter +snifters +snigger +sniggered +sniggering +sniggers +snip +snipe +sniped +sniper +snipers +snipes +sniping +snipped +snippet +snippets +snippier +snippiest +snipping +snippy +snips +snit +snitch +snitched +snitches +snitching +snits +snivel +sniveled +sniveling +snivels +snob +snobbery +snobbier +snobbiest +snobbish +snobbishness +snobby +snobs +snooker +snoop +snooped +snooper +snoopers +snoopier +snoopiest +snooping +snoops +snoopy +snoot +snootier +snootiest +snootiness +snoots +snooty +snooze +snoozed +snoozes +snoozing +snore +snored +snorer +snorers +snores +snoring +snorkel +snorkeled +snorkeler +snorkelers +snorkeling +snorkels +snort +snorted +snorting +snorts +snot +snots +snottier +snottiest +snotty +snout +snouts +snow +snowball +snowballed +snowballing +snowballs +snowblower +snowblowers +snowboard +snowboarded +snowboarding +snowboards +snowbound +snowdrift +snowdrifts +snowdrop +snowdrops +snowed +snowfall +snowfalls +snowflake +snowflakes +snowier +snowiest +snowing +snowman +snowmen +snowmobile +snowmobiled +snowmobiles +snowmobiling +snowplow +snowplowed +snowplowing +snowplows +snows +snowshed +snowshoe +snowshoeing +snowshoes +snowstorm +snowstorms +snowsuit +snowsuits +snowy +snub +snubbed +snubbing +snubs +snuff +snuffbox +snuffboxes +snuffed +snuffer +snuffers +snuffing +snuffle +snuffled +snuffles +snuffling +snuffs +snug +snugged +snugger +snuggest +snugging +snuggle +snuggled +snuggles +snuggling +snugly +snugs +so +soak +soaked +soaking +soakings +soaks +soap +soapbox +soapboxes +soaped +soapier +soapiest +soapiness +soaping +soaps +soapstone +soapsuds +soapy +soar +soared +soaring +soars +sob +sobbed +sobbing +sober +sobered +soberer +soberest +sobering +soberly +soberness +sobers +sobriety +sobriquet +sobriquets +sobs +soccer +sociability +sociable +sociables +sociably +social +socialism +socialist +socialistic +socialists +socialite +socialites +socialization +socialize +socialized +socializes +socializing +socially +socials +societal +societies +society +socioeconomic +sociological +sociologist +sociologists +sociology +sociopath +sociopaths +sock +socked +socket +sockets +socking +socks +sod +soda +sodas +sodded +sodden +sodding +sodium +sodomite +sodomites +sodomy +sods +sofa +sofas +soft +softball +softballs +soften +softened +softener +softeners +softening +softens +softer +softest +softhearted +softie +softies +softly +softness +software +softwood +softwoods +softy +soggier +soggiest +soggily +sogginess +soggy +soil +soiled +soiling +soils +soirée +soirées +sojourn +sojourned +sojourning +sojourns +sol +solace +solaced +solaces +solacing +solar +solaria +solarium +sold +solder +soldered +soldering +solders +soldier +soldiered +soldiering +soldierly +soldiers +sole +solecism +solecisms +soled +solely +solemn +solemner +solemnest +solemnity +solemnize +solemnized +solemnizes +solemnizing +solemnly +solenoid +solenoids +soles +solicit +solicitation +solicitations +solicited +soliciting +solicitor +solicitors +solicitous +solicitously +solicits +solicitude +solid +solidarity +solider +solidest +solidification +solidified +solidifies +solidify +solidifying +solidity +solidly +solidness +solids +soliloquies +soliloquize +soliloquized +soliloquizes +soliloquizing +soliloquy +soling +solitaire +solitaires +solitaries +solitary +solitude +solo +soloed +soloing +soloist +soloists +solos +sols +solstice +solstices +solubility +soluble +solubles +solution +solutions +solvable +solve +solved +solvency +solvent +solvents +solver +solvers +solves +solving +somber +somberly +sombre +sombrely +sombrero +sombreros +some +somebodies +somebody +someday +somehow +someone +someones +someplace +somersault +somersaulted +somersaulting +somersaults +something +somethings +sometime +sometimes +someway +somewhat +somewhats +somewhere +somnambulism +somnambulist +somnambulists +somnolence +somnolent +son +sonar +sonars +sonata +sonatas +song +songbird +songbirds +songs +songster +songsters +songwriter +songwriters +sonic +sonnet +sonnets +sonnies +sonny +sonority +sonorous +sons +soon +sooner +soonest +soot +sooth +soothe +soothed +soothes +soothing +soothingly +soothsayer +soothsayers +sootier +sootiest +sooty +sop +sophism +sophist +sophisticate +sophisticated +sophisticates +sophisticating +sophistication +sophistries +sophistry +sophists +sophomore +sophomores +sophomoric +soporific +soporifics +sopped +soppier +soppiest +sopping +soppy +soprano +sopranos +sops +sorbet +sorbets +sorcerer +sorcerers +sorceress +sorceresses +sorcery +sordid +sordidly +sordidness +sore +sorehead +soreheads +sorely +soreness +sorer +sores +sorest +sorghum +sororities +sorority +sorrel +sorrels +sorrier +sorriest +sorrow +sorrowed +sorrowful +sorrowfully +sorrowing +sorrows +sorry +sort +sorta +sorted +sorter +sorters +sortie +sortied +sortieing +sorties +sorting +sorts +sot +sots +sottish +soufflé +soufflés +sough +soughed +soughing +soughs +sought +soul +soulful +soulfully +soulfulness +soulless +soulmate +soulmates +souls +sound +sounded +sounder +soundest +sounding +soundings +soundless +soundlessly +soundly +soundness +soundproof +soundproofed +soundproofing +soundproofs +sounds +soundtrack +soundtracks +soup +souped +soupier +soupiest +souping +soups +soupy +soupçon +soupçons +sour +source +sourced +sources +sourcing +sourdough +sourdoughs +soured +sourer +sourest +souring +sourly +sourness +sourpuss +sourpusses +sours +souse +soused +souses +sousing +south +southbound +southeast +southeasterly +southeastern +southeastward +southerlies +southerly +southern +southerner +southerners +southernmost +southerns +southpaw +southpaws +southward +southwards +southwest +southwester +southwesterly +southwestern +southwesters +southwestward +souvenir +souvenirs +sovereign +sovereigns +sovereignty +soviet +soviets +sow +sowed +sower +sowers +sowing +sown +sows +soy +soybean +soybeans +spa +space +spacecraft +spacecrafts +spaced +spaceflight +spaceflights +spaceman +spacemen +spaces +spaceship +spaceships +spacesuit +spacesuits +spacewalk +spacewalked +spacewalking +spacewalks +spacey +spacial +spacier +spaciest +spacing +spacious +spaciously +spaciousness +spade +spaded +spadeful +spadefuls +spades +spadework +spading +spaghetti +spake +spam +spammed +spammer +spammers +spamming +spams +span +spandex +spangle +spangled +spangles +spangling +spaniel +spaniels +spank +spanked +spanking +spankings +spanks +spanned +spanner +spanners +spanning +spans +spar +spare +spared +sparely +spareness +sparer +spareribs +spares +sparest +sparing +sparingly +spark +sparked +sparking +sparkle +sparkled +sparkler +sparklers +sparkles +sparkling +sparks +sparred +sparring +sparrow +sparrows +spars +sparse +sparsely +sparseness +sparser +sparsest +sparsity +spartan +spas +spasm +spasmodic +spasmodically +spasms +spastic +spastics +spat +spate +spates +spatial +spatially +spats +spatted +spatter +spattered +spattering +spatters +spatting +spatula +spatulas +spawn +spawned +spawning +spawns +spay +spayed +spaying +spays +speak +speakeasies +speakeasy +speaker +speakers +speaking +speaks +spear +speared +spearhead +spearheaded +spearheading +spearheads +spearing +spearmint +spears +spec +special +specialist +specialists +specialization +specializations +specialize +specialized +specializes +specializing +specially +specials +specialties +specialty +specie +species +specifiable +specific +specifically +specification +specifications +specifics +specified +specifier +specifiers +specifies +specify +specifying +specimen +specimens +specious +speciously +speck +specked +specking +speckle +speckled +speckles +speckling +specks +specs +spectacle +spectacles +spectacular +spectacularly +spectaculars +spectator +spectators +specter +specters +spectra +spectral +spectroscope +spectroscopes +spectroscopic +spectroscopy +spectrum +speculate +speculated +speculates +speculating +speculation +speculations +speculative +speculator +speculators +sped +speech +speeches +speechless +speed +speedboat +speedboats +speeder +speeders +speedier +speediest +speedily +speeding +speedometer +speedometers +speeds +speedster +speedsters +speedup +speedups +speedway +speedways +speedy +spell +spellbind +spellbinder +spellbinders +spellbinding +spellbinds +spellbound +spellcheck +spellchecked +spellchecker +spellcheckers +spellchecking +spellchecks +spelled +speller +spellers +spelling +spellings +spells +spelunker +spelunkers +spend +spender +spenders +spending +spends +spendthrift +spendthrifts +spent +sperm +spermatozoa +spermatozoon +spermicide +spermicides +sperms +spew +spewed +spewing +spews +sphere +spheres +spherical +spheroid +spheroidal +spheroids +sphincter +sphincters +sphinx +sphinxes +spice +spiced +spices +spicier +spiciest +spiciness +spicing +spicy +spider +spiders +spidery +spied +spiel +spieled +spieling +spiels +spies +spiffier +spiffiest +spiffy +spigot +spigots +spike +spiked +spikes +spikier +spikiest +spiking +spiky +spill +spillage +spillages +spilled +spilling +spills +spillway +spillways +spin +spinach +spinal +spinals +spindle +spindled +spindles +spindlier +spindliest +spindling +spindly +spine +spineless +spines +spinet +spinets +spinier +spiniest +spinnaker +spinnakers +spinner +spinners +spinning +spinoff +spinoffs +spins +spinster +spinsterhood +spinsters +spiny +spiraea +spiraeas +spiral +spiraled +spiraling +spirally +spirals +spire +spirea +spireas +spires +spirit +spirited +spiriting +spiritless +spirits +spiritual +spiritualism +spiritualist +spiritualistic +spiritualists +spirituality +spiritually +spirituals +spirituous +spit +spitball +spitballs +spite +spited +spiteful +spitefuller +spitefullest +spitefully +spitefulness +spites +spitfire +spitfires +spiting +spits +spitted +spitting +spittle +spittoon +spittoons +splash +splashdown +splashdowns +splashed +splashes +splashier +splashiest +splashing +splashy +splat +splats +splatted +splatter +splattered +splattering +splatters +splatting +splay +splayed +splaying +splays +spleen +spleens +splendid +splendider +splendidest +splendidly +splendor +splenetic +splice +spliced +splicer +splicers +splices +splicing +spline +splines +splint +splinted +splinter +splintered +splintering +splinters +splinting +splints +split +splits +splitting +splittings +splodge +splotch +splotched +splotches +splotchier +splotchiest +splotching +splotchy +splurge +splurged +splurges +splurging +splutter +spluttered +spluttering +splutters +spoil +spoilage +spoiled +spoiler +spoilers +spoiling +spoils +spoilsport +spoilsports +spoke +spoken +spokes +spokesman +spokesmen +spokespeople +spokesperson +spokespersons +spokeswoman +spokeswomen +spoliation +sponge +sponged +sponger +spongers +sponges +spongier +spongiest +sponging +spongy +sponsor +sponsored +sponsoring +sponsors +sponsorship +spontaneity +spontaneous +spontaneously +spoof +spoofed +spoofing +spoofs +spook +spooked +spookier +spookiest +spooking +spooks +spooky +spool +spooled +spooling +spools +spoon +spoonbill +spoonbills +spooned +spoonerism +spoonerisms +spoonful +spoonfuls +spooning +spoons +spoor +spoored +spooring +spoors +sporadic +sporadically +spore +spored +spores +sporing +sporran +sport +sported +sportier +sportiest +sporting +sportive +sports +sportscast +sportscaster +sportscasters +sportscasting +sportscasts +sportsman +sportsmanlike +sportsmanship +sportsmen +sportswear +sportswoman +sportswomen +sporty +spot +spotless +spotlessly +spotlessness +spotlight +spotlighted +spotlighting +spotlights +spots +spotted +spotter +spotters +spottier +spottiest +spottiness +spotting +spotty +spouse +spouses +spout +spouted +spouting +spouts +sprain +sprained +spraining +sprains +sprang +sprat +sprats +sprawl +sprawled +sprawling +sprawls +spray +sprayed +sprayer +sprayers +spraying +sprays +spread +spreader +spreaders +spreading +spreads +spreadsheet +spreadsheets +spree +spreed +spreeing +sprees +sprier +spriest +sprig +sprightlier +sprightliest +sprightliness +sprightly +sprigs +spring +springboard +springboards +springier +springiest +springiness +springing +springs +springtime +springy +sprinkle +sprinkled +sprinkler +sprinklers +sprinkles +sprinkling +sprinklings +sprint +sprinted +sprinter +sprinters +sprinting +sprints +sprite +sprites +spritz +spritzed +spritzes +spritzing +sprocket +sprockets +sprout +sprouted +sprouting +sprouts +spruce +spruced +sprucer +spruces +sprucest +sprucing +sprung +spry +spryly +spryness +spud +spuds +spume +spumed +spumes +spuming +spumoni +spun +spunk +spunkier +spunkiest +spunky +spur +spurious +spuriously +spuriousness +spurn +spurned +spurning +spurns +spurred +spurring +spurs +spurt +spurted +spurting +spurts +sputter +sputtered +sputtering +sputters +sputum +spy +spyglass +spyglasses +spying +spyware +squab +squabble +squabbled +squabbles +squabbling +squabs +squad +squadron +squadrons +squads +squalid +squalider +squalidest +squall +squalled +squalling +squalls +squalor +squander +squandered +squandering +squanders +square +squared +squarely +squareness +squarer +squares +squarest +squaring +squash +squashed +squashes +squashier +squashiest +squashing +squashy +squat +squats +squatted +squatter +squatters +squattest +squatting +squaw +squawk +squawked +squawking +squawks +squaws +squeak +squeaked +squeakier +squeakiest +squeaking +squeaks +squeaky +squeal +squealed +squealer +squealers +squealing +squeals +squeamish +squeamishly +squeamishness +squeegee +squeegeed +squeegeeing +squeegees +squeeze +squeezed +squeezer +squeezers +squeezes +squeezing +squelch +squelched +squelches +squelching +squid +squids +squiggle +squiggled +squiggles +squiggling +squiggly +squint +squinted +squinter +squintest +squinting +squints +squire +squired +squires +squiring +squirm +squirmed +squirmier +squirmiest +squirming +squirms +squirmy +squirrel +squirreled +squirreling +squirrels +squirt +squirted +squirting +squirts +squish +squished +squishes +squishier +squishiest +squishing +squishy +sriracha +stab +stabbed +stabbing +stabbings +stability +stabilization +stabilize +stabilized +stabilizer +stabilizers +stabilizes +stabilizing +stable +stabled +stabler +stables +stablest +stabling +stabs +staccato +staccatos +stack +stacked +stacking +stacks +stadium +stadiums +staff +staffed +staffer +staffers +staffing +staffs +stag +stage +stagecoach +stagecoaches +staged +stagehand +stagehands +stages +stagflation +stagger +staggered +staggering +staggeringly +staggers +staging +stagings +stagnant +stagnate +stagnated +stagnates +stagnating +stagnation +stags +staid +staider +staidest +staidly +stain +stained +staining +stainless +stains +stair +staircase +staircases +stairs +stairway +stairways +stairwell +stairwells +stake +staked +stakeout +stakeouts +stakes +staking +stalactite +stalactites +stalagmite +stalagmites +stale +staled +stalemate +stalemated +stalemates +stalemating +staleness +staler +stales +stalest +staling +stalk +stalked +stalker +stalkers +stalking +stalkings +stalks +stall +stalled +stalling +stallion +stallions +stalls +stalwart +stalwarts +stamen +stamens +stamina +stammer +stammered +stammerer +stammerers +stammering +stammers +stamp +stamped +stampede +stampeded +stampedes +stampeding +stamping +stamps +stance +stances +stanch +stanched +stancher +stanches +stanchest +stanching +stanchion +stanchions +stand +standard +standardization +standardize +standardized +standardizes +standardizing +standards +standby +standbys +standing +standings +standoff +standoffish +standoffs +standout +standouts +standpoint +standpoints +stands +standstill +standstills +stank +stanza +stanzas +staph +staphylococci +staphylococcus +staple +stapled +stapler +staplers +staples +stapling +star +starboard +starch +starched +starches +starchier +starchiest +starching +starchy +stardom +stare +stared +stares +starfish +starfishes +stargazer +stargazers +staring +stark +starker +starkest +starkly +starkness +starless +starlet +starlets +starlight +starling +starlings +starlit +starred +starrier +starriest +starring +starry +stars +start +started +starter +starters +starting +startle +startled +startles +startling +startlingly +starts +startup +startups +starvation +starve +starved +starves +starving +starvings +stash +stashed +stashes +stashing +state +stated +statehood +statehouse +statehouses +stateless +statelier +stateliest +stateliness +stately +statement +statements +stater +stateroom +staterooms +states +stateside +statesman +statesmanlike +statesmanship +statesmen +statewide +static +statically +stating +station +stationary +stationed +stationer +stationers +stationery +stationing +stations +statistic +statistical +statistically +statistician +statisticians +statistics +stats +statuary +statue +statues +statuesque +statuette +statuettes +stature +statures +status +statuses +statute +statutes +statutory +staunch +staunched +stauncher +staunches +staunchest +staunching +staunchly +stave +staved +staves +staving +stay +stayed +staying +stays +stead +steadfast +steadfastly +steadfastness +steadied +steadier +steadies +steadiest +steadily +steadiness +steads +steady +steadying +steak +steakhouse +steakhouses +steaks +steal +stealing +steals +stealth +stealthier +stealthiest +stealthily +stealthy +steam +steamboat +steamboats +steamed +steamer +steamers +steamier +steamiest +steaming +steamroll +steamrolled +steamroller +steamrollered +steamrollering +steamrollers +steamrolling +steamrolls +steams +steamship +steamships +steamy +steed +steeds +steel +steeled +steelier +steeliest +steeling +steels +steely +steep +steeped +steeper +steepest +steeping +steeple +steeplechase +steeplechases +steeplejack +steeplejacks +steeples +steeply +steepness +steeps +steer +steerage +steered +steering +steers +stein +steins +stellar +stem +stemmed +stemming +stems +stench +stenches +stencil +stenciled +stenciling +stencils +stenographer +stenographers +stenographic +stenography +stent +stentorian +stents +step +stepbrother +stepbrothers +stepchild +stepchildren +stepdad +stepdads +stepdaughter +stepdaughters +stepfather +stepfathers +stepladder +stepladders +stepmom +stepmoms +stepmother +stepmothers +stepparent +stepparents +steppe +stepped +steppes +stepping +steppingstone +steppingstones +steps +stepsister +stepsisters +stepson +stepsons +stereo +stereophonic +stereos +stereoscope +stereoscopes +stereotype +stereotyped +stereotypes +stereotypical +stereotyping +sterile +sterility +sterilization +sterilize +sterilized +sterilizer +sterilizers +sterilizes +sterilizing +sterling +stern +sterner +sternest +sternly +sternness +sterns +sternum +sternums +steroid +steroids +stethoscope +stethoscopes +stevedore +stevedores +stew +steward +stewarded +stewardess +stewardesses +stewarding +stewards +stewardship +stewed +stewing +stews +stick +sticker +stickers +stickier +stickies +stickiest +stickiness +sticking +stickleback +sticklebacks +stickler +sticklers +stickpin +stickpins +sticks +stickup +stickups +sticky +sties +stiff +stiffed +stiffen +stiffened +stiffener +stiffeners +stiffening +stiffens +stiffer +stiffest +stiffing +stiffly +stiffness +stiffs +stifle +stifled +stifles +stifling +stiflings +stigma +stigmas +stigmata +stigmatize +stigmatized +stigmatizes +stigmatizing +stile +stiles +stiletto +stilettos +still +stillbirth +stillbirths +stillborn +stilled +stiller +stillest +stilling +stillness +stills +stilt +stilted +stilts +stimulant +stimulants +stimulate +stimulated +stimulates +stimulating +stimulation +stimuli +stimulus +sting +stinger +stingers +stingier +stingiest +stingily +stinginess +stinging +stingray +stingrays +stings +stingy +stink +stinker +stinkers +stinking +stinks +stint +stinted +stinting +stints +stipend +stipends +stipple +stippled +stipples +stippling +stipulate +stipulated +stipulates +stipulating +stipulation +stipulations +stir +stirred +stirrer +stirrers +stirring +stirrings +stirrup +stirrups +stirs +stitch +stitched +stitches +stitching +stoat +stoats +stochastic +stock +stockade +stockaded +stockades +stockading +stockbroker +stockbrokers +stocked +stockholder +stockholders +stockier +stockiest +stockiness +stocking +stockings +stockpile +stockpiled +stockpiles +stockpiling +stockroom +stockrooms +stocks +stocky +stockyard +stockyards +stodgier +stodgiest +stodginess +stodgy +stoic +stoical +stoically +stoicism +stoics +stoke +stoked +stoker +stokers +stokes +stoking +stole +stolen +stoles +stolid +stolider +stolidest +stolidity +stolidly +stomach +stomachache +stomachaches +stomached +stomaching +stomachs +stomp +stomped +stomping +stomps +stone +stoned +stoner +stoners +stones +stonewall +stonewalled +stonewalling +stonewalls +stoneware +stonework +stonier +stoniest +stonily +stoning +stony +stood +stooge +stooges +stool +stools +stoop +stooped +stooping +stoops +stop +stopcock +stopcocks +stopgap +stopgaps +stoplight +stoplights +stopover +stopovers +stoppable +stoppage +stoppages +stopped +stopper +stoppered +stoppering +stoppers +stopping +stops +stopwatch +stopwatches +storage +store +stored +storefront +storefronts +storehouse +storehouses +storekeeper +storekeepers +storeroom +storerooms +stores +storied +stories +storing +stork +storks +storm +stormed +stormier +stormiest +stormily +storminess +storming +storms +stormy +story +storybook +storybooks +storyteller +storytellers +stout +stouter +stoutest +stoutly +stoutness +stove +stovepipe +stovepipes +stoves +stow +stowaway +stowaways +stowed +stowing +stows +straddle +straddled +straddles +straddling +strafe +strafed +strafes +strafing +straggle +straggled +straggler +stragglers +straggles +stragglier +straggliest +straggling +straggly +straight +straightaway +straightaways +straightedge +straightedges +straighten +straightened +straightening +straightens +straighter +straightest +straightforward +straightforwardly +straightness +straights +strain +strained +strainer +strainers +straining +strains +strait +straiten +straitened +straitening +straitens +straitjacket +straitjacketed +straitjacketing +straitjackets +straits +strand +stranded +stranding +strands +strange +strangely +strangeness +stranger +strangers +strangest +strangle +strangled +stranglehold +strangleholds +strangler +stranglers +strangles +strangling +strangulate +strangulated +strangulates +strangulating +strangulation +strap +strapless +straplesses +strapped +strapping +straps +strata +stratagem +stratagems +strategic +strategically +strategies +strategist +strategists +strategy +stratification +stratified +stratifies +stratify +stratifying +stratosphere +stratospheres +stratum +straw +strawberries +strawberry +strawed +strawing +straws +stray +strayed +straying +strays +streak +streaked +streakier +streakiest +streaking +streaks +streaky +stream +streamed +streamer +streamers +streaming +streamline +streamlined +streamlines +streamlining +streams +street +streetcar +streetcars +streetlight +streetlights +streets +streetwalker +streetwalkers +streetwise +strength +strengthen +strengthened +strengthening +strengthens +strengths +strenuous +strenuously +strenuousness +strep +streptococcal +streptococci +streptococcus +streptomycin +stress +stressed +stresses +stressful +stressing +stretch +stretched +stretcher +stretchers +stretches +stretchier +stretchiest +stretching +stretchy +strew +strewed +strewing +strewn +strews +striated +stricken +strict +stricter +strictest +strictly +strictness +stricture +strictures +stridden +stride +strident +stridently +strides +striding +strife +strike +strikeout +strikeouts +striker +strikers +strikes +striking +strikingly +strikings +string +stringed +stringency +stringent +stringently +stringer +stringers +stringier +stringiest +stringing +strings +stringy +strip +stripe +striped +stripes +striping +stripling +striplings +stripped +stripper +strippers +stripping +strips +striptease +stripteased +stripteases +stripteasing +strive +striven +strives +striving +strobe +strobes +strode +stroke +stroked +strokes +stroking +stroll +strolled +stroller +strollers +strolling +strolls +strong +strongbox +strongboxes +stronger +strongest +stronghold +strongholds +strongly +strontium +strop +strophe +strophes +stropped +stropping +strops +strove +struck +structural +structuralist +structurally +structure +structured +structures +structuring +strudel +strudels +struggle +struggled +struggles +struggling +strum +strummed +strumming +strumpet +strumpets +strums +strung +strut +struts +strutted +strutting +strychnine +stub +stubbed +stubbier +stubbiest +stubbing +stubble +stubbly +stubborn +stubborner +stubbornest +stubbornly +stubbornness +stubby +stubs +stucco +stuccoed +stuccoes +stuccoing +stuck +stud +studded +studding +student +students +studentship +studentships +studied +studies +studio +studios +studious +studiously +studs +study +studying +stuff +stuffed +stuffier +stuffiest +stuffily +stuffiness +stuffing +stuffings +stuffs +stuffy +stultification +stultified +stultifies +stultify +stultifying +stumble +stumbled +stumbler +stumblers +stumbles +stumbling +stump +stumped +stumpier +stumpiest +stumping +stumps +stumpy +stun +stung +stunk +stunned +stunning +stunningly +stuns +stunt +stunted +stunting +stunts +stupefaction +stupefied +stupefies +stupefy +stupefying +stupendous +stupendously +stupid +stupider +stupidest +stupidities +stupidity +stupidly +stupids +stupor +stupors +sturdier +sturdiest +sturdily +sturdiness +sturdy +sturgeon +sturgeons +stutter +stuttered +stutterer +stutterers +stuttering +stutters +sty +stye +styes +style +styled +styles +styli +styling +stylish +stylishly +stylishness +stylist +stylistic +stylistically +stylists +stylize +stylized +stylizes +stylizing +stylus +styluses +stymie +stymied +stymieing +stymies +styptic +styptics +suave +suavely +suaver +suavest +suavity +sub +subatomic +subbasement +subbasements +subbed +subbing +subclass +subcommittee +subcommittees +subcompact +subcompacts +subconscious +subconsciously +subcontinent +subcontinents +subcontract +subcontracted +subcontracting +subcontractor +subcontractors +subcontracts +subculture +subcultures +subcutaneous +subdivide +subdivided +subdivides +subdividing +subdivision +subdivisions +subdue +subdued +subdues +subduing +subgroup +subgroups +subhead +subheading +subheadings +subheads +subhuman +subhumans +subject +subjected +subjecting +subjection +subjective +subjectively +subjectivity +subjects +subjoin +subjoined +subjoining +subjoins +subjugate +subjugated +subjugates +subjugating +subjugation +subjunctive +subjunctives +sublease +subleased +subleases +subleasing +sublet +sublets +subletting +sublimate +sublimated +sublimates +sublimating +sublimation +sublime +sublimed +sublimely +sublimer +sublimes +sublimest +subliminal +subliminally +subliming +sublimity +submarine +submarines +submerge +submerged +submergence +submerges +submerging +submerse +submersed +submerses +submersible +submersibles +submersing +submersion +submission +submissions +submissive +submit +submits +submitted +submitter +submitting +subnormal +suborbital +subordinate +subordinated +subordinates +subordinating +subordination +suborn +subornation +suborned +suborning +suborns +subplot +subplots +subpoena +subpoenaed +subpoenaing +subpoenas +subprime +subprogram +subprograms +subroutine +subroutines +subs +subscribe +subscribed +subscriber +subscribers +subscribes +subscribing +subscript +subscription +subscriptions +subscripts +subsection +subsections +subsequent +subsequently +subservience +subservient +subset +subsets +subside +subsided +subsidence +subsides +subsidiaries +subsidiary +subsidies +subsiding +subsidization +subsidize +subsidized +subsidizes +subsidizing +subsidy +subsist +subsisted +subsistence +subsisting +subsists +subsoil +subsonic +subspace +substance +substances +substandard +substantial +substantially +substantiate +substantiated +substantiates +substantiating +substantiation +substantiations +substantive +substantives +substation +substations +substitute +substituted +substitutes +substituting +substitution +substitutions +substrata +substrate +substratum +substructure +substructures +subsume +subsumed +subsumes +subsuming +subsystem +subsystems +subteen +subteens +subterfuge +subterfuges +subterranean +subtitle +subtitled +subtitles +subtitling +subtle +subtler +subtlest +subtleties +subtlety +subtly +subtotal +subtotaled +subtotaling +subtotals +subtract +subtracted +subtracting +subtraction +subtractions +subtracts +subtrahend +subtrahends +subtropical +suburb +suburban +suburbanite +suburbanites +suburbans +suburbia +suburbs +subversion +subversive +subversives +subvert +subverted +subverting +subverts +subway +subways +succeed +succeeded +succeeding +succeeds +success +successes +successful +successfully +succession +successions +successive +successively +successor +successors +succinct +succincter +succinctest +succinctly +succinctness +succor +succored +succoring +succors +succotash +succulence +succulent +succulents +succumb +succumbed +succumbing +succumbs +such +suchlike +suck +sucked +sucker +suckered +suckering +suckers +sucking +suckle +suckled +suckles +suckling +sucklings +sucks +sucrose +suction +suctioned +suctioning +suctions +sudden +suddenly +suddenness +suds +sudsier +sudsiest +sudsy +sue +sued +suede +sues +suet +suffer +sufferance +suffered +sufferer +sufferers +suffering +sufferings +suffers +suffice +sufficed +suffices +sufficiency +sufficient +sufficiently +sufficing +suffix +suffixed +suffixes +suffixing +suffocate +suffocated +suffocates +suffocating +suffocation +suffragan +suffragans +suffrage +suffragette +suffragettes +suffragist +suffragists +suffuse +suffused +suffuses +suffusing +suffusion +sugar +sugarcane +sugarcoat +sugarcoated +sugarcoating +sugarcoats +sugared +sugarier +sugariest +sugaring +sugarless +sugars +sugary +suggest +suggested +suggester +suggestible +suggesting +suggestion +suggestions +suggestive +suggestively +suggests +suicidal +suicide +suicides +suing +suit +suitability +suitable +suitably +suitcase +suitcases +suite +suited +suites +suiting +suitor +suitors +suits +sukiyaki +sulfate +sulfates +sulfide +sulfides +sulfur +sulfured +sulfuric +sulfuring +sulfurous +sulfurs +sulk +sulked +sulkier +sulkies +sulkiest +sulkily +sulkiness +sulking +sulks +sulky +sullen +sullener +sullenest +sullenly +sullenness +sullied +sullies +sully +sullying +sultan +sultana +sultanas +sultanate +sultanates +sultans +sultrier +sultriest +sultry +sum +sumac +summaries +summarily +summarize +summarized +summarizes +summarizing +summary +summation +summations +summed +summer +summered +summerhouse +summerhouses +summering +summers +summertime +summery +summing +summit +summitry +summits +summon +summoned +summoner +summoners +summoning +summons +summonsed +summonses +summonsing +sumo +sump +sumps +sumptuous +sums +sun +sunbathe +sunbathed +sunbather +sunbathers +sunbathes +sunbathing +sunbeam +sunbeams +sunblock +sunblocks +sunbonnet +sunbonnets +sunburn +sunburned +sunburning +sunburns +sundae +sundaes +sunder +sundered +sundering +sunders +sundial +sundials +sundown +sundowns +sundries +sundry +sunfish +sunfishes +sunflower +sunflowers +sung +sunglasses +sunk +sunken +sunlamp +sunlamps +sunless +sunlight +sunlit +sunned +sunnier +sunniest +sunning +sunny +sunrise +sunrises +sunroof +sunroofs +suns +sunscreen +sunscreens +sunset +sunsets +sunshine +sunspot +sunspots +sunstroke +suntan +suntanned +suntanning +suntans +sunup +sup +super +superabundance +superabundances +superabundant +superannuate +superannuated +superannuates +superannuating +superb +superber +superbest +superbly +supercharge +supercharged +supercharger +superchargers +supercharges +supercharging +supercilious +supercomputer +supercomputers +superconductivity +superconductor +superconductors +superego +superegos +superficial +superficiality +superficially +superfluity +superfluous +superhighway +superhighways +superhuman +superimpose +superimposed +superimposes +superimposing +superintend +superintended +superintendence +superintendency +superintendent +superintendents +superintending +superintends +superior +superiority +superiors +superlative +superlatively +superlatives +superman +supermarket +supermarkets +supermen +supermodel +supermodels +supernatural +supernaturals +supernova +supernovae +supernovas +supernumeraries +supernumerary +superpower +superpowers +supers +superscript +superscripts +supersede +superseded +supersedes +superseding +supersize +supersized +supersizes +supersizing +supersonic +superstar +superstars +superstition +superstitions +superstitious +superstitiously +superstructure +superstructures +supertanker +supertankers +supervene +supervened +supervenes +supervening +supervise +supervised +supervises +supervising +supervision +supervisions +supervisor +supervisors +supervisory +supine +supped +supper +suppers +supping +supplant +supplanted +supplanting +supplants +supple +supplement +supplemental +supplementary +supplemented +supplementing +supplements +suppleness +suppler +supplest +suppliant +suppliants +supplicant +supplicants +supplicate +supplicated +supplicates +supplicating +supplication +supplications +supplied +supplier +suppliers +supplies +supply +supplying +support +supportable +supported +supporter +supporters +supporting +supportive +supports +suppose +supposed +supposedly +supposes +supposing +supposition +suppositions +suppositories +suppository +suppress +suppressed +suppresses +suppressing +suppression +suppurate +suppurated +suppurates +suppurating +suppuration +supranational +supremacist +supremacists +supremacy +supreme +supremely +sups +surcease +surceased +surceases +surceasing +surcharge +surcharged +surcharges +surcharging +sure +surefire +surefooted +surely +sureness +surer +surest +sureties +surety +surf +surface +surfaced +surfaces +surfacing +surfboard +surfboarded +surfboarding +surfboards +surfed +surfeit +surfeited +surfeiting +surfeits +surfer +surfers +surfing +surfs +surge +surged +surgeon +surgeons +surgeries +surgery +surges +surgical +surgically +surging +surlier +surliest +surliness +surly +surmise +surmised +surmises +surmising +surmount +surmountable +surmounted +surmounting +surmounts +surname +surnames +surpass +surpassed +surpasses +surpassing +surplice +surplices +surplus +surpluses +surplussed +surplussing +surprise +surprised +surprises +surprising +surprisingly +surprisings +surreal +surrealism +surrealist +surrealistic +surrealists +surrender +surrendered +surrendering +surrenders +surreptitious +surreptitiously +surrey +surreys +surrogate +surrogates +surround +surrounded +surrounding +surroundings +surrounds +surtax +surtaxed +surtaxes +surtaxing +surveillance +survey +surveyed +surveying +surveyor +surveyors +surveys +survival +survivals +survive +survived +survives +surviving +survivor +survivors +susceptibility +susceptible +sushi +suspect +suspected +suspecting +suspects +suspend +suspended +suspender +suspenders +suspending +suspends +suspense +suspenseful +suspension +suspensions +suspicion +suspicions +suspicious +suspiciously +sustain +sustainable +sustained +sustaining +sustains +sustenance +suture +sutured +sutures +suturing +svelte +svelter +sveltest +swab +swabbed +swabbing +swabs +swaddle +swaddled +swaddles +swaddling +swag +swagged +swagger +swaggered +swaggerer +swaggering +swaggers +swagging +swags +swain +swains +swallow +swallowed +swallowing +swallows +swallowtail +swallowtails +swam +swami +swamis +swamp +swamped +swampier +swampiest +swamping +swamps +swampy +swan +swank +swanked +swanker +swankest +swankier +swankiest +swanking +swanks +swanky +swans +swap +swapped +swapping +swaps +sward +swards +swarm +swarmed +swarming +swarms +swarthier +swarthiest +swarthy +swash +swashbuckler +swashbucklers +swashbuckling +swashed +swashes +swashing +swastika +swastikas +swat +swatch +swatches +swath +swathe +swathed +swathes +swathing +swaths +swats +swatted +swatter +swattered +swattering +swatters +swatting +sway +swaybacked +swayed +swaying +sways +swear +swearer +swearers +swearing +swears +swearword +swearwords +sweat +sweater +sweaters +sweatier +sweatiest +sweating +sweatpants +sweats +sweatshirt +sweatshirts +sweatshop +sweatshops +sweaty +sweep +sweeper +sweepers +sweeping +sweepings +sweeps +sweepstakes +sweet +sweetbread +sweetbreads +sweetbriar +sweetbriars +sweetbrier +sweetbriers +sweeten +sweetened +sweetener +sweeteners +sweetening +sweetens +sweeter +sweetest +sweetheart +sweethearts +sweetie +sweeties +sweetish +sweetly +sweetmeat +sweetmeats +sweetness +sweets +swell +swelled +sweller +swellest +swellhead +swellheaded +swellheads +swelling +swellings +swells +swelter +sweltered +sweltering +swelters +swept +swerve +swerved +swerves +swerving +swift +swifter +swiftest +swiftly +swiftness +swifts +swig +swigged +swigging +swigs +swill +swilled +swilling +swills +swim +swimmer +swimmers +swimming +swims +swimsuit +swimsuits +swindle +swindled +swindler +swindlers +swindles +swindling +swine +swines +swing +swinger +swingers +swinging +swings +swinish +swipe +swiped +swipes +swiping +swirl +swirled +swirling +swirls +swirly +swish +swished +swisher +swishes +swishest +swishing +switch +switchable +switchback +switchbacks +switchblade +switchblades +switchboard +switchboards +switched +switcher +switches +switching +swivel +swiveled +swiveling +swivels +swollen +swoon +swooned +swooning +swoons +swoop +swooped +swooping +swoops +sword +swordfish +swordfishes +swordplay +swords +swordsman +swordsmen +swore +sworn +swum +swung +sybarite +sybarites +sybaritic +sycamore +sycamores +sycophant +sycophantic +sycophants +syllabic +syllabication +syllabification +syllabified +syllabifies +syllabify +syllabifying +syllable +syllables +syllabus +syllabuses +syllogism +syllogisms +syllogistic +sylph +sylphs +sylvan +symbioses +symbiosis +symbiotic +symbol +symbolic +symbolically +symbolism +symbolization +symbolize +symbolized +symbolizes +symbolizing +symbols +symmetric +symmetrical +symmetrically +symmetricly +symmetries +symmetry +sympathetic +sympathetically +sympathies +sympathize +sympathized +sympathizer +sympathizers +sympathizes +sympathizing +sympathy +symphonic +symphonies +symphony +symposium +symposiums +symptom +symptomatic +symptoms +synagogue +synagogues +synapse +synapses +sync +synced +synchronization +synchronizations +synchronize +synchronized +synchronizes +synchronizing +synchronous +synchronously +syncing +syncopate +syncopated +syncopates +syncopating +syncopation +syncs +syndicate +syndicated +syndicates +syndicating +syndication +syndrome +syndromes +synergism +synergistic +synergy +synod +synods +synonym +synonymous +synonyms +synopses +synopsis +syntactic +syntactical +syntactically +syntax +syntheses +synthesis +synthesize +synthesized +synthesizer +synthesizers +synthesizes +synthesizing +synthetic +synthetically +synthetics +syphilis +syphilitic +syphilitics +syringe +syringed +syringes +syringing +syrup +syrups +syrupy +system +systematic +systematically +systematize +systematized +systematizes +systematizing +systemic +systemics +systems +systolic +séance +séances +t +tab +tabbed +tabbies +tabbing +tabby +tabernacle +tabernacles +table +tableau +tableaux +tablecloth +tablecloths +tabled +tableland +tablelands +tables +tablespoon +tablespoonful +tablespoonfuls +tablespoons +tablet +tablets +tableware +tabling +tabloid +tabloids +taboo +tabooed +tabooing +taboos +tabs +tabular +tabulate +tabulated +tabulates +tabulating +tabulation +tabulator +tabulators +tachometer +tachometers +tacit +tacitly +tacitness +taciturn +taciturnity +tack +tacked +tackier +tackiest +tackiness +tacking +tackle +tackled +tackler +tacklers +tackles +tackling +tacks +tacky +taco +tacos +tact +tactful +tactfully +tactic +tactical +tactically +tactician +tacticians +tactics +tactile +tactless +tactlessly +tactlessness +tad +tadpole +tadpoles +tads +taffeta +taffies +taffy +tag +tagged +tagging +tags +tail +tailcoat +tailcoats +tailed +tailgate +tailgated +tailgates +tailgating +tailing +tailless +taillight +taillights +tailor +tailored +tailoring +tailors +tailpipe +tailpipes +tails +tailspin +tailspins +tailwind +tailwinds +taint +tainted +tainting +taints +take +takeaways +taken +takeoff +takeoffs +takeout +takeouts +takeover +takeovers +taker +takers +takes +taking +takings +talc +tale +talent +talented +talents +tales +talisman +talismans +talk +talkative +talkativeness +talked +talker +talkers +talking +talks +tall +taller +tallest +tallied +tallies +tallness +tallow +tally +tallyho +tallyhoed +tallyhoing +tallyhos +tallying +talon +talons +tam +tamable +tamale +tamales +tamarind +tamarinds +tambourine +tambourines +tame +tameable +tamed +tamely +tameness +tamer +tamers +tames +tamest +taming +tamp +tamped +tamper +tampered +tampering +tampers +tamping +tampon +tampons +tamps +tams +tan +tanager +tanagers +tandem +tandems +tang +tangelo +tangelos +tangent +tangential +tangents +tangerine +tangerines +tangibility +tangible +tangibles +tangibly +tangier +tangiest +tangle +tangled +tangles +tangling +tango +tangoed +tangoing +tangos +tangs +tangy +tank +tankard +tankards +tanked +tanker +tankers +tankful +tankfuls +tanking +tanks +tanned +tanner +tanneries +tanners +tannery +tannest +tannin +tanning +tans +tansy +tantalize +tantalized +tantalizes +tantalizing +tantalizingly +tantamount +tantrum +tantrums +tap +tape +taped +taper +tapered +tapering +tapers +tapes +tapestries +tapestry +tapeworm +tapeworms +taping +tapioca +tapir +tapirs +tapped +tapping +taproom +taprooms +taproot +taproots +taps +tar +tarantula +tarantulas +tardier +tardiest +tardily +tardiness +tardy +tare +tared +tares +target +targeted +targeting +targets +tariff +tariffs +taring +tarmac +tarmacked +tarmacking +tarmacs +tarnish +tarnished +tarnishes +tarnishing +taro +taros +tarot +tarots +tarp +tarpaulin +tarpaulins +tarpon +tarpons +tarps +tarragon +tarragons +tarred +tarried +tarrier +tarries +tarriest +tarring +tarry +tarrying +tars +tart +tartan +tartans +tartar +tartars +tarter +tartest +tartly +tartness +tarts +taser +tasered +tasering +tasers +task +tasked +tasking +taskmaster +taskmasters +tasks +tassel +tasseled +tasseling +tassels +taste +tasted +tasteful +tastefully +tasteless +tastelessly +tastelessness +taster +tasters +tastes +tastier +tastiest +tastiness +tasting +tasty +tat +tats +tatted +tatter +tattered +tattering +tatters +tatting +tattle +tattled +tattler +tattlers +tattles +tattletale +tattletales +tattling +tattoo +tattooed +tattooing +tattooist +tattooists +tattoos +tatty +taught +taunt +taunted +taunting +taunts +taupe +taut +tauter +tautest +tautly +tautness +tautological +tautologies +tautology +tavern +taverns +tawdrier +tawdriest +tawdriness +tawdry +tawnier +tawniest +tawny +tax +taxable +taxation +taxed +taxes +taxi +taxicab +taxicabs +taxidermist +taxidermists +taxidermy +taxied +taxiing +taxing +taxis +taxonomic +taxonomies +taxonomy +taxpayer +taxpayers +tea +teabag +teach +teachable +teacher +teachers +teaches +teaching +teachings +teacup +teacups +teak +teakettle +teakettles +teaks +teal +tealight +tealights +teals +team +teamed +teaming +teammate +teammates +teams +teamster +teamsters +teamwork +teapot +teapots +tear +teardrop +teardrops +teared +tearful +tearfully +teargas +teargases +teargassed +teargassing +tearier +teariest +tearing +tearjerker +tearjerkers +tearoom +tearooms +tears +teary +teas +tease +teased +teasel +teasels +teaser +teasers +teases +teasing +teaspoon +teaspoonful +teaspoonfuls +teaspoons +teat +teatime +teats +technical +technicalities +technicality +technically +technician +technicians +technique +techniques +techno +technocracy +technocrat +technocrats +technological +technologically +technologies +technologist +technologists +technology +techs +tectonics +tedious +tediously +tediousness +tedium +tee +teed +teeing +teem +teemed +teeming +teems +teen +teenage +teenager +teenagers +teenier +teeniest +teens +teeny +teepee +teepees +tees +teeter +teetered +teetering +teeters +teeth +teethe +teethed +teethes +teething +teetotal +teetotaler +teetotalers +telecast +telecaster +telecasters +telecasting +telecasts +telecommunication +telecommunications +telecommute +telecommuted +telecommuter +telecommuters +telecommutes +telecommuting +teleconference +teleconferenced +teleconferences +teleconferencing +telegram +telegrams +telegraph +telegraphed +telegrapher +telegraphers +telegraphic +telegraphing +telegraphs +telegraphy +telekinesis +telemarketing +telemeter +telemeters +telemetries +telemetry +telepathic +telepathically +telepathy +telephone +telephoned +telephones +telephonic +telephoning +telephony +telephoto +telephotos +telescope +telescoped +telescopes +telescopic +telescoping +telethon +telethons +teletype +teletypes +teletypewriter +teletypewriters +televangelist +televangelists +televise +televised +televises +televising +television +televisions +telex +telexed +telexes +telexing +tell +teller +tellers +telling +tellingly +tells +telltale +telltales +temblor +temblors +temerity +temp +temped +temper +tempera +temperament +temperamental +temperamentally +temperaments +temperance +temperas +temperate +temperature +temperatures +tempered +tempering +tempers +tempest +tempests +tempestuous +tempestuously +tempestuousness +tempi +temping +template +templates +temple +temples +tempo +temporal +temporally +temporaries +temporarily +temporary +temporize +temporized +temporizes +temporizing +tempos +temps +tempt +temptation +temptations +tempted +tempter +tempters +tempting +temptingly +temptress +temptresses +tempts +tempura +ten +tenability +tenable +tenacious +tenaciously +tenacity +tenancies +tenancy +tenant +tenanted +tenanting +tenants +tend +tended +tendencies +tendency +tendentious +tendentiously +tendentiousness +tender +tendered +tenderer +tenderest +tenderfoot +tenderfoots +tenderhearted +tendering +tenderize +tenderized +tenderizer +tenderizers +tenderizes +tenderizing +tenderloin +tenderloins +tenderly +tenderness +tenders +tending +tendinitis +tendon +tendonitis +tendons +tendril +tendrils +tends +tenement +tenements +tenet +tenets +tenfold +tennis +tenon +tenoned +tenoning +tenons +tenor +tenors +tenpin +tenpins +tens +tense +tensed +tensely +tenseness +tenser +tenses +tensest +tensile +tensing +tension +tensions +tensor +tensors +tent +tentacle +tentacles +tentative +tentatively +tented +tenth +tenths +tenting +tents +tenuous +tenuously +tenuousness +tenure +tenured +tenures +tenuring +tepee +tepees +tepid +tequila +tequilas +terabit +terabits +terabyte +terabytes +tercentenaries +tercentenary +term +termagant +termagants +termed +terminable +terminal +terminally +terminals +terminate +terminated +terminates +terminating +termination +terminations +terminator +terminators +terming +termini +terminological +terminologies +terminology +terminus +termite +termites +termly +terms +tern +terns +terrace +terraced +terraces +terracing +terrain +terrains +terrapin +terrapins +terrarium +terrariums +terrestrial +terrestrials +terrible +terribly +terrier +terriers +terrific +terrifically +terrified +terrifies +terrify +terrifying +terrifyingly +territorial +territorials +territories +territory +terror +terrorism +terrorist +terrorists +terrorize +terrorized +terrorizes +terrorizing +terrors +terry +terse +tersely +terseness +terser +tersest +tertiary +test +testable +testament +testamentary +testaments +testate +testates +tested +tester +testers +testes +testicle +testicles +testier +testiest +testified +testifies +testify +testifying +testily +testimonial +testimonials +testimonies +testimony +testiness +testing +testis +testosterone +tests +testy +tetanus +tether +tethered +tethering +tethers +tetrahedron +tetrahedrons +text +textbook +textbooks +texted +textile +textiles +texting +texts +textual +textually +textural +texture +textured +textures +texturing +thalami +thalamus +thallium +than +thank +thanked +thankful +thankfully +thankfulness +thanking +thankless +thanklessly +thanks +thanksgiving +thanksgivings +that +thatch +thatched +thatcher +thatches +thatching +thaw +thawed +thawing +thaws +the +theater +theaters +theatrical +theatrically +thee +thees +theft +thefts +their +theirs +theism +theist +theistic +theists +them +thematic +thematically +theme +themes +themselves +then +thence +thenceforth +thenceforward +theocracies +theocracy +theocratic +theologian +theologians +theological +theologies +theology +theorem +theorems +theoretic +theoretical +theoretically +theoretician +theoreticians +theories +theorist +theorists +theorize +theorized +theorizes +theorizing +theory +theosophy +therapeutic +therapeutically +therapeutics +therapies +therapist +therapists +therapy +there +thereabout +thereabouts +thereafter +thereby +therefore +therefrom +therein +thereof +thereon +thereto +thereupon +therewith +thermal +thermally +thermals +thermionic +thermodynamic +thermodynamics +thermometer +thermometers +thermonuclear +thermoplastic +thermoplastics +thermos +thermoses +thermostat +thermostatic +thermostats +thesauri +thesaurus +thesauruses +these +theses +thesis +thespian +thespians +theta +they +thiamine +thick +thicken +thickened +thickener +thickeners +thickening +thickenings +thickens +thicker +thickest +thicket +thickets +thickly +thickness +thicknesses +thickset +thief +thieve +thieved +thievery +thieves +thieving +thievish +thigh +thighbone +thighbones +thighs +thimble +thimbleful +thimblefuls +thimbles +thin +thine +thing +thingamajig +thingamajigs +things +think +thinker +thinkers +thinking +thinks +thinly +thinned +thinner +thinners +thinness +thinnest +thinning +thins +third +thirdly +thirds +thirst +thirsted +thirstier +thirstiest +thirstily +thirsting +thirsts +thirsty +thirteen +thirteens +thirteenth +thirteenths +thirties +thirtieth +thirtieths +thirty +this +thistle +thistledown +thistles +thither +tho +thong +thongs +thoracic +thorax +thoraxes +thorium +thorn +thornier +thorniest +thorns +thorny +thorough +thoroughbred +thoroughbreds +thorougher +thoroughest +thoroughfare +thoroughfares +thoroughgoing +thoroughly +thoroughness +those +thou +though +thought +thoughtful +thoughtfully +thoughtfulness +thoughtless +thoughtlessly +thoughtlessness +thoughts +thous +thousand +thousands +thousandth +thousandths +thraldom +thrall +thralldom +thralled +thralling +thralls +thrash +thrashed +thrasher +thrashers +thrashes +thrashing +thrashings +thread +threadbare +threaded +threading +threads +threat +threaten +threatened +threatening +threateningly +threatens +threats +three +threefold +threes +threescore +threescores +threesome +threesomes +threnodies +threnody +thresh +threshed +thresher +threshers +threshes +threshing +threshold +thresholds +threw +thrice +thrift +thriftier +thriftiest +thriftily +thriftiness +thrifts +thrifty +thrill +thrilled +thriller +thrillers +thrilling +thrills +thrive +thrived +thrives +thriving +throat +throatier +throatiest +throatily +throatiness +throats +throaty +throb +throbbed +throbbing +throbs +throe +throes +thromboses +thrombosis +throne +thrones +throng +thronged +thronging +throngs +throttle +throttled +throttles +throttling +through +throughout +throughput +throughway +throughways +throw +throwaway +throwaways +throwback +throwbacks +thrower +throwers +throwing +thrown +throws +thru +thrum +thrummed +thrumming +thrums +thrush +thrushes +thrust +thrusting +thrusts +thruway +thruways +thud +thudded +thudding +thuds +thug +thugs +thumb +thumbed +thumbing +thumbnail +thumbnails +thumbs +thumbscrew +thumbscrews +thumbtack +thumbtacks +thump +thumped +thumping +thumps +thunder +thunderbolt +thunderbolts +thunderclap +thunderclaps +thundercloud +thunderclouds +thundered +thunderhead +thunderheads +thundering +thunderous +thunderously +thunders +thundershower +thundershowers +thunderstorm +thunderstorms +thunderstruck +thus +thwack +thwacked +thwacking +thwacks +thwart +thwarted +thwarting +thwarts +thy +thyme +thymus +thymuses +thyroid +thyroids +thyself +ti +tiara +tiaras +tibia +tibiae +tic +tick +ticked +ticker +tickers +ticket +ticketed +ticketing +tickets +ticking +tickle +tickled +tickles +tickling +ticklish +ticks +tics +tidal +tidbit +tidbits +tiddlywinks +tide +tided +tides +tidewater +tidewaters +tidied +tidier +tidies +tidiest +tidily +tidiness +tiding +tidings +tidy +tidying +tie +tiebreaker +tiebreakers +tied +tier +tiers +ties +tiff +tiffed +tiffing +tiffs +tiger +tigers +tight +tighten +tightened +tightening +tightens +tighter +tightest +tightfisted +tightly +tightness +tightrope +tightropes +tights +tightwad +tightwads +tigress +tigresses +tilde +tildes +tile +tiled +tiles +tiling +till +tillable +tillage +tilled +tiller +tillers +tilling +tills +tilt +tilted +tilting +tilts +timber +timbered +timbering +timberland +timberline +timberlines +timbers +timbre +timbres +time +timed +timekeeper +timekeepers +timeless +timelessness +timelier +timeliest +timeline +timelines +timeliness +timely +timepiece +timepieces +timer +timers +times +timescale +timescales +timestamp +timestamps +timetable +timetabled +timetables +timetabling +timeworn +timezone +timid +timider +timidest +timidity +timidly +timing +timings +timorous +timorously +timpani +timpanist +timpanists +tin +tincture +tinctured +tinctures +tincturing +tinder +tinderbox +tinderboxes +tine +tines +tinfoil +ting +tinge +tinged +tingeing +tinges +tinging +tingle +tingled +tingles +tingling +tinglings +tingly +tings +tinier +tiniest +tinker +tinkered +tinkering +tinkers +tinkle +tinkled +tinkles +tinkling +tinned +tinnier +tinniest +tinning +tinny +tins +tinsel +tinseled +tinseling +tinsels +tinsmith +tinsmiths +tint +tinted +tinting +tintinnabulation +tintinnabulations +tints +tiny +tip +tipped +tipper +tippers +tipping +tipple +tippled +tippler +tipplers +tipples +tippling +tips +tipsier +tipsiest +tipsily +tipster +tipsters +tipsy +tiptoe +tiptoed +tiptoeing +tiptoes +tiptop +tiptops +tirade +tirades +tire +tired +tireder +tiredest +tiredness +tireless +tirelessly +tirelessness +tires +tiresome +tiresomely +tiresomeness +tiring +tissue +tissues +tit +titan +titanic +titanium +titans +tithe +tithed +tithes +tithing +titillate +titillated +titillates +titillating +titillation +title +titled +titles +titling +titmice +titmouse +tits +titter +tittered +tittering +titters +tittle +tittles +titular +tizzies +tizzy +to +toad +toadied +toadies +toads +toadstool +toadstools +toady +toadying +toast +toasted +toaster +toasters +toastier +toastiest +toasting +toastmaster +toastmasters +toasts +toasty +tobacco +tobacconist +tobacconists +tobaccos +toboggan +tobogganed +tobogganing +toboggans +tocsin +tocsins +today +toddies +toddle +toddled +toddler +toddlers +toddles +toddling +toddy +toe +toed +toehold +toeholds +toeing +toenail +toenails +toes +toffee +toffees +tofu +tog +toga +togas +together +togetherness +toggle +toggled +toggles +toggling +togs +toil +toiled +toiler +toilers +toilet +toileted +toileting +toiletries +toiletry +toilets +toilette +toiling +toils +toilsome +toke +toked +token +tokenism +tokens +tokes +toking +told +tolerable +tolerably +tolerance +tolerances +tolerant +tolerantly +tolerate +tolerated +tolerates +tolerating +toleration +toll +tollbooth +tollbooths +tolled +tollgate +tollgates +tolling +tolls +tom +tomahawk +tomahawked +tomahawking +tomahawks +tomato +tomatoes +tomb +tombed +tombing +tomboy +tomboys +tombs +tombstone +tombstones +tomcat +tomcats +tome +tomes +tomfooleries +tomfoolery +tomorrow +tomorrows +toms +ton +tonal +tonalities +tonality +tone +toned +toneless +toner +tones +tong +tongs +tongue +tongued +tongues +tonguing +tonic +tonics +tonier +toniest +tonight +toning +tonnage +tonnages +tonne +tonnes +tons +tonsil +tonsillectomies +tonsillectomy +tonsillitis +tonsils +tonsorial +tonsure +tonsured +tonsures +tonsuring +tony +too +took +tool +toolbar +toolbars +toolbox +toolboxes +tooled +tooling +toolkit +tools +toot +tooted +tooth +toothache +toothaches +toothbrush +toothbrushes +toothed +toothier +toothiest +toothless +toothpaste +toothpastes +toothpick +toothpicks +toothsome +toothy +tooting +toots +top +topaz +topazes +topcoat +topcoats +topic +topical +topically +topics +topknot +topknots +topless +topmast +topmasts +topmost +topographer +topographers +topographic +topographical +topographies +topography +topological +topologically +topology +topped +topping +toppings +topple +toppled +topples +toppling +tops +topsail +topsails +topside +topsides +topsoil +toque +toques +tor +torch +torched +torches +torching +torchlight +tore +toreador +toreadors +torment +tormented +tormenting +tormentor +tormentors +torments +torn +tornado +tornadoes +torpedo +torpedoed +torpedoes +torpedoing +torpid +torpidity +torpor +torque +torqued +torques +torquing +torrent +torrential +torrents +torrid +tors +torsion +torso +torsos +tort +torte +tortes +tortilla +tortillas +tortoise +tortoises +tortoiseshell +tortoiseshells +torts +tortuous +tortuously +torture +tortured +torturer +torturers +tortures +torturing +torus +toss +tossed +tosses +tossing +tossup +tossups +tot +total +totaled +totaling +totalitarian +totalitarianism +totalitarians +totalities +totality +totally +totals +tote +toted +totem +totemic +totems +totes +toting +tots +totted +totter +tottered +tottering +totters +totting +toucan +toucans +touch +touchdown +touchdowns +touched +touches +touchier +touchiest +touching +touchingly +touchings +touchstone +touchstones +touchy +touché +tough +toughen +toughened +toughening +toughens +tougher +toughest +toughly +toughness +toughs +toupee +toupees +tour +toured +touring +tourism +tourist +tourists +tourmaline +tournament +tournaments +tourney +tourneys +tourniquet +tourniquets +tours +tousle +tousled +tousles +tousling +tout +touted +touting +touts +tow +toward +towards +towed +towel +toweled +toweling +towelings +towels +tower +towered +towering +towers +towhead +towheaded +towheads +towing +town +townhouse +townhouses +towns +townsfolk +township +townships +townsman +townsmen +townspeople +towpath +towpaths +tows +toxemia +toxic +toxicity +toxicologist +toxicologists +toxicology +toxin +toxins +toy +toyed +toying +toys +trace +traceable +traced +tracer +traceries +tracers +tracery +traces +trachea +tracheae +tracheotomies +tracheotomy +tracing +tracings +track +tracked +tracker +trackers +tracking +tracks +tract +tractable +traction +tractor +tractors +tracts +trade +traded +trademark +trademarked +trademarking +trademarks +trader +traders +trades +tradesman +tradesmen +trading +tradition +traditional +traditionalist +traditionalists +traditionally +traditions +traduce +traduced +traduces +traducing +traffic +trafficked +trafficker +traffickers +trafficking +traffics +tragedian +tragedians +tragedies +tragedy +tragic +tragically +tragicomedies +tragicomedy +trail +trailblazer +trailblazers +trailed +trailer +trailers +trailing +trails +train +trained +trainee +trainees +trainer +trainers +training +trains +traipse +traipsed +traipses +traipsing +trait +traitor +traitorous +traitors +traits +trajectories +trajectory +tram +trammed +trammel +trammeled +trammeling +trammels +tramming +tramp +tramped +tramping +trample +trampled +tramples +trampling +trampoline +trampolines +tramps +trams +trance +trances +tranquil +tranquiler +tranquilest +tranquility +tranquilize +tranquilized +tranquilizer +tranquilizers +tranquilizes +tranquilizing +tranquillity +tranquilly +transact +transacted +transacting +transaction +transactions +transacts +transatlantic +transceiver +transceivers +transcend +transcended +transcendence +transcendent +transcendental +transcendentalism +transcendentalist +transcendentalists +transcendentally +transcending +transcends +transcontinental +transcribe +transcribed +transcribes +transcribing +transcript +transcription +transcriptions +transcripts +transducer +transducers +transept +transepts +transfer +transferable +transferal +transferals +transference +transferred +transferring +transfers +transfiguration +transfigure +transfigured +transfigures +transfiguring +transfinite +transfix +transfixed +transfixes +transfixing +transform +transformation +transformations +transformed +transformer +transformers +transforming +transforms +transfuse +transfused +transfuses +transfusing +transfusion +transfusions +transgress +transgressed +transgresses +transgressing +transgression +transgressions +transgressor +transgressors +transience +transiency +transient +transients +transistor +transistors +transit +transited +transiting +transition +transitional +transitioned +transitioning +transitions +transitive +transitively +transitives +transitory +transits +translate +translated +translates +translating +translation +translations +translator +translators +transliterate +transliterated +transliterates +transliterating +transliteration +transliterations +translucence +translucent +transmigrate +transmigrated +transmigrates +transmigrating +transmigration +transmissible +transmission +transmissions +transmit +transmits +transmittable +transmittal +transmitted +transmitter +transmitters +transmitting +transmutation +transmutations +transmute +transmuted +transmutes +transmuting +transnational +transnationals +transoceanic +transom +transoms +transparencies +transparency +transparent +transparently +transpiration +transpire +transpired +transpires +transpiring +transplant +transplantation +transplanted +transplanting +transplants +transponder +transponders +transport +transportable +transportation +transported +transporter +transporters +transporting +transports +transpose +transposed +transposes +transposing +transposition +transpositions +transsexual +transsexuals +transship +transshipment +transshipped +transshipping +transships +transubstantiation +transverse +transversely +transverses +transvestism +transvestite +transvestites +trap +trapdoor +trapdoors +trapeze +trapezes +trapezoid +trapezoidal +trapezoids +trappable +trapped +trapper +trappers +trapping +trappings +traps +trapshooting +trash +trashcan +trashcans +trashed +trashes +trashier +trashiest +trashing +trashy +trauma +traumas +traumatic +traumatize +traumatized +traumatizes +traumatizing +travail +travailed +travailing +travails +travel +traveled +traveler +travelers +traveling +travelings +travelog +travelogs +travelogue +travelogues +travels +traverse +traversed +traverses +traversing +travestied +travesties +travesty +travestying +trawl +trawled +trawler +trawlers +trawling +trawls +tray +trays +treacheries +treacherous +treacherously +treachery +treacle +tread +treading +treadle +treadled +treadles +treadling +treadmill +treadmills +treads +treason +treasonable +treasonous +treasure +treasured +treasurer +treasurers +treasures +treasuries +treasuring +treasury +treat +treatable +treated +treaties +treating +treatise +treatises +treatment +treatments +treats +treaty +treble +trebled +trebles +trebling +tree +treed +treeing +treeless +trees +treetop +treetops +trefoil +trefoils +trek +trekked +trekking +treks +trellis +trellised +trellises +trellising +tremble +trembled +trembles +trembling +tremendous +tremendously +tremolo +tremolos +tremor +tremors +tremulous +tremulously +trench +trenchant +trenchantly +trenched +trenches +trenching +trend +trended +trendier +trendies +trendiest +trending +trends +trendy +trepidation +trespass +trespassed +trespasser +trespassers +trespasses +trespassing +tress +tresses +trestle +trestles +triad +triads +triage +trial +trialed +trialing +trials +triangle +triangles +triangular +triangulation +triathlon +triathlons +tribal +tribalism +tribe +tribes +tribesman +tribesmen +tribulation +tribulations +tribunal +tribunals +tribune +tribunes +tributaries +tributary +tribute +tributes +trice +triceps +tricepses +triceratops +trick +tricked +trickery +trickier +trickiest +trickiness +tricking +trickle +trickled +trickles +trickling +tricks +trickster +tricksters +tricky +tricolor +tricolors +tricycle +tricycles +trident +tridents +tried +triennial +triennials +tries +trifecta +trifectas +trifle +trifled +trifler +triflers +trifles +trifling +trifocals +trig +trigger +triggered +triggering +triggers +triglyceride +triglycerides +trigonometric +trigonometry +trike +trikes +trilateral +trilaterals +trill +trilled +trilling +trillion +trillions +trillionth +trillionths +trills +trilogies +trilogy +trim +trimaran +trimarans +trimester +trimesters +trimly +trimmed +trimmer +trimmers +trimmest +trimming +trimmings +trimness +trims +trinities +trinity +trinket +trinkets +trio +trios +trip +tripartite +tripe +triple +tripled +triples +triplet +triplets +triplicate +triplicated +triplicates +triplicating +tripling +triply +tripod +tripods +tripos +tripped +tripping +trips +triptych +triptychs +trisect +trisected +trisecting +trisects +trite +tritely +triteness +triter +tritest +triumph +triumphal +triumphant +triumphantly +triumphed +triumphing +triumphs +triumvirate +triumvirates +trivet +trivets +trivia +trivial +trivialities +triviality +trivialize +trivialized +trivializes +trivializing +trivially +trochee +trochees +trod +trodden +troglodyte +troglodytes +troika +troikas +troll +trolled +trolley +trolleys +trolling +trollop +trollops +trolls +trombone +trombones +trombonist +trombonists +tromp +tromped +tromping +tromps +troop +trooped +trooper +troopers +trooping +troops +troopship +troopships +trope +tropes +trophies +trophy +tropic +tropical +tropics +tropism +tropisms +troposphere +tropospheres +trot +troth +trots +trotted +trotter +trotters +trotting +troubadour +troubadours +trouble +troubled +troublemaker +troublemakers +troubles +troubleshoot +troubleshooted +troubleshooter +troubleshooters +troubleshooting +troubleshoots +troubleshot +troublesome +troubling +trough +troughs +trounce +trounced +trounces +trouncing +troupe +trouped +trouper +troupers +troupes +trouping +trouser +trousers +trousseau +trousseaux +trout +trouts +trowel +troweled +troweling +trowels +troy +troys +truancy +truant +truanted +truanting +truants +truce +truces +truck +trucked +trucker +truckers +trucking +truckle +truckled +truckles +truckling +truckload +truckloads +trucks +truculence +truculent +truculently +trudge +trudged +trudges +trudging +true +trued +truer +trues +truest +truffle +truffles +truing +truism +truisms +truly +trump +trumped +trumpery +trumpet +trumpeted +trumpeter +trumpeters +trumpeting +trumpets +trumping +trumps +truncate +truncated +truncates +truncating +truncation +truncheon +truncheons +trundle +trundled +trundles +trundling +trunk +trunking +trunks +truss +trussed +trusses +trussing +trust +trusted +trustee +trustees +trusteeship +trusteeships +trustful +trustfully +trustfulness +trustier +trusties +trustiest +trusting +trusts +trustworthier +trustworthiest +trustworthiness +trustworthy +trusty +truth +truther +truthers +truthful +truthfully +truthfulness +truthiness +truths +try +trying +tryout +tryouts +tryst +trysted +trysting +trysts +ts +tsunami +tsunamis +tub +tuba +tubas +tubbier +tubbiest +tubby +tube +tubed +tubeless +tuber +tubercle +tubercles +tubercular +tuberculosis +tuberculous +tuberous +tubers +tubes +tubing +tubs +tubular +tuck +tucked +tucker +tuckered +tuckering +tuckers +tucking +tucks +tuft +tufted +tufting +tufts +tug +tugboat +tugboats +tugged +tugging +tugs +tuition +tulip +tulips +tulle +tumble +tumbled +tumbledown +tumbler +tumblers +tumbles +tumbleweed +tumbleweeds +tumbling +tumbrel +tumbrels +tumbril +tumbrils +tumid +tummies +tummy +tumor +tumors +tumult +tumults +tumultuous +tun +tuna +tunas +tundra +tundras +tune +tuned +tuneful +tunefully +tuneless +tunelessly +tuner +tuners +tunes +tungsten +tunic +tunics +tuning +tunnel +tunneled +tunneling +tunnelings +tunnels +tunnies +tunny +tuns +turban +turbans +turbid +turbine +turbines +turbojet +turbojets +turboprop +turboprops +turbot +turbots +turbulence +turbulent +turbulently +turd +turds +turducken +turduckens +tureen +tureens +turf +turfed +turfing +turfs +turgid +turgidity +turgidly +turkey +turkeys +turmeric +turmerics +turmoil +turmoils +turn +turnabout +turnabouts +turnaround +turnarounds +turncoat +turncoats +turned +turner +turners +turning +turnip +turnips +turnkey +turnkeys +turnoff +turnoffs +turnout +turnouts +turnover +turnovers +turnpike +turnpikes +turns +turnstile +turnstiles +turntable +turntables +turpentine +turpitude +turquoise +turquoises +turret +turrets +turtle +turtledove +turtledoves +turtleneck +turtlenecks +turtles +tush +tushes +tusk +tusked +tusks +tussle +tussled +tussles +tussling +tussock +tussocks +tutelage +tutor +tutored +tutorial +tutorials +tutoring +tutors +tutu +tutus +tux +tuxedo +tuxedos +tuxes +twaddle +twaddled +twaddles +twaddling +twain +twang +twanged +twanging +twangs +tweak +tweaked +tweaking +tweaks +twee +tweed +tweedier +tweediest +tweeds +tweedy +tweet +tweeted +tweeter +tweeters +tweeting +tweets +tweezers +twelfth +twelfths +twelve +twelves +twenties +twentieth +twentieths +twenty +twerk +twerked +twerking +twerks +twerp +twerps +twice +twiddle +twiddled +twiddles +twiddling +twig +twigged +twiggier +twiggiest +twigging +twiggy +twigs +twilight +twill +twilled +twin +twine +twined +twines +twinge +twinged +twinges +twinging +twining +twinkle +twinkled +twinkles +twinkling +twinklings +twinned +twinning +twins +twirl +twirled +twirler +twirlers +twirling +twirls +twist +twisted +twister +twisters +twisting +twists +twit +twitch +twitched +twitches +twitching +twits +twitted +twitter +twittered +twittering +twitters +twitting +two +twofer +twofers +twofold +twos +twosome +twosomes +tycoon +tycoons +tying +tyke +tykes +tympanum +tympanums +type +typecast +typecasting +typecasts +typed +typeface +typefaces +types +typescript +typescripts +typeset +typesets +typesetter +typesetters +typesetting +typewrite +typewriter +typewriters +typewrites +typewriting +typewritten +typewrote +typhoid +typhoon +typhoons +typhus +typical +typically +typified +typifies +typify +typifying +typing +typist +typists +typo +typographer +typographers +typographic +typographical +typographically +typography +typos +tyrannical +tyrannically +tyrannies +tyrannize +tyrannized +tyrannizes +tyrannizing +tyrannosaur +tyrannosaurs +tyrannosaurus +tyrannosauruses +tyrannous +tyranny +tyrant +tyrants +tyro +tyros +u +ubiquitous +ubiquitously +ubiquity +udder +udders +ugh +uglier +ugliest +ugliness +ugly +uh +ukulele +ukuleles +ulcer +ulcerate +ulcerated +ulcerates +ulcerating +ulceration +ulcerations +ulcerous +ulcers +ulna +ulnae +ulterior +ultimate +ultimately +ultimatum +ultimatums +ultra +ultraconservative +ultraconservatives +ultramarine +ultras +ultrasonic +ultrasonically +ultrasound +ultrasounds +ultraviolet +ululate +ululated +ululates +ululating +um +umbel +umbels +umber +umbilical +umbilici +umbilicus +umbrage +umbrella +umbrellas +umiak +umiaks +umlaut +umlauts +ump +umped +umping +umpire +umpired +umpires +umpiring +umps +umpteen +umpteenth +unabashed +unabated +unable +unabridged +unabridgeds +unaccented +unacceptability +unacceptable +unacceptably +unaccepted +unaccompanied +unaccountable +unaccountably +unaccustomed +unacknowledged +unacquainted +unadorned +unadulterated +unadvised +unaffected +unafraid +unaided +unalterable +unalterably +unaltered +unambiguous +unambiguously +unanimity +unanimous +unanimously +unannounced +unanswerable +unanswered +unanticipated +unappealing +unappetizing +unappreciated +unappreciative +unapproachable +unarmed +unashamed +unashamedly +unasked +unassailable +unassigned +unassisted +unassuming +unattached +unattainable +unattended +unattractive +unattributed +unauthenticated +unauthorized +unavailable +unavailing +unavoidable +unavoidably +unaware +unawares +unbalanced +unbar +unbarred +unbarring +unbars +unbearable +unbearably +unbeatable +unbeaten +unbecoming +unbeknown +unbeknownst +unbelief +unbelievable +unbelievably +unbeliever +unbelievers +unbend +unbending +unbends +unbent +unbiased +unbidden +unbind +unbinding +unbinds +unblock +unblocked +unblocking +unblocks +unblushing +unbolt +unbolted +unbolting +unbolts +unborn +unbosom +unbosomed +unbosoming +unbosoms +unbound +unbounded +unbranded +unbreakable +unbridled +unbroken +unbuckle +unbuckled +unbuckles +unbuckling +unburden +unburdened +unburdening +unburdens +unbutton +unbuttoned +unbuttoning +unbuttons +uncalled +uncannier +uncanniest +uncannily +uncanny +uncaring +uncased +uncatalogued +unceasing +unceasingly +uncensored +unceremonious +unceremoniously +uncertain +uncertainly +uncertainties +uncertainty +unchallenged +unchanged +unchanging +uncharacteristic +uncharacteristically +uncharitable +uncharitably +uncharted +unchecked +unchristian +uncivil +uncivilized +unclaimed +unclasp +unclasped +unclasping +unclasps +unclassified +uncle +unclean +uncleaner +uncleanest +uncleanlier +uncleanliest +uncleanly +uncleanness +unclear +unclearer +unclearest +uncles +unclothe +unclothed +unclothes +unclothing +uncluttered +uncoil +uncoiled +uncoiling +uncoils +uncollected +uncomfortable +uncomfortably +uncommitted +uncommon +uncommoner +uncommonest +uncommonly +uncommunicative +uncomplaining +uncompleted +uncomplicated +uncomplimentary +uncomprehending +uncompressed +uncompromising +uncompromisingly +unconcern +unconcerned +unconcernedly +unconditional +unconditionally +unconfirmed +unconnected +unconquerable +unconscionable +unconscionably +unconscious +unconsciously +unconsciousness +unconsidered +unconstitutional +uncontaminated +uncontested +uncontrollable +uncontrollably +uncontrolled +uncontroversial +unconventional +unconventionally +unconvinced +unconvincing +unconvincingly +uncooked +uncooperative +uncoordinated +uncork +uncorked +uncorking +uncorks +uncorrelated +uncorroborated +uncountable +uncounted +uncouple +uncoupled +uncouples +uncoupling +uncouth +uncover +uncovered +uncovering +uncovers +uncritical +unction +unctions +unctuous +unctuously +unctuousness +uncultivated +uncultured +uncut +undamaged +undated +undaunted +undeceive +undeceived +undeceives +undeceiving +undecidable +undecided +undecideds +undecipherable +undeclared +undefeated +undefended +undefinable +undefined +undelivered +undemanding +undemocratic +undemonstrative +undeniable +undeniably +undependable +under +underachieve +underachieved +underachiever +underachievers +underachieves +underachieving +underact +underacted +underacting +underacts +underage +underarm +underarms +underbellies +underbelly +underbid +underbidding +underbids +underbrush +undercarriage +undercarriages +undercharge +undercharged +undercharges +undercharging +underclass +underclassman +underclassmen +underclothes +underclothing +undercoat +undercoated +undercoating +undercoats +undercover +undercurrent +undercurrents +undercut +undercuts +undercutting +underdeveloped +underdog +underdogs +underdone +underemployed +underestimate +underestimated +underestimates +underestimating +underexpose +underexposed +underexposes +underexposing +underfed +underfeed +underfeeding +underfeeds +underflow +underfoot +underfunded +undergarment +undergarments +undergo +undergoes +undergoing +undergone +undergrad +undergrads +undergraduate +undergraduates +underground +undergrounds +undergrowth +underhand +underhanded +underhandedly +underlain +underlay +underlays +underlie +underlies +underline +underlined +underlines +underling +underlings +underlining +underlying +undermine +undermined +undermines +undermining +undermost +underneath +underneaths +undernourished +underpaid +underpants +underpass +underpasses +underpay +underpaying +underpays +underpin +underpinned +underpinning +underpinnings +underpins +underplay +underplayed +underplaying +underplays +underprivileged +underrate +underrated +underrates +underrating +underscore +underscored +underscores +underscoring +undersea +undersecretaries +undersecretary +undersell +underselling +undersells +undershirt +undershirts +undershoot +undershooting +undershoots +undershorts +undershot +underside +undersides +undersign +undersigned +undersigning +undersigns +undersized +underskirt +underskirts +undersold +understaffed +understand +understandable +understandably +understanding +understandingly +understandings +understands +understate +understated +understatement +understatements +understates +understating +understood +understudied +understudies +understudy +understudying +undertake +undertaken +undertaker +undertakers +undertakes +undertaking +undertakings +undertone +undertones +undertook +undertow +undertows +underused +undervalue +undervalued +undervalues +undervaluing +underwater +underwear +underweight +underwent +underworld +underworlds +underwrite +underwriter +underwriters +underwrites +underwriting +underwritten +underwrote +undeserved +undeservedly +undeserving +undesirability +undesirable +undesirables +undetectable +undetected +undetermined +undeterred +undeveloped +undid +undies +undignified +undiluted +undiminished +undisciplined +undisclosed +undiscovered +undiscriminating +undisguised +undisputed +undistinguished +undisturbed +undivided +undo +undocumented +undoes +undoing +undoings +undone +undoubted +undoubtedly +undress +undressed +undresses +undressing +undue +undulant +undulate +undulated +undulates +undulating +undulation +undulations +unduly +undying +unearned +unearth +unearthed +unearthing +unearthly +unearths +unease +uneasier +uneasiest +uneasily +uneasiness +uneasy +uneaten +uneconomic +uneconomical +unedited +uneducated +unembarrassed +unemotional +unemployable +unemployed +unemployment +unending +unendurable +unenforceable +unenlightened +unenthusiastic +unenviable +unequal +unequaled +unequally +unequivocal +unequivocally +unerring +unerringly +unethical +uneven +unevenly +unevenness +uneventful +uneventfully +unexampled +unexceptionable +unexceptional +unexciting +unexpected +unexpectedly +unexplained +unexplored +unexpurgated +unfailing +unfailingly +unfair +unfairer +unfairest +unfairly +unfairness +unfaithful +unfaithfully +unfaithfulness +unfamiliar +unfamiliarity +unfashionable +unfasten +unfastened +unfastening +unfastens +unfathomable +unfavorable +unfavorably +unfeasible +unfeeling +unfeelingly +unfeigned +unfetter +unfettered +unfettering +unfetters +unfilled +unfinished +unfit +unfits +unfitted +unfitting +unflagging +unflappable +unflattering +unflinching +unflinchingly +unfold +unfolded +unfolding +unfolds +unforeseeable +unforeseen +unforgettable +unforgettably +unforgivable +unforgiving +unformed +unfortunate +unfortunately +unfortunates +unfounded +unfrequented +unfriend +unfriended +unfriending +unfriendlier +unfriendliest +unfriendliness +unfriendly +unfriends +unfrock +unfrocked +unfrocking +unfrocks +unfulfilled +unfunny +unfurl +unfurled +unfurling +unfurls +unfurnished +ungainlier +ungainliest +ungainliness +ungainly +ungentlemanly +ungodlier +ungodliest +ungodly +ungovernable +ungracious +ungrammatical +ungrateful +ungratefully +ungratefulness +ungrudging +unguarded +unguent +unguents +ungulate +ungulates +unhand +unhanded +unhanding +unhands +unhappier +unhappiest +unhappily +unhappiness +unhappy +unharmed +unhealthful +unhealthier +unhealthiest +unhealthy +unheard +unheeded +unhelpful +unhesitating +unhesitatingly +unhindered +unhinge +unhinged +unhinges +unhinging +unhitch +unhitched +unhitches +unhitching +unholier +unholiest +unholy +unhook +unhooked +unhooking +unhooks +unhorse +unhorsed +unhorses +unhorsing +unhurried +unhurt +unicameral +unicorn +unicorns +unicycle +unicycles +unidentifiable +unidentified +unidirectional +unification +unified +unifies +uniform +uniformed +uniforming +uniformity +uniformly +uniforms +unify +unifying +unilateral +unilaterally +unimaginable +unimaginative +unimpaired +unimpeachable +unimplementable +unimplemented +unimportant +unimpressed +unimpressive +uninformative +uninformed +uninhabitable +uninhabited +uninhibited +uninitialized +uninitiated +uninjured +uninspired +uninspiring +uninstall +uninstallable +uninstalled +uninstaller +uninstallers +uninstalling +uninstalls +uninsured +unintelligent +unintelligible +unintelligibly +unintended +unintentional +unintentionally +uninterested +uninteresting +uninterpreted +uninterrupted +uninvited +uninviting +union +unionization +unionize +unionized +unionizes +unionizing +unions +unique +uniquely +uniqueness +uniquer +uniquest +unisex +unison +unit +unitary +unite +united +unites +unities +uniting +units +unity +universal +universality +universally +universals +universe +universes +universities +university +unjust +unjustifiable +unjustified +unjustly +unkempt +unkind +unkinder +unkindest +unkindlier +unkindliest +unkindly +unkindness +unknowable +unknowing +unknowingly +unknowings +unknown +unknowns +unlabeled +unlace +unlaced +unlaces +unlacing +unlatch +unlatched +unlatches +unlatching +unlawful +unlawfully +unleaded +unlearn +unlearned +unlearning +unlearns +unleash +unleashed +unleashes +unleashing +unleavened +unless +unlettered +unlicensed +unlike +unlikelier +unlikeliest +unlikelihood +unlikely +unlimited +unlisted +unload +unloaded +unloading +unloads +unlock +unlocked +unlocking +unlocks +unloose +unloosed +unlooses +unloosing +unloved +unluckier +unluckiest +unluckily +unlucky +unmade +unmake +unmakes +unmaking +unman +unmanageable +unmanlier +unmanliest +unmanly +unmanned +unmannerly +unmanning +unmans +unmarked +unmarried +unmask +unmasked +unmasking +unmasks +unmatched +unmemorable +unmentionable +unmentionables +unmerciful +unmercifully +unmindful +unmissed +unmistakable +unmistakably +unmitigated +unmodified +unmoral +unmoved +unnamed +unnatural +unnaturally +unnecessarily +unnecessary +unneeded +unnerve +unnerved +unnerves +unnerving +unnoticeable +unnoticed +unnumbered +unobjectionable +unobservant +unobserved +unobstructed +unobtainable +unobtrusive +unobtrusively +unoccupied +unoffensive +unofficial +unofficially +unopened +unopposed +unorganized +unoriginal +unorthodox +unpack +unpacked +unpacking +unpacks +unpaid +unpainted +unpalatable +unparalleled +unpardonable +unpatriotic +unpaved +unperturbed +unpick +unpin +unpinned +unpinning +unpins +unplanned +unpleasant +unpleasantly +unpleasantness +unplug +unplugged +unplugging +unplugs +unplumbed +unpolluted +unpopular +unpopularity +unprecedented +unpredictability +unpredictable +unprejudiced +unpremeditated +unprepared +unpretentious +unpreventable +unprincipled +unprintable +unprivileged +unproductive +unprofessional +unprofitable +unpromising +unprompted +unpronounceable +unprotected +unproved +unproven +unprovoked +unpublished +unpunished +unqualified +unquenchable +unquestionable +unquestionably +unquestioned +unquestioning +unquestioningly +unquote +unquoted +unquotes +unquoting +unravel +unraveled +unraveling +unravels +unreachable +unread +unreadable +unready +unreal +unrealistic +unrealistically +unrealized +unreasonable +unreasonableness +unreasonably +unreasoning +unrecognizable +unrecognized +unreconstructed +unrecorded +unrefined +unregenerate +unregistered +unregulated +unrehearsed +unrelated +unreleased +unrelenting +unrelentingly +unreliability +unreliable +unrelieved +unremarkable +unremitting +unrepeatable +unrepentant +unrepresentative +unrequited +unreserved +unreservedly +unresolved +unresponsive +unrest +unrestrained +unrestricted +unrewarding +unripe +unriper +unripest +unrivaled +unroll +unrolled +unrolling +unrolls +unromantic +unruffled +unrulier +unruliest +unruliness +unruly +unsaddle +unsaddled +unsaddles +unsaddling +unsafe +unsafer +unsafest +unsaid +unsalted +unsanctioned +unsanitary +unsatisfactory +unsatisfied +unsatisfying +unsaturated +unsavory +unsay +unsaying +unsays +unscathed +unscheduled +unschooled +unscientific +unscramble +unscrambled +unscrambles +unscrambling +unscrew +unscrewed +unscrewing +unscrews +unscrupulous +unscrupulously +unscrupulousness +unseal +unsealed +unsealing +unseals +unseasonable +unseasonably +unseasoned +unseat +unseated +unseating +unseats +unseeing +unseemlier +unseemliest +unseemliness +unseemly +unseen +unselfish +unselfishly +unselfishness +unsent +unsentimental +unset +unsettle +unsettled +unsettles +unsettling +unshakable +unshakeable +unshaven +unsheathe +unsheathed +unsheathes +unsheathing +unsightlier +unsightliest +unsightliness +unsightly +unsigned +unskilled +unskillful +unsmiling +unsnap +unsnapped +unsnapping +unsnaps +unsnarl +unsnarled +unsnarling +unsnarls +unsociable +unsold +unsolicited +unsolved +unsophisticated +unsound +unsounder +unsoundest +unsparing +unspeakable +unspeakably +unspecific +unspecified +unspoiled +unspoken +unsportsmanlike +unstable +unstated +unsteadier +unsteadiest +unsteadily +unsteadiness +unsteady +unstop +unstoppable +unstopped +unstopping +unstops +unstressed +unstructured +unstrung +unstuck +unstudied +unsubscribe +unsubscribed +unsubscribes +unsubscribing +unsubstantial +unsubstantiated +unsubtle +unsuccessful +unsuccessfully +unsuitable +unsuitably +unsuited +unsung +unsupervised +unsupportable +unsupported +unsure +unsurpassed +unsurprising +unsuspected +unsuspecting +unsweetened +unswerving +unsympathetic +untainted +untamed +untangle +untangled +untangles +untangling +untapped +untaught +untenable +untested +unthinkable +unthinking +unthinkingly +untidier +untidiest +untidiness +untidy +untie +untied +unties +until +untimelier +untimeliest +untimeliness +untimely +untiring +untiringly +untitled +unto +untold +untouchable +untouchables +untouched +untoward +untrained +untreated +untried +untroubled +untrue +untruer +untruest +untrustworthy +untruth +untruthful +untruthfully +untruths +untutored +untwist +untwisted +untwisting +untwists +untying +unusable +unused +unusual +unusually +unutterable +unutterably +unvarnished +unvarying +unveil +unveiled +unveiling +unveils +unverified +unvoiced +unwanted +unwarier +unwariest +unwariness +unwarranted +unwary +unwashed +unwavering +unwed +unwelcome +unwell +unwholesome +unwieldier +unwieldiest +unwieldiness +unwieldy +unwilling +unwillingly +unwillingness +unwind +unwinding +unwinds +unwise +unwisely +unwiser +unwisest +unwitting +unwittingly +unwonted +unworkable +unworldly +unworthier +unworthiest +unworthiness +unworthy +unwound +unwrap +unwrapped +unwrapping +unwraps +unwritten +unyielding +unzip +unzipped +unzipping +unzips +up +upbeat +upbeats +upbraid +upbraided +upbraiding +upbraids +upbringing +upbringings +upchuck +upchucked +upchucking +upchucks +upcoming +upcountry +update +updated +updater +updates +updating +updraft +updrafts +upend +upended +upending +upends +upfront +upgrade +upgraded +upgrades +upgrading +upheaval +upheavals +upheld +uphill +uphills +uphold +upholding +upholds +upholster +upholstered +upholsterer +upholsterers +upholstering +upholsters +upholstery +upkeep +upland +uplands +uplift +uplifted +uplifting +upliftings +uplifts +upload +upmarket +upon +upped +upper +uppercase +upperclassman +upperclassmen +uppercut +uppercuts +uppercutting +uppermost +uppers +upping +uppity +upraise +upraised +upraises +upraising +upright +uprights +uprising +uprisings +uproar +uproarious +uproariously +uproars +uproot +uprooted +uprooting +uproots +ups +upscale +upset +upsets +upsetting +upshot +upshots +upside +upsides +upstage +upstaged +upstages +upstaging +upstairs +upstanding +upstart +upstarted +upstarting +upstarts +upstate +upstream +upsurge +upsurged +upsurges +upsurging +upswing +upswings +uptake +uptakes +uptight +uptown +upturn +upturned +upturning +upturns +upward +upwardly +upwards +uranium +urban +urbane +urbaner +urbanest +urbanity +urbanization +urbanize +urbanized +urbanizes +urbanizing +urchin +urchins +urea +urethra +urethrae +urge +urged +urgency +urgent +urgently +urges +urging +uric +urinal +urinals +urinalyses +urinalysis +urinary +urinate +urinated +urinates +urinating +urination +urine +urn +urns +urologist +urologists +urology +us +usability +usable +usage +usages +use +used +useful +usefully +usefulness +useless +uselessly +uselessness +user +username +usernames +users +uses +usher +ushered +usherette +usherettes +ushering +ushers +using +usual +usually +usurer +usurers +usurious +usurp +usurpation +usurped +usurper +usurpers +usurping +usurps +usury +utensil +utensils +uteri +uterine +uterus +utilitarian +utilitarianism +utilitarians +utilities +utility +utilization +utilize +utilized +utilizes +utilizing +utmost +utopia +utopian +utopians +utopias +utter +utterance +utterances +uttered +uttering +utterly +uttermost +utters +uvula +uvular +uvulars +uvulas +v +vacancies +vacancy +vacant +vacantly +vacate +vacated +vacates +vacating +vacation +vacationed +vacationer +vacationers +vacationing +vacations +vaccinate +vaccinated +vaccinates +vaccinating +vaccination +vaccinations +vaccine +vaccines +vacillate +vacillated +vacillates +vacillating +vacillation +vacillations +vacuity +vacuous +vacuously +vacuum +vacuumed +vacuuming +vacuums +vagabond +vagabonded +vagabonding +vagabonds +vagaries +vagary +vagina +vaginae +vaginal +vagrancy +vagrant +vagrants +vague +vaguely +vagueness +vaguer +vaguest +vain +vainer +vainest +vainglorious +vainglory +vainly +valance +valances +vale +valedictorian +valedictorians +valedictories +valedictory +valence +valences +valentine +valentines +vales +valet +valeted +valeting +valets +valiant +valiantly +valid +validate +validated +validates +validating +validation +validations +validity +validly +validness +valise +valises +valley +valleys +valor +valorous +valuable +valuables +valuation +valuations +value +valued +valueless +values +valuing +valve +valved +valves +valving +vamoose +vamoosed +vamooses +vamoosing +vamp +vamped +vamping +vampire +vampires +vamps +van +vanadium +vandal +vandalism +vandalize +vandalized +vandalizes +vandalizing +vandals +vane +vanes +vanguard +vanguards +vanilla +vanillas +vanish +vanished +vanishes +vanishing +vanishings +vanities +vanity +vanned +vanning +vanquish +vanquished +vanquishes +vanquishing +vans +vantage +vantages +vape +vaped +vapes +vapid +vapidity +vapidness +vaping +vapor +vaporization +vaporize +vaporized +vaporizer +vaporizers +vaporizes +vaporizing +vaporous +vapors +variability +variable +variables +variably +variance +variances +variant +variants +variate +variation +variations +varicolored +varicose +varied +variegate +variegated +variegates +variegating +varies +varieties +variety +various +variously +varlet +varlets +varmint +varmints +varnish +varnished +varnishes +varnishing +varsities +varsity +vary +varying +vascular +vase +vasectomies +vasectomy +vases +vassal +vassalage +vassals +vast +vaster +vastest +vastly +vastness +vasts +vat +vats +vatted +vatting +vaudeville +vault +vaulted +vaulter +vaulters +vaulting +vaults +vaunt +vaunted +vaunting +vaunts +veal +vector +vectored +vectoring +vectors +veep +veeps +veer +veered +veering +veers +vegan +vegans +vegetable +vegetables +vegetarian +vegetarianism +vegetarians +vegetate +vegetated +vegetates +vegetating +vegetation +vegetative +veggie +veggies +vehemence +vehement +vehemently +vehicle +vehicles +vehicular +veil +veiled +veiling +veils +vein +veined +veining +veins +veld +velds +vellum +velocities +velocity +velour +velours +velvet +velveteen +velvety +venal +venality +venally +vend +vended +vendetta +vendettas +vending +vendor +vendors +vends +veneer +veneered +veneering +veneers +venerable +venerate +venerated +venerates +venerating +veneration +venereal +vengeance +vengeful +vengefully +venial +venison +venom +venomous +venomously +venous +vent +vented +ventilate +ventilated +ventilates +ventilating +ventilation +ventilator +ventilators +venting +ventral +ventricle +ventricles +ventricular +ventriloquism +ventriloquist +ventriloquists +vents +venture +ventured +ventures +venturesome +venturing +venturous +venue +venues +veracious +veracity +veranda +verandah +verandahs +verandas +verb +verbal +verbalize +verbalized +verbalizes +verbalizing +verbally +verbals +verbatim +verbena +verbenas +verbiage +verbose +verbosity +verbs +verdant +verdict +verdicts +verdigris +verdigrised +verdigrises +verdigrising +verdure +verge +verged +verges +verging +verier +veriest +verifiable +verification +verified +verifies +verify +verifying +verily +verisimilitude +veritable +veritably +verities +verity +vermicelli +vermilion +vermin +verminous +vermouth +vernacular +vernaculars +vernal +versatile +versatility +verse +versed +verses +versification +versified +versifies +versify +versifying +versing +version +versions +versus +vertebra +vertebrae +vertebral +vertebrate +vertebrates +vertex +vertexes +vertical +vertically +verticals +vertices +vertiginous +vertigo +verve +very +vesicle +vesicles +vesper +vespers +vessel +vessels +vest +vested +vestibule +vestibules +vestige +vestiges +vestigial +vesting +vestment +vestments +vestries +vestry +vests +vet +vetch +vetches +veteran +veterans +veterinarian +veterinarians +veterinaries +veterinary +veto +vetoed +vetoes +vetoing +vets +vetted +vetting +vex +vexation +vexations +vexatious +vexed +vexes +vexing +via +viability +viable +viaduct +viaducts +vial +vials +viand +viands +vibe +vibes +vibrancy +vibrant +vibrantly +vibraphone +vibraphones +vibrate +vibrated +vibrates +vibrating +vibration +vibrations +vibrato +vibrator +vibrators +vibratos +viburnum +viburnums +vicar +vicarage +vicarages +vicarious +vicariously +vicars +vice +viced +viceroy +viceroys +vices +vichyssoise +vicing +vicinity +vicious +viciously +viciousness +vicissitude +vicissitudes +victim +victimization +victimize +victimized +victimizes +victimizing +victims +victor +victories +victorious +victoriously +victors +victory +victual +victualed +victualing +victuals +vicuña +vicuñas +video +videocassette +videocassettes +videodisc +videodiscs +videos +videotape +videotaped +videotapes +videotaping +vie +vied +vies +view +viewed +viewer +viewers +viewfinder +viewfinders +viewing +viewings +viewpoint +viewpoints +views +vigil +vigilance +vigilant +vigilante +vigilantes +vigilantism +vigilantly +vigils +vignette +vignetted +vignettes +vignetting +vigor +vigorous +vigorously +vile +vilely +vileness +viler +vilest +vilification +vilified +vilifies +vilify +vilifying +villa +village +villager +villagers +villages +villain +villainies +villainous +villains +villainy +villas +villein +villeins +vim +vinaigrette +vindicate +vindicated +vindicates +vindicating +vindication +vindications +vindicator +vindicators +vindictive +vindictively +vindictiveness +vine +vinegar +vinegary +vines +vineyard +vineyards +vintage +vintages +vintner +vintners +vinyl +vinyls +viol +viola +violable +violas +violate +violated +violates +violating +violation +violations +violator +violators +violence +violent +violently +violet +violets +violin +violinist +violinists +violins +violist +violists +violoncello +violoncellos +viols +viper +vipers +virago +viragoes +viral +vireo +vireos +virgin +virginal +virginals +virginity +virgins +virgule +virgules +virile +virility +virology +virtual +virtually +virtue +virtues +virtuosity +virtuoso +virtuosos +virtuous +virtuously +virtuousness +virulence +virulent +virulently +virus +viruses +visa +visaed +visage +visages +visaing +visas +viscera +visceral +viscid +viscosity +viscount +viscountess +viscountesses +viscounts +viscous +viscus +vise +vised +vises +visibility +visible +visibly +vising +vision +visionaries +visionary +visioned +visioning +visions +visit +visitation +visitations +visited +visiting +visitor +visitors +visits +visor +visors +vista +vistas +visual +visualization +visualize +visualized +visualizes +visualizing +visually +visuals +vital +vitality +vitalize +vitalized +vitalizes +vitalizing +vitally +vitals +vitamin +vitamins +vitiate +vitiated +vitiates +vitiating +vitiation +viticulture +vitreous +vitriol +vitriolic +vituperate +vituperated +vituperates +vituperating +vituperation +vituperative +viva +vivace +vivacious +vivaciously +vivaciousness +vivacity +vivas +vivid +vivider +vividest +vividly +vividness +vivified +vivifies +vivify +vivifying +viviparous +vivisection +vixen +vixenish +vixens +vizier +viziers +vocabularies +vocabulary +vocal +vocalic +vocalist +vocalists +vocalization +vocalizations +vocalize +vocalized +vocalizes +vocalizing +vocally +vocals +vocation +vocational +vocations +vocative +vocatives +vociferate +vociferated +vociferates +vociferating +vociferation +vociferous +vociferously +vodka +vogue +vogues +voguish +voice +voiced +voiceless +voicemail +voicemails +voices +voicing +void +voided +voiding +voids +voile +volatile +volatility +volcanic +volcano +volcanoes +vole +voles +volition +volley +volleyball +volleyballs +volleyed +volleying +volleys +volt +voltage +voltages +voltaic +voltmeter +voltmeters +volts +volubility +voluble +volubly +volume +volumes +voluminous +voluminously +voluntaries +voluntarily +voluntary +volunteer +volunteered +volunteering +volunteers +voluptuaries +voluptuary +voluptuous +voluptuously +voluptuousness +vomit +vomited +vomiting +vomits +voodoo +voodooed +voodooing +voodooism +voodoos +voracious +voraciously +voracity +vortex +vortexes +votaries +votary +vote +voted +voter +voters +votes +voting +votive +vouch +vouched +voucher +vouchers +vouches +vouching +vouchsafe +vouchsafed +vouchsafes +vouchsafing +vow +vowed +vowel +vowels +vowing +vows +voyage +voyaged +voyager +voyagers +voyages +voyaging +voyeur +voyeurism +voyeuristic +voyeurs +vulcanization +vulcanize +vulcanized +vulcanizes +vulcanizing +vulgar +vulgarer +vulgarest +vulgarism +vulgarisms +vulgarities +vulgarity +vulgarization +vulgarize +vulgarized +vulgarizes +vulgarizing +vulgarly +vulnerabilities +vulnerability +vulnerable +vulnerably +vulture +vultures +vulva +vulvae +vuvuzela +vuvuzelas +vying +w +wack +wacker +wackest +wackier +wackiest +wackiness +wacko +wackos +wacks +wacky +wad +wadded +wadding +waddle +waddled +waddles +waddling +wade +waded +wader +waders +wades +wadi +wading +wadis +wads +wafer +wafers +waffle +waffled +waffles +waffling +waft +wafted +wafting +wafts +wag +wage +waged +wager +wagered +wagering +wagers +wages +wagged +wagging +waggish +waggle +waggled +waggles +waggling +waging +wagon +wagoner +wagoners +wagons +wags +waif +waifs +wail +wailed +wailing +wails +wainscot +wainscoted +wainscoting +wainscotings +wainscots +wainscotted +wainscotting +wainscottings +waist +waistband +waistbands +waistcoat +waistcoats +waistline +waistlines +waists +wait +waited +waiter +waiters +waiting +waitress +waitresses +waits +waive +waived +waiver +waivers +waives +waiving +wake +waked +wakeful +wakefulness +waken +wakened +wakening +wakens +wakes +waking +wale +waled +wales +waling +walk +walked +walker +walkers +walking +walkout +walkouts +walks +walkway +walkways +wall +wallabies +wallaby +wallboard +walled +wallet +wallets +walleye +walleyed +walleyes +wallflower +wallflowers +walling +wallop +walloped +walloping +wallopings +wallops +wallow +wallowed +wallowing +wallows +wallpaper +wallpapered +wallpapering +wallpapers +walls +walnut +walnuts +walrus +walruses +waltz +waltzed +waltzes +waltzing +wampum +wan +wand +wander +wandered +wanderer +wanderers +wandering +wanderlust +wanderlusts +wanders +wands +wane +waned +wanes +wangle +wangled +wangles +wangling +waning +wanly +wanna +wannabe +wannabes +wanner +wannest +want +wanted +wanting +wanton +wantoned +wantoning +wantonly +wantonness +wantons +wants +wapiti +wapitis +war +warble +warbled +warbler +warblers +warbles +warbling +ward +warded +warden +wardens +warder +warders +warding +wardrobe +wardrobes +wardroom +wardrooms +wards +ware +warehouse +warehoused +warehouses +warehousing +wares +warfare +warhead +warheads +warhorse +warhorses +warier +wariest +warily +wariness +warlike +warlock +warlocks +warlord +warlords +warm +warmed +warmer +warmers +warmest +warmhearted +warming +warmly +warmonger +warmongering +warmongers +warms +warmth +warn +warned +warning +warnings +warns +warp +warpath +warpaths +warped +warping +warps +warrant +warranted +warrantied +warranties +warranting +warrants +warranty +warrantying +warred +warren +warrens +warring +warrior +warriors +wars +warship +warships +wart +warthog +warthogs +wartier +wartiest +wartime +warts +warty +wary +was +wash +washable +washables +washbasin +washbasins +washboard +washboards +washbowl +washbowls +washcloth +washcloths +washed +washer +washers +washerwoman +washerwomen +washes +washing +washings +washout +washouts +washroom +washrooms +washstand +washstands +washtub +washtubs +wasp +waspish +wasps +wassail +wassailed +wassailing +wassails +wastage +waste +wastebasket +wastebaskets +wasted +wasteful +wastefully +wastefulness +wasteland +wastelands +wastepaper +waster +wasters +wastes +wastewater +wasting +wastrel +wastrels +watch +watchband +watchbands +watchdog +watchdogs +watched +watcher +watchers +watches +watchful +watchfully +watchfulness +watching +watchmaker +watchmakers +watchman +watchmen +watchtower +watchtowers +watchword +watchwords +water +waterbed +waterbeds +waterboard +waterboarded +waterboarding +waterboardings +waterboards +watercolor +watercolors +watercourse +watercourses +watercraft +watercress +watered +waterfall +waterfalls +waterfowl +waterfowls +waterfront +waterfronts +waterier +wateriest +watering +waterline +waterlines +waterlogged +watermark +watermarked +watermarking +watermarks +watermelon +watermelons +waterpower +waterproof +waterproofed +waterproofing +waterproofs +waters +watershed +watersheds +waterside +watersides +waterspout +waterspouts +watertight +waterway +waterways +waterworks +watery +watt +wattage +wattle +wattled +wattles +wattling +watts +wave +waved +waveform +wavelength +wavelengths +wavelet +wavelets +waver +wavered +wavering +wavers +waves +wavier +waviest +waviness +waving +wavy +wax +waxed +waxen +waxes +waxier +waxiest +waxiness +waxing +waxwing +waxwings +waxwork +waxworks +waxy +way +wayfarer +wayfarers +wayfaring +wayfarings +waylaid +waylay +waylaying +waylays +ways +wayside +waysides +wayward +waywardly +waywardness +we +weak +weaken +weakened +weakening +weakens +weaker +weakest +weakfish +weakfishes +weakling +weaklings +weakly +weakness +weaknesses +weal +weals +wealth +wealthier +wealthiest +wealthiness +wealthy +wean +weaned +weaning +weans +weapon +weaponless +weaponry +weapons +wear +wearable +wearer +wearers +wearied +wearier +wearies +weariest +wearily +weariness +wearing +wearisome +wears +weary +wearying +weasel +weaseled +weaseling +weasels +weather +weathercock +weathercocks +weathered +weathering +weatherize +weatherized +weatherizes +weatherizing +weatherman +weathermen +weatherproof +weatherproofed +weatherproofing +weatherproofs +weathers +weave +weaved +weaver +weavers +weaves +weaving +web +webbed +webbing +webcam +webcams +webcast +webcasting +webcasts +webinar +webinars +webisode +webisodes +webmaster +webmasters +webmistress +webmistresses +webs +website +websites +wed +wedded +wedder +wedding +weddings +wedge +wedged +wedges +wedging +wedlock +weds +wee +weed +weeded +weeder +weeders +weedier +weediest +weeding +weeds +weedy +weeing +week +weekday +weekdays +weekend +weekended +weekending +weekends +weeklies +weekly +weeknight +weeknights +weeks +weep +weeper +weepers +weepier +weepies +weepiest +weeping +weepings +weeps +weepy +weer +wees +weest +weevil +weevils +weft +wefts +weigh +weighed +weighing +weighs +weight +weighted +weightier +weightiest +weightiness +weighting +weightless +weightlessness +weightlifter +weightlifters +weightlifting +weights +weighty +weir +weird +weirder +weirdest +weirdly +weirdness +weirdo +weirdos +weirs +welcome +welcomed +welcomes +welcoming +weld +welded +welder +welders +welding +welds +welfare +welkin +well +welled +welling +wellington +wells +wellspring +wellsprings +welsh +welshed +welshes +welshing +welt +welted +welter +weltered +weltering +welters +welterweight +welterweights +welting +welts +wen +wench +wenches +wend +wended +wending +wends +wens +went +wept +were +werewolf +werewolves +west +westbound +westerlies +westerly +western +westerner +westerners +westernize +westernized +westernizes +westernizing +westernmost +westerns +westward +westwards +wet +wetback +wetbacks +wetland +wetlands +wetly +wetness +wets +wetter +wettest +wetting +whack +whacked +whacking +whacks +whale +whalebone +whaled +whaler +whalers +whales +whaling +wham +whammed +whammies +whamming +whammy +whams +wharf +wharves +what +whatchamacallit +whatchamacallits +whatever +whatnot +whats +whatsoever +wheal +wheals +wheat +wheaten +wheedle +wheedled +wheedles +wheedling +wheel +wheelbarrow +wheelbarrows +wheelbase +wheelbases +wheelchair +wheelchairs +wheeled +wheeler +wheeling +wheels +wheelwright +wheelwrights +wheeze +wheezed +wheezes +wheezier +wheeziest +wheezing +wheezy +whelk +whelked +whelks +whelp +whelped +whelping +whelps +when +whence +whenever +whens +where +whereabouts +whereas +whereat +whereby +wherefore +wherefores +wherein +whereof +whereon +wheres +wheresoever +whereupon +wherever +wherewithal +whet +whether +whets +whetstone +whetstones +whetted +whetting +whew +whey +which +whichever +whiff +whiffed +whiffing +whiffs +while +whiled +whiles +whiling +whilst +whim +whimper +whimpered +whimpering +whimpers +whims +whimsical +whimsicality +whimsically +whimsies +whimsy +whine +whined +whiner +whiners +whines +whinier +whiniest +whining +whinnied +whinnies +whinny +whinnying +whiny +whip +whipcord +whiplash +whiplashes +whipped +whippersnapper +whippersnappers +whippet +whippets +whipping +whippings +whippoorwill +whippoorwills +whips +whir +whirl +whirled +whirligig +whirligigs +whirling +whirlpool +whirlpools +whirls +whirlwind +whirlwinds +whirred +whirring +whirs +whisk +whisked +whisker +whiskered +whiskers +whiskey +whiskeys +whiskies +whisking +whisks +whisky +whiskys +whisper +whispered +whispering +whispers +whist +whistle +whistled +whistler +whistlers +whistles +whistling +whit +white +whitecap +whitecaps +whitefish +whitefishes +whiten +whitened +whitener +whiteners +whiteness +whitening +whitens +whiter +whites +whitest +whitewall +whitewalls +whitewash +whitewashed +whitewashes +whitewashing +whither +whiting +whitings +whitish +whits +whittle +whittled +whittler +whittlers +whittles +whittling +whiz +whizz +whizzed +whizzes +whizzing +who +whoa +whodunit +whodunits +whoever +whole +wholehearted +wholeheartedly +wholeness +wholes +wholesale +wholesaled +wholesaler +wholesalers +wholesales +wholesaling +wholesome +wholesomeness +wholly +whom +whomever +whomsoever +whoop +whooped +whoopee +whoopees +whooping +whoops +whoosh +whooshed +whooshes +whooshing +whopper +whoppers +whopping +whore +whorehouse +whorehouses +whores +whorl +whorled +whorls +whose +whosoever +why +whys +wick +wicked +wickeder +wickedest +wickedly +wickedness +wicker +wickers +wickerwork +wicket +wickets +wicks +wide +widely +widen +widened +wideness +widening +widens +wider +widescreen +widescreens +widespread +widest +widgeon +widgeons +widow +widowed +widower +widowers +widowhood +widowing +widows +width +widths +wield +wielded +wielding +wields +wiener +wieners +wife +wifely +wig +wigeon +wigeons +wigged +wigging +wiggle +wiggled +wiggler +wigglers +wiggles +wigglier +wiggliest +wiggling +wiggly +wight +wights +wigs +wigwag +wigwagged +wigwagging +wigwags +wigwam +wigwams +wiki +wikis +wild +wildcat +wildcats +wildcatted +wildcatting +wildebeest +wildebeests +wilder +wilderness +wildernesses +wildest +wildfire +wildfires +wildflower +wildflowers +wildfowl +wildlife +wildly +wildness +wilds +wile +wiled +wiles +wilful +wilfulness +wilier +wiliest +wiliness +wiling +will +willed +willful +willfully +willfulness +willies +willing +willingly +willingness +willow +willows +willowy +willpower +wills +wilt +wilted +wilting +wilts +wily +wimp +wimpier +wimpiest +wimple +wimpled +wimples +wimpling +wimps +wimpy +win +wince +winced +winces +winch +winched +winches +winching +wincing +wind +windbag +windbags +windbreak +windbreaker +windbreakers +windbreaks +windburn +winded +windfall +windfalls +windier +windiest +windiness +winding +windjammer +windjammers +windlass +windlasses +windmill +windmilled +windmilling +windmills +window +windowed +windowing +windowpane +windowpanes +windows +windowsill +windowsills +windpipe +windpipes +winds +windscreen +windscreens +windshield +windshields +windsock +windsocks +windstorm +windstorms +windsurf +windsurfed +windsurfing +windsurfs +windswept +windup +windups +windward +windy +wine +wined +wineglass +wineglasses +wineries +winery +wines +wing +winged +winger +wingers +winging +wingless +wingnut +wingnuts +wings +wingspan +wingspans +wingspread +wingspreads +wingtip +wingtips +wining +wink +winked +winking +winks +winner +winners +winning +winnings +winnow +winnowed +winnowing +winnows +wino +winos +wins +winsome +winsomely +winsomer +winsomest +winter +wintered +wintergreen +wintering +winterize +winterized +winterizes +winterizing +winters +wintertime +wintrier +wintriest +wintry +wipe +wiped +wiper +wipers +wipes +wiping +wire +wired +wireless +wirelesses +wires +wiretap +wiretapped +wiretapping +wiretaps +wirier +wiriest +wiriness +wiring +wiry +wisdom +wise +wiseacre +wiseacres +wisecrack +wisecracked +wisecracking +wisecracks +wisely +wiser +wises +wisest +wish +wishbone +wishbones +wished +wisher +wishers +wishes +wishful +wishfully +wishing +wisp +wispier +wispiest +wisps +wispy +wist +wisteria +wisterias +wistful +wistfully +wistfulness +wit +witch +witchcraft +witched +witchery +witches +witching +with +withal +withdraw +withdrawal +withdrawals +withdrawing +withdrawn +withdraws +withdrew +wither +withered +withering +withers +withheld +withhold +withholding +withholds +within +without +withstand +withstanding +withstands +withstood +witless +witlessly +witness +witnessed +witnesses +witnessing +wits +witticism +witticisms +wittier +wittiest +wittily +wittiness +witting +wittingly +witty +wive +wives +wiz +wizard +wizardry +wizards +wizened +wizes +wobble +wobbled +wobbles +wobblier +wobbliest +wobbling +wobbly +woe +woebegone +woeful +woefuller +woefullest +woefully +woes +wok +woke +woken +woks +wolf +wolfed +wolfhound +wolfhounds +wolfing +wolfish +wolfram +wolfs +wolverine +wolverines +wolves +woman +womanhood +womanish +womanize +womanized +womanizer +womanizers +womanizes +womanizing +womankind +womanlier +womanliest +womanlike +womanliness +womanly +womb +wombat +wombats +wombs +women +womenfolk +womenfolks +won +wonder +wondered +wonderful +wonderfully +wondering +wonderland +wonderlands +wonderment +wonders +wondrous +wondrously +wont +wonted +woo +wood +woodbine +woodcarving +woodcarvings +woodchuck +woodchucks +woodcock +woodcocks +woodcraft +woodcut +woodcuts +woodcutter +woodcutters +woodcutting +wooded +wooden +woodener +woodenest +woodenly +woodenness +woodier +woodies +woodiest +woodiness +wooding +woodland +woodlands +woodman +woodmen +woodpecker +woodpeckers +woodpile +woodpiles +woods +woodshed +woodsheds +woodsier +woodsiest +woodsman +woodsmen +woodsy +woodwind +woodwinds +woodwork +woodworking +woodworm +woody +wooed +wooer +wooers +woof +woofed +woofer +woofers +woofing +woofs +wooing +wool +woolen +woolens +woolgathering +woollier +woollies +woolliest +woolliness +woolly +woos +woozier +wooziest +wooziness +woozy +word +worded +wordier +wordiest +wordiness +wording +wordings +wordplay +words +wordy +wore +work +workable +workaday +workaholic +workaholics +workbench +workbenches +workbook +workbooks +workday +workdays +worked +worker +workers +workfare +workflow +workflows +workforce +workhorse +workhorses +workhouse +workhouses +working +workingman +workingmen +workings +workload +workloads +workman +workmanlike +workmanship +workmen +workout +workouts +workplace +workplaces +works +worksheet +worksheets +workshop +workshops +workstation +workstations +workweek +workweeks +world +worldlier +worldliest +worldliness +worldly +worlds +worldwide +worm +wormed +wormhole +wormholes +wormier +wormiest +worming +worms +wormwood +wormy +worn +worried +worrier +worriers +worries +worrisome +worry +worrying +worryings +worrywart +worrywarts +worse +worsen +worsened +worsening +worsens +worship +worshiped +worshiper +worshipers +worshipful +worshiping +worships +worst +worsted +worsting +worsts +worth +worthier +worthies +worthiest +worthily +worthiness +worthless +worthlessness +worthwhile +worthy +wot +would +woulds +wound +wounded +wounder +wounding +wounds +wove +woven +wow +wowed +wowing +wows +wrack +wraith +wraiths +wrangle +wrangled +wrangler +wranglers +wrangles +wrangling +wrap +wraparound +wraparounds +wrapped +wrapper +wrappers +wrapping +wrappings +wraps +wrath +wrathful +wrathfully +wreak +wreaked +wreaking +wreaks +wreath +wreathe +wreathed +wreathes +wreathing +wreaths +wreck +wreckage +wrecked +wrecker +wreckers +wrecking +wrecks +wren +wrench +wrenched +wrenches +wrenching +wrens +wrest +wrested +wresting +wrestle +wrestled +wrestler +wrestlers +wrestles +wrestling +wrests +wretch +wretched +wretcheder +wretchedest +wretchedly +wretchedness +wretches +wriggle +wriggled +wriggler +wrigglers +wriggles +wriggling +wriggly +wright +wring +wringer +wringers +wringing +wrings +wrinkle +wrinkled +wrinkles +wrinklier +wrinklies +wrinkliest +wrinkling +wrinkly +wrist +wristband +wristbands +wrists +wristwatch +wristwatches +writ +writable +write +writer +writers +writes +writhe +writhed +writhes +writhing +writing +writings +writs +written +wrong +wrongdoer +wrongdoers +wrongdoing +wrongdoings +wronged +wronger +wrongest +wrongful +wrongfully +wrongfulness +wrongheaded +wrongheadedly +wrongheadedness +wronging +wrongly +wrongness +wrongs +wrote +wroth +wrought +wrung +wry +wryer +wryest +wryly +wryness +wuss +wusses +x +xenon +xenophobia +xenophobic +xerographic +xerography +xylem +xylophone +xylophones +xylophonist +xylophonists +y +yacht +yachted +yachting +yachts +yachtsman +yachtsmen +yahoo +yahoos +yak +yakked +yakking +yaks +yam +yammer +yammered +yammering +yammers +yams +yank +yanked +yanking +yanks +yap +yapped +yapping +yaps +yard +yardage +yardages +yardarm +yardarms +yards +yardstick +yardsticks +yarmulke +yarmulkes +yarn +yarns +yaw +yawed +yawing +yawl +yawls +yawn +yawned +yawning +yawns +yaws +ye +yea +yeah +yeahs +year +yearbook +yearbooks +yearlies +yearling +yearlings +yearly +yearn +yearned +yearning +yearnings +yearns +years +yeas +yeast +yeastier +yeastiest +yeasts +yeasty +yell +yelled +yelling +yellow +yellowed +yellower +yellowest +yellowing +yellowish +yellows +yells +yelp +yelped +yelping +yelps +yen +yens +yeoman +yeomen +yep +yeps +yes +yeses +yeshiva +yeshivas +yeshivot +yessed +yessing +yest +yesterday +yesterdays +yesteryear +yet +yeti +yew +yews +yield +yielded +yielding +yieldings +yields +yip +yipped +yippee +yipping +yips +yo +yodel +yodeled +yodeler +yodelers +yodeling +yodels +yoga +yogi +yogis +yogurt +yogurts +yoke +yoked +yokel +yokels +yokes +yoking +yolk +yolks +yon +yonder +yore +you +young +younger +youngest +youngish +youngster +youngsters +your +yours +yourself +yourselves +yous +youth +youthful +youthfully +youthfulness +youths +yowl +yowled +yowling +yowls +yttrium +yucca +yuccas +yuck +yucked +yuckier +yuckiest +yucking +yucks +yucky +yuk +yukked +yukking +yuks +yule +yuletide +yum +yummier +yummiest +yummy +yup +yuppie +yuppies +yups +z +zanier +zanies +zaniest +zaniness +zany +zap +zapped +zapper +zappers +zapping +zaps +zeal +zealot +zealots +zealous +zealously +zealousness +zebra +zebras +zebu +zebus +zed +zeds +zenith +zeniths +zephyr +zephyrs +zeppelin +zeppelins +zero +zeroed +zeroing +zeros +zest +zestful +zestfully +zests +zeta +zigzag +zigzagged +zigzagging +zigzags +zilch +zillion +zillions +zinc +zincked +zincking +zincs +zing +zinged +zinger +zingers +zinging +zings +zinnia +zinnias +zip +zipped +zipper +zippered +zippering +zippers +zippier +zippiest +zipping +zippy +zips +zircon +zirconium +zircons +zit +zither +zithers +zits +zodiac +zodiacal +zodiacs +zombie +zombies +zonal +zone +zoned +zones +zoning +zonked +zoo +zoological +zoologist +zoologists +zoology +zoom +zoomed +zooming +zooms +zoos +zucchini +zucchinis +zwieback +zygote +zygotes +Ã…ngström +éclair +éclairs +éclat +élan +émigré +émigrés +épée +épées +étude +études diff --git a/examples/dotnet/cscvrptw.cs b/examples/dotnet/cscvrptw.cs index b00626c2da9..f241cc57aa3 100644 --- a/examples/dotnet/cscvrptw.cs +++ b/examples/dotnet/cscvrptw.cs @@ -63,10 +63,12 @@ public TimeWindow(int start, int end) { /// positions and computes the Manhattan distance between the two /// positions of two different indices. /// - class Manhattan : NodeEvaluator2 { - public Manhattan(Position[] locations, int coefficient) { + class Manhattan : IntIntToLong { + public Manhattan(RoutingIndexManager manager, + Position[] locations, int coefficient) { this.locations_ = locations; this.coefficient_ = coefficient; + this.manager_ = manager; } public override long Run(int first_index, int second_index) { @@ -74,28 +76,31 @@ public override long Run(int first_index, int second_index) { second_index >= locations_.Length) { return 0; } - return (Math.Abs(locations_[first_index].x_ - - locations_[second_index].x_) + - Math.Abs(locations_[first_index].y_ - - locations_[second_index].y_)) * coefficient_; + int first_node = manager_.IndexToNode(first_index); + int second_node = manager_.IndexToNode(second_index); + return (Math.Abs(locations_[first_node].x_ - + locations_[second_node].x_) + + Math.Abs(locations_[first_node].y_ - + locations_[second_node].y_)) * coefficient_; } private Position[] locations_; private int coefficient_; + private RoutingIndexManager manager_; }; ///

/// A callback that computes the volume of a demand stored in an /// integer array. /// - class Demand : NodeEvaluator2 { + class Demand : IntToLong { public Demand(int[] order_demands) { this.order_demands_ = order_demands; } - public override long Run(int first_index, int second_index) { - if (first_index < order_demands_.Length) { - return order_demands_[first_index]; + public override long Run(int index) { + if (index < order_demands_.Length) { + return order_demands_[index]; } return 0; } @@ -228,41 +233,49 @@ private void Solve(int number_of_orders, int number_of_vehicles) { // Finalizing model int number_of_locations = locations_.Length; - RoutingModel model = - new RoutingModel(number_of_locations, number_of_vehicles, - vehicle_starts_, vehicle_ends_); + RoutingIndexManager manager = + new RoutingIndexManager(number_of_locations, number_of_vehicles, + vehicle_starts_, vehicle_ends_); + RoutingModel model = new RoutingModel(manager); // Setting up dimensions const int big_number = 100000; - NodeEvaluator2 manhattan_callback = new Manhattan(locations_, 1); + IntIntToLong manhattan_callback = new Manhattan(manager, locations_, 1); model.AddDimension( - manhattan_callback, big_number, big_number, false, "time"); - NodeEvaluator2 demand_callback = new Demand(order_demands_); - model.AddDimension(demand_callback, 0, vehicle_capacity_, true, "capacity"); + model.RegisterTransitCallback(manhattan_callback), + big_number, big_number, false, "time"); + RoutingDimension time_dimension = model.GetDimensionOrDie("time"); + + IntToLong demand_callback = new Demand(order_demands_); + model.AddDimension(model.RegisterUnaryTransitCallback(demand_callback), + 0, vehicle_capacity_, true, "capacity"); + RoutingDimension capacity_dimension = model.GetDimensionOrDie("capacity"); // Setting up vehicles - NodeEvaluator2[] cost_callbacks = new NodeEvaluator2[number_of_vehicles]; + IntIntToLong[] cost_callbacks = new IntIntToLong[number_of_vehicles]; for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { int cost_coefficient = vehicle_cost_coefficients_[vehicle]; - NodeEvaluator2 manhattan_cost_callback = - new Manhattan(locations_, cost_coefficient); + IntIntToLong manhattan_cost_callback = + new Manhattan(manager, locations_, cost_coefficient); cost_callbacks[vehicle] = manhattan_cost_callback; - model.SetVehicleCost(vehicle, manhattan_cost_callback); - model.CumulVar(model.End(vehicle), "time").SetMax( + int manhattan_cost_index = + model.RegisterTransitCallback(manhattan_cost_callback); + model.SetArcCostEvaluatorOfVehicle(manhattan_cost_index, vehicle); + time_dimension.CumulVar(model.End(vehicle)).SetMax( vehicle_end_time_[vehicle]); } // Setting up orders for (int order = 0; order < number_of_orders; ++order) { - model.CumulVar(order, "time").SetRange(order_time_windows_[order].start_, - order_time_windows_[order].end_); - int[] orders = {order}; + time_dimension.CumulVar(order).SetRange(order_time_windows_[order].start_, + order_time_windows_[order].end_); + long[] orders = {order}; model.AddDisjunction(orders, order_penalties_[order]); } // Solving RoutingSearchParameters search_parameters = - RoutingModel.DefaultSearchParameters(); + operations_research_constraint_solver.DefaultRoutingSearchParameters(); search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.AllUnperformed; @@ -298,14 +311,14 @@ private void Solve(int number_of_orders, int number_of_vehicles) { for (; !model.IsEnd(order); order = solution.Value(model.NextVar(order))) { - IntVar local_load = model.CumulVar(order, "capacity"); - IntVar local_time = model.CumulVar(order, "time"); + IntVar local_load = capacity_dimension.CumulVar(order); + IntVar local_time = time_dimension.CumulVar(order); route += order + " Load(" + solution.Value(local_load) + ") " + "Time(" + solution.Min(local_time) + ", " + solution.Max(local_time) + ") -> "; } - IntVar load = model.CumulVar(order, "capacity"); - IntVar time = model.CumulVar(order, "time"); + IntVar load = capacity_dimension.CumulVar(order); + IntVar time = time_dimension.CumulVar(order); route += order + " Load(" + solution.Value(load) + ") " + "Time(" + solution.Min(time) + ", " + solution.Max(time) + ")"; } diff --git a/examples/dotnet/cstsp.cs b/examples/dotnet/cstsp.cs index 7ac974e6e21..f92f9cc6e1b 100644 --- a/examples/dotnet/cstsp.cs +++ b/examples/dotnet/cstsp.cs @@ -17,11 +17,12 @@ class Tsp { - class RandomManhattan : NodeEvaluator2 { - public RandomManhattan(int size, int seed) + class RandomManhattan : IntIntToLong { + public RandomManhattan(RoutingIndexManager manager, int size, int seed) { this.xs_ = new int[size]; this.ys_ = new int[size]; + this.manager_ = manager; Random generator = new Random(seed); for (int i = 0; i < size; ++i) { @@ -31,30 +32,35 @@ public RandomManhattan(int size, int seed) } public override long Run(int first_index, int second_index) { - return Math.Abs(xs_[first_index] - xs_[second_index]) + - Math.Abs(ys_[first_index] - ys_[second_index]); + int first_node = manager_.IndexToNode(first_index); + int second_node = manager_.IndexToNode(second_index); + return Math.Abs(xs_[first_node] - xs_[second_node]) + + Math.Abs(ys_[first_node] - ys_[second_node]); } private int[] xs_; private int[] ys_; + private RoutingIndexManager manager_; }; - class ConstantCallback : NodeEvaluator2 { - public override long Run(int first_index, int second_index) { + class ConstantCallback : IntToLong { + public override long Run(int index) { return 1; } }; static void Solve(int size, int forbidden, int seed) { - RoutingModel routing = new RoutingModel(size, 1, 0); + RoutingIndexManager manager = new RoutingIndexManager(size, 1, 0); + RoutingModel routing = new RoutingModel(manager); // Setting the cost function. // Put a permanent callback to the distance accessor here. The callback // has the following signature: ResultCallback2. // The two arguments are the from and to node inidices. - RandomManhattan distances = new RandomManhattan(size, seed); - routing.SetCost(distances); + RandomManhattan distances = new RandomManhattan(manager, size, seed); + routing.SetArcCostEvaluatorOfAllVehicles( + routing.RegisterTransitCallback(distances)); // Forbid node connections (randomly). Random randomizer = new Random(); @@ -70,15 +76,16 @@ static void Solve(int size, int forbidden, int seed) } // Add dummy dimension to test API. - routing.AddDimension(new ConstantCallback(), - size + 1, - size + 1, - true, - "dummy"); + routing.AddDimension( + routing.RegisterUnaryTransitCallback(new ConstantCallback()), + size + 1, + size + 1, + true, + "dummy"); // Solve, returns a solution if any (owned by RoutingModel). RoutingSearchParameters search_parameters = - RoutingModel.DefaultSearchParameters(); + operations_research_constraint_solver.DefaultRoutingSearchParameters(); // Setting first solution heuristic (cheapest addition). search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; diff --git a/examples/dotnet/tsp.cs b/examples/dotnet/tsp.cs index b0e62badebf..f16f603b786 100644 --- a/examples/dotnet/tsp.cs +++ b/examples/dotnet/tsp.cs @@ -16,7 +16,8 @@ using Google.OrTools.ConstraintSolver; /// -/// This is a sample using the routing library .Net wrapper to solve a TSP problem. +/// This is a sample using the routing library .Net wrapper to solve a TSP +/// problem. /// A description of the problem can be found here: /// http://en.wikipedia.org/wiki/Travelling_salesman_problem. /// @@ -61,20 +62,25 @@ public DataProblem() { /// positions and computes the Manhattan distance between the two /// positions of two different indices. /// - class ManhattanDistance : NodeEvaluator2 { + class ManhattanDistance : IntIntToLong { private int[,] distances_; + private RoutingIndexManager manager_; - public ManhattanDistance(in DataProblem data) { + public ManhattanDistance(in DataProblem data, + in RoutingIndexManager manager) { // precompute distance between location to have distance callback in O(1) distances_ = new int[data.GetLocationNumber(), data.GetLocationNumber()]; + manager_ = manager; for (int fromNode = 0; fromNode < data.GetLocationNumber(); fromNode++) { for (int toNode = 0; toNode < data.GetLocationNumber(); toNode++) { if (fromNode == toNode) distances_[fromNode, toNode] = 0; else distances_[fromNode, toNode] = - Math.Abs(data.GetLocations()[toNode, 0] - data.GetLocations()[fromNode, 0]) + - Math.Abs(data.GetLocations()[toNode, 1] - data.GetLocations()[fromNode, 1]); + Math.Abs(data.GetLocations()[toNode, 0] - + data.GetLocations()[fromNode, 0]) + + Math.Abs(data.GetLocations()[toNode, 1] - + data.GetLocations()[fromNode, 1]); } } } @@ -82,7 +88,9 @@ public ManhattanDistance(in DataProblem data) { /// /// Returns the manhattan distance between the two nodes /// - public override long Run(int FromNode, int ToNode) { + public override long Run(int FromIndex, int ToIndex) { + int FromNode = manager_.IndexToNode(FromIndex); + int ToNode = manager_.IndexToNode(ToIndex); return distances_[FromNode, ToNode]; } }; @@ -93,6 +101,7 @@ public override long Run(int FromNode, int ToNode) { static void PrintSolution( in DataProblem data, in RoutingModel routing, + in RoutingIndexManager manager, in Assignment solution) { Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); // Inspect solution. @@ -100,12 +109,12 @@ static void PrintSolution( Console.WriteLine("Route for Vehicle 0:"); long distance = 0; while (routing.IsEnd(index) == false) { - Console.Write("{0} -> ", routing.IndexToNode(index)); + Console.Write("{0} -> ", manager.IndexToNode((int)index)); var previousIndex = index; index = solution.Value(routing.NextVar(index)); distance += routing.GetArcCostForVehicle(previousIndex, index, 0); } - Console.WriteLine("{0}", routing.IndexToNode(index)); + Console.WriteLine("{0}", manager.IndexToNode((int)index)); Console.WriteLine("Distance of the route: {0}m", distance); } @@ -117,23 +126,27 @@ static void Solve() { DataProblem data = new DataProblem(); // Create Routing Model - RoutingModel routing = new RoutingModel( + RoutingIndexManager manager = new RoutingIndexManager( data.GetLocationNumber(), data.GetVehicleNumber(), data.GetDepot()); + RoutingModel routing = new RoutingModel(manager); // Define weight of each edge - NodeEvaluator2 distanceEvaluator = new ManhattanDistance(data); + IntIntToLong distanceEvaluator = new ManhattanDistance(data, manager); //protect callbacks from the GC GC.KeepAlive(distanceEvaluator); - routing.SetArcCostEvaluatorOfAllVehicles(distanceEvaluator); + routing.SetArcCostEvaluatorOfAllVehicles( + routing.RegisterTransitCallback(distanceEvaluator)); // Setting first solution heuristic (cheapest addition). - RoutingSearchParameters searchParameters = RoutingModel.DefaultSearchParameters(); - searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; Assignment solution = routing.SolveWithParameters(searchParameters); - PrintSolution(data, routing, solution); + PrintSolution(data, routing, manager, solution); } public static void Main(String[] args) { diff --git a/examples/dotnet/vrp.cs b/examples/dotnet/vrp.cs index 5cbb0b35660..6768827b898 100644 --- a/examples/dotnet/vrp.cs +++ b/examples/dotnet/vrp.cs @@ -61,20 +61,25 @@ public DataProblem() { /// positions and computes the Manhattan distance between the two /// positions of two different indices. /// - class ManhattanDistance : NodeEvaluator2 { + class ManhattanDistance : IntIntToLong { private int[,] distances_; + private RoutingIndexManager manager_; - public ManhattanDistance(in DataProblem data) { + public ManhattanDistance(in DataProblem data, + in RoutingIndexManager manager) { // precompute distance between location to have distance callback in O(1) distances_ = new int[data.GetLocationNumber(), data.GetLocationNumber()]; + manager_ = manager; for (int fromNode = 0; fromNode < data.GetLocationNumber(); fromNode++) { for (int toNode = 0; toNode < data.GetLocationNumber(); toNode++) { if (fromNode == toNode) distances_[fromNode, toNode] = 0; else distances_[fromNode, toNode] = - Math.Abs(data.GetLocations()[toNode, 0] - data.GetLocations()[fromNode, 0]) + - Math.Abs(data.GetLocations()[toNode, 1] - data.GetLocations()[fromNode, 1]); + Math.Abs(data.GetLocations()[toNode, 0] - + data.GetLocations()[fromNode, 0]) + + Math.Abs(data.GetLocations()[toNode, 1] - + data.GetLocations()[fromNode, 1]); } } } @@ -82,7 +87,9 @@ public ManhattanDistance(in DataProblem data) { /// /// Returns the manhattan distance between the two nodes /// - public override long Run(int FromNode, int ToNode) { + public override long Run(int FromIndex, int ToIndex) { + int FromNode = manager_.IndexToNode(FromIndex); + int ToNode = manager_.IndexToNode(ToIndex); return distances_[FromNode, ToNode]; } }; @@ -92,10 +99,11 @@ public override long Run(int FromNode, int ToNode) { /// static void AddDistanceDimension( in DataProblem data, - in RoutingModel routing) { + in RoutingModel routing, + in int distance_index) { String distance = "Distance"; routing.AddDimension( - new ManhattanDistance(data), + distance_index, 0, // null slack 3000, // maximum distance per vehicle true, // start cumul to zero @@ -112,6 +120,7 @@ static void AddDistanceDimension( static void PrintSolution( in DataProblem data, in RoutingModel routing, + in RoutingIndexManager manager, in Assignment solution) { Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); // Inspect solution. @@ -120,12 +129,12 @@ static void PrintSolution( long distance = 0; var index = routing.Start(i); while (routing.IsEnd(index) == false) { - Console.Write("{0} -> ", routing.IndexToNode(index)); + Console.Write("{0} -> ", manager.IndexToNode((int)index)); var previousIndex = index; index = solution.Value(routing.NextVar(index)); distance += routing.GetArcCostForVehicle(previousIndex, index, i); } - Console.WriteLine("{0}", routing.IndexToNode(index)); + Console.WriteLine("{0}", manager.IndexToNode((int)index)); Console.WriteLine("Distance of the route: {0}m", distance); } } @@ -138,24 +147,29 @@ static void Solve() { DataProblem data = new DataProblem(); // Create Routing Model - RoutingModel routing = new RoutingModel( + RoutingIndexManager manager = new RoutingIndexManager( data.GetLocationNumber(), data.GetVehicleNumber(), data.GetDepot()); + RoutingModel routing = new RoutingModel(manager); - // Define weight cost of each edge - NodeEvaluator2 distanceEvaluator = new ManhattanDistance(data); + // Define weight of each edge + IntIntToLong distanceEvaluator = new ManhattanDistance(data, manager); //protect callbacks from the GC GC.KeepAlive(distanceEvaluator); - routing.SetArcCostEvaluatorOfAllVehicles(distanceEvaluator); - AddDistanceDimension(data, routing); + int distance_index = routing.RegisterTransitCallback(distanceEvaluator); + routing.SetArcCostEvaluatorOfAllVehicles(distance_index); + + AddDistanceDimension(data, routing, distance_index); // Setting first solution heuristic (cheapest addition). - RoutingSearchParameters searchParameters = RoutingModel.DefaultSearchParameters(); - searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; Assignment solution = routing.SolveWithParameters(searchParameters); - PrintSolution(data, routing, solution); + PrintSolution(data, routing, manager, solution); } public static void Main(String[] args) { diff --git a/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java b/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java index 55c6551d435..d8f429fee42 100644 --- a/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java +++ b/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java @@ -13,12 +13,15 @@ // See the License for the specific language governing permissions and // limitations under the License. import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.IntIntToLong; +import com.google.ortools.constraintsolver.IntToLong; import com.google.ortools.constraintsolver.IntVar; -import com.google.ortools.constraintsolver.NodeEvaluator2; +import com.google.ortools.constraintsolver.RoutingDimension; +import com.google.ortools.constraintsolver.RoutingIndexManager; import com.google.ortools.constraintsolver.RoutingModel; -import com.google.ortools.constraintsolver.FirstSolutionStrategy; import com.google.ortools.constraintsolver.RoutingSearchParameters; - +import com.google.ortools.constraintsolver.main; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -41,12 +44,11 @@ public Pair(K element0, V element1) { } /** - * Sample showing how to model and solve a capacitated vehicle routing problem - * with time windows using the swig-wrapped version of the vehicle routing - * library in src/constraint_solver. + * Sample showing how to model and solve a capacitated vehicle routing problem with time windows + * using the swig-wrapped version of the vehicle routing library in src/constraint_solver. */ - public class CapacitatedVehicleRoutingProblemWithTimeWindows { + static { System.loadLibrary("jniortools"); } @@ -60,8 +62,6 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows { // Quantity to be picked up for each order. private List orderDemands = new ArrayList(); - // Time duration spent to deliver each order. - private List orderDurations = new ArrayList(); // Time window in which each order must be performed. private List> orderTimeWindows = new ArrayList(); // Penalty cost "paid" for dropping an order. @@ -69,8 +69,6 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows { // Capacity of the vehicles. private int vehicleCapacity = 0; - // Earliest time at which each vehicle must start its tour. - private List vehicleStartTime = new ArrayList(); // Latest time at which each vehicle must end its tour. private List vehicleEndTime = new ArrayList(); // Cost per unit of distance of each vehicle. @@ -84,53 +82,76 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows { private final Random randomGenerator = new Random(0xBEEF); /** - * Constructs a capacitated vehicle routing problem with time windows. + * Creates a Manhattan Distance evaluator with 'costCoefficient'. + * + * @param manager Node Index Manager. + * @param costCoefficient The coefficient to apply to the evaluator. */ - private CapacitatedVehicleRoutingProblemWithTimeWindows() {} + private IntIntToLong buildManhattanCallback(RoutingIndexManager manager, int costCoefficient) { + return new IntIntToLong() { + @Override + public long run(int firstIndex, int secondIndex) { + try { + int firstNode = manager.indexToNode(firstIndex); + int secondNode = manager.indexToNode(secondIndex); + Pair firstLocation = locations.get(firstNode); + Pair secondLocation = locations.get(secondNode); + return (long) costCoefficient + * (Math.abs(firstLocation.first - secondLocation.first) + + Math.abs(firstLocation.second - secondLocation.second)); + } catch (Throwable throwed) { + logger.warning(throwed.getMessage()); + return 0; + } + } + }; + } /** - * Creates order data. Location of the order is random, as well as its - * demand (quantity), time window and penalty. + * Creates order data. Location of the order is random, as well as its demand (quantity), time + * window and penalty. * * @param numberOfOrders number of orders to build. * @param xMax maximum x coordinate in which orders are located. * @param yMax maximum y coordinate in which orders are located. * @param demandMax maximum quantity of a demand. - * @param timeWindowMin minimum starting time of the order time window. * @param timeWindowMax maximum starting time of the order time window. * @param timeWindowWidth duration of the order time window. * @param penaltyMin minimum pernalty cost if order is dropped. * @param penaltyMax maximum pernalty cost if order is dropped. */ - private void buildOrders(int numberOfOrders, int xMax, int yMax, int demandMax, int timeWindowMin, - int timeWindowMax, int timeWindowWidth, int penaltyMin, int penaltyMax) { + private void buildOrders( + int numberOfOrders, + int xMax, + int yMax, + int demandMax, + int timeWindowMax, + int timeWindowWidth, + int penaltyMin, + int penaltyMax) { logger.info("Building orders."); for (int order = 0; order < numberOfOrders; ++order) { locations.add(Pair.of(randomGenerator.nextInt(xMax + 1), randomGenerator.nextInt(yMax + 1))); orderDemands.add(randomGenerator.nextInt(demandMax + 1)); - /** @todo 1) Specify deliver duration for each shipment*/ - orderDurations.add(2); // in minutes - int timeWindowStart = randomGenerator.nextInt(timeWindowMax - timeWindowMin) + timeWindowMin; + int timeWindowStart = randomGenerator.nextInt(timeWindowMax + 1); orderTimeWindows.add(Pair.of(timeWindowStart, timeWindowStart + timeWindowWidth)); orderPenalties.add(randomGenerator.nextInt(penaltyMax - penaltyMin + 1) + penaltyMin); } } /** - * Creates fleet data. Vehicle starting and ending locations are random, as - * well as vehicle costs per distance unit. + * Creates fleet data. Vehicle starting and ending locations are random, as well as vehicle costs + * per distance unit. * * @param numberOfVehicles * @param xMax maximum x coordinate in which orders are located. * @param yMax maximum y coordinate in which orders are located. - * @param startTime earliest start time of a tour of a vehicle. * @param endTime latest end time of a tour of a vehicle. * @param capacity capacity of a vehicle. - * @param costCoefficientMax maximum cost per distance unit of a vehicle - * (mimimum is 1), + * @param costCoefficientMax maximum cost per distance unit of a vehicle (mimimum is 1), */ - private void buildFleet(int numberOfVehicles, int xMax, int yMax, int startTime, int endTime, - int capacity, int costCoefficientMax) { + private void buildFleet( + int numberOfVehicles, int xMax, int yMax, int endTime, int capacity, int costCoefficientMax) { logger.info("Building fleet."); vehicleCapacity = capacity; vehicleStarts = new int[numberOfVehicles]; @@ -140,101 +161,75 @@ private void buildFleet(int numberOfVehicles, int xMax, int yMax, int startTime, locations.add(Pair.of(randomGenerator.nextInt(xMax + 1), randomGenerator.nextInt(yMax + 1))); vehicleEnds[vehicle] = locations.size(); locations.add(Pair.of(randomGenerator.nextInt(xMax + 1), randomGenerator.nextInt(yMax + 1))); - vehicleStartTime.add(startTime); vehicleEndTime.add(endTime); vehicleCostCoefficients.add(randomGenerator.nextInt(costCoefficientMax) + 1); } } - /** - * Solves the current routing problem. - */ + /** Solves the current routing problem. */ private void solve(final int numberOfOrders, final int numberOfVehicles) { logger.info( "Creating model with " + numberOfOrders + " orders and " + numberOfVehicles + " vehicles."); // Finalizing model final int numberOfLocations = locations.size(); - RoutingModel model = - new RoutingModel(numberOfLocations, numberOfVehicles, vehicleStarts, vehicleEnds); + RoutingIndexManager manager = + new RoutingIndexManager(numberOfLocations, numberOfVehicles, vehicleStarts, vehicleEnds); + RoutingModel model = new RoutingModel(manager); // Setting up dimensions final int bigNumber = 100000; - NodeEvaluator2 timeCallback = new NodeEvaluator2() { - @Override - public long run(int firstIndex, int secondIndex) { - try { - Pair firstLocation = locations.get(firstIndex); - Pair secondLocation = locations.get(secondIndex); - Integer distance = 0; - Integer duration = 0; - distance = Math.abs(firstLocation.first - secondLocation.first) - + Math.abs(firstLocation.second - secondLocation.second); - // Deal with Order duration shipment - if (firstIndex < numberOfOrders) { - // shipment duration - duration += orderDurations.get(firstIndex); - } - return distance + duration; - } catch (Throwable throwed) { - logger.warning(throwed.getMessage()); - return 0; - } - } - }; - model.addDimension(timeCallback, bigNumber, bigNumber, false, "time"); - NodeEvaluator2 demandCallback = new NodeEvaluator2() { - @Override - public long run(int firstIndex, int secondIndex) { - try { - if (firstIndex < numberOfOrders) { - return orderDemands.get(firstIndex); + final IntIntToLong callback = buildManhattanCallback(manager, 1); + final String timeStr = "time"; + model.addDimension( + model.registerTransitCallback(callback), bigNumber, bigNumber, false, timeStr); + RoutingDimension timeDimension = model.getMutableDimension(timeStr); + + IntToLong demandCallback = + new IntToLong() { + @Override + public long run(int index) { + try { + int node = manager.indexToNode(index); + if (node < numberOfOrders) { + return orderDemands.get(node); + } + return 0; + } catch (Throwable throwed) { + logger.warning(throwed.getMessage()); + return 0; + } } - return 0; - } catch (Throwable throwed) { - logger.warning(throwed.getMessage()); - return 0; - } - } - }; - model.addDimension(demandCallback, 0, vehicleCapacity, true, "capacity"); + }; + final String capacityStr = "capacity"; + model.addDimension( + model.registerUnaryTransitCallback(demandCallback), 0, vehicleCapacity, true, capacityStr); + RoutingDimension capacityDimension = model.getMutableDimension(capacityStr); // Setting up vehicles + IntIntToLong[] callbacks = new IntIntToLong[numberOfVehicles]; for (int vehicle = 0; vehicle < numberOfVehicles; ++vehicle) { final int costCoefficient = vehicleCostCoefficients.get(vehicle); - NodeEvaluator2 manhattanCostCallback = new NodeEvaluator2() { - @Override - public long run(int firstIndex, int secondIndex) { - try { - Pair firstLocation = locations.get(firstIndex); - Pair secondLocation = locations.get(secondIndex); - return costCoefficient - * (Math.abs(firstLocation.first - secondLocation.first) - + Math.abs(firstLocation.second - secondLocation.second)); - } catch (Throwable throwed) { - logger.warning(throwed.getMessage()); - return 0; - } - } - }; - model.setArcCostEvaluatorOfVehicle(manhattanCostCallback, vehicle); - model.cumulVar(model.start(vehicle), "time").setMin(vehicleStartTime.get(vehicle)); - model.cumulVar(model.end(vehicle), "time").setMax(vehicleEndTime.get(vehicle)); + callbacks[vehicle] = buildManhattanCallback(manager, costCoefficient); + final int vehicleCost = model.registerTransitCallback(callbacks[vehicle]); + model.setArcCostEvaluatorOfVehicle(vehicleCost, vehicle); + timeDimension.cumulVar(model.end(vehicle)).setMax(vehicleEndTime.get(vehicle)); } // Setting up orders for (int order = 0; order < numberOfOrders; ++order) { - model.cumulVar(model.nodeToIndex(order), "time") + timeDimension + .cumulVar(order) .setRange(orderTimeWindows.get(order).first, orderTimeWindows.get(order).second); - int[] orders = {order}; - model.addDisjunction(orders, orderPenalties.get(order)); + long[] orderIndices = {manager.nodeToIndex(order)}; + model.addDisjunction(orderIndices, orderPenalties.get(order)); } // Solving RoutingSearchParameters parameters = - RoutingSearchParameters.newBuilder() - .mergeFrom(RoutingModel.defaultSearchParameters()) - .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.ALL_UNPERFORMED) .build(); logger.info("Search"); @@ -258,25 +253,38 @@ public long run(int firstIndex, int secondIndex) { long order = model.start(vehicle); // Empty route has a minimum of two nodes: Start => End if (model.isEnd(solution.value(model.nextVar(order)))) { - route += "/!\\Empty Route/!\\ "; - } - { + route += "Empty"; + } else { for (; !model.isEnd(order); order = solution.value(model.nextVar(order))) { - IntVar load = model.cumulVar(order, "capacity"); - IntVar time = model.cumulVar(order, "time"); - route += order + " Load(" + solution.value(load) + ") " - + "Time(" + solution.min(time) + ", " + solution.max(time) + ") -> "; + IntVar load = capacityDimension.cumulVar(order); + IntVar time = timeDimension.cumulVar(order); + route += + order + + " Load(" + + solution.value(load) + + ") " + + "Time(" + + solution.min(time) + + ", " + + solution.max(time) + + ") -> "; } - IntVar load = model.cumulVar(order, "capacity"); - IntVar time = model.cumulVar(order, "time"); - route += order + " Load(" + solution.value(load) + ") " - + "Time(" + solution.min(time) + ", " + solution.max(time) + ")"; + IntVar load = capacityDimension.cumulVar(order); + IntVar time = timeDimension.cumulVar(order); + route += + order + + " Load(" + + solution.value(load) + + ") " + + "Time(" + + solution.min(time) + + ", " + + solution.max(time) + + ")"; } output += route + "\n"; } logger.info(output); - } else { - logger.info("No solution Found !"); } } @@ -286,24 +294,20 @@ public static void main(String[] args) throws Exception { final int xMax = 20; final int yMax = 20; final int demandMax = 3; - final int timeWindowMin = 8 * 60; - final int timeWindowMax = 17 * 60; + final int timeWindowMax = 24 * 60; final int timeWindowWidth = 4 * 60; final int penaltyMin = 50; final int penaltyMax = 100; - /** @todo Specify vehicle start time*/ - final int startTime = 8 * 60; - /** @todo Specify vehicle end time*/ - final int endTime = 17 * 60; + final int endTime = 24 * 60; final int costCoefficientMax = 3; final int orders = 100; final int vehicles = 20; final int capacity = 50; - problem.buildOrders(orders, xMax, yMax, demandMax, timeWindowMin, timeWindowMax, - timeWindowWidth, penaltyMin, penaltyMax); - problem.buildFleet(vehicles, xMax, yMax, startTime, endTime, capacity, costCoefficientMax); + problem.buildOrders( + orders, xMax, yMax, demandMax, timeWindowMax, timeWindowWidth, penaltyMin, penaltyMax); + problem.buildFleet(vehicles, xMax, yMax, endTime, capacity, costCoefficientMax); problem.solve(orders, vehicles); } } diff --git a/examples/java/FlowExample.java b/examples/java/FlowExample.java index 15e78723735..99798b22f18 100644 --- a/examples/java/FlowExample.java +++ b/examples/java/FlowExample.java @@ -14,12 +14,9 @@ import com.google.ortools.graph.MaxFlow; import com.google.ortools.graph.MinCostFlow; -/** - * Sample showing how to model using the flow solver. - * - */ - +/** Sample showing how to model using the flow solver. */ public class FlowExample { + static { System.loadLibrary("jniortools"); } @@ -29,7 +26,11 @@ private static void solveMinCostFlow() { final int numSources = 4; final int numTargets = 4; final int[][] costs = { - {90, 75, 75, 80}, {35, 85, 55, 65}, {125, 95, 90, 105}, {45, 110, 95, 115}}; + {90, 75, 75, 80}, + {35, 85, 55, 65}, + {125, 95, 90, 105}, + {45, 110, 95, 115} + }; final int expectedCost = 275; MinCostFlow minCostFlow = new MinCostFlow(); for (int source = 0; source < numSources; ++source) { @@ -47,8 +48,13 @@ private static void solveMinCostFlow() { System.out.println("total flow = " + totalFlowCost + "/" + expectedCost); for (int i = 0; i < minCostFlow.getNumArcs(); ++i) { if (minCostFlow.getFlow(i) > 0) { - System.out.println("From source " + minCostFlow.getTail(i) + " to target " - + minCostFlow.getHead(i) + ": cost " + minCostFlow.getUnitCost(i)); + System.out.println( + "From source " + + minCostFlow.getTail(i) + + " to target " + + minCostFlow.getHead(i) + + ": cost " + + minCostFlow.getUnitCost(i)); } } } else { @@ -69,8 +75,15 @@ private static void solveMaxFlow() { if (maxFlow.solve(0, 5) == MaxFlow.Status.OPTIMAL) { System.out.println("Total flow " + maxFlow.getOptimalFlow() + "/" + expectedTotalFlow); for (int i = 0; i < maxFlow.getNumArcs(); ++i) { - System.out.println("From source " + maxFlow.getTail(i) + " to target " + maxFlow.getHead(i) - + ": " + maxFlow.getFlow(i) + " / " + maxFlow.getCapacity(i)); + System.out.println( + "From source " + + maxFlow.getTail(i) + + " to target " + + maxFlow.getHead(i) + + ": " + + maxFlow.getFlow(i) + + " / " + + maxFlow.getCapacity(i)); } // TODO(user): Our SWIG configuration does not currently handle these // functions correctly in Java: diff --git a/examples/java/IntegerProgramming.java b/examples/java/IntegerProgramming.java index 8e4275d48cf..4c23ee3b370 100644 --- a/examples/java/IntegerProgramming.java +++ b/examples/java/IntegerProgramming.java @@ -16,11 +16,7 @@ import com.google.ortools.linearsolver.MPSolver; import com.google.ortools.linearsolver.MPVariable; -/** - * Integer programming example that shows how to use the API. - * - */ - +/** Integer programming example that shows how to use the API. */ public class IntegerProgramming { static { System.loadLibrary("jniortools"); @@ -67,9 +63,10 @@ private static void runIntegerProgrammingExample(String solverType) { // Verify that the solution satisfies all constraints (when using solvers // others than GLOP_LINEAR_PROGRAMMING, this is highly recommended!). - if (!solver.verifySolution(/*tolerance=*/1e-7, /*logErrors=*/true)) { - System.err.println("The solution returned by the solver violated the" - + " problem constraints by at least 1e-7"); + if (!solver.verifySolution(/*tolerance=*/ 1e-7, /*logErrors=*/ true)) { + System.err.println( + "The solution returned by the solver violated the" + + " problem constraints by at least 1e-7"); return; } diff --git a/examples/java/Knapsack.java b/examples/java/Knapsack.java index aa23413b241..91def4b1f72 100644 --- a/examples/java/Knapsack.java +++ b/examples/java/Knapsack.java @@ -13,26 +13,35 @@ import com.google.ortools.algorithms.KnapsackSolver; -/** - * Sample showing how to model using the knapsack solver. - * - */ - +/** Sample showing how to model using the knapsack solver. */ public class Knapsack { static { System.loadLibrary("jniortools"); } private static void solve() { - KnapsackSolver solver = new KnapsackSolver( - KnapsackSolver.SolverType.KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, "test"); - final long[] profits = {360, 83, 59, 130, 431, 67, 230, 52, 93, 125, 670, 892, 600, 38, 48, 147, - 78, 256, 63, 17, 120, 164, 432, 35, 92, 110, 22, 42, 50, 323, 514, 28, 87, 73, 78, 15, 26, - 78, 210, 36, 85, 189, 274, 43, 33, 10, 19, 389, 276, 312}; - - final long[][] weights = {{7, 0, 30, 22, 80, 94, 11, 81, 70, 64, 59, 18, 0, 36, 3, 8, 15, 42, 9, - 0, 42, 47, 52, 32, 26, 48, 55, 6, 29, 84, 2, 4, 18, 56, 7, 29, 93, 44, 71, 3, 86, 66, 31, - 65, 0, 79, 20, 65, 52, 13}}; + KnapsackSolver solver = + new KnapsackSolver( + KnapsackSolver.SolverType.KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, "test"); + final long[] profits = { + 360, 83, 59, 130, 431, 67, 230, 52, 93, + 125, 670, 892, 600, 38, 48, 147, 78, 256, + 63, 17, 120, 164, 432, 35, 92, 110, 22, + 42, 50, 323, 514, 28, 87, 73, 78, 15, + 26, 78, 210, 36, 85, 189, 274, 43, 33, + 10, 19, 389, 276, 312 + }; + + final long[][] weights = { + { + 7, 0, 30, 22, 80, 94, 11, 81, 70, + 64, 59, 18, 0, 36, 3, 8, 15, 42, + 9, 0, 42, 47, 52, 32, 26, 48, 55, + 6, 29, 84, 2, 4, 18, 56, 7, 29, + 93, 44, 71, 3, 86, 66, 31, 65, 0, + 79, 20, 65, 52, 13 + } + }; final long[] capacities = {850}; diff --git a/examples/java/LinearAssignmentAPI.java b/examples/java/LinearAssignmentAPI.java index 81db91bf0d7..2c81f15f591 100644 --- a/examples/java/LinearAssignmentAPI.java +++ b/examples/java/LinearAssignmentAPI.java @@ -1,4 +1,4 @@ -// Copyright 2010-2018 Google LLC +// Copyright 2010-2017 Google // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -15,21 +15,25 @@ /** * Test assignment on a 4x4 matrix. Example taken from - * http://www.ee.oulu.fi/~mpa/matreng/eem1_2-1.htm with kCost[0][1] - * modified so the optimum solution is unique. - * + * http://www.ee.oulu.fi/~mpa/matreng/eem1_2-1.htm with kCost[0][1] modified so the optimum solution + * is unique. */ - public class LinearAssignmentAPI { + static { System.loadLibrary("jniortools"); } + private static void runAssignmentOn4x4Matrix() { final int numSources = 4; final int numTargets = 4; final int[][] cost = { - {90, 76, 75, 80}, {35, 85, 55, 65}, {125, 95, 90, 105}, {45, 110, 95, 115}}; + {90, 76, 75, 80}, + {35, 85, 55, 65}, + {125, 95, 90, 105}, + {45, 110, 95, 115} + }; final int expectedCost = cost[0][3] + cost[1][2] + cost[2][1] + cost[3][0]; LinearSumAssignment assignment = new LinearSumAssignment(); @@ -42,8 +46,13 @@ private static void runAssignmentOn4x4Matrix() { if (assignment.solve() == LinearSumAssignment.Status.OPTIMAL) { System.out.println("Total cost = " + assignment.getOptimalCost() + "/" + expectedCost); for (int node = 0; node < assignment.getNumNodes(); ++node) { - System.out.println("Left node " + node + " assigned to right node " - + assignment.getRightMate(node) + " with cost " + assignment.getAssignmentCost(node)); + System.out.println( + "Left node " + + node + + " assigned to right node " + + assignment.getRightMate(node) + + " with cost " + + assignment.getAssignmentCost(node)); } } else { System.out.println("No solution found."); diff --git a/examples/java/LinearProgramming.java b/examples/java/LinearProgramming.java index c94e0090eac..c080051125c 100644 --- a/examples/java/LinearProgramming.java +++ b/examples/java/LinearProgramming.java @@ -16,11 +16,7 @@ import com.google.ortools.linearsolver.MPSolver; import com.google.ortools.linearsolver.MPVariable; -/** - * Linear programming example that shows how to use the API. - * - */ - +/** Linear programming example that shows how to use the API. */ public class LinearProgramming { static { System.loadLibrary("jniortools"); @@ -90,9 +86,10 @@ private static void runLinearProgrammingExample(String solverType, boolean print // Verify that the solution satisfies all constraints (when using solvers // others than GLOP_LINEAR_PROGRAMMING, this is highly recommended!). - if (!solver.verifySolution(/*tolerance=*/1e-7, /*logErrors=*/true)) { - System.err.println("The solution returned by the solver violated the" - + " problem constraints by at least 1e-7"); + if (!solver.verifySolution(/*tolerance=*/ 1e-7, /*logErrors=*/ true)) { + System.err.println( + "The solution returned by the solver violated the" + + " problem constraints by at least 1e-7"); return; } diff --git a/examples/java/LsApi.java b/examples/java/LsApi.java index 943739b9e7d..4385f7513e0 100644 --- a/examples/java/LsApi.java +++ b/examples/java/LsApi.java @@ -24,10 +24,7 @@ import com.google.ortools.constraintsolver.SolutionCollector; import com.google.ortools.constraintsolver.Solver; -/** - * Sample showing how to model using the constraint programming solver. - * - */ +/** Sample showing how to model using the constraint programming solver. */ public class LsApi { static { System.loadLibrary("jniortools"); diff --git a/examples/java/RabbitsPheasants.java b/examples/java/RabbitsPheasants.java index 12da7de1120..66a7dcf9514 100644 --- a/examples/java/RabbitsPheasants.java +++ b/examples/java/RabbitsPheasants.java @@ -14,13 +14,9 @@ import com.google.ortools.constraintsolver.DecisionBuilder; import com.google.ortools.constraintsolver.IntVar; import com.google.ortools.constraintsolver.Solver; - import java.util.logging.Logger; -/** - * Sample showing how to model using the constraint programming solver. - * - */ +/** Sample showing how to model using the constraint programming solver. */ public class RabbitsPheasants { private static Logger logger = Logger.getLogger(RabbitsPheasants.class.getName()); @@ -29,9 +25,8 @@ public class RabbitsPheasants { } /** - * Solves the rabbits + pheasants problem. We are seing 20 heads - * and 56 legs. How many rabbits and how many pheasants are we thus - * seeing? + * Solves the rabbits + pheasants problem. We are seing 20 heads and 56 legs. How many rabbits and + * how many pheasants are we thus seeing? */ private static void solve(boolean traceSearch) { ConstraintSolverParameters parameters = ConstraintSolverParameters.newBuilder() diff --git a/examples/java/Tsp.java b/examples/java/Tsp.java index 142db636e18..a955ac5c6e8 100644 --- a/examples/java/Tsp.java +++ b/examples/java/Tsp.java @@ -1,5 +1,5 @@ // -// Copyright 2012 Google +// Copyright 2010-2017 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,25 +12,28 @@ // 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. -import java.io.*; -import java.util.*; -import java.text.*; - import com.google.ortools.constraintsolver.Assignment; -import com.google.ortools.constraintsolver.NodeEvaluator2; -import com.google.ortools.constraintsolver.RoutingModel; import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.IntIntToLong; +import com.google.ortools.constraintsolver.IntToLong; +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.io.*; +import java.text.*; +import java.util.*; class Tsp { static { System.loadLibrary("jniortools"); } - static class RandomManhattan extends NodeEvaluator2 { - public RandomManhattan(int size, int seed) { + static class RandomManhattan extends IntIntToLong { + public RandomManhattan(RoutingIndexManager manager, int size, int seed) { this.xs = new int[size]; this.ys = new int[size]; + this.indexManager = manager; Random generator = new Random(seed); for (int i = 0; i < size; ++i) { xs[i] = generator.nextInt(1000); @@ -40,30 +43,33 @@ public RandomManhattan(int size, int seed) { @Override public long run(int firstIndex, int secondIndex) { - return Math.abs(xs[firstIndex] - xs[secondIndex]) - + Math.abs(ys[firstIndex] - ys[secondIndex]); + int firstNode = indexManager.indexToNode(firstIndex); + int secondNode = indexManager.indexToNode(secondIndex); + return Math.abs(xs[firstNode] - xs[secondNode]) + Math.abs(ys[firstNode] - ys[secondNode]); } private int[] xs; private int[] ys; + private RoutingIndexManager indexManager; } - static class ConstantCallback extends NodeEvaluator2 { + static class ConstantCallback extends IntToLong { @Override - public long run(int firstIndex, int secondIndex) { + public long run(int index) { return 1; } } static void solve(int size, int forbidden, int seed) { - RoutingModel routing = new RoutingModel(size, 1, 0); + RoutingIndexManager manager = new RoutingIndexManager(size, 1, 0); + RoutingModel routing = new RoutingModel(manager); // Setting the cost function. // Put a permanent callback to the distance accessor here. The callback // has the following signature: ResultCallback2. // The two arguments are the from and to node inidices. - NodeEvaluator2 distances = new RandomManhattan(size, seed); - routing.setArcCostEvaluatorOfAllVehicles(distances); + IntIntToLong distances = new RandomManhattan(manager, size, seed); + routing.setArcCostEvaluatorOfAllVehicles(routing.registerTransitCallback(distances)); // Forbid node connections (randomly). Random randomizer = new Random(); @@ -79,12 +85,17 @@ static void solve(int size, int forbidden, int seed) { } // Add dummy dimension to test API. - routing.addDimension(new ConstantCallback(), size + 1, size + 1, true, "dummy"); + routing.addDimension( + routing.registerUnaryTransitCallback(new ConstantCallback()), + size + 1, + size + 1, + true, + "dummy"); // Solve, returns a solution if any (owned by RoutingModel). RoutingSearchParameters search_parameters = RoutingSearchParameters.newBuilder() - .mergeFrom(RoutingModel.defaultSearchParameters()) + .mergeFrom(main.defaultRoutingSearchParameters()) .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) .build(); @@ -95,8 +106,9 @@ static void solve(int size, int forbidden, int seed) { // Inspect solution. // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1 int route_number = 0; - for (long node = routing.start(route_number); !routing.isEnd(node); - node = solution.value(routing.nextVar(node))) { + for (long node = routing.start(route_number); + !routing.isEnd(node); + node = solution.value(routing.nextVar(node))) { System.out.print("" + node + " -> "); } System.out.println("0"); diff --git a/examples/java/Vrp.java b/examples/java/Vrp.java index 89c7eacd07f..a9fd2e15129 100644 --- a/examples/java/Vrp.java +++ b/examples/java/Vrp.java @@ -10,22 +10,29 @@ // 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. -import java.io.*; import static java.lang.Math.abs; +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.IntIntToLong; +import com.google.ortools.constraintsolver.RoutingDimension; +import com.google.ortools.constraintsolver.RoutingIndexManager; import com.google.ortools.constraintsolver.RoutingModel; import com.google.ortools.constraintsolver.NodeEvaluator2; import com.google.ortools.constraintsolver.RoutingDimension; import com.google.ortools.constraintsolver.RoutingSearchParameters; -import com.google.ortools.constraintsolver.FirstSolutionStrategy; -import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.main; +import java.io.*; class DataProblem { private int[][] locations_; public DataProblem() { - locations_ = new int[][] {{4, 4}, {2, 0}, {8, 0}, {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}}; + locations_ = + new int[][] { + {4, 4}, {2, 0}, {8, 0}, {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} + }; // Compute locations in meters using the block dimension defined as follow // Manhattan average block: 750ft x 264ft -> 228m x 80m @@ -60,28 +67,31 @@ public int getDepot() { /// @details It uses an array of positions and computes /// the Manhattan distance between the two positions of /// two different indices. -class ManhattanDistance extends NodeEvaluator2 { - private int[][] distances_; +class ManhattanDistance extends IntIntToLong { + private int[][] distances; + private RoutingIndexManager indexManager; - public ManhattanDistance(DataProblem data) { + public ManhattanDistance(DataProblem data, RoutingIndexManager manager) { // precompute distance between location to have distance callback in O(1) - distances_ = new int[data.getLocationNumber()][data.getLocationNumber()]; + distances = new int[data.getLocationNumber()][data.getLocationNumber()]; + indexManager = manager; for (int fromNode = 0; fromNode < data.getLocationNumber(); ++fromNode) { for (int toNode = 0; toNode < data.getLocationNumber(); ++toNode) { - if (fromNode == toNode) - distances_[fromNode][toNode] = 0; + if (fromNode == toNode) distances[fromNode][toNode] = 0; else - distances_[fromNode][toNode] = + distances[fromNode][toNode] = abs(data.getLocations()[toNode][0] - data.getLocations()[fromNode][0]) - + abs(data.getLocations()[toNode][1] - data.getLocations()[fromNode][1]); + + abs(data.getLocations()[toNode][1] - data.getLocations()[fromNode][1]); } } } @Override /// @brief Returns the manhattan distance between the two nodes. - public long run(int fromNode, int toNode) { - return distances_[fromNode][toNode]; + public long run(int fromIndex, int toIndex) { + int fromNode = indexManager.indexToNode(fromIndex); + int toNode = indexManager.indexToNode(toIndex); + return distances[fromNode][toNode]; } } @@ -91,9 +101,10 @@ class Vrp { } /// @brief Add Global Span constraint. - static void addDistanceDimension(RoutingModel routing, DataProblem data) { + static void addDistanceDimension(RoutingModel routing, DataProblem data, int distanceIndex) { String distance = "Distance"; - routing.addDimension(new ManhattanDistance(data), + routing.addDimension( + distanceIndex, 0, // null slack 3000, // maximum distance per vehicle true, // start cumul to zero @@ -105,21 +116,22 @@ static void addDistanceDimension(RoutingModel routing, DataProblem data) { } /// @brief Print the solution - static void printSolution(DataProblem data, RoutingModel routing, Assignment solution) { + static void printSolution( + DataProblem data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { // Solution cost. System.out.println("Objective : " + solution.objectiveValue()); // Inspect solution. for (int i = 0; i < data.getVehicleNumber(); ++i) { System.out.println("Route for Vehicle " + i + ":"); long distance = 0; - for (long index = routing.start(i); !routing.isEnd(index);) { - System.out.print(routing.indexToNode(index) + " -> "); + for (long index = routing.start(i); !routing.isEnd(index); ) { + System.out.print(manager.indexToNode((int) index) + " -> "); long previousIndex = index; index = solution.value(routing.nextVar(index)); distance += routing.getArcCostForVehicle(previousIndex, index, i); } - System.out.println(routing.indexToNode(routing.end(i))); + System.out.println(manager.indexToNode((int) routing.end(i))); System.out.println("Distance of the route: " + distance + "m"); } } @@ -130,24 +142,26 @@ static void solve() { DataProblem data = new DataProblem(); // Create Routing Model - RoutingModel routing = - new RoutingModel(data.getLocationNumber(), data.getVehicleNumber(), data.getDepot()); + RoutingIndexManager manager = + new RoutingIndexManager(data.getLocationNumber(), data.getVehicleNumber(), data.getDepot()); + RoutingModel routing = new RoutingModel(manager); // Setting the cost function. // [todo]: protect callback from the GC - NodeEvaluator2 distanceEvaluator = new ManhattanDistance(data); - routing.setArcCostEvaluatorOfAllVehicles(distanceEvaluator); - addDistanceDimension(routing, data); + IntIntToLong distanceEvaluator = new ManhattanDistance(data, manager); + int distanceIndex = routing.registerTransitCallback(distanceEvaluator); + routing.setArcCostEvaluatorOfAllVehicles(distanceIndex); + addDistanceDimension(routing, data, distanceIndex); // Setting first solution heuristic (cheapest addition). RoutingSearchParameters search_parameters = RoutingSearchParameters.newBuilder() - .mergeFrom(RoutingModel.defaultSearchParameters()) + .mergeFrom(main.defaultRoutingSearchParameters()) .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) .build(); Assignment solution = routing.solveWithParameters(search_parameters); - printSolution(data, routing, solution); + printSolution(data, routing, manager, solution); } /// @brief Entry point of the program. diff --git a/examples/python/cvrp.py b/examples/python/cvrp.py index 1eab5d21396..23c02065e96 100755 --- a/examples/python/cvrp.py +++ b/examples/python/cvrp.py @@ -24,7 +24,10 @@ """ from __future__ import print_function + +from functools import partial from six.moves import xrange + from ortools.constraint_solver import pywrapcp from ortools.constraint_solver import routing_enums_pb2 @@ -33,153 +36,160 @@ # Problem Data Definition # ########################### def create_data_model(): - """Stores the data for the problem""" - data = {} - # Locations in block unit - _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)] - # Compute locations in meters using the block dimension defined as follow - # Manhattan average block: 750ft x 264ft -> 228m x 80m - # here we use: 114m x 80m city block - # src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" - data["locations"] = [(l[0] * 114, l[1] * 80) for l in _locations] - data["num_locations"] = len(data["locations"]) - data["demands"] = \ - [0, # depot - 1, 1, # 1, 2 - 2, 4, # 3, 4 - 2, 4, # 5, 6 - 8, 8, # 7, 8 - 1, 2, # 9,10 - 1, 2, # 11,12 - 4, 4, # 13, 14 - 8, 8] # 15, 16 - data["num_vehicles"] = 4 - data["vehicle_capacity"] = 15 - data["depot"] = 0 - return data + """Stores the data for the problem""" + data = {} + # Locations in block unit + _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)] + # Compute locations in meters using the block dimension defined as follow + # Manhattan average block: 750ft x 264ft -> 228m x 80m + # here we use: 114m x 80m city block + # src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance' + data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations] + data['num_locations'] = len(data['locations']) + data['demands'] = \ + [0, # depot + 1, 1, # 1, 2 + 2, 4, # 3, 4 + 2, 4, # 5, 6 + 8, 8, # 7, 8 + 1, 2, # 9,10 + 1, 2, # 11,12 + 4, 4, # 13, 14 + 8, 8] # 15, 16 + data['num_vehicles'] = 4 + data['vehicle_capacity'] = 15 + data['depot'] = 0 + return data ####################### # Problem Constraints # ####################### def manhattan_distance(position_1, position_2): - """Computes the Manhattan distance between two points""" - return (abs(position_1[0] - position_2[0]) + - abs(position_1[1] - position_2[1])) + """Computes the Manhattan distance between two points""" + return ( + abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1])) def create_distance_evaluator(data): - """Creates callback to return distance between points.""" - _distances = {} - # precompute distance between location to have distance callback in O(1) - for from_node in xrange(data["num_locations"]): - _distances[from_node] = {} - for to_node in xrange(data["num_locations"]): - if from_node == to_node: - _distances[from_node][to_node] = 0 - else: - _distances[from_node][to_node] = (manhattan_distance( - data["locations"][from_node], data["locations"][to_node])) - - def distance_evaluator(from_node, to_node): - """Returns the manhattan distance between the two nodes""" - return _distances[from_node][to_node] - - return distance_evaluator + """Creates callback to return distance between points.""" + _distances = {} + # precompute distance between location to have distance callback in O(1) + for from_node in xrange(data['num_locations']): + _distances[from_node] = {} + for to_node in xrange(data['num_locations']): + if from_node == to_node: + _distances[from_node][to_node] = 0 + else: + _distances[from_node][to_node] = ( + manhattan_distance(data['locations'][from_node], + data['locations'][to_node])) + + def distance_evaluator(manager, from_node, to_node): + """Returns the manhattan distance between the two nodes""" + return _distances[manager.IndexToNode(from_node)][manager.IndexToNode( + to_node)] + + return distance_evaluator def create_demand_evaluator(data): - """Creates callback to get demands at each location.""" - _demands = data["demands"] + """Creates callback to get demands at each location.""" + _demands = data['demands'] - def demand_evaluator(from_node, to_node): - """Returns the demand of the current node""" - del to_node - return _demands[from_node] + def demand_evaluator(manager, node): + """Returns the demand of the current node""" + return _demands[manager.IndexToNode(node)] - return demand_evaluator + return demand_evaluator -def add_capacity_constraints(routing, data, demand_evaluator): - """Adds capacity constraint""" - capacity = 'Capacity' - routing.AddDimension( - demand_evaluator, - 0, # null capacity slack - data["vehicle_capacity"], - True, # start cumul to zero - capacity) +def add_capacity_constraints(routing, data, demand_evaluator_index): + """Adds capacity constraint""" + capacity = 'Capacity' + routing.AddDimension( + demand_evaluator_index, + 0, # null capacity slack + data['vehicle_capacity'], + True, # start cumul to zero + capacity) ########### # Printer # ########### -def print_solution(data, routing, assignment): - """Prints assignment on console""" - print('Objective: {}'.format(assignment.ObjectiveValue())) - total_distance = 0 - total_load = 0 - capacity_dimension = routing.GetDimensionOrDie('Capacity') - for vehicle_id in xrange(data["num_vehicles"]): - index = routing.Start(vehicle_id) - plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) - distance = 0 - while not routing.IsEnd(index): - load_var = capacity_dimension.CumulVar(index) - plan_output += ' {} Load({}) -> '.format( - routing.IndexToNode(index), assignment.Value(load_var)) - previous_index = index - index = assignment.Value(routing.NextVar(index)) - distance += routing.GetArcCostForVehicle(previous_index, index, - vehicle_id) - load_var = capacity_dimension.CumulVar(index) - plan_output += ' {0} Load({1})\n'.format( - routing.IndexToNode(index), assignment.Value(load_var)) - plan_output += 'Distance of the route: {}m\n'.format(distance) - plan_output += 'Load of the route: {}\n'.format( - assignment.Value(load_var)) - print(plan_output) - total_distance += distance - total_load += assignment.Value(load_var) - print('Total Distance of all routes: {}m'.format(total_distance)) - print('Total Load of all routes: {}'.format(total_load)) +def print_solution(data, routing, manager, assignment): # pylint:disable=too-many-locals + """Prints assignment on console""" + print('Objective: {}'.format(assignment.ObjectiveValue())) + total_distance = 0 + total_load = 0 + capacity_dimension = routing.GetDimensionOrDie('Capacity') + for vehicle_id in xrange(data['num_vehicles']): + index = routing.Start(vehicle_id) + plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) + distance = 0 + while not routing.IsEnd(index): + load_var = capacity_dimension.CumulVar(index) + plan_output += ' {} Load({}) -> '.format( + manager.IndexToNode(index), assignment.Value(load_var)) + previous_index = index + index = assignment.Value(routing.NextVar(index)) + distance += routing.GetArcCostForVehicle(previous_index, index, + vehicle_id) + load_var = capacity_dimension.CumulVar(index) + plan_output += ' {0} Load({1})\n'.format( + manager.IndexToNode(index), assignment.Value(load_var)) + plan_output += 'Distance of the route: {}m\n'.format(distance) + plan_output += 'Load of the route: {}\n'.format(assignment.Value(load_var)) + print(plan_output) + total_distance += distance + total_load += assignment.Value(load_var) + print('Total Distance of all routes: {}m'.format(total_distance)) + print('Total Load of all routes: {}'.format(total_load)) ######## # Main # ######## def main(): - """Entry point of the program""" - # Instantiate the data problem. - data = create_data_model() - - # Create Routing Model - routing = pywrapcp.RoutingModel(data["num_locations"], - data["num_vehicles"], data["depot"]) - # Define weight of each edge - distance_evaluator = create_distance_evaluator(data) - routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator) - # Add Capacity constraint - demand_evaluator = create_demand_evaluator(data) - add_capacity_constraints(routing, data, demand_evaluator) - - # Setting first solution heuristic (cheapest addition). - search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters() - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member - # Solve the problem. - assignment = routing.SolveWithParameters(search_parameters) - print_solution(data, routing, assignment) + """Entry point of the program""" + # Instantiate the data problem. + data = create_data_model() + + # Create the routing index manager + manager = pywrapcp.RoutingIndexManager(data['num_locations'], + data['num_vehicles'], data['depot']) + + # Create Routing Model + routing = pywrapcp.RoutingModel(manager) + + # Define weight of each edge + distance_evaluator = routing.RegisterTransitCallback( + partial(create_distance_evaluator(data), manager)) + routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator) + + # Add Capacity constraint + demand_evaluator_index = routing.RegisterUnaryTransitCallback( + partial(create_demand_evaluator(data), manager)) + add_capacity_constraints(routing, data, demand_evaluator_index) + + # Setting first solution heuristic (cheapest addition). + search_parameters = pywrapcp.DefaultRoutingSearchParameters() + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member + # Solve the problem. + assignment = routing.SolveWithParameters(search_parameters) + print_solution(data, routing, manager, assignment) if __name__ == '__main__': - main() + main() diff --git a/examples/python/cvrp_reload.py b/examples/python/cvrp_reload.py index 6db9e8d4800..3777a346700 100755 --- a/examples/python/cvrp_reload.py +++ b/examples/python/cvrp_reload.py @@ -24,7 +24,10 @@ """ from __future__ import print_function + +from functools import partial from six.moves import xrange + from ortools.constraint_solver import pywrapcp from ortools.constraint_solver import routing_enums_pb2 @@ -33,304 +36,315 @@ # Problem Data Definition # ########################### def create_data_model(): - """Stores the data for the problem""" - data = {} - _capacity = 15 - # Locations in block unit - _locations = [ - (4, 4), # depot - (4, 4), # unload depot_prime - (4, 4), # unload depot_second - (4, 4), # unload depot_fourth - (4, 4), # unload depot_fourth - (4, 4), # unload depot_fifth - (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) - ] - # Compute locations in meters using the block dimension defined as follow - # Manhattan average block: 750ft x 264ft -> 228m x 80m - # here we use: 114m x 80m city block - # src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" - data["locations"] = [(l[0] * 114, l[1] * 80) for l in _locations] - data["num_locations"] = len(data["locations"]) - data["demands"] = \ - [0, # depot - -_capacity, - -_capacity, - -_capacity, - -_capacity, - -_capacity, - 1, 1, # 1, 2 - 2, 4, # 3, 4 - 2, 4, # 5, 6 - 8, 8, # 7, 8 - 1, 2, # 9,10 - 1, 2, # 11,12 - 4, 4, # 13, 14 - 8, 8] # 15, 16 - data["time_per_demand_unit"] = 5 # 5 minutes/unit - data["time_windows"] = \ - [(0, 0), # depot - (0, 1000), - (0, 1000), - (0, 1000), - (0, 1000), - (0, 1000), - (75, 8500), (75, 8500), # 1, 2 - (60, 7000), (45, 5500), # 3, 4 - (0, 8000), (50, 6000), # 5, 6 - (0, 1000), (10, 2000), # 7, 8 - (0, 1000), (75, 8500), # 9, 10 - (85, 9500), (5, 1500), # 11, 12 - (15, 2500), (10, 2000), # 13, 14 - (45, 5500), (30, 4000)] # 15, 16 - data["num_vehicles"] = 3 - data["vehicle_capacity"] = _capacity - data[ - "vehicle_speed"] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min - data["depot"] = 0 - return data + """Stores the data for the problem""" + data = {} + _capacity = 15 + # Locations in block unit + _locations = [ + (4, 4), # depot + (4, 4), # unload depot_prime + (4, 4), # unload depot_second + (4, 4), # unload depot_fourth + (4, 4), # unload depot_fourth + (4, 4), # unload depot_fifth + (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) + ] + # Compute locations in meters using the block dimension defined as follow + # Manhattan average block: 750ft x 264ft -> 228m x 80m + # here we use: 114m x 80m city block + # src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance' + data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations] + data['num_locations'] = len(data['locations']) + data['demands'] = \ + [0, # depot + -_capacity, + -_capacity, + -_capacity, + -_capacity, + -_capacity, + 1, 1, # 1, 2 + 2, 4, # 3, 4 + 2, 4, # 5, 6 + 8, 8, # 7, 8 + 1, 2, # 9,10 + 1, 2, # 11,12 + 4, 4, # 13, 14 + 8, 8] # 15, 16 + data['time_per_demand_unit'] = 5 # 5 minutes/unit + data['time_windows'] = \ + [(0, 0), # depot + (0, 1000), + (0, 1000), + (0, 1000), + (0, 1000), + (0, 1000), + (75, 8500), (75, 8500), # 1, 2 + (60, 7000), (45, 5500), # 3, 4 + (0, 8000), (50, 6000), # 5, 6 + (0, 1000), (10, 2000), # 7, 8 + (0, 1000), (75, 8500), # 9, 10 + (85, 9500), (5, 1500), # 11, 12 + (15, 2500), (10, 2000), # 13, 14 + (45, 5500), (30, 4000)] # 15, 16 + data['num_vehicles'] = 3 + data['vehicle_capacity'] = _capacity + data[ + 'vehicle_speed'] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min + data['depot'] = 0 + return data ####################### # Problem Constraints # ####################### def manhattan_distance(position_1, position_2): - """Computes the Manhattan distance between two points""" - return (abs(position_1[0] - position_2[0]) + - abs(position_1[1] - position_2[1])) + """Computes the Manhattan distance between two points""" + return ( + abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1])) def create_distance_evaluator(data): - """Creates callback to return distance between points.""" - _distances = {} - # precompute distance between location to have distance callback in O(1) - for from_node in xrange(data["num_locations"]): - _distances[from_node] = {} - for to_node in xrange(data["num_locations"]): - if from_node == to_node: - _distances[from_node][to_node] = 0 - else: - _distances[from_node][to_node] = (manhattan_distance( - data["locations"][from_node], data["locations"][to_node])) - - def distance_evaluator(from_node, to_node): - """Returns the manhattan distance between the two nodes""" - return _distances[from_node][to_node] - - return distance_evaluator - - -def add_distance_dimension(routing, distance_evaluator): - """Add Global Span constraint""" - distance = 'Distance' - routing.AddDimension( - distance_evaluator, - 0, # null slack - 10000, # maximum distance per vehicle - True, # start cumul to zero - distance) - distance_dimension = routing.GetDimensionOrDie(distance) - # Try to minimize the max distance among vehicles. - # /!\ It doesn't mean the standard deviation is minimized - distance_dimension.SetGlobalSpanCostCoefficient(100) + """Creates callback to return distance between points.""" + _distances = {} + # precompute distance between location to have distance callback in O(1) + for from_node in xrange(data['num_locations']): + _distances[from_node] = {} + for to_node in xrange(data['num_locations']): + if from_node == to_node: + _distances[from_node][to_node] = 0 + else: + _distances[from_node][to_node] = ( + manhattan_distance(data['locations'][from_node], + data['locations'][to_node])) + + def distance_evaluator(manager, from_node, to_node): + """Returns the manhattan distance between the two nodes""" + return _distances[manager.IndexToNode(from_node)][manager.IndexToNode( + to_node)] + + return distance_evaluator + + +def add_distance_dimension(routing, distance_evaluator_index): + """Add Global Span constraint""" + distance = 'Distance' + routing.AddDimension( + distance_evaluator_index, + 0, # null slack + 10000, # maximum distance per vehicle + True, # start cumul to zero + distance) + distance_dimension = routing.GetDimensionOrDie(distance) + # Try to minimize the max distance among vehicles. + # /!\ It doesn't mean the standard deviation is minimized + distance_dimension.SetGlobalSpanCostCoefficient(100) def create_demand_evaluator(data): - """Creates callback to get demands at each location.""" - _demands = data["demands"] - - def demand_evaluator(from_node, to_node): - """Returns the demand of the current node""" - del to_node - return _demands[from_node] - - return demand_evaluator - - -def add_capacity_constraints(routing, data, demand_evaluator): - """Adds capacity constraint""" - vehicle_capacity = data["vehicle_capacity"] - capacity = 'Capacity' - routing.AddDimension( - demand_evaluator, - 0, # Null slack - vehicle_capacity, - True, # start cumul to zero - capacity) - - # Add Slack for reseting to zero unload depot nodes. - # e.g. vehicle with load 10/15 arrives at node 1 (depot unload) - # so we have CumulVar = 10(current load) + -15(unload) + 5(slack) = 0. - capacity_dimension = routing.GetDimensionOrDie(capacity) - for node_index in [1, 2, 3, 4, 5]: - index = routing.NodeToIndex(node_index) - capacity_dimension.SlackVar(index).SetRange(0, vehicle_capacity) - routing.AddDisjunction([node_index], 0) + """Creates callback to get demands at each location.""" + _demands = data['demands'] + + def demand_evaluator(manager, from_node): + """Returns the demand of the current node""" + return _demands[manager.IndexToNode(from_node)] + + return demand_evaluator + + +def add_capacity_constraints(routing, manager, data, demand_evaluator_index): + """Adds capacity constraint""" + vehicle_capacity = data['vehicle_capacity'] + capacity = 'Capacity' + routing.AddDimension( + demand_evaluator_index, + 0, # Null slack + vehicle_capacity, + True, # start cumul to zero + capacity) + + # Add Slack for reseting to zero unload depot nodes. + # e.g. vehicle with load 10/15 arrives at node 1 (depot unload) + # so we have CumulVar = 10(current load) + -15(unload) + 5(slack) = 0. + capacity_dimension = routing.GetDimensionOrDie(capacity) + for node_index in [1, 2, 3, 4, 5]: + index = manager.NodeToIndex(node_index) + capacity_dimension.SlackVar(index).SetRange(0, vehicle_capacity) + routing.AddDisjunction([node_index], 0) def create_time_evaluator(data): - """Creates callback to get total times between locations.""" - - def service_time(data, node): - """Gets the service time for the specified location.""" - return abs(data["demands"][node]) * data["time_per_demand_unit"] - - def travel_time(data, from_node, to_node): - """Gets the travel times between two locations.""" - if from_node == to_node: - travel_time = 0 - else: - travel_time = manhattan_distance( - data["locations"][from_node], - data["locations"][to_node]) / data["vehicle_speed"] - return travel_time - - _total_time = {} - # precompute total time to have time callback in O(1) - for from_node in xrange(data["num_locations"]): - _total_time[from_node] = {} - for to_node in xrange(data["num_locations"]): - if from_node == to_node: - _total_time[from_node][to_node] = 0 - else: - _total_time[from_node][to_node] = int( - service_time(data, from_node) + - travel_time(data, from_node, to_node)) - - def time_evaluator(from_node, to_node): - """Returns the total time between the two nodes""" - return _total_time[from_node][to_node] - - return time_evaluator - - -def add_time_window_constraints(routing, data, time_evaluator): - """Add Time windows constraint""" - time = 'Time' - horizon = 1500 - routing.AddDimension( - time_evaluator, - horizon, # allow waiting time - horizon, # maximum time per vehicle - False, # don't force start cumul to zero since we are giving TW to start nodes - 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 = routing.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 xrange(data["num_vehicles"]): - index = routing.Start(vehicle_id) - time_dimension.CumulVar(index).SetRange(data["time_windows"][0][0], - data["time_windows"][0][1]) - routing.AddToAssignment(time_dimension.SlackVar(index)) - # Warning: Slack var is not defined for vehicle's end node - #routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id))) + """Creates callback to get total times between locations.""" + + def service_time(data, node): + """Gets the service time for the specified location.""" + return abs(data['demands'][node]) * data['time_per_demand_unit'] + + def travel_time(data, from_node, to_node): + """Gets the travel times between two locations.""" + if from_node == to_node: + travel_time = 0 + else: + travel_time = manhattan_distance( + data['locations'][from_node], + data['locations'][to_node]) / data['vehicle_speed'] + return travel_time + + _total_time = {} + # precompute total time to have time callback in O(1) + for from_node in xrange(data['num_locations']): + _total_time[from_node] = {} + for to_node in xrange(data['num_locations']): + if from_node == to_node: + _total_time[from_node][to_node] = 0 + else: + _total_time[from_node][to_node] = int( + service_time(data, from_node) + + travel_time(data, from_node, to_node)) + + def time_evaluator(manager, from_node, to_node): + """Returns the total time between the two nodes""" + return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode( + to_node)] + + return time_evaluator + + +def add_time_window_constraints(routing, manager, data, time_evaluator): + """Add Time windows constraint""" + time = 'Time' + horizon = 1500 + routing.AddDimension( + time_evaluator, + horizon, # allow waiting time + horizon, # maximum time per vehicle + False, # don't force start cumul to zero since we are giving TW to start nodes + 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 xrange(data['num_vehicles']): + index = routing.Start(vehicle_id) + time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0], + data['time_windows'][0][1]) + routing.AddToAssignment(time_dimension.SlackVar(index)) + # Warning: Slack var is not defined for vehicle's end node + #routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id))) ########### # Printer # ########### -def print_solution(data, routing, assignment): - """Prints assignment on console""" - print('Objective: {}'.format(assignment.ObjectiveValue())) - total_distance = 0 - total_load = 0 - total_time = 0 - capacity_dimension = routing.GetDimensionOrDie('Capacity') - time_dimension = routing.GetDimensionOrDie('Time') - dropped = [] - for order in xrange(0, routing.nodes()): - index = routing.NodeToIndex(order) - if assignment.Value(routing.NextVar(index)) == index: - dropped.append(order) - print('dropped orders: {}'.format(dropped)) - - for vehicle_id in xrange(data["num_vehicles"]): - index = routing.Start(vehicle_id) - plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) - distance = 0 - while not routing.IsEnd(index): - load_var = capacity_dimension.CumulVar(index) - time_var = time_dimension.CumulVar(index) - plan_output += ' {0} Load({1}) Time({2},{3}) ->'.format( - routing.IndexToNode(index), assignment.Value(load_var), - assignment.Min(time_var), assignment.Max(time_var)) - previous_index = index - index = assignment.Value(routing.NextVar(index)) - distance += routing.GetArcCostForVehicle(previous_index, index, - vehicle_id) - load_var = capacity_dimension.CumulVar(index) - time_var = time_dimension.CumulVar(index) - plan_output += ' {0} Load({1}) Time({2},{3})\n'.format( - routing.IndexToNode(index), assignment.Value(load_var), - assignment.Min(time_var), assignment.Max(time_var)) - plan_output += 'Distance of the route: {}m\n'.format(distance) - plan_output += 'Load of the route: {}\n'.format( - assignment.Value(load_var)) - plan_output += 'Time of the route: {}min\n'.format( - assignment.Value(time_var)) - print(plan_output) - total_distance += distance - total_load += assignment.Value(load_var) - total_time += assignment.Value(time_var) - print('Total Distance of all routes: {}m'.format(total_distance)) - print('Total Load of all routes: {}'.format(total_load)) - print('Total Time of all routes: {}min'.format(total_time)) +def print_solution(data, manager, routing, assignment): # pylint:disable=too-many-locals + """Prints assignment on console""" + print('Objective: {}'.format(assignment.ObjectiveValue())) + total_distance = 0 + total_load = 0 + total_time = 0 + capacity_dimension = routing.GetDimensionOrDie('Capacity') + time_dimension = routing.GetDimensionOrDie('Time') + dropped = [] + for order in xrange(0, routing.nodes()): + index = manager.NodeToIndex(order) + if assignment.Value(routing.NextVar(index)) == index: + dropped.append(order) + print('dropped orders: {}'.format(dropped)) + + for vehicle_id in xrange(data['num_vehicles']): + index = routing.Start(vehicle_id) + plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) + distance = 0 + while not routing.IsEnd(index): + load_var = capacity_dimension.CumulVar(index) + time_var = time_dimension.CumulVar(index) + plan_output += ' {0} Load({1}) Time({2},{3}) ->'.format( + manager.IndexToNode(index), assignment.Value(load_var), + assignment.Min(time_var), assignment.Max(time_var)) + previous_index = index + index = assignment.Value(routing.NextVar(index)) + distance += routing.GetArcCostForVehicle(previous_index, index, + vehicle_id) + load_var = capacity_dimension.CumulVar(index) + time_var = time_dimension.CumulVar(index) + plan_output += ' {0} Load({1}) Time({2},{3})\n'.format( + manager.IndexToNode(index), assignment.Value(load_var), + assignment.Min(time_var), assignment.Max(time_var)) + plan_output += 'Distance of the route: {}m\n'.format(distance) + plan_output += 'Load of the route: {}\n'.format(assignment.Value(load_var)) + plan_output += 'Time of the route: {}min\n'.format( + assignment.Value(time_var)) + print(plan_output) + total_distance += distance + total_load += assignment.Value(load_var) + total_time += assignment.Value(time_var) + print('Total Distance of all routes: {}m'.format(total_distance)) + print('Total Load of all routes: {}'.format(total_load)) + print('Total Time of all routes: {}min'.format(total_time)) ######## # Main # ######## def main(): - """Entry point of the program""" - # Instantiate the data problem. - data = create_data_model() - - # Create Routing Model - routing = pywrapcp.RoutingModel(data["num_locations"], - data["num_vehicles"], data["depot"]) - # Define weight of each edge - distance_evaluator = create_distance_evaluator(data) - routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator) - # Add Distance constraint to minimize the longuest route - add_distance_dimension(routing, distance_evaluator) - # Add Capacity constraint - demand_evaluator = create_demand_evaluator(data) - add_capacity_constraints(routing, data, demand_evaluator) - # Add Time Window constraint - time_evaluator = create_time_evaluator(data) - add_time_window_constraints(routing, data, time_evaluator) - - # Setting first solution heuristic (cheapest addition). - search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters() - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member - # Solve the problem. - assignment = routing.SolveWithParameters(search_parameters) - print_solution(data, routing, assignment) + """Entry point of the program""" + # Instantiate the data problem. + data = create_data_model() + + # Create the routing index manager + manager = pywrapcp.RoutingIndexManager(data['num_locations'], + data['num_vehicles'], data['depot']) + + # Create Routing Model + routing = pywrapcp.RoutingModel(manager) + + # Define weight of each edge + distance_evaluator_index = routing.RegisterTransitCallback( + partial(create_distance_evaluator(data), manager)) + routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index) + + # Add Distance constraint to minimize the longuest route + add_distance_dimension(routing, distance_evaluator_index) + + # Add Capacity constraint + demand_evaluator_index = routing.RegisterUnaryTransitCallback( + partial(create_demand_evaluator(data), manager)) + add_capacity_constraints(routing, manager, data, demand_evaluator_index) + + # Add Time Window constraint + time_evaluator_index = routing.RegisterTransitCallback( + partial(create_time_evaluator(data), manager)) + add_time_window_constraints(routing, manager, data, time_evaluator_index) + + # Setting first solution heuristic (cheapest addition). + search_parameters = pywrapcp.DefaultRoutingSearchParameters() + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member + # Solve the problem. + assignment = routing.SolveWithParameters(search_parameters) + print_solution(data, manager, routing, assignment) if __name__ == '__main__': - main() + main() diff --git a/examples/python/cvrptw.py b/examples/python/cvrptw.py index 41d2eec1c3f..9491668b76f 100755 --- a/examples/python/cvrptw.py +++ b/examples/python/cvrptw.py @@ -24,7 +24,10 @@ """ from __future__ import print_function + +from functools import partial from six.moves import xrange + from ortools.constraint_solver import pywrapcp from ortools.constraint_solver import routing_enums_pb2 @@ -33,248 +36,256 @@ # Problem Data Definition # ########################### def create_data_model(): - """Stores the data for the problem""" - data = {} - # Locations in block unit - _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)] - # Compute locations in meters using the block dimension defined as follow - # Manhattan average block: 750ft x 264ft -> 228m x 80m - # here we use: 114m x 80m city block - # src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" - data["locations"] = [(l[0] * 114, l[1] * 80) for l in _locations] - data["num_locations"] = len(data["locations"]) - data["time_windows"] = \ - [(0, 0), - (75, 85), (75, 85), # 1, 2 - (60, 70), (45, 55), # 3, 4 - (0, 8), (50, 60), # 5, 6 - (0, 10), (10, 20), # 7, 8 - (0, 10), (75, 85), # 9, 10 - (85, 95), (5, 15), # 11, 12 - (15, 25), (10, 20), # 13, 14 - (45, 55), (30, 40)] # 15, 16 - data["demands"] = \ - [0, # depot - 1, 1, # 1, 2 - 2, 4, # 3, 4 - 2, 4, # 5, 6 - 8, 8, # 7, 8 - 1, 2, # 9,10 - 1, 2, # 11,12 - 4, 4, # 13, 14 - 8, 8] # 15, 16 - data["time_per_demand_unit"] = 5 # 5 minutes/unit - data["num_vehicles"] = 4 - data["vehicle_capacity"] = 15 - data[ - "vehicle_speed"] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min - data["depot"] = 0 - return data + """Stores the data for the problem""" + data = {} + # Locations in block unit + _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)] + # Compute locations in meters using the block dimension defined as follow + # Manhattan average block: 750ft x 264ft -> 228m x 80m + # here we use: 114m x 80m city block + # src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" + data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations] + data['num_locations'] = len(data['locations']) + data['time_windows'] = \ + [(0, 0), + (75, 85), (75, 85), # 1, 2 + (60, 70), (45, 55), # 3, 4 + (0, 8), (50, 60), # 5, 6 + (0, 10), (10, 20), # 7, 8 + (0, 10), (75, 85), # 9, 10 + (85, 95), (5, 15), # 11, 12 + (15, 25), (10, 20), # 13, 14 + (45, 55), (30, 40)] # 15, 16 + data['demands'] = \ + [0, # depot + 1, 1, # 1, 2 + 2, 4, # 3, 4 + 2, 4, # 5, 6 + 8, 8, # 7, 8 + 1, 2, # 9,10 + 1, 2, # 11,12 + 4, 4, # 13, 14 + 8, 8] # 15, 16 + data['time_per_demand_unit'] = 5 # 5 minutes/unit + data['num_vehicles'] = 4 + data['vehicle_capacity'] = 15 + data['vehicle_speed'] = 83 # Travel speed: 5km/h converted in m/min + data['depot'] = 0 + return data ####################### # Problem Constraints # ####################### def manhattan_distance(position_1, position_2): - """Computes the Manhattan distance between two points""" - return (abs(position_1[0] - position_2[0]) + - abs(position_1[1] - position_2[1])) + """Computes the Manhattan distance between two points""" + return ( + abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1])) def create_distance_evaluator(data): - """Creates callback to return distance between points.""" - _distances = {} - # precompute distance between location to have distance callback in O(1) - for from_node in xrange(data["num_locations"]): - _distances[from_node] = {} - for to_node in xrange(data["num_locations"]): - if from_node == to_node: - _distances[from_node][to_node] = 0 - else: - _distances[from_node][to_node] = (manhattan_distance( - data["locations"][from_node], data["locations"][to_node])) - - def distance_evaluator(from_node, to_node): - """Returns the manhattan distance between the two nodes""" - return _distances[from_node][to_node] - - return distance_evaluator + """Creates callback to return distance between points.""" + _distances = {} + # precompute distance between location to have distance callback in O(1) + for from_node in xrange(data['num_locations']): + _distances[from_node] = {} + for to_node in xrange(data['num_locations']): + if from_node == to_node: + _distances[from_node][to_node] = 0 + else: + _distances[from_node][to_node] = ( + manhattan_distance(data['locations'][from_node], + data['locations'][to_node])) + + def distance_evaluator(manager, from_node, to_node): + """Returns the manhattan distance between the two nodes""" + return _distances[manager.IndexToNode(from_node)][manager.IndexToNode( + to_node)] + + return distance_evaluator def create_demand_evaluator(data): - """Creates callback to get demands at each location.""" - _demands = data["demands"] + """Creates callback to get demands at each location.""" + _demands = data['demands'] - def demand_evaluator(from_node, to_node): - """Returns the demand of the current node""" - del to_node - return _demands[from_node] + def demand_evaluator(manager, node): + """Returns the demand of the current node""" + return _demands[manager.IndexToNode(node)] - return demand_evaluator + return demand_evaluator -def add_capacity_constraints(routing, data, demand_evaluator): - """Adds capacity constraint""" - capacity = 'Capacity' - routing.AddDimension( - demand_evaluator, - 0, # null capacity slack - data["vehicle_capacity"], - True, # start cumul to zero - capacity) +def add_capacity_constraints(routing, data, demand_evaluator_index): + """Adds capacity constraint""" + capacity = 'Capacity' + routing.AddDimension( + demand_evaluator_index, + 0, # null capacity slack + data['vehicle_capacity'], + True, # start cumul to zero + capacity) def create_time_evaluator(data): - """Creates callback to get total times between locations.""" - - def service_time(data, node): - """Gets the service time for the specified location.""" - return data["demands"][node] * data["time_per_demand_unit"] - - def travel_time(data, from_node, to_node): - """Gets the travel times between two locations.""" - if from_node == to_node: - travel_time = 0 - else: - travel_time = manhattan_distance( - data["locations"][from_node], - data["locations"][to_node]) / data["vehicle_speed"] - return travel_time - - _total_time = {} - # precompute total time to have time callback in O(1) - for from_node in xrange(data["num_locations"]): - _total_time[from_node] = {} - for to_node in xrange(data["num_locations"]): - if from_node == to_node: - _total_time[from_node][to_node] = 0 - else: - _total_time[from_node][to_node] = int( - service_time(data, from_node) + - travel_time(data, from_node, to_node)) - - def time_evaluator(from_node, to_node): - """Returns the total time between the two nodes""" - return _total_time[from_node][to_node] - - return time_evaluator - - -def add_time_window_constraints(routing, data, time_evaluator): - """Add Global Span constraint""" - time = 'Time' - horizon = 120 - routing.AddDimension( - time_evaluator, - horizon, # allow waiting time - horizon, # maximum time per vehicle - False, # don't force start cumul to zero since we are giving TW to start nodes - 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 = routing.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 xrange(data["num_vehicles"]): - index = routing.Start(vehicle_id) - time_dimension.CumulVar(index).SetRange(data["time_windows"][0][0], - data["time_windows"][0][1]) - routing.AddToAssignment(time_dimension.SlackVar(index)) - # Warning: Slack var is not defined for vehicle's end node - #routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id))) + """Creates callback to get total times between locations.""" + + def service_time(data, node): + """Gets the service time for the specified location.""" + return data['demands'][node] * data['time_per_demand_unit'] + + def travel_time(data, from_node, to_node): + """Gets the travel times between two locations.""" + if from_node == to_node: + travel_time = 0 + else: + travel_time = manhattan_distance( + data['locations'][from_node], + data['locations'][to_node]) / data['vehicle_speed'] + return travel_time + + _total_time = {} + # precompute total time to have time callback in O(1) + for from_node in xrange(data['num_locations']): + _total_time[from_node] = {} + for to_node in xrange(data['num_locations']): + if from_node == to_node: + _total_time[from_node][to_node] = 0 + else: + _total_time[from_node][to_node] = int( + service_time(data, from_node) + + travel_time(data, from_node, to_node)) + + def time_evaluator(manager, from_node, to_node): + """Returns the total time between the two nodes""" + return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode( + to_node)] + + return time_evaluator + + +def add_time_window_constraints(routing, manager, data, time_evaluator_index): + """Add Global Span constraint""" + time = 'Time' + horizon = 120 + routing.AddDimension( + time_evaluator_index, + horizon, # allow waiting time + horizon, # maximum time per vehicle + False, # don't force start cumul to zero since we are giving TW to start nodes + 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 xrange(data['num_vehicles']): + index = routing.Start(vehicle_id) + time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0], + data['time_windows'][0][1]) + routing.AddToAssignment(time_dimension.SlackVar(index)) + # Warning: Slack var is not defined for vehicle's end node + #routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id))) ########### # Printer # ########### -def print_solution(data, routing, assignment): # pylint:disable=too-many-locals - """Prints assignment on console""" - print('Objective: {}'.format(assignment.ObjectiveValue())) - total_distance = 0 - total_load = 0 - total_time = 0 - capacity_dimension = routing.GetDimensionOrDie('Capacity') - time_dimension = routing.GetDimensionOrDie('Time') - for vehicle_id in xrange(data["num_vehicles"]): - index = routing.Start(vehicle_id) - plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) - distance = 0 - while not routing.IsEnd(index): - load_var = capacity_dimension.CumulVar(index) - time_var = time_dimension.CumulVar(index) - slack_var = time_dimension.SlackVar(index) - plan_output += ' {0} Load({1}) Time({2},{3}) Slack({4},{5}) ->'.format( - routing.IndexToNode(index), assignment.Value(load_var), - assignment.Min(time_var), assignment.Max(time_var), - assignment.Min(slack_var), assignment.Max(slack_var)) - previous_index = index - index = assignment.Value(routing.NextVar(index)) - distance += routing.GetArcCostForVehicle(previous_index, index, - vehicle_id) - load_var = capacity_dimension.CumulVar(index) - time_var = time_dimension.CumulVar(index) - slack_var = time_dimension.SlackVar(index) - plan_output += ' {0} Load({1}) Time({2},{3})\n'.format( - routing.IndexToNode(index), assignment.Value(load_var), - assignment.Min(time_var), assignment.Max(time_var)) - plan_output += 'Distance of the route: {0}m\n'.format(distance) - plan_output += 'Load of the route: {}\n'.format( - assignment.Value(load_var)) - plan_output += 'Time of the route: {}\n'.format( - assignment.Value(time_var)) - print(plan_output) - total_distance += distance - total_load += assignment.Value(load_var) - total_time += assignment.Value(time_var) - print('Total Distance of all routes: {0}m'.format(total_distance)) - print('Total Load of all routes: {}'.format(total_load)) - print('Total Time of all routes: {0}min'.format(total_time)) +def print_solution(data, manager, routing, assignment): # pylint:disable=too-many-locals + """Prints assignment on console""" + print('Objective: {}'.format(assignment.ObjectiveValue())) + total_distance = 0 + total_load = 0 + total_time = 0 + capacity_dimension = routing.GetDimensionOrDie('Capacity') + time_dimension = routing.GetDimensionOrDie('Time') + for vehicle_id in xrange(data['num_vehicles']): + index = routing.Start(vehicle_id) + plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) + distance = 0 + while not routing.IsEnd(index): + load_var = capacity_dimension.CumulVar(index) + time_var = time_dimension.CumulVar(index) + slack_var = time_dimension.SlackVar(index) + plan_output += ' {0} Load({1}) Time({2},{3}) Slack({4},{5}) ->'.format( + manager.IndexToNode(index), assignment.Value(load_var), + assignment.Min(time_var), assignment.Max(time_var), + assignment.Min(slack_var), assignment.Max(slack_var)) + previous_index = index + index = assignment.Value(routing.NextVar(index)) + distance += routing.GetArcCostForVehicle(previous_index, index, + vehicle_id) + load_var = capacity_dimension.CumulVar(index) + time_var = time_dimension.CumulVar(index) + slack_var = time_dimension.SlackVar(index) + plan_output += ' {0} Load({1}) Time({2},{3})\n'.format( + manager.IndexToNode(index), assignment.Value(load_var), + assignment.Min(time_var), assignment.Max(time_var)) + plan_output += 'Distance of the route: {0}m\n'.format(distance) + plan_output += 'Load of the route: {}\n'.format(assignment.Value(load_var)) + plan_output += 'Time of the route: {}\n'.format(assignment.Value(time_var)) + print(plan_output) + total_distance += distance + total_load += assignment.Value(load_var) + total_time += assignment.Value(time_var) + print('Total Distance of all routes: {0}m'.format(total_distance)) + print('Total Load of all routes: {}'.format(total_load)) + print('Total Time of all routes: {0}min'.format(total_time)) ######## # Main # ######## def main(): - """Entry point of the program""" - # Instantiate the data problem. - data = create_data_model() - - # Create Routing Model - routing = pywrapcp.RoutingModel(data["num_locations"], - data["num_vehicles"], data["depot"]) - # Define weight of each edge - distance_evaluator = create_distance_evaluator(data) - routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator) - # Add Capacity constraint - demand_evaluator = create_demand_evaluator(data) - add_capacity_constraints(routing, data, demand_evaluator) - # Add Time Window constraint - time_evaluator = create_time_evaluator(data) - add_time_window_constraints(routing, data, time_evaluator) - - # Setting first solution heuristic (cheapest addition). - search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters() - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member - # Solve the problem. - assignment = routing.SolveWithParameters(search_parameters) - print_solution(data, routing, assignment) + """Entry point of the program""" + # Instantiate the data problem. + data = create_data_model() + + # Create the routing index manager + manager = pywrapcp.RoutingIndexManager(data['num_locations'], + data['num_vehicles'], data['depot']) + + # Create Routing Model + routing = pywrapcp.RoutingModel(manager) + + # Define weight of each edge + distance_evaluator_index = routing.RegisterTransitCallback( + partial(create_distance_evaluator(data), manager)) + routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index) + + # Add Capacity constraint + demand_evaluator_index = routing.RegisterUnaryTransitCallback( + partial(create_demand_evaluator(data), manager)) + add_capacity_constraints(routing, data, demand_evaluator_index) + + # Add Time Window constraint + time_evaluator_index = routing.RegisterTransitCallback( + partial(create_time_evaluator(data), manager)) + add_time_window_constraints(routing, manager, data, time_evaluator_index) + + # Setting first solution heuristic (cheapest addition). + search_parameters = pywrapcp.DefaultRoutingSearchParameters() + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member + # Solve the problem. + assignment = routing.SolveWithParameters(search_parameters) + print_solution(data, manager, routing, assignment) if __name__ == '__main__': - main() + main() diff --git a/examples/python/cvrptw_plot.py b/examples/python/cvrptw_plot.py index 480c681a200..a30fb5a8226 100644 --- a/examples/python/cvrptw_plot.py +++ b/examples/python/cvrptw_plot.py @@ -44,7 +44,7 @@ class Customers(): - """ + """ A class that generates and holds customers information. Randomly normally distribute a number of customers and locations within @@ -76,94 +76,94 @@ class Customers(): customers = Customers(num_stops=75, extents=extents) """ - def __init__(self, - extents=None, - center=(53.381393, -1.474611), - box_size=10, - num_stops=100, - min_demand=0, - max_demand=25, - min_tw=1, - max_tw=5): - self.number = num_stops #: The number of customers and depots - #: Location, a named tuple for locations. - Location = namedtuple('Location', ['lat', 'lon']) - if extents is not None: - self.extents = extents #: The lower left and upper right points - #: Location[lat,lon]: the centre point of the area. - self.center = Location( - extents['urcrnrlat'] - - 0.5 * (extents['urcrnrlat'] - extents['llcrnrlat']), - extents['urcrnrlon'] - - 0.5 * (extents['urcrnrlon'] - extents['llcrnrlon'])) - else: - #: Location[lat,lon]: the centre point of the area. - (clat, clon) = self.center = Location(center[0], center[1]) - rad_earth = 6367 # km - circ_earth = np.pi * rad_earth - #: The lower left and upper right points - self.extents = { - 'llcrnrlon': - (clon - - 180 * box_size / (circ_earth * np.cos(np.deg2rad(clat)))), - 'llcrnrlat': - clat - 180 * box_size / circ_earth, - 'urcrnrlon': - (clon + - 180 * box_size / (circ_earth * np.cos(np.deg2rad(clat)))), - 'urcrnrlat': - clat + 180 * box_size / circ_earth - } - # The 'name' of the stop, indexed from 0 to num_stops-1 - stops = np.array(range(0, num_stops)) - # normaly distributed random distribution of stops within the box - stdv = 6 # the number of standard deviations 99.9% will be within +-3 - lats = (self.extents['llcrnrlat'] + np.random.randn(num_stops) * - (self.extents['urcrnrlat'] - self.extents['llcrnrlat']) / stdv) - lons = (self.extents['llcrnrlon'] + np.random.randn(num_stops) * - (self.extents['urcrnrlon'] - self.extents['llcrnrlon']) / stdv) - # uniformly distributed integer demands. - demmands = np.random.randint(min_demand, max_demand, num_stops) - - self.time_horizon = 24 * 60**2 # A 24 hour period. - - # The customers demand min_tw to max_tw hour time window for each - # delivery - time_windows = np.random.random_integers(min_tw * 3600, max_tw * 3600, - num_stops) - # The last time a delivery window can start - latest_time = self.time_horizon - time_windows - start_times = [None for o in time_windows] - stop_times = [None for o in time_windows] - # Make random timedeltas, nominaly from the start of the day. - for idx in range(self.number): - stime = int(np.random.random_integers(0, latest_time[idx])) - start_times[idx] = timedelta(seconds=stime) - stop_times[idx] = ( - start_times[idx] + timedelta(seconds=int(time_windows[idx]))) - # A named tuple for the customer - Customer = namedtuple( - 'Customer', - [ - 'index', # the index of the stop - 'demand', # the demand for the stop - 'lat', # the latitude of the stop - 'lon', # the longitude of the stop - 'tw_open', # timedelta window open - 'tw_close' - ]) # timedelta window cls - - self.customers = [ - Customer(idx, dem, lat, lon, tw_open, tw_close) - for idx, dem, lat, lon, tw_open, tw_close in zip( - stops, demmands, lats, lons, start_times, stop_times) - ] - - # The number of seconds needed to 'unload' 1 unit of goods. - self.service_time_per_dem = 300 # seconds - - def central_start_node(self, invert=False): - """ + def __init__(self, + extents=None, + center=(53.381393, -1.474611), + box_size=10, + num_stops=100, + min_demand=0, + max_demand=25, + min_tw=1, + max_tw=5): + self.number = num_stops #: The number of customers and depots + #: Location, a named tuple for locations. + Location = namedtuple('Location', ['lat', 'lon']) + if extents is not None: + self.extents = extents #: The lower left and upper right points + #: Location[lat,lon]: the centre point of the area. + self.center = Location( + extents['urcrnrlat'] - + 0.5 * (extents['urcrnrlat'] - extents['llcrnrlat']), + extents['urcrnrlon'] - + 0.5 * (extents['urcrnrlon'] - extents['llcrnrlon'])) + else: + #: Location[lat,lon]: the centre point of the area. + (clat, clon) = self.center = Location(center[0], center[1]) + rad_earth = 6367 # km + circ_earth = np.pi * rad_earth + #: The lower left and upper right points + self.extents = { + 'llcrnrlon': ( + clon - 180 * box_size / (circ_earth * np.cos(np.deg2rad(clat)))), + 'llcrnrlat': + clat - 180 * box_size / circ_earth, + 'urcrnrlon': ( + clon + 180 * box_size / (circ_earth * np.cos(np.deg2rad(clat)))), + 'urcrnrlat': + clat + 180 * box_size / circ_earth + } + # The 'name' of the stop, indexed from 0 to num_stops-1 + stops = np.array(range(0, num_stops)) + # normaly distributed random distribution of stops within the box + stdv = 6 # the number of standard deviations 99.9% will be within +-3 + lats = ( + self.extents['llcrnrlat'] + np.random.randn(num_stops) * + (self.extents['urcrnrlat'] - self.extents['llcrnrlat']) / stdv) + lons = ( + self.extents['llcrnrlon'] + np.random.randn(num_stops) * + (self.extents['urcrnrlon'] - self.extents['llcrnrlon']) / stdv) + # uniformly distributed integer demands. + demands = np.random.randint(min_demand, max_demand, num_stops) + + self.time_horizon = 24 * 60**2 # A 24 hour period. + + # The customers demand min_tw to max_tw hour time window for each + # delivery + time_windows = np.random.random_integers(min_tw * 3600, max_tw * 3600, + num_stops) + # The last time a delivery window can start + latest_time = self.time_horizon - time_windows + start_times = [None for o in time_windows] + stop_times = [None for o in time_windows] + # Make random timedeltas, nominaly from the start of the day. + for idx in range(self.number): + stime = int(np.random.random_integers(0, latest_time[idx])) + start_times[idx] = timedelta(seconds=stime) + stop_times[idx] = ( + start_times[idx] + timedelta(seconds=int(time_windows[idx]))) + # A named tuple for the customer + Customer = namedtuple( + 'Customer', + [ + 'index', # the index of the stop + 'demand', # the demand for the stop + 'lat', # the latitude of the stop + 'lon', # the longitude of the stop + 'tw_open', # timedelta window open + 'tw_close' + ]) # timedelta window cls + + self.customers = [ + Customer(idx, dem, lat, lon, tw_open, tw_close) + for idx, dem, lat, lon, tw_open, tw_close in zip( + stops, demands, lats, lons, start_times, stop_times) + ] + + # The number of seconds needed to 'unload' 1 unit of goods. + self.service_time_per_dem = 300 # seconds + + def central_start_node(self, invert=False): + """ Return a random starting node, with probability weighted by distance from the centre of the extents, so that a central starting node is likely. @@ -178,25 +178,25 @@ def central_start_node(self, invert=False): >>> customers.central_start_node(invert=True) 42 """ - num_nodes = len(self.customers) - dist = np.empty((num_nodes, 1)) - for idx_to in range(num_nodes): - dist[idx_to] = self._haversine(self.center.lon, self.center.lat, - self.customers[idx_to].lon, - self.customers[idx_to].lat) - furthest = np.max(dist) - - if invert: - prob = dist * 1.0 / sum(dist) - else: - prob = (furthest - dist * 1.0) / sum(furthest - dist) - indexes = np.array([range(num_nodes)]) - start_node = np.random.choice( - indexes.flatten(), size=1, replace=True, p=prob.flatten()) - return start_node[0] - - def make_distance_mat(self, method='haversine'): - """ + num_nodes = len(self.customers) + dist = np.empty((num_nodes, 1)) + for idx_to in range(num_nodes): + dist[idx_to] = self._haversine(self.center.lon, self.center.lat, + self.customers[idx_to].lon, + self.customers[idx_to].lat) + furthest = np.max(dist) + + if invert: + prob = dist * 1.0 / sum(dist) + else: + prob = (furthest - dist * 1.0) / sum(furthest - dist) + indexes = np.array([range(num_nodes)]) + start_node = np.random.choice( + indexes.flatten(), size=1, replace=True, p=prob.flatten()) + return start_node[0] + + def make_distance_mat(self, method='haversine'): + """ Return a distance matrix and make it a member of Customer, using the method given in the call. Currently only Haversine (GC distance) is implemented, but Manhattan, or using a maps API could be added here. @@ -213,20 +213,20 @@ def make_distance_mat(self, method='haversine'): >>> dist_mat = customers.make_distance_mat(method='manhattan') AssertionError """ - self.distmat = np.zeros((self.number, self.number)) - methods = {'haversine': self._haversine} - assert (method in methods) - for frm_idx in range(self.number): - for to_idx in range(self.number): - if frm_idx != to_idx: - frm_c = self.customers[frm_idx] - to_c = self.customers[to_idx] - self.distmat[frm_idx, to_idx] = self._haversine( - frm_c.lon, frm_c.lat, to_c.lon, to_c.lat) - return (self.distmat) - - def _haversine(self, lon1, lat1, lon2, lat2): - """ + self.distmat = np.zeros((self.number, self.number)) + methods = {'haversine': self._haversine} + assert (method in methods) + for frm_idx in range(self.number): + for to_idx in range(self.number): + if frm_idx != to_idx: + frm_c = self.customers[frm_idx] + to_c = self.customers[to_idx] + self.distmat[frm_idx, to_idx] = self._haversine( + frm_c.lon, frm_c.lat, to_c.lon, to_c.lat) + return (self.distmat) + + def _haversine(self, lon1, lat1, lon2, lat2): + """ Calculate the great circle distance between two points on the earth specified in decimal degrees of latitude and longitude. https://en.wikipedia.org/wiki/Haversine_formula @@ -240,28 +240,28 @@ def _haversine(self, lon1, lat1, lon2, lat2): Returns: the distace in km between pt1 and pt2 """ - # convert decimal degrees to radians - lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2]) + # convert decimal degrees to radians + lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2]) - # haversine formula - dlon = lon2 - lon1 - dlat = lat2 - lat1 - a = (np.sin(dlat / 2)**2 + - np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2)**2) - c = 2 * np.arcsin(np.sqrt(a)) + # haversine formula + dlon = lon2 - lon1 + dlat = lat2 - lat1 + a = ( + np.sin(dlat / 2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2)**2) + c = 2 * np.arcsin(np.sqrt(a)) - # 6367 km is the radius of the Earth - km = 6367 * c - return km + # 6367 km is the radius of the Earth + km = 6367 * c + return km - def get_total_demand(self): - """ + def get_total_demand(self): + """ Return the total demand of all customers. """ - return (sum([c.demand for c in self.customers])) + return (sum([c.demand for c in self.customers])) - def return_dist_callback(self, **kwargs): - """ + def return_dist_callback(self, **kwargs): + """ Return a callback function for the distance matrix. Args: **kwargs: Arbitrary keyword arguments passed on to @@ -271,15 +271,15 @@ def return_dist_callback(self, **kwargs): function: dist_return(a,b) A function that takes the 'from' node index and the 'to' node index and returns the distance in km. """ - self.make_distance_mat(**kwargs) + self.make_distance_mat(**kwargs) - def dist_return(a, b): - return (self.distmat[a][b]) + def dist_return(a, b): + return (self.distmat[a][b]) - return dist_return + return dist_return - def return_dem_callback(self): - """ + def return_dem_callback(self): + """ Return a callback function that gives the demands. Returns: @@ -287,13 +287,13 @@ def return_dem_callback(self): index and the 'to' node index and returns the distance in km. """ - def dem_return(a, b): - return (self.customers[a].demand) + def dem_return(a, b): + return (self.customers[a].demand) - return dem_return + return dem_return - def zero_depot_demands(self, depot): - """ + def zero_depot_demands(self, depot): + """ Zero out the demands and time windows of depot. The Depots do not have demands or time windows so this function clears them. @@ -301,12 +301,12 @@ def zero_depot_demands(self, depot): Examples: >>> customers.zero_depot_demands(5) >>> customers.customers[5].demand == 0 True """ - start_depot = self.customers[depot] - self.customers[depot] = start_depot._replace( - demand=0, tw_open=None, tw_close=None) + start_depot = self.customers[depot] + self.customers[depot] = start_depot._replace( + demand=0, tw_open=None, tw_close=None) - def make_service_time_call_callback(self): - """ + def make_service_time_call_callback(self): + """ Return a callback function that provides the time spent servicing the customer. Here is it proportional to the demand given by self.service_time_per_dem, default 300 seconds per unit demand. @@ -317,13 +317,13 @@ def make_service_time_call_callback(self): """ - def service_time_return(a, b): - return (self.customers[a].demand * self.service_time_per_dem) + def service_time_return(a, b): + return (self.customers[a].demand * self.service_time_per_dem) - return service_time_return + return service_time_return - def make_transit_time_callback(self, speed_kmph=10): - """ + def make_transit_time_callback(self, speed_kmph=10): + """ Creates a callback function for transit time. Assuming an average speed of speed_kmph Args: @@ -335,14 +335,14 @@ def make_transit_time_callback(self, speed_kmph=10): tranit time from a to b. """ - def tranit_time_return(a, b): - return (self.distmat[a][b] / (speed_kmph * 1.0 / 60**2)) + def tranit_time_return(a, b): + return (self.distmat[a][b] / (speed_kmph * 1.0 / 60**2)) - return tranit_time_return + return tranit_time_return class Vehicles(): - """ + """ A Class to create and hold vehicle information. The Vehicles in a CVRPTW problem service the customers and belong to a @@ -363,79 +363,79 @@ class Vehicles(): (Optional [int]): The number of vehicles in a homogenious fleet. """ - def __init__(self, capacity=100, cost=100, number=None): - - Vehicle = namedtuple('Vehicle', ['index', 'capacity', 'cost']) - - if number is None: - self.number = np.size(capacity) - else: - self.number = number - idxs = np.array(range(0, self.number)) - - if np.isscalar(capacity): - capacities = capacity * np.ones_like(idxs) - elif np.size(capacity) != np.size(capacity): - print('capacity is neither scalar, nor the same size as num!') - else: - capacities = capacity - - if np.isscalar(cost): - costs = cost * np.ones_like(idxs) - elif np.size(cost) != self.number: - print(np.size(cost)) - print('cost is neither scalar, nor the same size as num!') - else: - costs = cost - - self.vehicles = [ - Vehicle(idx, capacity, cost) - for idx, capacity, cost in zip(idxs, capacities, costs) - ] - - def get_total_capacity(self): - return (sum([c.capacity for c in self.vehicles])) - - def return_starting_callback(self, customers, sameStartFinish=False): - # create a different starting and finishing depot for each vehicle - self.starts = [ - int(customers.central_start_node()) for o in range(self.number) - ] - if sameStartFinish: - self.ends = self.starts - else: - self.ends = [ - int(customers.central_start_node(invert=True)) - for o in range(self.number) - ] - # the depots will not have demands, so zero them. - for depot in self.starts: - customers.zero_depot_demands(depot) - for depot in self.ends: - customers.zero_depot_demands(depot) - - def start_return(v): - return (self.starts[v]) - - return start_return + def __init__(self, capacity=100, cost=100, number=None): + + Vehicle = namedtuple('Vehicle', ['index', 'capacity', 'cost']) + + if number is None: + self.number = np.size(capacity) + else: + self.number = number + idxs = np.array(range(0, self.number)) + + if np.isscalar(capacity): + capacities = capacity * np.ones_like(idxs) + elif np.size(capacity) != np.size(capacity): + print('capacity is neither scalar, nor the same size as num!') + else: + capacities = capacity + + if np.isscalar(cost): + costs = cost * np.ones_like(idxs) + elif np.size(cost) != self.number: + print(np.size(cost)) + print('cost is neither scalar, nor the same size as num!') + else: + costs = cost + + self.vehicles = [ + Vehicle(idx, capacity, cost) + for idx, capacity, cost in zip(idxs, capacities, costs) + ] + + def get_total_capacity(self): + return (sum([c.capacity for c in self.vehicles])) + + def return_starting_callback(self, customers, sameStartFinish=False): + # create a different starting and finishing depot for each vehicle + self.starts = [ + int(customers.central_start_node()) for o in range(self.number) + ] + if sameStartFinish: + self.ends = self.starts + else: + self.ends = [ + int(customers.central_start_node(invert=True)) + for o in range(self.number) + ] + # the depots will not have demands, so zero them. + for depot in self.starts: + customers.zero_depot_demands(depot) + for depot in self.ends: + customers.zero_depot_demands(depot) + + def start_return(v): + return (self.starts[v]) + + return start_return def discrete_cmap(N, base_cmap=None): - """ + """ Create an N-bin discrete colormap from the specified input map """ - # Note that if base_cmap is a string or None, you can simply do - # return plt.cm.get_cmap(base_cmap, N) - # The following works for string, None, or a colormap instance: + # Note that if base_cmap is a string or None, you can simply do + # return plt.cm.get_cmap(base_cmap, N) + # The following works for string, None, or a colormap instance: - base = plt.cm.get_cmap(base_cmap) - color_list = base(np.linspace(0, 1, N)) - cmap_name = base.name + str(N) - return base.from_list(cmap_name, color_list, N) + base = plt.cm.get_cmap(base_cmap) + color_list = base(np.linspace(0, 1, N)) + cmap_name = base.name + str(N) + return base.from_list(cmap_name, color_list, N) def vehicle_output_string(routing, plan): - """ + """ Return a string displaying the output of the routing instance and assignment (plan). @@ -448,42 +448,42 @@ def vehicle_output_string(routing, plan): (List) dropped: list of dropped orders. """ - dropped = [] - for order in range(routing.Size()): - if (plan.Value(routing.NextVar(order)) == order): - dropped.append(str(order)) - - capacity_dimension = routing.GetDimensionOrDie('Capacity') - time_dimension = routing.GetDimensionOrDie('Time') - plan_output = '' - - for route_number in range(routing.vehicles()): - order = routing.Start(route_number) - plan_output += 'Route {0}:'.format(route_number) - if routing.IsEnd(plan.Value(routing.NextVar(order))): - plan_output += ' Empty \n' - else: - while True: - load_var = capacity_dimension.CumulVar(order) - time_var = time_dimension.CumulVar(order) - plan_output += \ - ' {order} Load({load}) Time({tmin}, {tmax}) -> '.format( - order=order, - load=plan.Value(load_var), - tmin=str(timedelta(seconds=plan.Min(time_var))), - tmax=str(timedelta(seconds=plan.Max(time_var)))) - - if routing.IsEnd(order): - plan_output += ' EndRoute {0}. \n'.format(route_number) - break - order = plan.Value(routing.NextVar(order)) - plan_output += '\n' - - return (plan_output, dropped) + dropped = [] + for order in range(routing.Size()): + if (plan.Value(routing.NextVar(order)) == order): + dropped.append(str(order)) + + capacity_dimension = routing.GetDimensionOrDie('Capacity') + time_dimension = routing.GetDimensionOrDie('Time') + plan_output = '' + + for route_number in range(routing.vehicles()): + order = routing.Start(route_number) + plan_output += 'Route {0}:'.format(route_number) + if routing.IsEnd(plan.Value(routing.NextVar(order))): + plan_output += ' Empty \n' + else: + while True: + load_var = capacity_dimension.CumulVar(order) + time_var = time_dimension.CumulVar(order) + plan_output += \ + ' {order} Load({load}) Time({tmin}, {tmax}) -> '.format( + order=order, + load=plan.Value(load_var), + tmin=str(timedelta(seconds=plan.Min(time_var))), + tmax=str(timedelta(seconds=plan.Max(time_var)))) + + if routing.IsEnd(order): + plan_output += ' EndRoute {0}. \n'.format(route_number) + break + order = plan.Value(routing.NextVar(order)) + plan_output += '\n' + + return (plan_output, dropped) def build_vehicle_route(routing, plan, customers, veh_number): - """ + """ Build a route for a vehicle by starting at the strat node and continuing to the end node. @@ -495,24 +495,24 @@ def build_vehicle_route(routing, plan, customers, veh_number): Returns: (List) route: indexes of the customers for vehicle veh_number """ - veh_used = routing.IsVehicleUsed(plan, veh_number) - print('Vehicle {0} is used {1}'.format(veh_number, veh_used)) - if veh_used: - route = [] - node = routing.Start(veh_number) # Get the starting node index - route.append(customers.customers[routing.IndexToNode(node)]) - while not routing.IsEnd(node): - route.append(customers.customers[routing.IndexToNode(node)]) - node = plan.Value(routing.NextVar(node)) - - route.append(customers.customers[routing.IndexToNode(node)]) - return route - else: - return None + veh_used = routing.IsVehicleUsed(plan, veh_number) + print('Vehicle {0} is used {1}'.format(veh_number, veh_used)) + if veh_used: + route = [] + node = routing.Start(veh_number) # Get the starting node index + route.append(customers.customers[routing.IndexToNode(node)]) + while not routing.IsEnd(node): + route.append(customers.customers[routing.IndexToNode(node)]) + node = plan.Value(routing.NextVar(node)) + + route.append(customers.customers[routing.IndexToNode(node)]) + return route + else: + return None def plot_vehicle_routes(veh_route, ax1, customers, vehicles): - """ + """ Plot the vehicle routes on matplotlib axis ax1. Args: veh_route (dict): a dictionary of routes keyed by vehicle idx. ax1 @@ -520,213 +520,212 @@ def plot_vehicle_routes(veh_route, ax1, customers, vehicles): (Customers): the customers instance. vehicles (Vehicles): the vehicles instance. """ - veh_used = [v for v in veh_route if veh_route[v] is not None] - - cmap = discrete_cmap(vehicles.number + 2, 'nipy_spectral') - - for veh_number in veh_used: - - lats, lons = zip(*[(c.lat, c.lon) for c in veh_route[veh_number]]) - lats = np.array(lats) - lons = np.array(lons) - s_dep = customers.customers[vehicles.starts[veh_number]] - s_fin = customers.customers[vehicles.ends[veh_number]] - ax1.annotate( - 'v({veh}) S @ {node}'.format( - veh=veh_number, node=vehicles.starts[veh_number]), - xy=(s_dep.lon, s_dep.lat), - xytext=(10, 10), - xycoords='data', - textcoords='offset points', - arrowprops=dict( - arrowstyle='->', - connectionstyle='angle3,angleA=90,angleB=0', - shrinkA=0.05), - ) - ax1.annotate( - 'v({veh}) F @ {node}'.format( - veh=veh_number, node=vehicles.ends[veh_number]), - xy=(s_fin.lon, s_fin.lat), - xytext=(10, -20), - xycoords='data', - textcoords='offset points', - arrowprops=dict( - arrowstyle='->', - connectionstyle='angle3,angleA=-90,angleB=0', - shrinkA=0.05), - ) - ax1.plot(lons, lats, 'o', mfc=cmap(veh_number + 1)) - ax1.quiver( - lons[:-1], - lats[:-1], - lons[1:] - lons[:-1], - lats[1:] - lats[:-1], - scale_units='xy', - angles='xy', - scale=1, - color=cmap(veh_number + 1)) + veh_used = [v for v in veh_route if veh_route[v] is not None] + + cmap = discrete_cmap(vehicles.number + 2, 'nipy_spectral') + + for veh_number in veh_used: + + lats, lons = zip(*[(c.lat, c.lon) for c in veh_route[veh_number]]) + lats = np.array(lats) + lons = np.array(lons) + s_dep = customers.customers[vehicles.starts[veh_number]] + s_fin = customers.customers[vehicles.ends[veh_number]] + ax1.annotate( + 'v({veh}) S @ {node}'.format( + veh=veh_number, node=vehicles.starts[veh_number]), + xy=(s_dep.lon, s_dep.lat), + xytext=(10, 10), + xycoords='data', + textcoords='offset points', + arrowprops=dict( + arrowstyle='->', + connectionstyle='angle3,angleA=90,angleB=0', + shrinkA=0.05), + ) + ax1.annotate( + 'v({veh}) F @ {node}'.format( + veh=veh_number, node=vehicles.ends[veh_number]), + xy=(s_fin.lon, s_fin.lat), + xytext=(10, -20), + xycoords='data', + textcoords='offset points', + arrowprops=dict( + arrowstyle='->', + connectionstyle='angle3,angleA=-90,angleB=0', + shrinkA=0.05), + ) + ax1.plot(lons, lats, 'o', mfc=cmap(veh_number + 1)) + ax1.quiver( + lons[:-1], + lats[:-1], + lons[1:] - lons[:-1], + lats[1:] - lats[:-1], + scale_units='xy', + angles='xy', + scale=1, + color=cmap(veh_number + 1)) def main(): - # Create a set of customer, (and depot) stops. - customers = Customers( - num_stops=50, - min_demand=1, - max_demand=15, - box_size=40, - min_tw=3, - max_tw=6) - - # Create callback fns for distances, demands, service and transit-times. - dist_fn = customers.return_dist_callback() - dem_fn = customers.return_dem_callback() - serv_time_fn = customers.make_service_time_call_callback() - transit_time_fn = customers.make_transit_time_callback() - - def tot_time_fn(a, b): - """ + # Create a set of customer, (and depot) stops. + customers = Customers( + num_stops=50, + min_demand=1, + max_demand=15, + box_size=40, + min_tw=3, + max_tw=6) + + # Create callback fns for distances, demands, service and transit-times. + dist_fn = customers.return_dist_callback() + dem_fn = customers.return_dem_callback() + serv_time_fn = customers.make_service_time_call_callback() + transit_time_fn = customers.make_transit_time_callback() + + def tot_time_fn(a, b): + """ The time function we want is both transit time and service time. """ - return serv_time_fn(a, b) + transit_time_fn(a, b) - - # Create a list of inhomgenious vehicle capacities as integer units. - capacity = [50, 75, 100, 125, 150, 175, 200, 250] - - # Create a list of inhomogenious fixed vehicle costs. - cost = [int(100 + 2 * np.sqrt(c)) for c in capacity] - - # Create a set of vehicles, the number set by the length of capacity. - vehicles = Vehicles(capacity=capacity, cost=cost) - - # check to see that the problem is feasible, if we don't have enough - # vehicles to cover the demand, there is no point in going further. - assert (customers.get_total_demand() < vehicles.get_total_capacity()) - - # Set the starting nodes, and create a callback fn for the starting node. - start_fn = vehicles.return_starting_callback( - customers, sameStartFinish=False) - - # Set model parameters - model_parameters = pywrapcp.RoutingModel.DefaultModelParameters() - - # The solver parameters can be accessed from the model parameters. For example : - # model_parameters.solver_parameters.CopyFrom( - # pywrapcp.Solver.DefaultSolverParameters()) - # model_parameters.solver_parameters.trace_propagation = True - - # Make the routing model instance. - routing = pywrapcp.RoutingModel( - customers.number, # int number - vehicles.number, # int number - vehicles.starts, # List of int start depot - vehicles.ends, # List of int end depot - model_parameters) - - parameters = routing.DefaultSearchParameters() - # Setting first solution heuristic (cheapest addition). - parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) - # Disabling Large Neighborhood Search, (this is the default behaviour) - parameters.local_search_operators.use_path_lns = False - parameters.local_search_operators.use_inactive_lns = False - # Routing: forbids use of TSPOpt neighborhood, - parameters.local_search_operators.use_tsp_opt = False - - parameters.time_limit_ms = 10 * 1000 # 10 seconds - parameters.use_light_propagation = False - # parameters.log_search = True - - # Set the cost function (distance callback) for each arc, homogenious for - # all vehicles. - routing.SetArcCostEvaluatorOfAllVehicles(dist_fn) - - # Set vehicle costs for each vehicle, not homogenious. - for veh in vehicles.vehicles: - routing.SetFixedCostOfVehicle(veh.cost, int(veh.index)) - - # Add a dimension for vehicle capacities - null_capacity_slack = 0 - routing.AddDimensionWithVehicleCapacity( - dem_fn, # demand callback - null_capacity_slack, - capacity, # capacity array - True, - 'Capacity') - # Add a dimension for time and a limit on the total time_horizon - routing.AddDimension( - tot_time_fn, # total time function callback - customers.time_horizon, - customers.time_horizon, - True, - 'Time') - - time_dimension = routing.GetDimensionOrDie('Time') - for cust in customers.customers: - if cust.tw_open is not None: - time_dimension.CumulVar(routing.NodeToIndex(cust.index)).SetRange( - cust.tw_open.seconds, cust.tw_close.seconds) - """ + return serv_time_fn(a, b) + transit_time_fn(a, b) + + # Create a list of inhomgenious vehicle capacities as integer units. + capacity = [50, 75, 100, 125, 150, 175, 200, 250] + + # Create a list of inhomogenious fixed vehicle costs. + cost = [int(100 + 2 * np.sqrt(c)) for c in capacity] + + # Create a set of vehicles, the number set by the length of capacity. + vehicles = Vehicles(capacity=capacity, cost=cost) + + # check to see that the problem is feasible, if we don't have enough + # vehicles to cover the demand, there is no point in going further. + assert (customers.get_total_demand() < vehicles.get_total_capacity()) + + # Set the starting nodes, and create a callback fn for the starting node. + start_fn = vehicles.return_starting_callback(customers, sameStartFinish=False) + + # Set model parameters + model_parameters = pywrapcp.RoutingModel.DefaultModelParameters() + + # The solver parameters can be accessed from the model parameters. For example : + # model_parameters.solver_parameters.CopyFrom( + # pywrapcp.Solver.DefaultSolverParameters()) + # model_parameters.solver_parameters.trace_propagation = True + + # Make the routing model instance. + routing = pywrapcp.RoutingModel( + customers.number, # int number + vehicles.number, # int number + vehicles.starts, # List of int start depot + vehicles.ends, # List of int end depot + model_parameters) + + parameters = routing.DefaultSearchParameters() + # Setting first solution heuristic (cheapest addition). + parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) + # Disabling Large Neighborhood Search, (this is the default behaviour) + parameters.local_search_operators.use_path_lns = False + parameters.local_search_operators.use_inactive_lns = False + # Routing: forbids use of TSPOpt neighborhood, + parameters.local_search_operators.use_tsp_opt = False + + parameters.time_limit_ms = 10 * 1000 # 10 seconds + parameters.use_light_propagation = False + # parameters.log_search = True + + # Set the cost function (distance callback) for each arc, homogenious for + # all vehicles. + routing.SetArcCostEvaluatorOfAllVehicles(dist_fn) + + # Set vehicle costs for each vehicle, not homogenious. + for veh in vehicles.vehicles: + routing.SetFixedCostOfVehicle(veh.cost, int(veh.index)) + + # Add a dimension for vehicle capacities + null_capacity_slack = 0 + routing.AddDimensionWithVehicleCapacity( + dem_fn, # demand callback + null_capacity_slack, + capacity, # capacity array + True, + 'Capacity') + # Add a dimension for time and a limit on the total time_horizon + routing.AddDimension( + tot_time_fn, # total time function callback + customers.time_horizon, + customers.time_horizon, + True, + 'Time') + + time_dimension = routing.GetDimensionOrDie('Time') + for cust in customers.customers: + if cust.tw_open is not None: + time_dimension.CumulVar(routing.NodeToIndex(cust.index)).SetRange( + cust.tw_open.seconds, cust.tw_close.seconds) + """ To allow the dropping of orders, we add disjunctions to all the customer nodes. Each disjunction is a list of 1 index, which allows that customer to be active or not, with a penalty if not. The penalty should be larger than the cost of servicing that customer, or it will always be dropped! """ - # To add disjunctions just to the customers, make a list of non-depots. - non_depot = set(range(customers.number)) - non_depot.difference_update(vehicles.starts) - non_depot.difference_update(vehicles.ends) - penalty = 400000 # The cost for dropping a node from the plan. - nodes = [routing.AddDisjunction([int(c)], penalty) for c in non_depot] - - # This is how you would implement partial routes if you already knew part - # of a feasible solution for example: - # partial = np.random.choice(list(non_depot), size=(4,5), replace=False) - - # routing.CloseModel() - # partial_list = [partial[0,:].tolist(), - # partial[1,:].tolist(), - # partial[2,:].tolist(), - # partial[3,:].tolist(), - # [],[],[],[]] - # print(routing.ApplyLocksToAllVehicles(partial_list, False)) - - # Solve the problem ! - assignment = routing.SolveWithParameters(parameters) - - # The rest is all optional for saving, printing or plotting the solution. - if assignment: - # save the assignment, (Google Protobuf format) - save_file_base = os.path.realpath(__file__).split('.')[0] - if routing.WriteAssignment(save_file_base + '_assignment.ass'): - print('succesfully wrote assignment to file ' + save_file_base + - '_assignment.ass') - - print('The Objective Value is {0}'.format(assignment.ObjectiveValue())) - - plan_output, dropped = vehicle_output_string(routing, assignment) - print(plan_output) - print('dropped nodes: ' + ', '.join(dropped)) - - # you could print debug information like this: - # print(routing.DebugOutputAssignment(assignment, 'Capacity')) - - vehicle_routes = {} - for veh in range(vehicles.number): - vehicle_routes[veh] = build_vehicle_route(routing, assignment, - customers, veh) - - # Plotting of the routes in matplotlib. - fig = plt.figure() - ax = fig.add_subplot(111) - # Plot all the nodes as black dots. - clon, clat = zip(*[(c.lon, c.lat) for c in customers.customers]) - ax.plot(clon, clat, 'k.') - # plot the routes as arrows - plot_vehicle_routes(vehicle_routes, ax, customers, vehicles) - - else: - print('No assignment') + # To add disjunctions just to the customers, make a list of non-depots. + non_depot = set(range(customers.number)) + non_depot.difference_update(vehicles.starts) + non_depot.difference_update(vehicles.ends) + penalty = 400000 # The cost for dropping a node from the plan. + nodes = [routing.AddDisjunction([int(c)], penalty) for c in non_depot] + + # This is how you would implement partial routes if you already knew part + # of a feasible solution for example: + # partial = np.random.choice(list(non_depot), size=(4,5), replace=False) + + # routing.CloseModel() + # partial_list = [partial[0,:].tolist(), + # partial[1,:].tolist(), + # partial[2,:].tolist(), + # partial[3,:].tolist(), + # [],[],[],[]] + # print(routing.ApplyLocksToAllVehicles(partial_list, False)) + + # Solve the problem ! + assignment = routing.SolveWithParameters(parameters) + + # The rest is all optional for saving, printing or plotting the solution. + if assignment: + # save the assignment, (Google Protobuf format) + save_file_base = os.path.realpath(__file__).split('.')[0] + if routing.WriteAssignment(save_file_base + '_assignment.ass'): + print('succesfully wrote assignment to file ' + save_file_base + + '_assignment.ass') + + print('The Objective Value is {0}'.format(assignment.ObjectiveValue())) + + plan_output, dropped = vehicle_output_string(routing, assignment) + print(plan_output) + print('dropped nodes: ' + ', '.join(dropped)) + + # you could print debug information like this: + # print(routing.DebugOutputAssignment(assignment, 'Capacity')) + + vehicle_routes = {} + for veh in range(vehicles.number): + vehicle_routes[veh] = build_vehicle_route(routing, assignment, customers, + veh) + + # Plotting of the routes in matplotlib. + fig = plt.figure() + ax = fig.add_subplot(111) + # Plot all the nodes as black dots. + clon, clat = zip(*[(c.lon, c.lat) for c in customers.customers]) + ax.plot(clon, clat, 'k.') + # plot the routes as arrows + plot_vehicle_routes(vehicle_routes, ax, customers, vehicles) + + else: + print('No assignment') if __name__ == '__main__': - main() + main() diff --git a/examples/python/hidato_sat.py b/examples/python/hidato_sat.py index c96d18c3e66..11713f6b578 100644 --- a/examples/python/hidato_sat.py +++ b/examples/python/hidato_sat.py @@ -1,4 +1,4 @@ -# Copyright 2010-2018 Google LLC +# Copyright 2010-2017 Google # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at diff --git a/examples/tests/tsp.py b/examples/python/random_tsp.py old mode 100644 new mode 100755 similarity index 81% rename from examples/tests/tsp.py rename to examples/python/random_tsp.py index 58b7f2094b8..8e5434a563a --- a/examples/tests/tsp.py +++ b/examples/python/random_tsp.py @@ -22,13 +22,15 @@ (forbidden arcs). """ -import random import argparse -from ortools.constraint_solver import pywrapcp -# You need to import routing_enums_pb2 after pywrapcp! +from functools import partial +import random + from ortools.constraint_solver import routing_enums_pb2 +from ortools.constraint_solver import pywrapcp parser = argparse.ArgumentParser() + parser.add_argument( '--tsp_size', default=10, @@ -46,19 +48,16 @@ help='Number of random forbidden connections.') parser.add_argument( '--tsp_random_seed', default=0, type=int, help='Random seed.') -parser.add_argument( - '--light_propagation', - default=False, - type=bool, - help='Use light propagation') # Cost/distance functions. -def Distance(i, j): +def Distance(manager, i, j): """Sample function.""" # Put your distance code here. - return i + j + node_i = manager.IndexToNode(i) + node_j = manager.IndexToNode(j) + return node_i + node_j class RandomMatrix(object): @@ -79,8 +78,9 @@ def __init__(self, size, seed): else: self.matrix[from_node][to_node] = rand.randrange(distance_max) - def Distance(self, from_node, to_node): - return self.matrix[from_node][to_node] + def Distance(self, manager, from_index, to_index): + return self.matrix[manager.IndexToNode(from_index)][manager.IndexToNode( + to_index)] def main(args): @@ -88,25 +88,26 @@ def main(args): if args.tsp_size > 0: # TSP of size args.tsp_size # Second argument = 1 to build a single tour (it's a TSP). - # Nodes are indexed from 0 to parser_tsp_size - 1, by default the start of + # Nodes are indexed from 0 to args_tsp_size - 1, by default the start of # the route is node 0. - routing = pywrapcp.RoutingModel(args.tsp_size, 1, 0) - - search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters() + manager = pywrapcp.RoutingIndexManager(args.tsp_size, 1, 0) + routing = pywrapcp.RoutingModel(manager) + search_parameters = pywrapcp.DefaultRoutingSearchParameters() # Setting first solution heuristic (cheapest addition). search_parameters.first_solution_strategy = ( routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # Setting the cost function. # Put a callback to the distance accessor here. The callback takes two - # arguments (the from and to node inidices) and returns the distance between - # these nodes. - matrix = RandomMatrix(args.tsp_size, args.tsp_random_seed) - matrix_callback = matrix.Distance + # arguments (the from and to node indices) and returns the distance between + # these indices. + cost = 0 if args.tsp_use_random_matrix: - routing.SetArcCostEvaluatorOfAllVehicles(matrix_callback) + matrix = RandomMatrix(args.tsp_size, args.tsp_random_seed) + cost = routing.RegisterTransitCallback(partial(matrix.Distance, manager)) else: - routing.SetArcCostEvaluatorOfAllVehicles(Distance) + cost = routing.RegisterTransitCallback(partial(Distance, manager)) + routing.SetArcCostEvaluatorOfAllVehicles(cost) # Forbid node connections (randomly). rand = random.Random() rand.seed(args.tsp_random_seed) @@ -120,9 +121,6 @@ def main(args): forbidden_connections += 1 # Solve, returns a solution if any. - - -# assignment = routing.SolveWithParameters(search_parameters) assignment = routing.Solve() if assignment: # Solution cost. @@ -142,5 +140,6 @@ def main(args): else: print('Specify an instance greater than 0.') + if __name__ == '__main__': main(parser.parse_args()) diff --git a/examples/python/steel_mill_slab_sat.py b/examples/python/steel_mill_slab_sat.py index 3d36cc1b177..c21ab9ce998 100644 --- a/examples/python/steel_mill_slab_sat.py +++ b/examples/python/steel_mill_slab_sat.py @@ -507,6 +507,13 @@ def collect_valid_slabs(capacities, colors, widths, loss_array, all_colors): print('Collect Valid Slabs...DONE') return collector.all_solutions() + num_orders = len(orders) + num_capacities = len(capacities) + all_slabs = range(num_slabs) + all_colors = range(num_colors) + all_orders = range(len(orders)) + print('Solving steel mill with %i orders, %i slabs, and %i capacities' % + (num_orders, num_slabs, num_capacities - 1)) def steel_mill_slab_with_valid_slabs(problem, break_symmetries, output_proto): """Solves the Steel Mill Slab Problem.""" diff --git a/examples/python/task_allocation_sat.py b/examples/python/task_allocation_sat.py index c3987e10b92..a39ef1ef947 100644 --- a/examples/python/task_allocation_sat.py +++ b/examples/python/task_allocation_sat.py @@ -10,6 +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. + """CP-SAT model for task allocation problem. see http://yetanothermathprogrammingconsultant.blogspot.com/2018/09/minizinc-cpsat-vs-mip.html diff --git a/examples/python/transit_time.py b/examples/python/transit_time.py index daa74606277..c4625600e23 100755 --- a/examples/python/transit_time.py +++ b/examples/python/transit_time.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Display Transit Time + Distances are in meters and time in minutes. Manhattan average block: 750ft x 264ft -> 228m x 80m diff --git a/examples/python/tsp.py b/examples/python/tsp.py index 4b2d47df509..3f4e61cfdda 100755 --- a/examples/python/tsp.py +++ b/examples/python/tsp.py @@ -24,7 +24,10 @@ """ from __future__ import print_function + +from functools import partial from six.moves import xrange + from ortools.constraint_solver import pywrapcp from ortools.constraint_solver import routing_enums_pb2 @@ -33,101 +36,108 @@ # Problem Data Definition # ########################### def create_data_model(): - """Stores the data for the problem""" - data = {} - # Locations in block unit - _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)] - # Compute locations in meters using the block dimension defined as follow - # Manhattan average block: 750ft x 264ft -> 228m x 80m - # here we use: 114m x 80m city block - # src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" - data["locations"] = [(l[0] * 114, l[1] * 80) for l in _locations] - data["num_locations"] = len(data["locations"]) - data["num_vehicles"] = 1 - data["depot"] = 0 - return data + """Stores the data for the problem""" + data = {} + # Locations in block unit + _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)] + # Compute locations in meters using the block dimension defined as follow + # Manhattan average block: 750ft x 264ft -> 228m x 80m + # here we use: 114m x 80m city block + # src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance' + data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations] + data['num_locations'] = len(data['locations']) + data['num_vehicles'] = 1 + data['depot'] = 0 + return data ####################### # Problem Constraints # ####################### def manhattan_distance(position_1, position_2): - """Computes the Manhattan distance between two points""" - return (abs(position_1[0] - position_2[0]) + - abs(position_1[1] - position_2[1])) + """Computes the Manhattan distance between two points""" + return ( + abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1])) def create_distance_evaluator(data): - """Creates callback to return distance between points.""" - _distances = {} - # precompute distance between location to have distance callback in O(1) - for from_node in xrange(data["num_locations"]): - _distances[from_node] = {} - for to_node in xrange(data["num_locations"]): - if from_node == to_node: - _distances[from_node][to_node] = 0 - else: - _distances[from_node][to_node] = (manhattan_distance( - data["locations"][from_node], data["locations"][to_node])) - - def distance_evaluator(from_node, to_node): - """Returns the manhattan distance between the two nodes""" - return _distances[from_node][to_node] - - return distance_evaluator + """Creates callback to return distance between points.""" + _distances = {} + # precompute distance between location to have distance callback in O(1) + for from_node in xrange(data['num_locations']): + _distances[from_node] = {} + for to_node in xrange(data['num_locations']): + if from_node == to_node: + _distances[from_node][to_node] = 0 + else: + _distances[from_node][to_node] = ( + manhattan_distance(data['locations'][from_node], + data['locations'][to_node])) + + def distance_evaluator(manager, from_node, to_node): + """Returns the manhattan distance between the two nodes""" + return _distances[manager.IndexToNode(from_node)][manager.IndexToNode( + to_node)] + + return distance_evaluator ########### # Printer # ########### -def print_solution(routing, assignment): - """Prints assignment on console""" - print('Objective: {}'.format(assignment.ObjectiveValue())) - index = routing.Start(0) - plan_output = 'Route:\n' - distance = 0 - while not routing.IsEnd(index): - plan_output += ' {} ->'.format(routing.IndexToNode(index)) - previous_index = index - index = assignment.Value(routing.NextVar(index)) - distance += routing.GetArcCostForVehicle(previous_index, index, 0) - plan_output += ' {}\n'.format(routing.IndexToNode(index)) - plan_output += 'Distance of the route: {}m\n'.format(distance) - print(plan_output) +def print_solution(routing, manager, assignment): + """Prints assignment on console""" + print('Objective: {}'.format(assignment.ObjectiveValue())) + index = routing.Start(0) + plan_output = 'Route:\n' + distance = 0 + while not routing.IsEnd(index): + plan_output += ' {} ->'.format(manager.IndexToNode(index)) + previous_index = index + index = assignment.Value(routing.NextVar(index)) + distance += routing.GetArcCostForVehicle(previous_index, index, 0) + plan_output += ' {}\n'.format(manager.IndexToNode(index)) + plan_output += 'Distance of the route: {}m\n'.format(distance) + print(plan_output) ######## # Main # ######## def main(): - """Entry point of the program""" - # Instantiate the data problem. - data = create_data_model() - - # Create Routing Model - routing = pywrapcp.RoutingModel(data["num_locations"], - data["num_vehicles"], data["depot"]) - # Define weight of each edge - distance_evaluator = create_distance_evaluator(data) - routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator) - - # Setting first solution heuristic (cheapest addition). - search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters() - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) - # Solve the problem. - assignment = routing.SolveWithParameters(search_parameters) - print_solution(routing, assignment) + """Entry point of the program""" + # Instantiate the data problem. + data = create_data_model() + + # Create the routing index manager + manager = pywrapcp.RoutingIndexManager(data['num_locations'], + data['num_vehicles'], data['depot']) + + # Create Routing Model + routing = pywrapcp.RoutingModel(manager) + + # Define weight of each edge + distance_evaluator_index = routing.RegisterTransitCallback( + partial(create_distance_evaluator(data), manager)) + routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index) + + # Setting first solution heuristic (cheapest addition). + search_parameters = pywrapcp.DefaultRoutingSearchParameters() + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member + # Solve the problem. + assignment = routing.SolveWithParameters(search_parameters) + print_solution(routing, manager, assignment) if __name__ == '__main__': - main() + main() diff --git a/examples/python/vrp.py b/examples/python/vrp.py index bc74006aeae..0ca7c16274b 100755 --- a/examples/python/vrp.py +++ b/examples/python/vrp.py @@ -24,7 +24,10 @@ """ from __future__ import print_function + +from functools import partial from six.moves import xrange + from ortools.constraint_solver import pywrapcp from ortools.constraint_solver import routing_enums_pb2 @@ -33,106 +36,113 @@ # Problem Data Definition # ########################### def create_data_model(): - """Stores the data for the problem""" - data = {} - # Locations in block unit - _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)] - # Compute locations in meters using the block dimension defined as follow - # Manhattan average block: 750ft x 264ft -> 228m x 80m - # here we use: 114m x 80m city block - # src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" - data["locations"] = [(l[0] * 114, l[1] * 80) for l in _locations] - data["num_locations"] = len(data["locations"]) - data["num_vehicles"] = 4 - data["depot"] = 0 - return data + """Stores the data for the problem""" + data = {} + # Locations in block unit + _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)] + # Compute locations in meters using the block dimension defined as follow + # Manhattan average block: 750ft x 264ft -> 228m x 80m + # here we use: 114m x 80m city block + # src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance' + data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations] + data['num_locations'] = len(data['locations']) + data['num_vehicles'] = 4 + data['depot'] = 0 + return data ####################### # Problem Constraints # ####################### def manhattan_distance(position_1, position_2): - """Computes the Manhattan distance between two points""" - return (abs(position_1[0] - position_2[0]) + - abs(position_1[1] - position_2[1])) + """Computes the Manhattan distance between two points""" + return ( + abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1])) def create_distance_evaluator(data): - """Creates callback to return distance between points.""" - _distances = {} - # precompute distance between location to have distance callback in O(1) - for from_node in xrange(data["num_locations"]): - _distances[from_node] = {} - for to_node in xrange(data["num_locations"]): - if from_node == to_node: - _distances[from_node][to_node] = 0 - else: - _distances[from_node][to_node] = (manhattan_distance( - data["locations"][from_node], data["locations"][to_node])) - - def distance_evaluator(from_node, to_node): - """Returns the manhattan distance between the two nodes""" - return _distances[from_node][to_node] - - return distance_evaluator + """Creates callback to return distance between points.""" + _distances = {} + # precompute distance between location to have distance callback in O(1) + for from_node in xrange(data['num_locations']): + _distances[from_node] = {} + for to_node in xrange(data['num_locations']): + if from_node == to_node: + _distances[from_node][to_node] = 0 + else: + _distances[from_node][to_node] = ( + manhattan_distance(data['locations'][from_node], + data['locations'][to_node])) + + def distance_evaluator(manager, from_node, to_node): + """Returns the manhattan distance between the two nodes""" + return _distances[manager.IndexToNode(from_node)][manager.IndexToNode( + to_node)] + + return distance_evaluator ########### # Printer # ########### -def print_solution(data, routing, assignment): - """Prints assignment on console""" - print('Objective: {}'.format(assignment.ObjectiveValue())) - total_distance = 0 - for vehicle_id in xrange(data["num_vehicles"]): - index = routing.Start(vehicle_id) - plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) - distance = 0 - while not routing.IsEnd(index): - plan_output += ' {} ->'.format(routing.IndexToNode(index)) - previous_index = index - index = assignment.Value(routing.NextVar(index)) - distance += routing.GetArcCostForVehicle(previous_index, index, - vehicle_id) - plan_output += ' {}\n'.format(routing.IndexToNode(index)) - plan_output += 'Distance of the route: {}m\n'.format(distance) - print(plan_output) - total_distance += distance - print('Total Distance of all routes: {}m'.format(total_distance)) +def print_solution(data, routing, manager, assignment): # pylint:disable=too-many-locals + """Prints assignment on console""" + print('Objective: {}'.format(assignment.ObjectiveValue())) + total_distance = 0 + for vehicle_id in xrange(data['num_vehicles']): + index = routing.Start(vehicle_id) + plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) + distance = 0 + while not routing.IsEnd(index): + plan_output += ' {} ->'.format(manager.IndexToNode(index)) + previous_index = index + index = assignment.Value(routing.NextVar(index)) + distance += routing.GetArcCostForVehicle(previous_index, index, + vehicle_id) + plan_output += ' {}\n'.format(manager.IndexToNode(index)) + plan_output += 'Distance of the route: {}m\n'.format(distance) + print(plan_output) + total_distance += distance + print('Total Distance of all routes: {}m'.format(total_distance)) ######## # Main # ######## def main(): - """Entry point of the program""" - # Instantiate the data problem. - data = create_data_model() - - # Create Routing Model - routing = pywrapcp.RoutingModel(data["num_locations"], - data["num_vehicles"], data["depot"]) - # Define weight of each edge - distance_evaluator = create_distance_evaluator(data) - routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator) - - # Setting first solution heuristic (cheapest addition). - search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters() - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member - # Solve the problem. - assignment = routing.SolveWithParameters(search_parameters) - print_solution(data, routing, assignment) + """Entry point of the program""" + # Instantiate the data problem. + data = create_data_model() + + # Create the routing index manager + manager = pywrapcp.RoutingIndexManager(data['num_locations'], + data['num_vehicles'], data['depot']) + + # Create Routing Model + routing = pywrapcp.RoutingModel(manager) + + # Define weight of each edge + distance_evaluator_index = routing.RegisterTransitCallback( + partial(create_distance_evaluator(data), manager)) + routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index) + + # Setting first solution heuristic (cheapest addition). + search_parameters = pywrapcp.DefaultRoutingSearchParameters() + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member + # Solve the problem. + assignment = routing.SolveWithParameters(search_parameters) + print_solution(data, routing, manager, assignment) if __name__ == '__main__': - main() + main() diff --git a/examples/python/vrpgs.py b/examples/python/vrpgs.py index e6752170adc..9431a398ac5 100755 --- a/examples/python/vrpgs.py +++ b/examples/python/vrpgs.py @@ -24,7 +24,10 @@ """ from __future__ import print_function + +from functools import partial from six.moves import xrange + from ortools.constraint_solver import pywrapcp from ortools.constraint_solver import routing_enums_pb2 @@ -33,122 +36,129 @@ # Problem Data Definition # ########################### def create_data_model(): - """Stores the data for the problem""" - data = {} - # Locations in block unit - _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)] - # Compute locations in meters using the block dimension defined as follow - # Manhattan average block: 750ft x 264ft -> 228m x 80m - # here we use: 114m x 80m city block - # src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" - data["locations"] = [(l[0] * 114, l[1] * 80) for l in _locations] - data["num_locations"] = len(data["locations"]) - data["num_vehicles"] = 4 - data["depot"] = 0 - return data + """Stores the data for the problem""" + data = {} + # Locations in block unit + _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)] + # Compute locations in meters using the block dimension defined as follow + # Manhattan average block: 750ft x 264ft -> 228m x 80m + # here we use: 114m x 80m city block + # src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance' + data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations] + data['num_locations'] = len(data['locations']) + data['num_vehicles'] = 4 + data['depot'] = 0 + return data ####################### # Problem Constraints # ####################### def manhattan_distance(position_1, position_2): - """Computes the Manhattan distance between two points""" - return (abs(position_1[0] - position_2[0]) + - abs(position_1[1] - position_2[1])) + """Computes the Manhattan distance between two points""" + return ( + abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1])) def create_distance_evaluator(data): - """Creates callback to return distance between points.""" - _distances = {} - # precompute distance between location to have distance callback in O(1) - for from_node in xrange(data["num_locations"]): - _distances[from_node] = {} - for to_node in xrange(data["num_locations"]): - if from_node == to_node: - _distances[from_node][to_node] = 0 - else: - _distances[from_node][to_node] = (manhattan_distance( - data["locations"][from_node], data["locations"][to_node])) - - def distance_evaluator(from_node, to_node): - """Returns the manhattan distance between the two nodes""" - return _distances[from_node][to_node] - - return distance_evaluator - - -def add_distance_dimension(routing, distance_evaluator): - """Add Global Span constraint""" - distance = 'Distance' - routing.AddDimension( - distance_evaluator, - 0, # null slack - 3000, # maximum distance per vehicle - True, # start cumul to zero - distance) - distance_dimension = routing.GetDimensionOrDie(distance) - # Try to minimize the max distance among vehicles. - # /!\ It doesn't mean the standard deviation is minimized - distance_dimension.SetGlobalSpanCostCoefficient(100) + """Creates callback to return distance between points.""" + _distances = {} + # precompute distance between location to have distance callback in O(1) + for from_node in xrange(data['num_locations']): + _distances[from_node] = {} + for to_node in xrange(data['num_locations']): + if from_node == to_node: + _distances[from_node][to_node] = 0 + else: + _distances[from_node][to_node] = ( + manhattan_distance(data['locations'][from_node], + data['locations'][to_node])) + + def distance_evaluator(manager, from_node, to_node): + """Returns the manhattan distance between the two nodes""" + return _distances[manager.IndexToNode(from_node)][manager.IndexToNode( + to_node)] + + return distance_evaluator + + +def add_distance_dimension(routing, distance_evaluator_index): + """Add Global Span constraint""" + distance = 'Distance' + routing.AddDimension( + distance_evaluator_index, + 0, # null slack + 3000, # maximum distance per vehicle + True, # start cumul to zero + distance) + distance_dimension = routing.GetDimensionOrDie(distance) + # Try to minimize the max distance among vehicles. + # /!\ It doesn't mean the standard deviation is minimized + distance_dimension.SetGlobalSpanCostCoefficient(100) ########### # Printer # ########### -def print_solution(data, routing, assignment): - """Prints assignment on console""" - print('Objective: {}'.format(assignment.ObjectiveValue())) - total_distance = 0 - for vehicle_id in xrange(data["num_vehicles"]): - index = routing.Start(vehicle_id) - plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) - distance = 0 - while not routing.IsEnd(index): - plan_output += ' {} ->'.format(routing.IndexToNode(index)) - previous_index = index - index = assignment.Value(routing.NextVar(index)) - distance += routing.GetArcCostForVehicle(previous_index, index, - vehicle_id) - plan_output += ' {}\n'.format(routing.IndexToNode(index)) - plan_output += 'Distance of the route: {}m\n'.format(distance) - print(plan_output) - total_distance += distance - print('Total Distance of all routes: {}m'.format(total_distance)) +def print_solution(data, routing, manager, assignment): # pylint:disable=too-many-locals + """Prints assignment on console""" + print('Objective: {}'.format(assignment.ObjectiveValue())) + total_distance = 0 + for vehicle_id in xrange(data['num_vehicles']): + index = routing.Start(vehicle_id) + plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) + distance = 0 + while not routing.IsEnd(index): + plan_output += ' {} ->'.format(manager.IndexToNode(index)) + previous_index = index + index = assignment.Value(routing.NextVar(index)) + distance += routing.GetArcCostForVehicle(previous_index, index, + vehicle_id) + plan_output += ' {}\n'.format(manager.IndexToNode(index)) + plan_output += 'Distance of the route: {}m\n'.format(distance) + print(plan_output) + total_distance += distance + print('Total Distance of all routes: {}m'.format(total_distance)) ######## # Main # ######## def main(): - """Entry point of the program""" - # Instantiate the data problem. - data = create_data_model() - - # Create Routing Model - routing = pywrapcp.RoutingModel(data["num_locations"], - data["num_vehicles"], data["depot"]) - # Define weight of each edge - distance_evaluator = create_distance_evaluator(data) - routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator) - add_distance_dimension(routing, distance_evaluator) - - # Setting first solution heuristic (cheapest addition). - search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters() - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member - # Solve the problem. - assignment = routing.SolveWithParameters(search_parameters) - print_solution(data, routing, assignment) + """Entry point of the program""" + # Instantiate the data problem. + data = create_data_model() + + # Create the routing index manager + manager = pywrapcp.RoutingIndexManager(data['num_locations'], + data['num_vehicles'], data['depot']) + + # Create Routing Model + routing = pywrapcp.RoutingModel(manager) + + # Define weight of each edge + distance_evaluator_index = routing.RegisterTransitCallback( + partial(create_distance_evaluator(data), manager)) + routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index) + add_distance_dimension(routing, distance_evaluator_index) + + # Setting first solution heuristic (cheapest addition). + search_parameters = pywrapcp.DefaultRoutingSearchParameters() + search_parameters.first_solution_strategy = ( + routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member + # Solve the problem. + assignment = routing.SolveWithParameters(search_parameters) + print_solution(data, routing, manager, assignment) if __name__ == '__main__': - main() + main() diff --git a/examples/tests/ac4r_table_test.cc b/examples/tests/ac4r_table_test.cc deleted file mode 100644 index afaf4f81896..00000000000 --- a/examples/tests/ac4r_table_test.cc +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2011-2012 Jean Charles Régin -// 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. - -#include "ortools/base/hash.h" -#include "ortools/base/map_util.h" -#include "ortools/base/stl_util.h" -#include "ortools/base/random.h" -#include "ortools/constraint_solver/constraint_solveri.h" -#include "ortools/constraint_solver/constraint_solver.h" - -DEFINE_int32(arity, 3, "Arity of tuples"); -DEFINE_int32(upper, 10, "Upper bound of variables, lower is always 0"); -DEFINE_int32(tuples, 1000, "Number of tuples"); -DEFINE_int32(bucket, 64, "Size of buckets"); -DEFINE_bool(ac4, false, "Use AC4 Table only"); - -namespace operations_research { -extern Constraint* BuildAc4TableConstraint(Solver* const solver, - const IntTupleSet& tuples, - const std::vector& vars); - -void RandomFillTable(int num_tuples, - int64 lower, - int64 upper, - IntTupleSet* const tuples) { - ACMRandom rgen(0); // defines a random generator - std::vector vals(tuples->Arity()); - - for(int t = 0; t < num_tuples; ++t){ - for(int i = 0; i < tuples->Arity(); i++){ - int64 rnumber = rgen.Next64(); // generates a 64bits number - vals[i] = (rnumber % (upper - lower + 1)) + lower; - } - tuples->Insert(vals); - } -} - -void TestTable(int arity, int num_tuples, int upper, bool use_ac4r_table) { - if (use_ac4r_table) { - LOG(INFO) << "Creation of a AC4-Regin tuple Table with :"; - } else { - LOG(INFO) << "Creation of a Allowed Assignment Table with :"; - } - LOG(INFO) << " - " << arity << " variables"; - LOG(INFO) << " - " << upper + 1 << " values per domain"; - LOG(INFO) << " - " << num_tuples << " tuples"; - - Solver solver("SolverInBk"); - std::vector vars; - solver.MakeIntVarArray(arity, 0, upper, &vars); - - IntTupleSet table(arity); - RandomFillTable(num_tuples, 0, upper, &table); - LOG(INFO) << "Table is created"; - - Constraint* const ct = use_ac4r_table ? - BuildAc4TableConstraint(&solver, table, vars) : - solver.MakeAllowedAssignments(vars, table); - solver.AddConstraint(ct); - - DecisionBuilder* const db = solver.MakePhase(vars, - Solver::CHOOSE_FIRST_UNBOUND, - Solver::ASSIGN_MIN_VALUE); - - LOG(INFO) << "Start search"; - CycleTimer t; - t.Start(); - solver.NewSearch(db); - int counter = 0; - while(solver.NextSolution()) { - counter++; - } - solver.EndSearch(); - t.Stop(); - - LOG(INFO) << "test time : " << t.GetInUsec() << " micro seconds"; - CHECK_EQ(counter, table.NumTuples()); -} -} // namespace operations_research - - -int main(int argc, char** argv) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - if (!FLAGS_ac4) { - operations_research::TestTable(FLAGS_arity, - FLAGS_tuples, - FLAGS_upper, - false); - } - operations_research::TestTable(FLAGS_arity, - FLAGS_tuples, - FLAGS_upper, - true); - return 0; -} - diff --git a/examples/tests/boolean_test.cc b/examples/tests/boolean_test.cc index b53e4e2be06..c4fc0028159 100644 --- a/examples/tests/boolean_test.cc +++ b/examples/tests/boolean_test.cc @@ -18,7 +18,6 @@ #include "ortools/base/random.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/constraint_solver/constraint_solver.h" -#include "ortools/constraint_solver/model.pb.h" #include "ortools/util/string_array.h" #include "ortools/flatzinc/sat_constraint.h" diff --git a/examples/tests/gcc_test.cc b/examples/tests/gcc_test.cc deleted file mode 100644 index 1b9e373faff..00000000000 --- a/examples/tests/gcc_test.cc +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2011-2012 Google -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/base/hash.h" -#include "ortools/base/map_util.h" -#include "ortools/base/stl_util.h" -#include "ortools/base/random.h" -#include "ortools/constraint_solver/constraint_solveri.h" -#include "ortools/constraint_solver/constraint_solver.h" -#include "ortools/util/string_array.h" - -DEFINE_int32(vars, 3, "Number of variables"); -DEFINE_int32(values, 5, "Number of values"); -DEFINE_int32(slack, 1, "Slack in cardinalities"); -DEFINE_int32(seed, 1, "Random seed"); -DEFINE_int32(offset, 0, "Min value of variables"); - -namespace operations_research { -extern Constraint* MakeGcc(Solver* const solver, - const std::vector& vars, - int64 first_domain_value, - const std::vector& min_occurrences, - const std::vector& max_occurrences); - -extern Constraint* MakeSoftGcc(Solver* const solver, - const std::vector& vars, - int64 min_value, - const std::vector& card_mins, - const std::vector& card_max, - IntVar* const violation_var); - - -static const char* kConstraintName[] = { "Distribute", "Gcc", "SoftGcc" }; - -int64 TestGcc(int num_vars, - int num_values, - int slack, - int seed, - int type, - int offset) { - ACMRandom rgen(seed); // defines a random generator - - std::vector card_min(num_values, 0); - std::vector card_max(num_values, 0); - std::vector values(num_values); - for (int i = 0; i< num_vars - slack; ++i) { - const int index = rgen.Uniform(num_values); - card_min[index]++; - card_max[index]++; - } - for (int i = 0; i < num_values; ++i) { - values[i] = offset + i; - } - for (int i = 0; i < 2 * slack; ++i) { - card_max[rgen.Uniform(num_values)]++; - } - - LOG(INFO) << kConstraintName[type] << " constraint"; - LOG(INFO) << " - num variables = " << num_vars; - LOG(INFO) << " - num values = " << num_values; - LOG(INFO) << " - slack = " << slack; - LOG(INFO) << " - seed = " << seed; - { - std::string tmp; - for (const auto& it : card_min) - tmp += ' ' + it; - LOG(INFO) << " - card_min = [" << tmp << "]"; - } - { - std::string tmp; - for (const auto& it : card_max) - tmp += ' ' + it; - LOG(INFO) << " - card_max = [" << tmp << "]"; - } - - Solver solver("TestGcc"); - std::vector vars; - solver.MakeIntVarArray(num_vars, offset, offset + num_values - 1, "v", &vars); - switch (type) { - case 0: - solver.AddConstraint( - solver.MakeDistribute(vars, values, card_min, card_max)); - break; - case 1: - solver.AddConstraint(MakeGcc(&solver, vars, offset, card_min, card_max)); - break; - case 2: - solver.AddConstraint(MakeSoftGcc(&solver, - vars, - offset, - card_min, - card_max, - solver.MakeIntConst(0))); - break; - default: - LOG(FATAL) << "Constraint type not recognized"; - } - DecisionBuilder* const db = solver.MakePhase(vars, - Solver::CHOOSE_FIRST_UNBOUND, - Solver::ASSIGN_MIN_VALUE); - - LOG(INFO) << "Start search"; - CycleTimer t; - t.Start(); - solver.NewSearch(db); - int counter = 0; - while(solver.NextSolution()) { - counter++; - } - solver.EndSearch(); - t.Stop(); - - LOG(INFO) << "test time : " << t.GetInUsec() << " micro seconds"; - LOG(INFO) << "Found " << counter << " solutions"; - return counter; -} -} // namespace operations_research - -int main(int argc, char** argv) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - const int dis = operations_research::TestGcc(FLAGS_vars, - FLAGS_values, - FLAGS_slack, - FLAGS_seed, - 0, - FLAGS_offset); - const int gcc = operations_research::TestGcc(FLAGS_vars, - FLAGS_values, - FLAGS_slack, - FLAGS_seed, - 1, - FLAGS_offset); - const int soft = operations_research::TestGcc(FLAGS_vars, - FLAGS_values, - FLAGS_slack, - FLAGS_seed, - 2, FLAGS_offset); - if (gcc != dis && gcc != soft && dis == soft) { - LOG(INFO) << "Problem with vars = " << FLAGS_vars - << ", and values = " << FLAGS_values - << ", seed = " << FLAGS_seed - << ", slack = " << FLAGS_slack; - } - return 0; -} diff --git a/examples/tests/issue173.cc b/examples/tests/issue173.cc index 2610a5f8796..4e270ea3c1a 100644 --- a/examples/tests/issue173.cc +++ b/examples/tests/issue173.cc @@ -19,7 +19,7 @@ void SolveLP() { } void BreakLoop() { - for (int i = 0; i < 50000; i++) { + for (int i = 0; i < 500; i++) { SolveLP(); } } diff --git a/examples/tests/lp_test.cc b/examples/tests/lp_test.cc index 0870ad7fd8b..e2602c93daf 100644 --- a/examples/tests/lp_test.cc +++ b/examples/tests/lp_test.cc @@ -18,88 +18,88 @@ #include "ortools/linear_solver/linear_solver.pb.h" namespace operations_research { - void RunLinearProgrammingExample( - MPSolver::OptimizationProblemType optimization_problem_type) { - MPSolver solver("LinearProgrammingExample", optimization_problem_type); - const double infinity = solver.infinity(); - // x and y are continuous non-negative variables. - MPVariable* const x = solver.MakeNumVar(0.0, infinity, "x"); - MPVariable* const y = solver.MakeNumVar(0.0, infinity, "y"); +void RunLinearProgrammingExample( + MPSolver::OptimizationProblemType optimization_problem_type) { + MPSolver solver("LinearProgrammingExample", optimization_problem_type); + const double infinity = solver.infinity(); + // x and y are continuous non-negative variables. + MPVariable* const x = solver.MakeNumVar(0.0, infinity, "x"); + MPVariable* const y = solver.MakeNumVar(0.0, infinity, "y"); - // Objectif function: Maximize 3x + 4y). - MPObjective* const objective = solver.MutableObjective(); - objective->SetCoefficient(x, 3); - objective->SetCoefficient(y, 4); - objective->SetMaximization(); + // Objectif function: Maximize 3x + 4y). + MPObjective* const objective = solver.MutableObjective(); + objective->SetCoefficient(x, 3); + objective->SetCoefficient(y, 4); + objective->SetMaximization(); - // x + 2y <= 14. - MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 14.0); - c0->SetCoefficient(x, 1); - c0->SetCoefficient(y, 2); + // x + 2y <= 14. + MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 14.0); + c0->SetCoefficient(x, 1); + c0->SetCoefficient(y, 2); - // 3x - y >= 0. - MPConstraint* const c1 = solver.MakeRowConstraint(0.0, infinity); - c1->SetCoefficient(x, 3); - c1->SetCoefficient(y, -1); + // 3x - y >= 0. + MPConstraint* const c1 = solver.MakeRowConstraint(0.0, infinity); + c1->SetCoefficient(x, 3); + c1->SetCoefficient(y, -1); - // x - y <= 2. - MPConstraint* const c2 = solver.MakeRowConstraint(-infinity, 2.0); - c2->SetCoefficient(x, 1); - c2->SetCoefficient(y, -1); + // x - y <= 2. + MPConstraint* const c2 = solver.MakeRowConstraint(-infinity, 2.0); + c2->SetCoefficient(x, 1); + c2->SetCoefficient(y, -1); - LOG(INFO) << "Number of variables = " << solver.NumVariables(); - LOG(INFO) << "Number of constraints = " << solver.NumConstraints(); + LOG(INFO) << "Number of variables = " << solver.NumVariables(); + LOG(INFO) << "Number of constraints = " << solver.NumConstraints(); - const MPSolver::ResultStatus result_status = solver.Solve(); - // Check that the problem has an optimal solution. - if (result_status != MPSolver::OPTIMAL) { - LOG(FATAL) << "The problem does not have an optimal solution!"; - } - LOG(INFO) << "Solution:"; - LOG(INFO) << "x = " << x->solution_value(); - LOG(INFO) << "y = " << y->solution_value(); - LOG(INFO) << "Optimal objective value = " << objective->Value(); - LOG(INFO) << ""; - LOG(INFO) << "Advanced usage:"; - LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds"; - LOG(INFO) << "Problem solved in " << solver.iterations() << " iterations"; - LOG(INFO) << "x: reduced cost = " << x->reduced_cost(); - LOG(INFO) << "y: reduced cost = " << y->reduced_cost(); - const std::vector activities = solver.ComputeConstraintActivities(); - LOG(INFO) << "c0: dual value = " << c0->dual_value() - << " activity = " << activities[c0->index()]; - LOG(INFO) << "c1: dual value = " << c1->dual_value() - << " activity = " << activities[c1->index()]; - LOG(INFO) << "c2: dual value = " << c2->dual_value() - << " activity = " << activities[c2->index()]; + const MPSolver::ResultStatus result_status = solver.Solve(); + // Check that the problem has an optimal solution. + if (result_status != MPSolver::OPTIMAL) { + LOG(FATAL) << "The problem does not have an optimal solution!"; } + LOG(INFO) << "Solution:"; + LOG(INFO) << "x = " << x->solution_value(); + LOG(INFO) << "y = " << y->solution_value(); + LOG(INFO) << "Optimal objective value = " << objective->Value(); + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds"; + LOG(INFO) << "Problem solved in " << solver.iterations() << " iterations"; + LOG(INFO) << "x: reduced cost = " << x->reduced_cost(); + LOG(INFO) << "y: reduced cost = " << y->reduced_cost(); + const std::vector activities = solver.ComputeConstraintActivities(); + LOG(INFO) << "c0: dual value = " << c0->dual_value() + << " activity = " << activities[c0->index()]; + LOG(INFO) << "c1: dual value = " << c1->dual_value() + << " activity = " << activities[c1->index()]; + LOG(INFO) << "c2: dual value = " << c2->dual_value() + << " activity = " << activities[c2->index()]; +} - void RunAllExamples() { +void RunAllExamples() { #if defined(USE_GLOP) - LOG(INFO) << "---- Linear programming example with GLOP ----"; - RunLinearProgrammingExample(MPSolver::GLOP_LINEAR_PROGRAMMING); + LOG(INFO) << "---- Linear programming example with GLOP ----"; + RunLinearProgrammingExample(MPSolver::GLOP_LINEAR_PROGRAMMING); #endif // USE_GLOP #if defined(USE_CLP) - LOG(INFO) << "---- Linear programming example with CLP ----"; - RunLinearProgrammingExample(MPSolver::CLP_LINEAR_PROGRAMMING); + LOG(INFO) << "---- Linear programming example with CLP ----"; + RunLinearProgrammingExample(MPSolver::CLP_LINEAR_PROGRAMMING); #endif // USE_CLP #if defined(USE_GLPK) - LOG(INFO) << "---- Linear programming example with GLPK ----"; - RunLinearProgrammingExample(MPSolver::GLPK_LINEAR_PROGRAMMING); + LOG(INFO) << "---- Linear programming example with GLPK ----"; + RunLinearProgrammingExample(MPSolver::GLPK_LINEAR_PROGRAMMING); #endif // USE_GLPK #if defined(USE_SLM) - LOG(INFO) << "---- Linear programming example with Sulum ----"; - RunLinearProgrammingExample(MPSolver::SULUM_LINEAR_PROGRAMMING); + LOG(INFO) << "---- Linear programming example with Sulum ----"; + RunLinearProgrammingExample(MPSolver::SULUM_LINEAR_PROGRAMMING); #endif // USE_SLM #if defined(USE_GUROBI) - LOG(INFO) << "---- Linear programming example with Gurobi ----"; - RunLinearProgrammingExample(MPSolver::GUROBI_LINEAR_PROGRAMMING); + LOG(INFO) << "---- Linear programming example with Gurobi ----"; + RunLinearProgrammingExample(MPSolver::GUROBI_LINEAR_PROGRAMMING); #endif // USE_GUROBI #if defined(USE_CPLEX) - LOG(INFO) << "---- Linear programming example with CPLEX ----"; - RunLinearProgrammingExample(MPSolver::CPLEX_LINEAR_PROGRAMMING); + LOG(INFO) << "---- Linear programming example with CPLEX ----"; + RunLinearProgrammingExample(MPSolver::CPLEX_LINEAR_PROGRAMMING); #endif // USE_CPLEX - } +} } // namespace operations_research int main(int argc, char** argv) { diff --git a/examples/tests/min_max_test.cc b/examples/tests/min_max_test.cc index 2d00925cf23..0daaf210ec5 100644 --- a/examples/tests/min_max_test.cc +++ b/examples/tests/min_max_test.cc @@ -138,7 +138,7 @@ class MinArrayCtTest { SetUp(); std::vector vars; for (int i = 0; i < 1001; ++i) { - vars.push_back(solver_->MakeIntVar(i, 3000 - i, StringPrintf("x%d", i))); + vars.push_back(solver_->MakeIntVar(i, 3000 - i, absl::StrFormat("x%d", i))); } IntExpr* expr = solver_->MakeMin(vars); CHECK_EQ(2000, expr->Max()); @@ -150,7 +150,7 @@ class MinArrayCtTest { std::vector vars; vars.reserve(1001); for (int i = 0; i < 1001; ++i) { - vars.push_back(solver_->MakeIntVar(i, 3000 - i, StringPrintf("x%d", i))); + vars.push_back(solver_->MakeIntVar(i, 3000 - i, absl::StrFormat("x%d", i))); } IntExpr* expr = solver_->MakeMin(vars); CHECK_EQ(2000, expr->Max()); @@ -184,15 +184,15 @@ class MinArrayCtTest { IntExpr* expr = solver_->MakeMin(vars); CHECK_EQ(kint64max, expr->Min()); CHECK_EQ(kint64max, expr->Max()); - vars.push_back(solver_->MakeIntVar(1, 10, StringPrintf("x%d", 0))); + vars.push_back(solver_->MakeIntVar(1, 10, absl::StrFormat("x%d", 0))); expr = solver_->MakeMin(vars); CHECK_EQ(1, expr->Min()); CHECK_EQ(10, expr->Max()); - vars.push_back(solver_->MakeIntVar(1, 9, StringPrintf("x%d", 1))); + vars.push_back(solver_->MakeIntVar(1, 9, absl::StrFormat("x%d", 1))); expr = solver_->MakeMin(vars); CHECK_EQ(1, expr->Min()); CHECK_EQ(9, expr->Max()); - vars.push_back(solver_->MakeIntVar(1, 8, StringPrintf("x%d", 2))); + vars.push_back(solver_->MakeIntVar(1, 8, absl::StrFormat("x%d", 2))); expr = solver_->MakeMin(vars); CHECK_EQ(1, expr->Min()); CHECK_EQ(8, expr->Max()); @@ -312,7 +312,7 @@ class MaxArrayCtTest { std::vector vars; vars.reserve(1001); for (int i = 0; i < 1001; ++i) { - vars.push_back(solver_->MakeIntVar(i, 3000 - i, StringPrintf("x%d", i))); + vars.push_back(solver_->MakeIntVar(i, 3000 - i, absl::StrFormat("x%d", i))); } IntExpr* expr = solver_->MakeMax(vars); CHECK_EQ(3000, expr->Max()); @@ -323,7 +323,7 @@ class MaxArrayCtTest { SetUp(); std::vector vars; for (int i = 0; i < 1001; ++i) { - vars.push_back(solver_->MakeIntVar(i, 3000 - i, StringPrintf("x%d", i))); + vars.push_back(solver_->MakeIntVar(i, 3000 - i, absl::StrFormat("x%d", i))); } IntExpr* expr = solver_->MakeMax(vars); CHECK_EQ(3000, expr->Max()); @@ -356,15 +356,15 @@ class MaxArrayCtTest { IntExpr* expr = solver_->MakeMax(vars); CHECK_EQ(kint64min, expr->Min()); CHECK_EQ(kint64min, expr->Max()); - vars.push_back(solver_->MakeIntVar(1, 10, StringPrintf("x%d", 0))); + vars.push_back(solver_->MakeIntVar(1, 10, absl::StrFormat("x%d", 0))); expr = solver_->MakeMax(vars); CHECK_EQ(1, expr->Min()); CHECK_EQ(10, expr->Max()); - vars.push_back(solver_->MakeIntVar(2, 10, StringPrintf("x%d", 1))); + vars.push_back(solver_->MakeIntVar(2, 10, absl::StrFormat("x%d", 1))); expr = solver_->MakeMax(vars); CHECK_EQ(2, expr->Min()); CHECK_EQ(10, expr->Max()); - vars.push_back(solver_->MakeIntVar(3, 10, StringPrintf("x%d", 2))); + vars.push_back(solver_->MakeIntVar(3, 10, absl::StrFormat("x%d", 2))); expr = solver_->MakeMax(vars); CHECK_EQ(3, expr->Min()); CHECK_EQ(10, expr->Max()); @@ -400,5 +400,3 @@ int main(int argc, char** argv) { return 0; } - - diff --git a/examples/tests/test_cp_api.py b/examples/tests/test_cp_api.py index 84a7165cc91..eb59fcee7f4 100644 --- a/examples/tests/test_cp_api.py +++ b/examples/tests/test_cp_api.py @@ -1,6 +1,5 @@ # Various calls to CP api from python to verify they work. from ortools.constraint_solver import pywrapcp -from ortools.constraint_solver import model_pb2 from ortools.constraint_solver import search_limit_pb2 @@ -48,18 +47,6 @@ def test_limit(): print(limit) -def test_export(): - solver = pywrapcp.Solver('test export') - x = solver.IntVar(1, 10, 'x') - ct = x.Member([1, 2, 3, 5]) - solver.Add(ct) - proto = model_pb2.CPModelProto() - proto.model = 'wrong name' - solver.ExportModel(proto) - print(repr(proto)) - print(str(proto)) - - class SearchMonitorTest(pywrapcp.SearchMonitor): def __init__(self, solver, nexts): diff --git a/examples/tests/testcp.cs b/examples/tests/testcp.cs index 99cf7c0bf62..bbe49de20c7 100644 --- a/examples/tests/testcp.cs +++ b/examples/tests/testcp.cs @@ -595,152 +595,6 @@ static void HoleIteratorTest() { "GetHoles() iterator, or the WhenDomain() demon invocation."); } - static void CpModelTest() { - Solver solver = new Solver("TestConstraint"); - IntVar x = solver.MakeIntVar(0, 10, "x"); - IntVar y = solver.MakeIntVar(0, 10, "y"); - solver.Add(x + y == 5); - CpModel model = solver.ExportModel(); - - Console.WriteLine(model); - - Solver copy = new Solver("loader"); - copy.LoadModel(model); - CpModelLoader loader = copy.ModelLoader(); - IntVar xc = loader.IntegerExpression(0).Var(); - IntVar yc = loader.IntegerExpression(1).Var(); - DecisionBuilder db = copy.MakePhase(new IntVar[]{xc, yc}, - Solver.CHOOSE_FIRST_UNBOUND, - Solver.ASSIGN_MIN_VALUE); - copy.NewSearch(db); - while (copy.NextSolution()) { - Console.WriteLine("xc = " + xc.Value() + ", yx = " + yc.Value()); - } - copy.EndSearch(); - } - - static void CpSimpleLoadModelTest() { - - CpModel expected; - - const string constraintName = "equation"; - const string constraintText = "((x(0..10) + y(0..10)) == 5)"; - - // Make sure that resources are isolated in the using block. - using (var s = new Solver("TestConstraint")) - { - var x = s.MakeIntVar(0, 10, "x"); - var y = s.MakeIntVar(0, 10, "y"); - Check(x.Name() == "x", "x variable name incorrect."); - Check(y.Name() == "y", "y variable name incorrect."); - var c = x + y == 5; - // c.Cst.SetName(constraintName); - // Check(c.Cst.Name() == constraintName, "Constraint name incorrect."); - Check(c.Cst.ToString() == constraintText, "Constraint is incorrect."); - s.Add(c); - expected = s.ExportModel(); - Console.WriteLine("Expected model string after export: {0}", expected); - } - - // While interesting, this is not very useful nor especially typical use case scenario. - using (var s = new Solver("TestConstraint")) - { - s.LoadModel(expected); - Check(s.Constraints() == 1, "Incorrect number of constraints."); - var actual = s.ExportModel(); - Console.WriteLine("Actual model string after load: {0}", actual); - // Even the simple example should PASS this I think, but it is not currently. - Check(expected.ToString() == actual.ToString(), "Model string incorrect."); - var loader = s.ModelLoader(); - var x = loader.IntegerExpression(0).Var(); - var y = loader.IntegerExpression(1).Var(); - Check(!ReferenceEquals(null, x), "x variable not found after loaded model."); - Check(!ReferenceEquals(null, y), "y variable not found after loaded model."); - { - // Sanity check that what we loaded from the Proto is actually correct according to what we expected - var c = x + y == 5; - Check(c.Cst.ToString() == constraintText, "Constraint is incorrect."); - } - var db = s.MakePhase(x, y, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); - s.NewSearch(db); - while (s.NextSolution()) { - Console.WriteLine("x = {0}, y = {1}", x.Value(), y.Value()); - } - s.EndSearch(); - } - } - - static void CpLoadModelAfterSearchTest() { - - CpModel expected; - - const string constraintName = "equation"; - const string constraintText = "((x(0..10) + y(0..10)) == 5)"; - - // Make sure that resources are isolated in the using block. - using (var s = new Solver("TestConstraint")) - { - var x = s.MakeIntVar(0, 10, "x"); - var y = s.MakeIntVar(0, 10, "y"); - Check(x.Name() == "x", "x variable name incorrect."); - Check(y.Name() == "y", "y variable name incorrect."); - var c = x + y == 5; -// c.Cst.SetName(constraintName); -// Check(c.Cst.Name() == constraintName, "Constraint name incorrect."); - Check(c.Cst.ToString() == constraintText, "Constraint is incorrect."); - s.Add(c); - // TODO: TBD: support solution collector? - var db = s.MakePhase(x, y, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); - Check(!ReferenceEquals(null, db), "Expected a valid Decision Builder"); - s.NewSearch(db); - var count = 0; - while (s.NextSolution()) { - Console.WriteLine("x = {0}, y = {1}", x.Value(), y.Value()); - ++count; - // Break after we found at least one solution. - break; - } - s.EndSearch(); - Check(count > 0, "Must find at least one solution."); - // TODO: TBD: export with monitors and/or decision builder? - expected = s.ExportModel(); - Console.WriteLine("Expected model string after export: {0}", expected); - } - - // While interesting, this is not very useful nor especially typical use case scenario. - using (var s = new Solver("TestConstraint")) - { - // TODO: TBD: load with monitors and/or decision builder? - s.LoadModel(expected); - // This is the first test that should PASS when loading; however, it FAILS because the Constraint is NOT loaded as it is a "TrueConstraint()" - Check(s.Constraints() == 1, "Incorrect number of constraints."); - var actual = s.ExportModel(); - Console.WriteLine("Actual model string after load: {0}", actual); - // Should also be correct after re-load, but I suspect isn't even close, but I could be wrong. - Check(expected.ToString() == actual.ToString(), "Model string incorrect."); - var loader = s.ModelLoader(); - var x = loader.IntegerExpression(0).Var(); - var y = loader.IntegerExpression(1).Var(); - Check(!ReferenceEquals(null, x), "x variable not found after loaded model."); - Check(!ReferenceEquals(null, y), "y variable not found after loaded model."); - { - // Do this sanity check that what we loaded is actually what we expected should load. - var c = x + y == 5; - // Further documented verification, provided we got this far, and/or with "proper" unit test Assertions... - Check(c.Cst.ToString() == constraintText, "Constraint is incorrect."); - Check(c.Cst.ToString() != "TrueConstraint()", "Constraint is incorrect."); - } - // TODO: TBD: support solution collector? - // Should pick up where we left off. - var db = s.MakePhase(x, y, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); - s.NewSearch(db); - while (s.NextSolution()) { - Console.WriteLine("x = {0}, y = {1}", x.Value(), y.Value()); - } - s.EndSearch(); - } - } - static void Main() { ConstructorsTest(); ConstraintWithExprTest(); @@ -753,9 +607,6 @@ static void Main() { FailingConstraintTest(); DomainIteratorTest(); HoleIteratorTest(); - CpModelTest(); - CpSimpleLoadModelTest(); - CpLoadModelAfterSearchTest(); if (error_count_ != 0) { Console.WriteLine("Found " + error_count_ + " errors."); Environment.Exit(1); diff --git a/examples/tests/visitor_test.cc b/examples/tests/visitor_test.cc index 3f2e61ba40f..db5363bd695 100644 --- a/examples/tests/visitor_test.cc +++ b/examples/tests/visitor_test.cc @@ -18,7 +18,6 @@ #include "ortools/base/random.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/constraint_solver/constraint_solver.h" -#include "ortools/constraint_solver/model.pb.h" #include "ortools/util/string_array.h" namespace operations_research { @@ -55,119 +54,11 @@ void TestVisitSumEqual() { &interval_variables); } - -void RunExport(CpModel* const model) { - const int total_items = 3; - const int total_bins = 2; - - Solver solver("BinPacking"); - - std::vector vars; - solver.MakeIntVarArray(total_items * total_bins, 0, 1, "vars_", &vars); - - //Contrainte ct1 : un item appartient qu'à un seul bin - for (int i = 0; i < total_items; ++i) { - std::vector item_column(total_bins); - for(int j = 0; j < total_bins; ++j) { - item_column[j] = vars[j + i * total_bins]; - } - solver.AddConstraint(solver.MakeSumEquality(item_column, 1)); - ////////////////////////////////Constraint - } - - //binNo[i(item)] = b <=> xib = 1 - std::vector tabItemInNoBin; - for (int i = 0; i < total_items; ++i) { - std::vector item_column(total_bins); - for(int j = 0; j < total_bins; ++j) { - item_column[j] = vars[j + i * total_bins]; - } - tabItemInNoBin.push_back( - solver.MakeIntVar(0, total_bins - 1)); - - solver.AddConstraint(solver.MakeMapDomain(tabItemInNoBin[i], item_column)); - } - - std::vector items_per_bin; - for(int j = 0; j < total_bins; ++j) { - std::vector bin_column(total_items); - std::vector weights(total_items); - for(int i = 0; i < total_items; ++i) { - bin_column[i] = vars[i + j * total_items]; - weights[i] = i + 1; - } - items_per_bin.push_back(solver.MakeScalProd(bin_column, weights)->Var()); - } - - ////////////////////////// Optimization - std::vector bin_used; - for(int j = 0; j < total_bins; ++j) { - bin_used.push_back(solver.MakeIsGreaterCstVar(items_per_bin[j], 0)->Var()); - } - IntVar* const numNotEmptyBins = - solver.MakeSum(bin_used)->VarWithName("objective"); - - OptimizeVar* const minimizeNumBins = solver.MakeMinimize(numNotEmptyBins, 1); - std::vector monitors; - monitors.push_back(minimizeNumBins); - - //Export the model - *model = solver.ExportModelWithSearchMonitors(monitors); -} - -void TestExport() { - LOG(INFO) << "----- Test Export -----"; - CpModel model; - RunExport(&model); - CHECK(model.has_objective()); -} - -// Test 3 - -bool sortIntVar(const IntVar* x, const IntVar* y) { - return x->name() < y->name(); -} - -void TestImport() { - LOG(INFO) << "----- Test Import -----"; - CpModel model; - RunExport(&model); - Solver solver("BinPacking"); - std::vector monitors; - solver.LoadModelWithSearchMonitors(model, &monitors); - - std::vector primary_integer_variables; - std::vector secondary_integer_variables; - std::vector sequence_variables; - std::vector interval_variables; - solver.CollectDecisionVariables(&primary_integer_variables, - &secondary_integer_variables, - &sequence_variables, - &interval_variables); - std::sort(primary_integer_variables.begin(), - primary_integer_variables.end(), - sortIntVar); - std::vector new_vars; - for (int i = 0; i < primary_integer_variables.size(); ++i) { - if (primary_integer_variables[i]->HasName()) { - new_vars.push_back(primary_integer_variables[i]); - } - } - DecisionBuilder* const db = solver.MakePhase(new_vars, - Solver::CHOOSE_FIRST_UNBOUND, - Solver::ASSIGN_MIN_VALUE); - - solver.NewSearch(db, monitors); - - bool result = solver.NextSolution(); -} } // namespace operations_research int main(int argc, char** argv) { gflags::ParseCommandLineFlags(&argc, &argv, true); operations_research::TestVisitSumEqual(); - operations_research::TestExport(); - operations_research::TestImport(); return 0; } diff --git a/makefiles/Makefile.cpp.mk b/makefiles/Makefile.cpp.mk index 04968bf85f8..cc4f49e19a7 100755 --- a/makefiles/Makefile.cpp.mk +++ b/makefiles/Makefile.cpp.mk @@ -137,8 +137,28 @@ $(OBJ_DIR)/swig: | $(OBJ_DIR) ############### ## CPP LIB ## ############### +# build from: $> grep "pb\.h:" makefiles/Makefile.gen.mk +PROTO_DEPS = \ +$(GEN_DIR)/ortools/util/optional_boolean.pb.h \ +$(GEN_DIR)/ortools/data/jobshop_scheduling.pb.h \ +$(GEN_DIR)/ortools/data/rcpsp.pb.h \ +$(GEN_DIR)/ortools/glop/parameters.pb.h \ +$(GEN_DIR)/ortools/graph/flow_problem.pb.h \ +$(GEN_DIR)/ortools/sat/boolean_problem.pb.h \ +$(GEN_DIR)/ortools/sat/cp_model.pb.h \ +$(GEN_DIR)/ortools/sat/sat_parameters.pb.h \ +$(GEN_DIR)/ortools/bop/bop_parameters.pb.h \ +$(GEN_DIR)/ortools/linear_solver/linear_solver.pb.h \ +$(GEN_DIR)/ortools/constraint_solver/assignment.pb.h \ +$(GEN_DIR)/ortools/constraint_solver/demon_profiler.pb.h \ +$(GEN_DIR)/ortools/constraint_solver/routing_enums.pb.h \ +$(GEN_DIR)/ortools/constraint_solver/routing_parameters.pb.h \ +$(GEN_DIR)/ortools/constraint_solver/search_limit.pb.h \ +$(GEN_DIR)/ortools/constraint_solver/solver_parameters.pb.h include $(OR_ROOT)makefiles/Makefile.gen.mk +all_protos: $(PROTO_DEPS) + # OR Tools unique library. $(OR_TOOLS_LIBS): \ dependencies/check.log \ diff --git a/makefiles/Makefile.gen.mk b/makefiles/Makefile.gen.mk index 8994513e103..1e90772dafd 100644 --- a/makefiles/Makefile.gen.mk +++ b/makefiles/Makefile.gen.mk @@ -56,25 +56,16 @@ BASE_DEPS = \ $(SRC_DIR)/ortools/base/thorough_hash.h \ $(SRC_DIR)/ortools/base/threadpool.h \ $(SRC_DIR)/ortools/base/timer.h \ - $(SRC_DIR)/ortools/base/time_support.h \ $(SRC_DIR)/ortools/base/typeid.h BASE_LIB_OBJS = \ $(OBJ_DIR)/base/bitmap.$O \ $(OBJ_DIR)/base/callback.$O \ $(OBJ_DIR)/base/file.$O \ - $(OBJ_DIR)/base/join.$O \ - $(OBJ_DIR)/base/mutex.$O \ - $(OBJ_DIR)/base/notification.$O \ - $(OBJ_DIR)/base/numbers.$O \ $(OBJ_DIR)/base/random.$O \ $(OBJ_DIR)/base/recordio.$O \ - $(OBJ_DIR)/base/split.$O \ - $(OBJ_DIR)/base/string_view.$O \ - $(OBJ_DIR)/base/stringprintf.$O \ $(OBJ_DIR)/base/sysinfo.$O \ $(OBJ_DIR)/base/threadpool.$O \ - $(OBJ_DIR)/base/time_support.$O \ $(OBJ_DIR)/base/timer.$O objs/base/bitmap.$O: ortools/base/bitmap.cc ortools/base/bitmap.h \ @@ -87,32 +78,11 @@ objs/base/callback.$O: ortools/base/callback.cc ortools/base/callback.h \ ortools/base/macros.h | $(OBJ_DIR)/base $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Scallback.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Scallback.$O -objs/base/file.$O: ortools/base/file.cc ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/integral_types.h \ - ortools/base/logging.h ortools/base/macros.h ortools/base/string_view.h \ - ortools/base/file.h ortools/base/status.h | $(OBJ_DIR)/base +objs/base/file.$O: ortools/base/file.cc ortools/base/file.h \ + ortools/base/integral_types.h ortools/base/logging.h \ + ortools/base/macros.h ortools/base/status.h | $(OBJ_DIR)/base $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Sfile.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Sfile.$O -objs/base/join.$O: ortools/base/join.cc ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/integral_types.h \ - ortools/base/logging.h ortools/base/macros.h ortools/base/string_view.h \ - ortools/base/stringprintf.h | $(OBJ_DIR)/base - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Sjoin.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Sjoin.$O - -objs/base/mutex.$O: ortools/base/mutex.cc ortools/base/mutex.h \ - ortools/base/macros.h | $(OBJ_DIR)/base - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Smutex.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Smutex.$O - -objs/base/notification.$O: ortools/base/notification.cc \ - ortools/base/notification.h | $(OBJ_DIR)/base - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Snotification.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Snotification.$O - -objs/base/numbers.$O: ortools/base/numbers.cc ortools/base/numbers.h \ - ortools/base/integral_types.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/logging.h ortools/base/macros.h \ - ortools/base/string_view.h | $(OBJ_DIR)/base - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Snumbers.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Snumbers.$O - objs/base/random.$O: ortools/base/random.cc ortools/base/hash.h \ ortools/base/basictypes.h ortools/base/integral_types.h \ ortools/base/logging.h ortools/base/macros.h ortools/base/random.h | $(OBJ_DIR)/base @@ -120,23 +90,9 @@ objs/base/random.$O: ortools/base/random.cc ortools/base/hash.h \ objs/base/recordio.$O: ortools/base/recordio.cc ortools/base/recordio.h \ ortools/base/file.h ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/status.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/string_view.h | $(OBJ_DIR)/base + ortools/base/macros.h ortools/base/status.h | $(OBJ_DIR)/base $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Srecordio.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Srecordio.$O -objs/base/split.$O: ortools/base/split.cc ortools/base/split.h \ - ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/string_view.h | $(OBJ_DIR)/base - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Ssplit.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Ssplit.$O - -objs/base/string_view.$O: ortools/base/string_view.cc \ - ortools/base/string_view.h | $(OBJ_DIR)/base - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Sstring_view.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Sstring_view.$O - -objs/base/stringprintf.$O: ortools/base/stringprintf.cc \ - ortools/base/stringprintf.h | $(OBJ_DIR)/base - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Sstringprintf.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Sstringprintf.$O - objs/base/sysinfo.$O: ortools/base/sysinfo.cc ortools/base/sysinfo.h \ ortools/base/basictypes.h ortools/base/integral_types.h \ ortools/base/logging.h ortools/base/macros.h | $(OBJ_DIR)/base @@ -146,13 +102,9 @@ objs/base/threadpool.$O: ortools/base/threadpool.cc \ ortools/base/threadpool.h | $(OBJ_DIR)/base $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Sthreadpool.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Sthreadpool.$O -objs/base/time_support.$O: ortools/base/time_support.cc \ - ortools/base/time_support.h ortools/base/integral_types.h | $(OBJ_DIR)/base - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Stime_support.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Stime_support.$O - objs/base/timer.$O: ortools/base/timer.cc ortools/base/timer.h \ ortools/base/basictypes.h ortools/base/integral_types.h \ - ortools/base/logging.h ortools/base/macros.h ortools/base/time_support.h | $(OBJ_DIR)/base + ortools/base/logging.h ortools/base/macros.h | $(OBJ_DIR)/base $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sbase$Stimer.cc $(OBJ_OUT)$(OBJ_DIR)$Sbase$Stimer.$O PORT_DEPS = \ @@ -166,10 +118,8 @@ PORT_LIB_OBJS = \ $(OBJ_DIR)/port/sysinfo_nonport.$O objs/port/file_nonport.$O: ortools/port/file_nonport.cc \ - ortools/port/file.h ortools/base/status.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/integral_types.h \ - ortools/base/logging.h ortools/base/macros.h ortools/base/string_view.h \ - ortools/base/file.h | $(OBJ_DIR)/port + ortools/port/file.h ortools/base/status.h ortools/base/logging.h \ + ortools/base/integral_types.h ortools/base/macros.h ortools/base/file.h | $(OBJ_DIR)/port $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sport$Sfile_nonport.cc $(OBJ_OUT)$(OBJ_DIR)$Sport$Sfile_nonport.$O objs/port/sysinfo_nonport.$O: ortools/port/sysinfo_nonport.cc \ @@ -244,9 +194,7 @@ objs/util/cached_log.$O: ortools/util/cached_log.cc \ objs/util/file_util.$O: ortools/util/file_util.cc ortools/util/file_util.h \ ortools/base/file.h ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/status.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/string_view.h \ - ortools/base/recordio.h | $(OBJ_DIR)/util + ortools/base/macros.h ortools/base/status.h ortools/base/recordio.h | $(OBJ_DIR)/util $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sutil$Sfile_util.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Sfile_util.$O objs/util/fp_utils.$O: ortools/util/fp_utils.cc ortools/util/fp_utils.h \ @@ -257,9 +205,7 @@ objs/util/fp_utils.$O: ortools/util/fp_utils.cc ortools/util/fp_utils.h \ objs/util/graph_export.$O: ortools/util/graph_export.cc \ ortools/util/graph_export.h ortools/base/file.h \ ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/status.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/string_view.h \ - ortools/base/stringprintf.h | $(OBJ_DIR)/util + ortools/base/macros.h ortools/base/status.h | $(OBJ_DIR)/util $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sutil$Sgraph_export.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Sgraph_export.$O objs/util/piecewise_linear_function.$O: \ @@ -267,13 +213,11 @@ objs/util/piecewise_linear_function.$O: \ ortools/util/piecewise_linear_function.h ortools/base/basictypes.h \ ortools/base/integral_types.h ortools/base/logging.h \ ortools/base/macros.h ortools/util/saturated_arithmetic.h \ - ortools/base/casts.h ortools/util/bitset.h ortools/base/stringprintf.h | $(OBJ_DIR)/util + ortools/util/bitset.h | $(OBJ_DIR)/util $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sutil$Spiecewise_linear_function.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Spiecewise_linear_function.$O objs/util/proto_tools.$O: ortools/util/proto_tools.cc \ - ortools/util/proto_tools.h ortools/base/join.h ortools/base/basictypes.h \ - ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/string_view.h | $(OBJ_DIR)/util + ortools/util/proto_tools.h | $(OBJ_DIR)/util $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sutil$Sproto_tools.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Sproto_tools.$O objs/util/range_query_function.$O: ortools/util/range_query_function.cc \ @@ -296,34 +240,27 @@ objs/util/sigint.$O: ortools/util/sigint.cc ortools/util/sigint.h \ objs/util/sorted_interval_list.$O: ortools/util/sorted_interval_list.cc \ ortools/util/sorted_interval_list.h ortools/base/integral_types.h \ - ortools/base/span.h ortools/base/inlined_vector.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/stringprintf.h \ - ortools/util/saturated_arithmetic.h ortools/base/casts.h \ - ortools/util/bitset.h ortools/base/basictypes.h | $(OBJ_DIR)/util + ortools/base/logging.h ortools/base/macros.h \ + ortools/util/saturated_arithmetic.h ortools/util/bitset.h \ + ortools/base/basictypes.h | $(OBJ_DIR)/util $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sutil$Ssorted_interval_list.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Ssorted_interval_list.$O objs/util/stats.$O: ortools/util/stats.cc ortools/util/stats.h \ ortools/base/timer.h ortools/base/basictypes.h \ ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/time_support.h \ - ortools/base/stringprintf.h ortools/base/stl_util.h \ - ortools/port/sysinfo.h ortools/port/utf8.h ortools/base/encodingutils.h | $(OBJ_DIR)/util + ortools/base/macros.h ortools/base/stl_util.h ortools/port/sysinfo.h \ + ortools/port/utf8.h ortools/base/encodingutils.h | $(OBJ_DIR)/util $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sutil$Sstats.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Sstats.$O objs/util/time_limit.$O: ortools/util/time_limit.cc \ ortools/util/time_limit.h ortools/base/commandlineflags.h \ ortools/base/logging.h ortools/base/integral_types.h \ - ortools/base/macros.h ortools/base/memory.h ortools/base/port.h \ - ortools/base/time_support.h ortools/base/timer.h \ - ortools/base/basictypes.h ortools/util/running_stat.h \ - ortools/base/join.h ortools/base/string_view.h | $(OBJ_DIR)/util + ortools/base/macros.h ortools/base/timer.h ortools/base/basictypes.h \ + ortools/util/running_stat.h | $(OBJ_DIR)/util $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sutil$Stime_limit.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Stime_limit.$O objs/util/xml_helper.$O: ortools/util/xml_helper.cc \ - ortools/util/xml_helper.h ortools/base/macros.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/integral_types.h \ - ortools/base/logging.h ortools/base/string_view.h \ - ortools/base/stringprintf.h ortools/base/strutil.h | $(OBJ_DIR)/util + ortools/util/xml_helper.h ortools/base/macros.h | $(OBJ_DIR)/util $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sutil$Sxml_helper.cc $(OBJ_OUT)$(OBJ_DIR)$Sutil$Sxml_helper.$O ortools/util/optional_boolean.proto: ; @@ -358,25 +295,19 @@ DATA_LIB_OBJS = \ objs/data/jobshop_scheduling_parser.$O: \ ortools/data/jobshop_scheduling_parser.cc \ - ortools/data/jobshop_scheduling_parser.h ortools/base/match.h \ - ortools/base/string_view.h \ + ortools/data/jobshop_scheduling_parser.h \ ortools/gen/ortools/data/jobshop_scheduling.pb.h \ - ortools/base/commandlineflags.h ortools/base/filelineiter.h \ - ortools/base/file.h ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/status.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/stringpiece_utils.h \ - ortools/base/strutil.h ortools/base/split.h ortools/base/stringprintf.h \ - ortools/base/strtoint.h | $(OBJ_DIR)/data + ortools/base/filelineiter.h ortools/base/file.h \ + ortools/base/integral_types.h ortools/base/logging.h \ + ortools/base/macros.h ortools/base/status.h ortools/base/strtoint.h \ + ortools/base/basictypes.h | $(OBJ_DIR)/data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sdata$Sjobshop_scheduling_parser.cc $(OBJ_OUT)$(OBJ_DIR)$Sdata$Sjobshop_scheduling_parser.$O objs/data/rcpsp_parser.$O: ortools/data/rcpsp_parser.cc \ ortools/data/rcpsp_parser.h ortools/base/integral_types.h \ ortools/gen/ortools/data/rcpsp.pb.h ortools/base/filelineiter.h \ ortools/base/file.h ortools/base/logging.h ortools/base/macros.h \ - ortools/base/status.h ortools/base/join.h ortools/base/basictypes.h \ - ortools/base/string_view.h ortools/base/stringpiece_utils.h \ - ortools/base/strutil.h ortools/base/numbers.h ortools/base/split.h \ - ortools/base/strtoint.h | $(OBJ_DIR)/data + ortools/base/status.h ortools/base/strtoint.h ortools/base/basictypes.h | $(OBJ_DIR)/data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sdata$Srcpsp_parser.cc $(OBJ_OUT)$(OBJ_DIR)$Sdata$Srcpsp_parser.$O objs/data/set_covering_data.$O: ortools/data/set_covering_data.cc \ @@ -387,10 +318,7 @@ objs/data/set_covering_parser.$O: ortools/data/set_covering_parser.cc \ ortools/data/set_covering_parser.h ortools/base/integral_types.h \ ortools/data/set_covering_data.h ortools/base/filelineiter.h \ ortools/base/file.h ortools/base/logging.h ortools/base/macros.h \ - ortools/base/status.h ortools/base/join.h ortools/base/basictypes.h \ - ortools/base/string_view.h ortools/base/stringpiece_utils.h \ - ortools/base/strutil.h ortools/base/numbers.h ortools/base/split.h \ - ortools/base/strtoint.h | $(OBJ_DIR)/data + ortools/base/status.h ortools/base/strtoint.h ortools/base/basictypes.h | $(OBJ_DIR)/data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sdata$Sset_covering_parser.cc $(OBJ_OUT)$(OBJ_DIR)$Sdata$Sset_covering_parser.$O ortools/data/jobshop_scheduling.proto: ; @@ -463,9 +391,8 @@ objs/lp_data/lp_data.$O: ortools/lp_data/lp_data.cc \ ortools/util/bitset.h ortools/lp_data/sparse.h \ ortools/lp_data/permutation.h ortools/base/random.h \ ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ - ortools/graph/iterators.h ortools/util/fp_utils.h ortools/base/join.h \ - ortools/base/string_view.h ortools/lp_data/lp_print_utils.h \ + ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ + ortools/util/fp_utils.h ortools/lp_data/lp_print_utils.h \ ortools/lp_data/lp_utils.h ortools/base/accurate_sum.h \ ortools/lp_data/matrix_utils.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Slp_data.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Slp_data.$O @@ -479,13 +406,11 @@ objs/lp_data/lp_data_utils.$O: ortools/lp_data/lp_data_utils.cc \ ortools/util/bitset.h ortools/lp_data/sparse.h \ ortools/lp_data/permutation.h ortools/base/random.h \ ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ - ortools/graph/iterators.h ortools/util/fp_utils.h \ - ortools/lp_data/matrix_scaler.h ortools/glop/revised_simplex.h \ - ortools/glop/basis_representation.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/glop/status.h ortools/util/stats.h \ - ortools/base/timer.h ortools/base/time_support.h \ + ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ + ortools/util/fp_utils.h ortools/lp_data/matrix_scaler.h \ + ortools/glop/revised_simplex.h ortools/glop/basis_representation.h \ + ortools/glop/lu_factorization.h ortools/glop/markowitz.h \ + ortools/glop/status.h ortools/util/stats.h ortools/base/timer.h \ ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ ortools/base/accurate_sum.h ortools/glop/dual_edge_norms.h \ ortools/glop/entering_variable.h ortools/glop/primal_edge_norms.h \ @@ -493,32 +418,29 @@ objs/lp_data/lp_data_utils.$O: ortools/lp_data/lp_data_utils.cc \ ortools/glop/reduced_costs.h ortools/util/random_engine.h \ ortools/glop/variable_values.h ortools/lp_data/lp_print_utils.h \ ortools/lp_data/sparse_row.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h \ - ortools/util/running_stat.h | $(OBJ_DIR)/lp_data + ortools/base/commandlineflags.h ortools/util/running_stat.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Slp_data_utils.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Slp_data_utils.$O objs/lp_data/lp_decomposer.$O: ortools/lp_data/lp_decomposer.cc \ - ortools/lp_data/lp_decomposer.h ortools/base/mutex.h \ - ortools/base/macros.h ortools/lp_data/lp_data.h ortools/base/hash.h \ - ortools/base/basictypes.h ortools/base/integral_types.h \ - ortools/base/logging.h ortools/base/int_type.h \ + ortools/lp_data/lp_decomposer.h ortools/lp_data/lp_data.h \ + ortools/base/hash.h ortools/base/basictypes.h \ + ortools/base/integral_types.h ortools/base/logging.h \ + ortools/base/macros.h ortools/base/int_type.h \ ortools/base/int_type_indexed_vector.h \ ortools/gen/ortools/glop/parameters.pb.h ortools/lp_data/lp_types.h \ ortools/util/bitset.h ortools/lp_data/sparse.h \ ortools/lp_data/permutation.h ortools/base/random.h \ ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ - ortools/graph/iterators.h ortools/util/fp_utils.h \ - ortools/algorithms/dynamic_partition.h ortools/lp_data/lp_utils.h \ - ortools/base/accurate_sum.h | $(OBJ_DIR)/lp_data + ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ + ortools/util/fp_utils.h ortools/algorithms/dynamic_partition.h \ + ortools/lp_data/lp_utils.h ortools/base/accurate_sum.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Slp_decomposer.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Slp_decomposer.$O objs/lp_data/lp_print_utils.$O: ortools/lp_data/lp_print_utils.cc \ ortools/lp_data/lp_print_utils.h ortools/base/integral_types.h \ - ortools/base/stringprintf.h ortools/lp_data/lp_types.h \ - ortools/base/basictypes.h ortools/base/logging.h ortools/base/macros.h \ - ortools/base/int_type.h ortools/base/int_type_indexed_vector.h \ - ortools/util/bitset.h ortools/base/join.h ortools/base/string_view.h \ + ortools/lp_data/lp_types.h ortools/base/basictypes.h \ + ortools/base/logging.h ortools/base/macros.h ortools/base/int_type.h \ + ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ ortools/util/rational_approximation.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Slp_print_utils.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Slp_print_utils.$O @@ -536,9 +458,8 @@ objs/lp_data/lp_utils.$O: ortools/lp_data/lp_utils.cc \ ortools/base/macros.h ortools/base/int_type.h \ ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/lp_data/permutation.h ortools/base/random.h \ - ortools/util/return_macros.h | $(OBJ_DIR)/lp_data + ortools/graph/iterators.h ortools/lp_data/permutation.h \ + ortools/base/random.h ortools/util/return_macros.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Slp_utils.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Slp_utils.$O objs/lp_data/matrix_scaler.$O: ortools/lp_data/matrix_scaler.cc \ @@ -547,14 +468,13 @@ objs/lp_data/matrix_scaler.$O: ortools/lp_data/matrix_scaler.cc \ ortools/base/integral_types.h ortools/gen/ortools/glop/parameters.pb.h \ ortools/glop/revised_simplex.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/glop/status.h ortools/lp_data/lp_types.h \ - ortools/base/basictypes.h ortools/util/bitset.h ortools/lp_data/sparse.h \ + ortools/glop/markowitz.h ortools/glop/status.h \ + ortools/lp_data/lp_types.h ortools/base/basictypes.h \ + ortools/util/bitset.h ortools/lp_data/sparse.h \ ortools/lp_data/permutation.h ortools/base/random.h \ ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ - ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/glop/rank_one_update.h \ + ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ + ortools/util/stats.h ortools/base/timer.h ortools/glop/rank_one_update.h \ ortools/lp_data/lp_utils.h ortools/base/accurate_sum.h \ ortools/glop/dual_edge_norms.h ortools/lp_data/lp_data.h \ ortools/base/hash.h ortools/util/fp_utils.h \ @@ -563,8 +483,7 @@ objs/lp_data/matrix_scaler.$O: ortools/lp_data/matrix_scaler.cc \ ortools/glop/reduced_costs.h ortools/util/random_engine.h \ ortools/glop/variable_values.h ortools/lp_data/lp_print_utils.h \ ortools/lp_data/sparse_row.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h \ - ortools/util/running_stat.h | $(OBJ_DIR)/lp_data + ortools/base/commandlineflags.h ortools/util/running_stat.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Smatrix_scaler.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Smatrix_scaler.$O objs/lp_data/matrix_utils.$O: ortools/lp_data/matrix_utils.cc \ @@ -575,8 +494,7 @@ objs/lp_data/matrix_utils.$O: ortools/lp_data/matrix_utils.cc \ ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ ortools/base/random.h ortools/util/return_macros.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/base/hash.h | $(OBJ_DIR)/lp_data + ortools/graph/iterators.h ortools/base/hash.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Smatrix_utils.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Smatrix_utils.$O objs/lp_data/model_reader.$O: ortools/lp_data/model_reader.cc \ @@ -591,9 +509,8 @@ objs/lp_data/model_reader.$O: ortools/lp_data/model_reader.cc \ ortools/util/bitset.h ortools/lp_data/sparse.h \ ortools/lp_data/permutation.h ortools/base/random.h \ ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ - ortools/graph/iterators.h ortools/util/fp_utils.h ortools/base/file.h \ - ortools/base/status.h ortools/base/join.h ortools/base/string_view.h \ + ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ + ortools/util/fp_utils.h ortools/base/file.h ortools/base/status.h \ ortools/lp_data/mps_reader.h ortools/base/commandlineflags.h \ ortools/base/map_util.h ortools/lp_data/proto_utils.h \ ortools/util/file_util.h ortools/base/recordio.h | $(OBJ_DIR)/lp_data @@ -605,17 +522,14 @@ objs/lp_data/mps_reader.$O: ortools/lp_data/mps_reader.cc \ ortools/base/integral_types.h ortools/base/logging.h \ ortools/base/macros.h ortools/base/int_type.h \ ortools/base/int_type_indexed_vector.h ortools/base/map_util.h \ - ortools/base/stringprintf.h ortools/lp_data/lp_data.h \ - ortools/gen/ortools/glop/parameters.pb.h ortools/lp_data/lp_types.h \ - ortools/util/bitset.h ortools/lp_data/sparse.h \ - ortools/lp_data/permutation.h ortools/base/random.h \ - ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ - ortools/util/fp_utils.h ortools/base/callback.h ortools/base/file.h \ - ortools/base/status.h ortools/base/join.h ortools/base/string_view.h \ - ortools/base/filelineiter.h ortools/base/stringpiece_utils.h \ - ortools/base/strutil.h ortools/base/match.h ortools/base/numbers.h \ - ortools/base/split.h ortools/lp_data/lp_print_utils.h | $(OBJ_DIR)/lp_data + ortools/lp_data/lp_data.h ortools/gen/ortools/glop/parameters.pb.h \ + ortools/lp_data/lp_types.h ortools/util/bitset.h \ + ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ + ortools/base/random.h ortools/util/return_macros.h \ + ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ + ortools/graph/iterators.h ortools/util/fp_utils.h ortools/base/file.h \ + ortools/base/status.h ortools/base/filelineiter.h \ + ortools/lp_data/lp_print_utils.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Smps_reader.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Smps_reader.$O objs/lp_data/proto_utils.$O: ortools/lp_data/proto_utils.cc \ @@ -630,8 +544,8 @@ objs/lp_data/proto_utils.$O: ortools/lp_data/proto_utils.cc \ ortools/util/bitset.h ortools/lp_data/sparse.h \ ortools/lp_data/permutation.h ortools/base/random.h \ ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ - ortools/graph/iterators.h ortools/util/fp_utils.h | $(OBJ_DIR)/lp_data + ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ + ortools/util/fp_utils.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Sproto_utils.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Sproto_utils.$O objs/lp_data/sparse.$O: ortools/lp_data/sparse.cc ortools/lp_data/sparse.h \ @@ -641,19 +555,17 @@ objs/lp_data/sparse.$O: ortools/lp_data/sparse.cc ortools/lp_data/sparse.h \ ortools/util/bitset.h ortools/lp_data/permutation.h \ ortools/base/random.h ortools/util/return_macros.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/base/join.h ortools/base/string_view.h | $(OBJ_DIR)/lp_data + ortools/graph/iterators.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Ssparse.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Ssparse.$O objs/lp_data/sparse_column.$O: ortools/lp_data/sparse_column.cc \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/stringprintf.h \ - ortools/graph/iterators.h ortools/lp_data/lp_types.h \ - ortools/base/basictypes.h ortools/base/int_type.h \ - ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ - ortools/lp_data/permutation.h ortools/base/random.h \ - ortools/util/return_macros.h | $(OBJ_DIR)/lp_data + ortools/base/macros.h ortools/graph/iterators.h \ + ortools/lp_data/lp_types.h ortools/base/basictypes.h \ + ortools/base/int_type.h ortools/base/int_type_indexed_vector.h \ + ortools/util/bitset.h ortools/lp_data/permutation.h \ + ortools/base/random.h ortools/util/return_macros.h | $(OBJ_DIR)/lp_data $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Slp_data$Ssparse_column.cc $(OBJ_OUT)$(OBJ_DIR)$Slp_data$Ssparse_column.$O GLOP_DEPS = \ @@ -697,16 +609,14 @@ objs/glop/basis_representation.$O: ortools/glop/basis_representation.cc \ ortools/glop/basis_representation.h ortools/base/logging.h \ ortools/base/integral_types.h ortools/base/macros.h \ ortools/glop/lu_factorization.h ortools/glop/markowitz.h \ - ortools/base/inlined_vector.h ortools/base/port.h \ ortools/gen/ortools/glop/parameters.pb.h ortools/glop/status.h \ ortools/lp_data/lp_types.h ortools/base/basictypes.h \ ortools/base/int_type.h ortools/base/int_type_indexed_vector.h \ ortools/util/bitset.h ortools/lp_data/sparse.h \ ortools/lp_data/permutation.h ortools/base/random.h \ ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ - ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/glop/rank_one_update.h \ + ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ + ortools/util/stats.h ortools/base/timer.h ortools/glop/rank_one_update.h \ ortools/lp_data/lp_utils.h ortools/base/accurate_sum.h \ ortools/base/stl_util.h | $(OBJ_DIR)/glop $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sglop$Sbasis_representation.cc $(OBJ_OUT)$(OBJ_DIR)$Sglop$Sbasis_representation.$O @@ -715,16 +625,14 @@ objs/glop/dual_edge_norms.$O: ortools/glop/dual_edge_norms.cc \ ortools/glop/dual_edge_norms.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/gen/ortools/glop/parameters.pb.h \ + ortools/glop/markowitz.h ortools/gen/ortools/glop/parameters.pb.h \ ortools/glop/status.h ortools/lp_data/lp_types.h \ ortools/base/basictypes.h ortools/base/int_type.h \ ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ ortools/base/random.h ortools/util/return_macros.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ + ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ ortools/base/accurate_sum.h ortools/lp_data/lp_data.h \ ortools/base/hash.h ortools/util/fp_utils.h | $(OBJ_DIR)/glop @@ -734,23 +642,20 @@ objs/glop/entering_variable.$O: ortools/glop/entering_variable.cc \ ortools/glop/entering_variable.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/gen/ortools/glop/parameters.pb.h \ + ortools/glop/markowitz.h ortools/gen/ortools/glop/parameters.pb.h \ ortools/glop/status.h ortools/lp_data/lp_types.h \ ortools/base/basictypes.h ortools/base/int_type.h \ ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ ortools/base/random.h ortools/util/return_macros.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ + ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ ortools/base/accurate_sum.h ortools/glop/primal_edge_norms.h \ ortools/glop/update_row.h ortools/glop/variables_info.h \ ortools/lp_data/lp_data.h ortools/base/hash.h ortools/util/fp_utils.h \ ortools/glop/reduced_costs.h ortools/util/random_engine.h \ - ortools/port/proto_utils.h ortools/base/join.h \ - ortools/base/string_view.h | $(OBJ_DIR)/glop + ortools/port/proto_utils.h | $(OBJ_DIR)/glop $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sglop$Sentering_variable.cc $(OBJ_OUT)$(OBJ_DIR)$Sglop$Sentering_variable.$O objs/glop/initial_basis.$O: ortools/glop/initial_basis.cc \ @@ -763,12 +668,10 @@ objs/glop/initial_basis.$O: ortools/glop/initial_basis.cc \ ortools/util/bitset.h ortools/lp_data/sparse.h \ ortools/lp_data/permutation.h ortools/base/random.h \ ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ - ortools/graph/iterators.h ortools/util/fp_utils.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/glop/status.h ortools/util/stats.h \ - ortools/base/timer.h ortools/base/time_support.h \ - ortools/lp_data/lp_utils.h ortools/base/accurate_sum.h | $(OBJ_DIR)/glop + ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ + ortools/util/fp_utils.h ortools/glop/markowitz.h ortools/glop/status.h \ + ortools/util/stats.h ortools/base/timer.h ortools/lp_data/lp_utils.h \ + ortools/base/accurate_sum.h | $(OBJ_DIR)/glop $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sglop$Sinitial_basis.cc $(OBJ_OUT)$(OBJ_DIR)$Sglop$Sinitial_basis.$O objs/glop/lp_solver.$O: ortools/glop/lp_solver.cc ortools/glop/lp_solver.h \ @@ -776,27 +679,24 @@ objs/glop/lp_solver.$O: ortools/glop/lp_solver.cc ortools/glop/lp_solver.h \ ortools/glop/revised_simplex.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/glop/status.h ortools/lp_data/lp_types.h \ - ortools/base/basictypes.h ortools/base/int_type.h \ - ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ - ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ - ortools/base/random.h ortools/util/return_macros.h \ - ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ - ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ - ortools/base/accurate_sum.h ortools/glop/dual_edge_norms.h \ - ortools/lp_data/lp_data.h ortools/base/hash.h ortools/util/fp_utils.h \ + ortools/glop/markowitz.h ortools/glop/status.h \ + ortools/lp_data/lp_types.h ortools/base/basictypes.h \ + ortools/base/int_type.h ortools/base/int_type_indexed_vector.h \ + ortools/util/bitset.h ortools/lp_data/sparse.h \ + ortools/lp_data/permutation.h ortools/base/random.h \ + ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ + ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ + ortools/util/stats.h ortools/base/timer.h ortools/glop/rank_one_update.h \ + ortools/lp_data/lp_utils.h ortools/base/accurate_sum.h \ + ortools/glop/dual_edge_norms.h ortools/lp_data/lp_data.h \ + ortools/base/hash.h ortools/util/fp_utils.h \ ortools/glop/entering_variable.h ortools/glop/primal_edge_norms.h \ ortools/glop/update_row.h ortools/glop/variables_info.h \ ortools/glop/reduced_costs.h ortools/util/random_engine.h \ ortools/glop/variable_values.h ortools/lp_data/lp_print_utils.h \ ortools/lp_data/sparse_row.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h \ - ortools/util/running_stat.h ortools/lp_data/matrix_scaler.h \ - ortools/base/join.h ortools/base/string_view.h ortools/base/strutil.h \ - ortools/lp_data/proto_utils.h \ + ortools/base/commandlineflags.h ortools/util/running_stat.h \ + ortools/lp_data/matrix_scaler.h ortools/lp_data/proto_utils.h \ ortools/gen/ortools/linear_solver/linear_solver.pb.h \ ortools/gen/ortools/util/optional_boolean.pb.h ortools/util/file_util.h \ ortools/base/file.h ortools/base/status.h ortools/base/recordio.h | $(OBJ_DIR)/glop @@ -804,33 +704,29 @@ objs/glop/lp_solver.$O: ortools/glop/lp_solver.cc ortools/glop/lp_solver.h \ objs/glop/lu_factorization.$O: ortools/glop/lu_factorization.cc \ ortools/glop/lu_factorization.h ortools/glop/markowitz.h \ - ortools/base/inlined_vector.h ortools/base/logging.h \ - ortools/base/integral_types.h ortools/base/macros.h ortools/base/port.h \ - ortools/gen/ortools/glop/parameters.pb.h ortools/glop/status.h \ - ortools/lp_data/lp_types.h ortools/base/basictypes.h \ - ortools/base/int_type.h ortools/base/int_type_indexed_vector.h \ - ortools/util/bitset.h ortools/lp_data/sparse.h \ - ortools/lp_data/permutation.h ortools/base/random.h \ - ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ + ortools/base/logging.h ortools/base/integral_types.h \ + ortools/base/macros.h ortools/gen/ortools/glop/parameters.pb.h \ + ortools/glop/status.h ortools/lp_data/lp_types.h \ + ortools/base/basictypes.h ortools/base/int_type.h \ + ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ + ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ + ortools/base/random.h ortools/util/return_macros.h \ + ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/lp_data/lp_utils.h \ - ortools/base/accurate_sum.h | $(OBJ_DIR)/glop + ortools/lp_data/lp_utils.h ortools/base/accurate_sum.h | $(OBJ_DIR)/glop $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sglop$Slu_factorization.cc $(OBJ_OUT)$(OBJ_DIR)$Sglop$Slu_factorization.$O objs/glop/markowitz.$O: ortools/glop/markowitz.cc ortools/glop/markowitz.h \ - ortools/base/inlined_vector.h ortools/base/logging.h \ - ortools/base/integral_types.h ortools/base/macros.h ortools/base/port.h \ - ortools/gen/ortools/glop/parameters.pb.h ortools/glop/status.h \ - ortools/lp_data/lp_types.h ortools/base/basictypes.h \ - ortools/base/int_type.h ortools/base/int_type_indexed_vector.h \ - ortools/util/bitset.h ortools/lp_data/sparse.h \ - ortools/lp_data/permutation.h ortools/base/random.h \ - ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ + ortools/base/logging.h ortools/base/integral_types.h \ + ortools/base/macros.h ortools/gen/ortools/glop/parameters.pb.h \ + ortools/glop/status.h ortools/lp_data/lp_types.h \ + ortools/base/basictypes.h ortools/base/int_type.h \ + ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ + ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ + ortools/base/random.h ortools/util/return_macros.h \ + ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/lp_data/lp_utils.h \ - ortools/base/accurate_sum.h | $(OBJ_DIR)/glop + ortools/lp_data/lp_utils.h ortools/base/accurate_sum.h | $(OBJ_DIR)/glop $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sglop$Smarkowitz.cc $(OBJ_OUT)$(OBJ_DIR)$Sglop$Smarkowitz.$O objs/glop/preprocessor.$O: ortools/glop/preprocessor.cc \ @@ -838,42 +734,39 @@ objs/glop/preprocessor.$O: ortools/glop/preprocessor.cc \ ortools/glop/revised_simplex.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/glop/status.h ortools/lp_data/lp_types.h \ - ortools/base/basictypes.h ortools/base/int_type.h \ - ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ - ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ - ortools/base/random.h ortools/util/return_macros.h \ - ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ - ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ - ortools/base/accurate_sum.h ortools/glop/dual_edge_norms.h \ - ortools/lp_data/lp_data.h ortools/base/hash.h ortools/util/fp_utils.h \ + ortools/glop/markowitz.h ortools/glop/status.h \ + ortools/lp_data/lp_types.h ortools/base/basictypes.h \ + ortools/base/int_type.h ortools/base/int_type_indexed_vector.h \ + ortools/util/bitset.h ortools/lp_data/sparse.h \ + ortools/lp_data/permutation.h ortools/base/random.h \ + ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ + ortools/lp_data/sparse_vector.h ortools/graph/iterators.h \ + ortools/util/stats.h ortools/base/timer.h ortools/glop/rank_one_update.h \ + ortools/lp_data/lp_utils.h ortools/base/accurate_sum.h \ + ortools/glop/dual_edge_norms.h ortools/lp_data/lp_data.h \ + ortools/base/hash.h ortools/util/fp_utils.h \ ortools/glop/entering_variable.h ortools/glop/primal_edge_norms.h \ ortools/glop/update_row.h ortools/glop/variables_info.h \ ortools/glop/reduced_costs.h ortools/util/random_engine.h \ ortools/glop/variable_values.h ortools/lp_data/lp_print_utils.h \ ortools/lp_data/sparse_row.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h \ - ortools/util/running_stat.h ortools/lp_data/matrix_scaler.h \ - ortools/lp_data/lp_data_utils.h ortools/lp_data/matrix_utils.h | $(OBJ_DIR)/glop + ortools/base/commandlineflags.h ortools/util/running_stat.h \ + ortools/lp_data/matrix_scaler.h ortools/lp_data/lp_data_utils.h \ + ortools/lp_data/matrix_utils.h | $(OBJ_DIR)/glop $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sglop$Spreprocessor.cc $(OBJ_OUT)$(OBJ_DIR)$Sglop$Spreprocessor.$O objs/glop/primal_edge_norms.$O: ortools/glop/primal_edge_norms.cc \ ortools/glop/primal_edge_norms.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/gen/ortools/glop/parameters.pb.h \ + ortools/glop/markowitz.h ortools/gen/ortools/glop/parameters.pb.h \ ortools/glop/status.h ortools/lp_data/lp_types.h \ ortools/base/basictypes.h ortools/base/int_type.h \ ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ ortools/base/random.h ortools/util/return_macros.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ + ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ ortools/base/accurate_sum.h ortools/glop/update_row.h \ ortools/glop/variables_info.h ortools/lp_data/lp_data.h \ @@ -884,16 +777,14 @@ objs/glop/reduced_costs.$O: ortools/glop/reduced_costs.cc \ ortools/glop/reduced_costs.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/gen/ortools/glop/parameters.pb.h \ + ortools/glop/markowitz.h ortools/gen/ortools/glop/parameters.pb.h \ ortools/glop/status.h ortools/lp_data/lp_types.h \ ortools/base/basictypes.h ortools/base/int_type.h \ ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ ortools/base/random.h ortools/util/return_macros.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ + ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ ortools/base/accurate_sum.h ortools/glop/primal_edge_norms.h \ ortools/glop/update_row.h ortools/glop/variables_info.h \ @@ -905,16 +796,14 @@ objs/glop/revised_simplex.$O: ortools/glop/revised_simplex.cc \ ortools/glop/revised_simplex.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/gen/ortools/glop/parameters.pb.h \ + ortools/glop/markowitz.h ortools/gen/ortools/glop/parameters.pb.h \ ortools/glop/status.h ortools/lp_data/lp_types.h \ ortools/base/basictypes.h ortools/base/int_type.h \ ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ ortools/base/random.h ortools/util/return_macros.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ + ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ ortools/base/accurate_sum.h ortools/glop/dual_edge_norms.h \ ortools/lp_data/lp_data.h ortools/base/hash.h ortools/util/fp_utils.h \ @@ -923,10 +812,8 @@ objs/glop/revised_simplex.$O: ortools/glop/revised_simplex.cc \ ortools/glop/reduced_costs.h ortools/util/random_engine.h \ ortools/glop/variable_values.h ortools/lp_data/lp_print_utils.h \ ortools/lp_data/sparse_row.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h \ - ortools/util/running_stat.h ortools/base/join.h \ - ortools/base/string_view.h ortools/glop/initial_basis.h \ - ortools/lp_data/matrix_utils.h | $(OBJ_DIR)/glop + ortools/base/commandlineflags.h ortools/util/running_stat.h \ + ortools/glop/initial_basis.h ortools/lp_data/matrix_utils.h | $(OBJ_DIR)/glop $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sglop$Srevised_simplex.cc $(OBJ_OUT)$(OBJ_DIR)$Sglop$Srevised_simplex.$O objs/glop/status.$O: ortools/glop/status.cc ortools/glop/status.h \ @@ -938,16 +825,14 @@ objs/glop/update_row.$O: ortools/glop/update_row.cc \ ortools/glop/update_row.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/gen/ortools/glop/parameters.pb.h \ + ortools/glop/markowitz.h ortools/gen/ortools/glop/parameters.pb.h \ ortools/glop/status.h ortools/lp_data/lp_types.h \ ortools/base/basictypes.h ortools/base/int_type.h \ ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ ortools/base/random.h ortools/util/return_macros.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ + ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ ortools/base/accurate_sum.h ortools/glop/variables_info.h | $(OBJ_DIR)/glop $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sglop$Supdate_row.cc $(OBJ_OUT)$(OBJ_DIR)$Sglop$Supdate_row.$O @@ -956,16 +841,14 @@ objs/glop/variable_values.$O: ortools/glop/variable_values.cc \ ortools/glop/variable_values.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/glop/lu_factorization.h \ - ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/gen/ortools/glop/parameters.pb.h \ + ortools/glop/markowitz.h ortools/gen/ortools/glop/parameters.pb.h \ ortools/glop/status.h ortools/lp_data/lp_types.h \ ortools/base/basictypes.h ortools/base/int_type.h \ ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ ortools/base/random.h ortools/util/return_macros.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ + ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ ortools/base/accurate_sum.h ortools/glop/variables_info.h | $(OBJ_DIR)/glop $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sglop$Svariable_values.cc $(OBJ_OUT)$(OBJ_DIR)$Sglop$Svariable_values.$O @@ -978,7 +861,7 @@ objs/glop/variables_info.$O: ortools/glop/variables_info.cc \ ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ ortools/base/random.h ortools/util/return_macros.h \ ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ - ortools/base/stringprintf.h ortools/graph/iterators.h | $(OBJ_DIR)/glop + ortools/graph/iterators.h | $(OBJ_DIR)/glop $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sglop$Svariables_info.cc $(OBJ_OUT)$(OBJ_DIR)$Sglop$Svariables_info.$O ortools/glop/parameters.proto: ; @@ -1034,17 +917,15 @@ GRAPH_LIB_OBJS = \ objs/graph/assignment.$O: ortools/graph/assignment.cc \ ortools/graph/assignment.h ortools/graph/ebert_graph.h \ - ortools/base/integral_types.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/logging.h ortools/base/macros.h \ - ortools/base/string_view.h ortools/base/stringprintf.h \ - ortools/util/permutation.h ortools/util/zvector.h \ + ortools/base/integral_types.h ortools/base/logging.h \ + ortools/base/macros.h ortools/util/permutation.h ortools/util/zvector.h \ ortools/base/commandlineflags.h ortools/graph/linear_assignment.h | $(OBJ_DIR)/graph $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sgraph$Sassignment.cc $(OBJ_OUT)$(OBJ_DIR)$Sgraph$Sassignment.$O objs/graph/astar.$O: ortools/graph/astar.cc \ ortools/base/adjustable_priority_queue.h ortools/base/basictypes.h \ ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/callback.h | $(OBJ_DIR)/graph + ortools/base/macros.h | $(OBJ_DIR)/graph $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sgraph$Sastar.cc $(OBJ_OUT)$(OBJ_DIR)$Sgraph$Sastar.$O objs/graph/bellman_ford.$O: ortools/graph/bellman_ford.cc \ @@ -1053,12 +934,11 @@ objs/graph/bellman_ford.$O: ortools/graph/bellman_ford.cc \ objs/graph/cliques.$O: ortools/graph/cliques.cc ortools/graph/cliques.h \ ortools/base/int_type.h ortools/base/macros.h \ - ortools/base/int_type_indexed_vector.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/integral_types.h \ - ortools/base/logging.h ortools/base/string_view.h \ - ortools/util/time_limit.h ortools/base/commandlineflags.h \ - ortools/base/memory.h ortools/base/port.h ortools/base/time_support.h \ - ortools/base/timer.h ortools/util/running_stat.h ortools/base/hash.h | $(OBJ_DIR)/graph + ortools/base/int_type_indexed_vector.h ortools/base/logging.h \ + ortools/base/integral_types.h ortools/util/time_limit.h \ + ortools/base/commandlineflags.h ortools/base/timer.h \ + ortools/base/basictypes.h ortools/util/running_stat.h \ + ortools/base/hash.h | $(OBJ_DIR)/graph $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sgraph$Scliques.cc $(OBJ_OUT)$(OBJ_DIR)$Sgraph$Scliques.$O objs/graph/connected_components.$O: ortools/graph/connected_components.cc \ @@ -1076,33 +956,27 @@ objs/graph/dijkstra.$O: ortools/graph/dijkstra.cc \ objs/graph/linear_assignment.$O: ortools/graph/linear_assignment.cc \ ortools/graph/linear_assignment.h ortools/base/commandlineflags.h \ ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/stringprintf.h \ - ortools/graph/ebert_graph.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/string_view.h \ + ortools/base/macros.h ortools/graph/ebert_graph.h \ ortools/util/permutation.h ortools/util/zvector.h | $(OBJ_DIR)/graph $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sgraph$Slinear_assignment.cc $(OBJ_OUT)$(OBJ_DIR)$Sgraph$Slinear_assignment.$O objs/graph/max_flow.$O: ortools/graph/max_flow.cc ortools/graph/max_flow.h \ ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/graph/ebert_graph.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/string_view.h \ - ortools/base/stringprintf.h ortools/util/permutation.h \ - ortools/util/zvector.h ortools/gen/ortools/graph/flow_problem.pb.h \ - ortools/graph/graph.h ortools/base/port.h ortools/graph/iterators.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ - ortools/base/memory.h ortools/graph/graphs.h | $(OBJ_DIR)/graph + ortools/base/macros.h ortools/graph/ebert_graph.h \ + ortools/util/permutation.h ortools/util/zvector.h \ + ortools/gen/ortools/graph/flow_problem.pb.h ortools/graph/graph.h \ + ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ + ortools/base/basictypes.h ortools/graph/graphs.h | $(OBJ_DIR)/graph $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sgraph$Smax_flow.cc $(OBJ_OUT)$(OBJ_DIR)$Sgraph$Smax_flow.$O objs/graph/min_cost_flow.$O: ortools/graph/min_cost_flow.cc \ ortools/graph/min_cost_flow.h ortools/base/integral_types.h \ ortools/base/logging.h ortools/base/macros.h ortools/graph/ebert_graph.h \ - ortools/base/join.h ortools/base/basictypes.h ortools/base/string_view.h \ - ortools/base/stringprintf.h ortools/util/permutation.h \ - ortools/util/zvector.h ortools/graph/graph.h ortools/base/port.h \ + ortools/util/permutation.h ortools/util/zvector.h ortools/graph/graph.h \ ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/base/commandlineflags.h \ - ortools/base/mathutil.h ortools/base/casts.h ortools/graph/graphs.h \ - ortools/graph/max_flow.h ortools/gen/ortools/graph/flow_problem.pb.h | $(OBJ_DIR)/graph + ortools/base/basictypes.h ortools/base/commandlineflags.h \ + ortools/base/mathutil.h ortools/graph/graphs.h ortools/graph/max_flow.h \ + ortools/gen/ortools/graph/flow_problem.pb.h | $(OBJ_DIR)/graph $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sgraph$Smin_cost_flow.cc $(OBJ_OUT)$(OBJ_DIR)$Sgraph$Smin_cost_flow.$O objs/graph/shortestpaths.$O: ortools/graph/shortestpaths.cc \ @@ -1116,7 +990,7 @@ objs/graph/util.$O: ortools/graph/util.cc ortools/graph/util.h \ ortools/base/integral_types.h ortools/base/logging.h \ ortools/base/macros.h ortools/base/map_util.h \ ortools/graph/connected_components.h ortools/base/ptr_util.h \ - ortools/graph/graph.h ortools/base/port.h ortools/graph/iterators.h | $(OBJ_DIR)/graph + ortools/graph/graph.h ortools/graph/iterators.h | $(OBJ_DIR)/graph $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Sgraph$Sutil.cc $(OBJ_OUT)$(OBJ_DIR)$Sgraph$Sutil.$O ortools/graph/flow_problem.proto: ; @@ -1153,10 +1027,8 @@ ALGORITHMS_LIB_OBJS = \ objs/algorithms/dynamic_partition.$O: \ ortools/algorithms/dynamic_partition.cc \ ortools/algorithms/dynamic_partition.h ortools/base/logging.h \ - ortools/base/integral_types.h ortools/base/macros.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/string_view.h \ - ortools/base/murmur.h ortools/base/thorough_hash.h \ - ortools/base/stringprintf.h | $(OBJ_DIR)/algorithms + ortools/base/integral_types.h ortools/base/macros.h \ + ortools/base/murmur.h ortools/base/thorough_hash.h | $(OBJ_DIR)/algorithms $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Salgorithms$Sdynamic_partition.cc $(OBJ_OUT)$(OBJ_DIR)$Salgorithms$Sdynamic_partition.$O objs/algorithms/dynamic_permutation.$O: \ @@ -1172,16 +1044,13 @@ objs/algorithms/find_graph_symmetries.$O: \ ortools/algorithms/dynamic_partition.h ortools/base/logging.h \ ortools/base/integral_types.h ortools/base/macros.h \ ortools/algorithms/dynamic_permutation.h ortools/base/status.h \ - ortools/base/join.h ortools/base/basictypes.h ortools/base/string_view.h \ - ortools/base/time_support.h ortools/graph/graph.h ortools/base/port.h \ - ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ - ortools/util/time_limit.h ortools/base/commandlineflags.h \ - ortools/base/memory.h ortools/util/running_stat.h \ + ortools/graph/graph.h ortools/graph/iterators.h ortools/util/stats.h \ + ortools/base/timer.h ortools/base/basictypes.h ortools/util/time_limit.h \ + ortools/base/commandlineflags.h ortools/util/running_stat.h \ ortools/algorithms/dense_doubly_linked_list.h \ ortools/algorithms/sparse_permutation.h ortools/base/canonical_errors.h \ - ortools/base/stringprintf.h ortools/graph/util.h ortools/base/hash.h \ - ortools/base/map_util.h ortools/graph/connected_components.h \ - ortools/base/ptr_util.h | $(OBJ_DIR)/algorithms + ortools/graph/util.h ortools/base/hash.h ortools/base/map_util.h \ + ortools/graph/connected_components.h ortools/base/ptr_util.h | $(OBJ_DIR)/algorithms $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Salgorithms$Sfind_graph_symmetries.cc $(OBJ_OUT)$(OBJ_DIR)$Salgorithms$Sfind_graph_symmetries.$O objs/algorithms/hungarian.$O: ortools/algorithms/hungarian.cc \ @@ -1191,13 +1060,10 @@ objs/algorithms/hungarian.$O: ortools/algorithms/hungarian.cc \ objs/algorithms/knapsack_solver.$O: ortools/algorithms/knapsack_solver.cc \ ortools/algorithms/knapsack_solver.h ortools/base/basictypes.h \ ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/memory.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/port.h \ - ortools/base/time_support.h ortools/base/timer.h \ + ortools/base/macros.h ortools/util/time_limit.h \ + ortools/base/commandlineflags.h ortools/base/timer.h \ ortools/util/running_stat.h ortools/base/stl_util.h \ - ortools/linear_solver/linear_solver.h ortools/base/optional.h \ - ortools/base/status.h ortools/base/join.h ortools/base/string_view.h \ - ortools/base/stringprintf.h ortools/base/strutil.h \ + ortools/linear_solver/linear_solver.h ortools/base/status.h \ ortools/gen/ortools/glop/parameters.pb.h \ ortools/linear_solver/linear_expr.h \ ortools/gen/ortools/linear_solver/linear_solver.pb.h \ @@ -1208,8 +1074,7 @@ objs/algorithms/knapsack_solver.$O: ortools/algorithms/knapsack_solver.cc \ objs/algorithms/sparse_permutation.$O: \ ortools/algorithms/sparse_permutation.cc \ ortools/algorithms/sparse_permutation.h ortools/base/logging.h \ - ortools/base/integral_types.h ortools/base/macros.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/string_view.h | $(OBJ_DIR)/algorithms + ortools/base/integral_types.h ortools/base/macros.h | $(OBJ_DIR)/algorithms $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Salgorithms$Ssparse_permutation.cc $(OBJ_OUT)$(OBJ_DIR)$Salgorithms$Ssparse_permutation.$O SAT_DEPS = \ @@ -1317,23 +1182,19 @@ SAT_LIB_OBJS = \ objs/sat/all_different.$O: ortools/sat/all_different.cc \ ortools/sat/all_different.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/sat/integer.h ortools/base/hash.h \ - ortools/base/basictypes.h ortools/base/logging.h \ - ortools/base/inlined_vector.h ortools/base/int_type.h \ - ortools/base/int_type_indexed_vector.h ortools/base/join.h \ - ortools/base/string_view.h ortools/base/map_util.h ortools/base/port.h \ - ortools/base/span.h ortools/graph/iterators.h ortools/sat/model.h \ - ortools/base/typeid.h ortools/sat/sat_base.h ortools/base/stringprintf.h \ - ortools/util/bitset.h ortools/sat/sat_solver.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/sat/clause.h \ + ortools/base/basictypes.h ortools/base/logging.h ortools/base/int_type.h \ + ortools/base/int_type_indexed_vector.h ortools/base/map_util.h \ + ortools/graph/iterators.h ortools/sat/model.h ortools/base/typeid.h \ + ortools/sat/sat_base.h ortools/util/bitset.h ortools/sat/sat_solver.h \ + ortools/base/timer.h ortools/sat/clause.h \ ortools/sat/drat_proof_handler.h ortools/sat/drat_checker.h \ ortools/sat/drat_writer.h ortools/base/file.h ortools/base/status.h \ ortools/gen/ortools/sat/sat_parameters.pb.h ortools/util/random_engine.h \ ortools/util/stats.h ortools/sat/pb_constraint.h ortools/sat/restart.h \ ortools/util/running_stat.h ortools/sat/sat_decision.h \ ortools/util/integer_pq.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h ortools/util/rev.h \ - ortools/util/saturated_arithmetic.h ortools/base/casts.h \ - ortools/util/sorted_interval_list.h \ + ortools/base/commandlineflags.h ortools/util/rev.h \ + ortools/util/saturated_arithmetic.h ortools/util/sorted_interval_list.h \ ortools/graph/strongly_connected_components.h ortools/util/sort.h | $(OBJ_DIR)/sat $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Sall_different.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Sall_different.$O @@ -1341,26 +1202,21 @@ objs/sat/boolean_problem.$O: ortools/sat/boolean_problem.cc \ ortools/sat/boolean_problem.h ortools/algorithms/sparse_permutation.h \ ortools/base/logging.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/base/int_type_indexed_vector.h \ - ortools/base/int_type.h ortools/base/status.h ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/string_view.h \ + ortools/base/int_type.h ortools/base/status.h \ ortools/gen/ortools/sat/boolean_problem.pb.h \ ortools/gen/ortools/sat/cp_model.pb.h ortools/sat/pb_constraint.h \ - ortools/base/hash.h ortools/base/span.h ortools/base/inlined_vector.h \ - ortools/sat/sat_base.h ortools/base/port.h ortools/base/stringprintf.h \ + ortools/base/hash.h ortools/base/basictypes.h ortools/sat/sat_base.h \ ortools/sat/model.h ortools/base/map_util.h ortools/base/typeid.h \ ortools/util/bitset.h ortools/gen/ortools/sat/sat_parameters.pb.h \ - ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ - ortools/sat/sat_solver.h ortools/sat/clause.h \ - ortools/sat/drat_proof_handler.h ortools/sat/drat_checker.h \ - ortools/sat/drat_writer.h ortools/base/file.h \ + ortools/util/stats.h ortools/base/timer.h ortools/sat/sat_solver.h \ + ortools/sat/clause.h ortools/sat/drat_proof_handler.h \ + ortools/sat/drat_checker.h ortools/sat/drat_writer.h ortools/base/file.h \ ortools/util/random_engine.h ortools/sat/restart.h \ ortools/util/running_stat.h ortools/sat/sat_decision.h \ ortools/util/integer_pq.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h \ - ortools/sat/simplification.h ortools/base/adjustable_priority_queue.h \ - ortools/graph/io.h ortools/base/filelineiter.h \ - ortools/base/stringpiece_utils.h ortools/base/strutil.h \ - ortools/base/numbers.h ortools/base/split.h ortools/base/statusor.h \ + ortools/base/commandlineflags.h ortools/sat/simplification.h \ + ortools/base/adjustable_priority_queue.h ortools/graph/io.h \ + ortools/base/filelineiter.h ortools/base/statusor.h \ ortools/graph/graph.h ortools/graph/iterators.h \ ortools/algorithms/find_graph_symmetries.h \ ortools/algorithms/dynamic_partition.h \ @@ -1373,37 +1229,30 @@ objs/sat/circuit.$O: ortools/sat/circuit.cc ortools/sat/circuit.h \ ortools/base/int_type.h ortools/base/macros.h \ ortools/base/integral_types.h ortools/base/logging.h \ ortools/sat/integer.h ortools/base/hash.h ortools/base/basictypes.h \ - ortools/base/inlined_vector.h ortools/base/int_type_indexed_vector.h \ - ortools/base/join.h ortools/base/string_view.h ortools/base/map_util.h \ - ortools/base/port.h ortools/base/span.h ortools/graph/iterators.h \ - ortools/sat/model.h ortools/base/typeid.h ortools/sat/sat_base.h \ - ortools/base/stringprintf.h ortools/util/bitset.h \ - ortools/sat/sat_solver.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/sat/clause.h \ + ortools/base/int_type_indexed_vector.h ortools/base/map_util.h \ + ortools/graph/iterators.h ortools/sat/model.h ortools/base/typeid.h \ + ortools/sat/sat_base.h ortools/util/bitset.h ortools/sat/sat_solver.h \ + ortools/base/timer.h ortools/sat/clause.h \ ortools/sat/drat_proof_handler.h ortools/sat/drat_checker.h \ ortools/sat/drat_writer.h ortools/base/file.h ortools/base/status.h \ ortools/gen/ortools/sat/sat_parameters.pb.h ortools/util/random_engine.h \ ortools/util/stats.h ortools/sat/pb_constraint.h ortools/sat/restart.h \ ortools/util/running_stat.h ortools/sat/sat_decision.h \ ortools/util/integer_pq.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h ortools/util/rev.h \ - ortools/util/saturated_arithmetic.h ortools/base/casts.h \ - ortools/util/sorted_interval_list.h | $(OBJ_DIR)/sat + ortools/base/commandlineflags.h ortools/util/rev.h \ + ortools/util/saturated_arithmetic.h ortools/util/sorted_interval_list.h | $(OBJ_DIR)/sat $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Scircuit.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Scircuit.$O objs/sat/clause.$O: ortools/sat/clause.cc ortools/sat/clause.h \ ortools/base/hash.h ortools/base/basictypes.h \ ortools/base/integral_types.h ortools/base/logging.h \ - ortools/base/macros.h ortools/base/inlined_vector.h \ - ortools/base/int_type.h ortools/base/int_type_indexed_vector.h \ - ortools/base/span.h ortools/sat/drat_proof_handler.h \ - ortools/sat/drat_checker.h ortools/sat/sat_base.h ortools/base/port.h \ - ortools/base/stringprintf.h ortools/sat/model.h ortools/base/map_util.h \ - ortools/base/typeid.h ortools/util/bitset.h ortools/sat/drat_writer.h \ - ortools/base/file.h ortools/base/status.h ortools/base/join.h \ - ortools/base/string_view.h ortools/gen/ortools/sat/sat_parameters.pb.h \ - ortools/util/random_engine.h ortools/util/stats.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/base/stl_util.h \ + ortools/base/macros.h ortools/base/int_type.h \ + ortools/base/int_type_indexed_vector.h ortools/sat/drat_proof_handler.h \ + ortools/sat/drat_checker.h ortools/sat/sat_base.h ortools/sat/model.h \ + ortools/base/map_util.h ortools/base/typeid.h ortools/util/bitset.h \ + ortools/sat/drat_writer.h ortools/base/file.h ortools/base/status.h \ + ortools/gen/ortools/sat/sat_parameters.pb.h ortools/util/random_engine.h \ + ortools/util/stats.h ortools/base/timer.h ortools/base/stl_util.h \ ortools/graph/strongly_connected_components.h | $(OBJ_DIR)/sat $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Sclause.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Sclause.$O @@ -1411,32 +1260,27 @@ objs/sat/cp_constraints.$O: ortools/sat/cp_constraints.cc \ ortools/sat/cp_constraints.h ortools/base/int_type.h \ ortools/base/macros.h ortools/base/integral_types.h \ ortools/base/logging.h ortools/sat/integer.h ortools/base/hash.h \ - ortools/base/basictypes.h ortools/base/inlined_vector.h \ - ortools/base/int_type_indexed_vector.h ortools/base/join.h \ - ortools/base/string_view.h ortools/base/map_util.h ortools/base/port.h \ - ortools/base/span.h ortools/graph/iterators.h ortools/sat/model.h \ - ortools/base/typeid.h ortools/sat/sat_base.h ortools/base/stringprintf.h \ - ortools/util/bitset.h ortools/sat/sat_solver.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/sat/clause.h \ + ortools/base/basictypes.h ortools/base/int_type_indexed_vector.h \ + ortools/base/map_util.h ortools/graph/iterators.h ortools/sat/model.h \ + ortools/base/typeid.h ortools/sat/sat_base.h ortools/util/bitset.h \ + ortools/sat/sat_solver.h ortools/base/timer.h ortools/sat/clause.h \ ortools/sat/drat_proof_handler.h ortools/sat/drat_checker.h \ ortools/sat/drat_writer.h ortools/base/file.h ortools/base/status.h \ ortools/gen/ortools/sat/sat_parameters.pb.h ortools/util/random_engine.h \ ortools/util/stats.h ortools/sat/pb_constraint.h ortools/sat/restart.h \ ortools/util/running_stat.h ortools/sat/sat_decision.h \ ortools/util/integer_pq.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h ortools/util/rev.h \ - ortools/util/saturated_arithmetic.h ortools/base/casts.h \ - ortools/util/sorted_interval_list.h ortools/util/sort.h | $(OBJ_DIR)/sat + ortools/base/commandlineflags.h ortools/util/rev.h \ + ortools/util/saturated_arithmetic.h ortools/util/sorted_interval_list.h \ + ortools/util/sort.h | $(OBJ_DIR)/sat $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Scp_constraints.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Scp_constraints.$O -objs/sat/cp_model.$O: ortools/sat/cp_model.cc ortools/base/join.h \ - ortools/base/basictypes.h ortools/base/integral_types.h \ - ortools/base/logging.h ortools/base/macros.h ortools/base/string_view.h \ - ortools/sat/cp_model.h ortools/base/span.h ortools/base/inlined_vector.h \ +objs/sat/cp_model.$O: ortools/sat/cp_model.cc ortools/sat/cp_model.h \ ortools/gen/ortools/sat/cp_model.pb.h ortools/sat/cp_model_solver.h \ - ortools/sat/model.h ortools/base/map_util.h ortools/base/typeid.h \ + ortools/base/integral_types.h ortools/sat/model.h ortools/base/logging.h \ + ortools/base/macros.h ortools/base/map_util.h ortools/base/typeid.h \ ortools/gen/ortools/sat/sat_parameters.pb.h ortools/sat/cp_model_utils.h \ - ortools/util/sorted_interval_list.h ortools/base/stringprintf.h | $(OBJ_DIR)/sat + ortools/util/sorted_interval_list.h | $(OBJ_DIR)/sat $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Scp_model.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Scp_model.$O objs/sat/cp_model_checker.$O: ortools/sat/cp_model_checker.cc \ @@ -1470,34 +1314,6 @@ objs/sat/cp_model_lns.$O: ortools/sat/cp_model_lns.cc \ ortools/base/inlined_vector.h ortools/util/random_engine.h | $(OBJ_DIR)/sat $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Scp_model_lns.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Scp_model_lns.$O -objs/sat/cp_model_loader.$O: ortools/sat/cp_model_loader.cc \ - ortools/sat/cp_model_loader.h ortools/base/integral_types.h \ - ortools/base/int_type.h ortools/base/macros.h \ - ortools/base/int_type_indexed_vector.h ortools/base/logging.h \ - ortools/base/map_util.h ortools/gen/ortools/sat/cp_model.pb.h \ - ortools/sat/cp_model_utils.h ortools/util/sorted_interval_list.h \ - ortools/base/span.h ortools/base/inlined_vector.h ortools/sat/integer.h \ - ortools/base/hash.h ortools/base/basictypes.h ortools/base/join.h \ - ortools/base/string_view.h ortools/base/port.h ortools/graph/iterators.h \ - ortools/sat/model.h ortools/base/typeid.h ortools/sat/sat_base.h \ - ortools/base/stringprintf.h ortools/util/bitset.h \ - ortools/sat/sat_solver.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/sat/clause.h \ - ortools/sat/drat_proof_handler.h ortools/sat/drat_checker.h \ - ortools/sat/drat_writer.h ortools/base/file.h ortools/base/status.h \ - ortools/gen/ortools/sat/sat_parameters.pb.h ortools/util/random_engine.h \ - ortools/util/stats.h ortools/sat/pb_constraint.h ortools/sat/restart.h \ - ortools/util/running_stat.h ortools/sat/sat_decision.h \ - ortools/util/integer_pq.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h ortools/util/rev.h \ - ortools/util/saturated_arithmetic.h ortools/base/casts.h \ - ortools/sat/intervals.h ortools/sat/cp_constraints.h \ - ortools/sat/integer_expr.h ortools/sat/precedences.h \ - ortools/base/stl_util.h ortools/sat/all_different.h \ - ortools/sat/circuit.h ortools/sat/cumulative.h ortools/sat/disjunctive.h \ - ortools/sat/theta_tree.h ortools/sat/table.h | $(OBJ_DIR)/sat - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Scp_model_loader.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Scp_model_loader.$O - objs/sat/cp_model_objective.$O: ortools/sat/cp_model_objective.cc \ ortools/sat/cp_model_objective.h ortools/gen/ortools/sat/cp_model.pb.h \ ortools/sat/cp_model_utils.h ortools/base/integral_types.h \ @@ -1512,27 +1328,22 @@ objs/sat/cp_model_presolve.$O: ortools/sat/cp_model_presolve.cc \ ortools/base/integral_types.h ortools/base/logging.h \ ortools/base/macros.h ortools/base/join.h ortools/base/string_view.h \ ortools/base/map_util.h ortools/base/port.h ortools/base/stl_util.h \ - ortools/port/proto_utils.h ortools/sat/cp_model_checker.h \ - ortools/sat/cp_model_loader.h ortools/base/int_type.h \ - ortools/base/int_type_indexed_vector.h ortools/sat/cp_model_utils.h \ - ortools/util/sorted_interval_list.h ortools/base/span.h \ - ortools/base/inlined_vector.h ortools/sat/integer.h \ - ortools/graph/iterators.h ortools/sat/model.h ortools/base/typeid.h \ - ortools/sat/sat_base.h ortools/base/stringprintf.h ortools/util/bitset.h \ - ortools/sat/sat_solver.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/sat/clause.h \ + ortools/sat/cp_model_checker.h ortools/sat/cp_model_objective.h \ + ortools/sat/cp_model_utils.h ortools/util/sorted_interval_list.h \ + ortools/base/span.h ortools/base/inlined_vector.h ortools/sat/sat_base.h \ + ortools/base/int_type.h ortools/base/int_type_indexed_vector.h \ + ortools/base/stringprintf.h ortools/sat/model.h ortools/base/typeid.h \ + ortools/util/bitset.h ortools/gen/ortools/sat/sat_parameters.pb.h \ + ortools/sat/simplification.h ortools/base/adjustable_priority_queue.h \ ortools/sat/drat_proof_handler.h ortools/sat/drat_checker.h \ ortools/sat/drat_writer.h ortools/base/file.h ortools/base/status.h \ - ortools/gen/ortools/sat/sat_parameters.pb.h ortools/util/random_engine.h \ - ortools/util/stats.h ortools/sat/pb_constraint.h ortools/sat/restart.h \ + ortools/sat/sat_solver.h ortools/base/timer.h \ + ortools/base/time_support.h ortools/sat/clause.h \ + ortools/util/random_engine.h ortools/util/stats.h \ + ortools/sat/pb_constraint.h ortools/sat/restart.h \ ortools/util/running_stat.h ortools/sat/sat_decision.h \ ortools/util/integer_pq.h ortools/util/time_limit.h \ - ortools/base/commandlineflags.h ortools/base/memory.h ortools/util/rev.h \ - ortools/util/saturated_arithmetic.h ortools/base/casts.h \ - ortools/sat/intervals.h ortools/sat/cp_constraints.h \ - ortools/sat/integer_expr.h ortools/sat/precedences.h \ - ortools/sat/cp_model_objective.h ortools/sat/probing.h \ - ortools/sat/simplification.h ortools/base/adjustable_priority_queue.h \ + ortools/base/commandlineflags.h ortools/base/memory.h \ ortools/util/affine_relation.h ortools/base/iterator_adaptors.h | $(OBJ_DIR)/sat $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Scp_model_presolve.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Scp_model_presolve.$O @@ -1557,8 +1368,7 @@ objs/sat/cp_model_search.$O: ortools/sat/cp_model_search.cc \ ortools/base/commandlineflags.h ortools/base/memory.h ortools/util/rev.h \ ortools/util/saturated_arithmetic.h ortools/base/casts.h \ ortools/util/sorted_interval_list.h ortools/sat/integer_search.h \ - ortools/base/cleanup.h ortools/sat/cp_model_utils.h ortools/sat/util.h \ - ortools/base/random.h | $(OBJ_DIR)/sat + ortools/sat/cp_model_utils.h ortools/sat/util.h ortools/base/random.h | $(OBJ_DIR)/sat $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Scp_model_search.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Scp_model_search.$O objs/sat/cp_model_solver.$O: ortools/sat/cp_model_solver.cc \ @@ -1574,7 +1384,7 @@ objs/sat/cp_model_solver.$O: ortools/sat/cp_model_solver.cc \ ortools/base/iterator_adaptors.h ortools/base/join.h \ ortools/base/string_view.h ortools/base/memory.h ortools/base/stl_util.h \ ortools/graph/connectivity.h ortools/port/proto_utils.h \ - ortools/base/port.h ortools/sat/circuit.h ortools/sat/integer.h \ + ortools/base/port.h ortools/sat/all_different.h ortools/sat/integer.h \ ortools/base/hash.h ortools/base/inlined_vector.h ortools/base/span.h \ ortools/graph/iterators.h ortools/sat/sat_base.h ortools/util/bitset.h \ ortools/sat/sat_solver.h ortools/sat/clause.h \ @@ -1585,13 +1395,15 @@ objs/sat/cp_model_solver.$O: ortools/sat/cp_model_solver.cc \ ortools/util/running_stat.h ortools/sat/sat_decision.h \ ortools/util/integer_pq.h ortools/util/time_limit.h ortools/util/rev.h \ ortools/util/saturated_arithmetic.h ortools/base/casts.h \ - ortools/util/sorted_interval_list.h ortools/sat/cp_model_checker.h \ + ortools/util/sorted_interval_list.h ortools/sat/circuit.h \ + ortools/sat/cp_constraints.h ortools/sat/cp_model_checker.h \ ortools/sat/cp_model_expand.h ortools/sat/cp_model_lns.h \ - ortools/sat/cp_model_loader.h ortools/sat/cp_model_utils.h \ - ortools/sat/intervals.h ortools/sat/cp_constraints.h \ - ortools/sat/integer_expr.h ortools/sat/precedences.h \ ortools/sat/cp_model_presolve.h ortools/sat/cp_model_search.h \ - ortools/sat/integer_search.h ortools/sat/linear_programming_constraint.h \ + ortools/sat/integer_search.h ortools/sat/cp_model_utils.h \ + ortools/sat/cumulative.h ortools/sat/intervals.h \ + ortools/sat/integer_expr.h ortools/sat/precedences.h \ + ortools/sat/disjunctive.h ortools/sat/theta_tree.h \ + ortools/sat/linear_programming_constraint.h \ ortools/glop/revised_simplex.h ortools/glop/basis_representation.h \ ortools/glop/lu_factorization.h ortools/glop/markowitz.h \ ortools/gen/ortools/glop/parameters.pb.h ortools/glop/status.h \ @@ -1608,8 +1420,9 @@ objs/sat/cp_model_solver.$O: ortools/sat/cp_model_solver.cc \ ortools/lp_data/sparse_row.h ortools/lp_data/matrix_scaler.h \ ortools/sat/linear_relaxation.h ortools/sat/lns.h \ ortools/base/threadpool.h ortools/sat/optimization.h \ - ortools/gen/ortools/sat/boolean_problem.pb.h ortools/sat/probing.h \ - ortools/sat/simplification.h ortools/base/adjustable_priority_queue.h | $(OBJ_DIR)/sat + ortools/gen/ortools/sat/boolean_problem.pb.h \ + ortools/sat/simplification.h ortools/base/adjustable_priority_queue.h \ + ortools/sat/table.h | $(OBJ_DIR)/sat $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Scp_model_solver.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Scp_model_solver.$O objs/sat/cp_model_symmetries.$O: ortools/sat/cp_model_symmetries.cc \ @@ -1861,18 +1674,17 @@ objs/sat/linear_programming_constraint.$O: \ ortools/base/integral_types.h ortools/glop/basis_representation.h \ ortools/base/logging.h ortools/glop/lu_factorization.h \ ortools/glop/markowitz.h ortools/base/inlined_vector.h \ - ortools/base/port.h ortools/gen/ortools/glop/parameters.pb.h \ - ortools/glop/status.h ortools/lp_data/lp_types.h \ - ortools/base/basictypes.h ortools/base/int_type_indexed_vector.h \ - ortools/util/bitset.h ortools/lp_data/sparse.h \ - ortools/lp_data/permutation.h ortools/base/random.h \ - ortools/util/return_macros.h ortools/lp_data/sparse_column.h \ - ortools/lp_data/sparse_vector.h ortools/base/stringprintf.h \ - ortools/graph/iterators.h ortools/util/stats.h ortools/base/timer.h \ - ortools/base/time_support.h ortools/glop/rank_one_update.h \ - ortools/lp_data/lp_utils.h ortools/base/accurate_sum.h \ - ortools/glop/dual_edge_norms.h ortools/lp_data/lp_data.h \ - ortools/base/hash.h ortools/util/fp_utils.h \ + ortools/gen/ortools/glop/parameters.pb.h ortools/glop/status.h \ + ortools/base/port.h ortools/lp_data/lp_types.h ortools/base/basictypes.h \ + ortools/base/int_type_indexed_vector.h ortools/util/bitset.h \ + ortools/lp_data/sparse.h ortools/lp_data/permutation.h \ + ortools/base/random.h ortools/util/return_macros.h \ + ortools/lp_data/sparse_column.h ortools/lp_data/sparse_vector.h \ + ortools/base/stringprintf.h ortools/graph/iterators.h \ + ortools/util/stats.h ortools/base/timer.h ortools/base/time_support.h \ + ortools/glop/rank_one_update.h ortools/lp_data/lp_utils.h \ + ortools/base/accurate_sum.h ortools/glop/dual_edge_norms.h \ + ortools/lp_data/lp_data.h ortools/base/hash.h ortools/util/fp_utils.h \ ortools/glop/entering_variable.h ortools/glop/primal_edge_norms.h \ ortools/glop/update_row.h ortools/glop/variables_info.h \ ortools/glop/reduced_costs.h ortools/util/random_engine.h \ @@ -2067,28 +1879,6 @@ objs/sat/precedences.$O: ortools/sat/precedences.cc \ ortools/base/stl_util.h ortools/sat/cp_constraints.h | $(OBJ_DIR)/sat $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Sprecedences.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Sprecedences.$O -objs/sat/probing.$O: ortools/sat/probing.cc ortools/sat/probing.h \ - ortools/sat/model.h ortools/base/logging.h ortools/base/integral_types.h \ - ortools/base/macros.h ortools/base/map_util.h ortools/base/typeid.h \ - ortools/base/timer.h ortools/base/basictypes.h \ - ortools/base/time_support.h ortools/sat/clause.h ortools/base/hash.h \ - ortools/base/inlined_vector.h ortools/base/int_type.h \ - ortools/base/int_type_indexed_vector.h ortools/base/span.h \ - ortools/sat/drat_proof_handler.h ortools/sat/drat_checker.h \ - ortools/sat/sat_base.h ortools/base/port.h ortools/base/stringprintf.h \ - ortools/util/bitset.h ortools/sat/drat_writer.h ortools/base/file.h \ - ortools/base/status.h ortools/base/join.h ortools/base/string_view.h \ - ortools/gen/ortools/sat/sat_parameters.pb.h ortools/util/random_engine.h \ - ortools/util/stats.h ortools/sat/integer.h ortools/graph/iterators.h \ - ortools/sat/sat_solver.h ortools/sat/pb_constraint.h \ - ortools/sat/restart.h ortools/util/running_stat.h \ - ortools/sat/sat_decision.h ortools/util/integer_pq.h \ - ortools/util/time_limit.h ortools/base/commandlineflags.h \ - ortools/base/memory.h ortools/util/rev.h \ - ortools/util/saturated_arithmetic.h ortools/base/casts.h \ - ortools/util/sorted_interval_list.h | $(OBJ_DIR)/sat - $(CCC) $(CFLAGS) -c $(SRC_DIR)$Sortools$Ssat$Sprobing.cc $(OBJ_OUT)$(OBJ_DIR)$Ssat$Sprobing.$O - objs/sat/restart.$O: ortools/sat/restart.cc ortools/sat/restart.h \ ortools/sat/model.h ortools/base/logging.h ortools/base/integral_types.h \ ortools/base/macros.h ortools/base/map_util.h ortools/base/typeid.h \ diff --git a/makefiles/Makefile.java.mk b/makefiles/Makefile.java.mk index c5f400a9332..3fdcaf1ca0b 100755 --- a/makefiles/Makefile.java.mk +++ b/makefiles/Makefile.java.mk @@ -64,6 +64,9 @@ $(GEN_DIR)/com/google/ortools/flatzinc: $(GEN_DIR)/com/google/ortools/sat: -$(MKDIR_P) $(GEN_PATH)$Scom$Sgoogle$Sortools$Ssat +$(GEN_DIR)/com/google/ortools/util: + $(MKDIR_P) $(GEN_PATH)$Scom$Sgoogle$Sortools$Sutil + $(CLASS_DIR): -$(MKDIR_P) $(CLASS_DIR) @@ -92,7 +95,7 @@ $(GEN_DIR)/ortools/constraint_solver/constraint_solver_java_wrap.cc: \ $(SWIG_BINARY) -I$(INC_DIR) -c++ -java \ -o $(GEN_PATH)$Sortools$Sconstraint_solver$Sconstraint_solver_java_wrap.cc \ -package com.google.ortools.constraintsolver \ - -module operations_research_constraint_solver \ + -module main \ -outdir $(GEN_PATH)$Scom$Sgoogle$Sortools$Sconstraintsolver \ $(SRC_DIR)$Sortools$Sconstraint_solver$Sjava$Srouting.i @@ -114,7 +117,7 @@ $(GEN_DIR)/ortools/algorithms/knapsack_solver_java_wrap.cc: \ $(SWIG_BINARY) -I$(INC_DIR) -c++ -java \ -o $(GEN_PATH)$Sortools$Salgorithms$Sknapsack_solver_java_wrap.cc \ -package com.google.ortools.algorithms \ - -module operations_research_algorithms \ + -module main \ -outdir $(GEN_PATH)$Scom$Sgoogle$Sortools$Salgorithms \ $(SRC_DIR)$Sortools$Salgorithms$Sjava$Sknapsack_solver.i @@ -133,7 +136,7 @@ $(GEN_DIR)/ortools/graph/graph_java_wrap.cc: \ $(SWIG_BINARY) -I$(INC_DIR) -c++ -java \ -o $(GEN_PATH)$Sortools$Sgraph$Sgraph_java_wrap.cc \ -package com.google.ortools.graph \ - -module operations_research_graph \ + -module main \ -outdir $(GEN_PATH)$Scom$Sgoogle$Sortools$Sgraph \ $(SRC_DIR)$Sortools$Sgraph$Sjava$Sgraph.i @@ -154,7 +157,7 @@ $(GEN_DIR)/ortools/linear_solver/linear_solver_java_wrap.cc: \ $(SWIG_BINARY) $(SWIG_INC) -I$(INC_DIR) -c++ -java \ -o $(GEN_PATH)$Sortools$Slinear_solver$Slinear_solver_java_wrap.cc \ -package com.google.ortools.linearsolver \ - -module operations_research_linear_solver \ + -module main_research_linear_solver \ -outdir $(GEN_PATH)$Scom$Sgoogle$Sortools$Slinearsolver \ $(SRC_DIR)$Sortools$Slinear_solver$Sjava$Slinear_solver.i @@ -174,7 +177,7 @@ $(GEN_DIR)/ortools/sat/sat_java_wrap.cc: \ $(SWIG_BINARY) -I$(INC_DIR) -c++ -java \ -o $(GEN_PATH)$Sortools$Ssat$Ssat_java_wrap.cc \ -package com.google.ortools.sat \ - -module operations_research_sat \ + -module main \ -outdir $(GEN_PATH)$Scom$Sgoogle$Sortools$Ssat \ $(SRC_DIR)$Sortools$Ssat$Sjava$Ssat.i @@ -240,6 +243,11 @@ $(GEN_DIR)/com/google/ortools/sat/SatParameters.java: \ | $(GEN_DIR)/com/google/ortools/sat $(PROTOC) --proto_path=$(SRC_DIR) --java_out=$(GEN_PATH) $(SRC_DIR)$Sortools$Ssat$Ssat_parameters.proto +$(GEN_DIR)/com/google/ortools/util/OptionalBoolean.java: \ + $(SRC_DIR)/ortools/util/optional_boolean.proto \ + | $(GEN_DIR)/com/google/ortools/util + $(PROTOC) --proto_path=$(SRC_DIR) --java_out=$(GEN_PATH) $(SRC_DIR)$Sortools$Sutil$Soptional_boolean.proto + $(JAVA_OR_TOOLS_LIBS): \ $(JAVA_OR_TOOLS_NATIVE_LIBS) \ $(LIB_DIR)/protobuf.jar \ @@ -248,6 +256,7 @@ $(JAVA_OR_TOOLS_LIBS): \ $(GEN_DIR)/com/google/ortools/constraintsolver/RoutingParameters.java \ $(GEN_DIR)/com/google/ortools/constraintsolver/RoutingEnums.java \ $(GEN_DIR)/com/google/ortools/sat/SatParameters.java \ + $(GEN_DIR)/com/google/ortools/util/OptionalBoolean.java \ $(GEN_DIR)/com/google/ortools/sat/CpModel.java | \ $(CLASS_DIR)/com/google/ortools "$(JAVAC_BIN)" -d $(CLASS_DIR) \ @@ -256,6 +265,7 @@ $(JAVA_OR_TOOLS_LIBS): \ $(SRC_DIR)$Sortools$Scom$Sgoogle$Sortools$Ssat$S*.java \ $(GEN_PATH)$Scom$Sgoogle$Sortools$Sconstraintsolver$S*.java \ $(GEN_PATH)$Scom$Sgoogle$Sortools$Ssat$S*.java \ + $(GEN_PATH)$Scom$Sgoogle$Sortools$Sutil$S*.java \ $(GEN_PATH)$Scom$Sgoogle$Sortools$Salgorithms$S*.java \ $(GEN_PATH)$Scom$Sgoogle$Sortools$Sgraph$S*.java \ $(GEN_PATH)$Scom$Sgoogle$Sortools$Slinearsolver$S*.java diff --git a/makefiles/Makefile.third_party.unix.mk b/makefiles/Makefile.third_party.unix.mk index 23ffbf492e2..6451d47e430 100644 --- a/makefiles/Makefile.third_party.unix.mk +++ b/makefiles/Makefile.third_party.unix.mk @@ -9,6 +9,7 @@ UNIX_GFLAGS_DIR ?= $(OR_TOOLS_TOP)/dependencies/install UNIX_GLOG_DIR ?= $(OR_TOOLS_TOP)/dependencies/install UNIX_PROTOBUF_DIR ?= $(OR_TOOLS_TOP)/dependencies/install UNIX_PROTOC_BINARY ?= $(UNIX_PROTOBUF_DIR)/bin/protoc +UNIX_ABSL_DIR ?= $(OR_TOOLS_TOP)/dependencies/install UNIX_CBC_DIR ?= $(OR_TOOLS_TOP)/dependencies/install UNIX_CGL_DIR ?= $(UNIX_CBC_DIR) UNIX_CLP_DIR ?= $(UNIX_CBC_DIR) @@ -21,6 +22,7 @@ PROTOC_BINARY := $(shell $(WHICH) ${UNIX_PROTOC_BINARY}) GFLAGS_TAG = 2.2.1 GLOG_TAG = 0.3.5 PROTOBUF_TAG = 3.6.1 +ABSL_TAG = master CBC_TAG = 2.9.9 CGL_TAG = 0.59.10 CLP_TAG = 1.16.11 @@ -56,6 +58,11 @@ ifeq ($(wildcard $(PROTOC_BINARY)),) else $(info PROTOC: found) endif +ifeq ($(wildcard $(UNIX_ABSL_DIR)/include/absl/base/config.h),) + $(error Third party Abseil-cpp files was not found! did you run 'make third_party' or set UNIX_ABSL_DIR ?) +else + $(info ABSEIL-CPP: found) +endif ifeq ($(wildcard $(UNIX_COINUTILS_DIR)/include/coinutils/coin/CoinModel.hpp $(UNIX_COINUTILS_DIR)/include/coin/CoinModel.hpp),) $(error Third party CoinUtils files was not found! did you run 'make third_party' or set UNIX_COINUTILS_DIR ?) else @@ -110,6 +117,7 @@ build_third_party: \ build_gflags \ build_glog \ build_protobuf \ + build_absl \ build_cbc .PHONY: archives_directory @@ -313,15 +321,79 @@ DYLD_LIBRARY_PATH="$(UNIX_PROTOBUF_DIR)/lib":$(DYLD_LIBRARY_PATH) $(PROTOC_BINAR endif # Install Java protobuf +# - Compile generic message proto. +# - Compile duration.proto dependencies/install/lib/protobuf.jar: | dependencies/install/lib/libprotobuf.$L cd dependencies/sources/protobuf-$(PROTOBUF_TAG)/java && \ $(PROTOC) --java_out=core/src/main/java -I../src \ ../src/google/protobuf/descriptor.proto + cd dependencies/sources/protobuf-$(PROTOBUF_TAG)/java && \ + $(PROTOC) --java_out=core/src/main/java -I../src \ + ../src/google/protobuf/duration.proto cd dependencies/sources/protobuf-$(PROTOBUF_TAG)/java/core/src/main/java && \ "$(JAVAC_BIN)" com/google/protobuf/*java cd dependencies/sources/protobuf-$(PROTOBUF_TAG)/java/core/src/main/java && \ "$(JAR_BIN)" cvf ../../../../../../../install/lib/protobuf.jar com/google/protobuf/*class +################## +## ABSEIL-CPP ## +################## +# This uses abseil-cpp cmake-based build. +build_absl: dependencies/install/lib/libabsl.$L + +dependencies/install/lib/libabsl.$L: dependencies/sources/abseil-cpp-$(ABSL_TAG) | dependencies/install + cd dependencies/sources/abseil-cpp-$(ABSL_TAG) && \ + $(SET_COMPILER) $(CMAKE) -H. -Bbuild_cmake \ + -DCMAKE_PREFIX_PATH="$(OR_TOOLS_TOP)/dependencies/install" \ + -DBUILD_SHARED_LIBS=ON \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DCMAKE_CXX_FLAGS="$(MAC_VERSION)" \ + -DBUILD_TESTING=OFF \ + -DCMAKE_INSTALL_PREFIX=../../install && \ + $(CMAKE) --build build_cmake -- -j4 && \ + $(CMAKE) --build build_cmake --target install + +dependencies/sources/abseil-cpp-$(ABSL_TAG): | dependencies/sources + -$(DELREC) dependencies/sources/abseil-cpp-$(ABSL_TAG) + git clone --quiet -b $(ABSL_TAG) https://github.com/abseil/abseil-cpp.git dependencies/sources/abseil-cpp-$(ABSL_TAG) + cd dependencies/sources/abseil-cpp-$(ABSL_TAG) && \ + git reset --hard 45221cc + cd dependencies/sources/abseil-cpp-$(ABSL_TAG) && \ + git apply "$(OR_TOOLS_TOP)/patches/abseil-cpp-$(ABSL_TAG).patch" + $(COPY) patches/absl-config.cmake dependencies/sources/abseil-cpp-$(ABSL_TAG)/CMake + +ABSL_INC = -I$(UNIX_ABSL_DIR)/include +ABSL_SWIG = $(ABSL_INC) +STATIC_ABSL_LNK = $(UNIX_ABSL_DIR)/lib/libabsl.a +DYNAMIC_ABSL_LNK = -L$(UNIX_ABSL_DIR)/lib \ +-labsl_bad_any_cast \ +-labsl_bad_optional_access \ +-labsl_base \ +-labsl_container \ +-labsl_dynamic_annotations \ +-labsl_examine_stack \ +-labsl_failure_signal_handler \ +-labsl_hash \ +-labsl_int128 \ +-labsl_leak_check \ +-labsl_malloc_internal \ +-labsl_optional \ +-labsl_spinlock_wait \ +-labsl_stack_consumption \ +-labsl_stacktrace \ +-labsl_strings \ +-labsl_str_format_extension_internal \ +-labsl_str_format_internal \ +-labsl_symbolize \ +-labsl_synchronization \ +-labsl_throw_delegate \ +-labsl_time \ +-labsl_variant + +ABSL_LNK = $(DYNAMIC_ABSL_LNK) +DEPENDENCIES_LNK += $(ABSL_LNK) +OR_TOOLS_LNK += $(ABSL_LNK) + ############################################ ## Install Patchelf on linux platforms. ## ############################################ @@ -672,6 +744,7 @@ clean_third_party: -$(DELREC) dependencies/sources/protobuf* -$(DELREC) dependencies/sources/abseil-cpp* -$(DELREC) dependencies/sources/google* + -$(DELREC) dependencies/sources/abseil-cpp* -$(DELREC) dependencies/sources/Cbc* -$(DELREC) dependencies/sources/Cgl* -$(DELREC) dependencies/sources/Clp* diff --git a/makefiles/Makefile.third_party.win.mk b/makefiles/Makefile.third_party.win.mk index c89404d0a7d..9acb8107767 100644 --- a/makefiles/Makefile.third_party.win.mk +++ b/makefiles/Makefile.third_party.win.mk @@ -14,6 +14,8 @@ WINDOWS_GLOG_DIR ?= $(OR_ROOT)dependencies/install WINDOWS_GLOG_PATH = $(subst /,$S,$(WINDOWS_GLOG_DIR)) WINDOWS_PROTOBUF_DIR ?= $(OR_ROOT)dependencies/install WINDOWS_PROTOBUF_PATH = $(subst /,$S,$(WINDOWS_PROTOBUF_DIR)) +WINDOWS_ABSL_DIR ?= $(OR_ROOT)dependencies/install +WINDOWS_ABSL_PATH = $(subst /,$S,$(WINDOWS_ABSL_DIR)) WINDOWS_CBC_DIR ?= $(OR_ROOT)dependencies/install WINDOWS_CBC_PATH = $(subst /,$S,$(WINDOWS_CBC_DIR)) WINDOWS_CGL_DIR ?= $(WINDOWS_CBC_DIR) @@ -36,6 +38,7 @@ ZLIB_ARCHIVE_TAG = 1211 GFLAGS_TAG = 2.2.1 GLOG_TAG = 0.3.5 PROTOBUF_TAG = 3.6.1 +ABSL_TAG = master CBC_TAG = 2.9.9 CGL_TAG = 0.59.10 CLP_TAG = 1.16.11 @@ -128,6 +131,7 @@ build_third_party: \ install_gflags \ install_glog \ install_protobuf \ + install_absl \ install_swig \ install_coin_cbc @@ -328,13 +332,77 @@ PROTOBUF_LNK = $(STATIC_PROTOBUF_LNK) DEPENDENCIES_LNK += $(PROTOBUF_LNK) # Install Java protobuf +# - Compile generic message proto. +# - Compile duration.proto dependencies/install/lib/protobuf.jar: | dependencies/install/bin/protoc.exe cd dependencies\\sources\\protobuf-$(PROTOBUF_TAG)\\java && \ ..\\..\\..\\install\\bin\\protoc --java_out=core/src/main/java -I../src \ ../src/google/protobuf/descriptor.proto + cd dependencies\\sources\\protobuf-$(PROTOBUF_TAG)\\java && \ + ..\\..\\..\\install\\bin\\protoc --java_out=core/src/main/java -I../src \ + ../src/google/protobuf/duration.proto cd dependencies\\sources\\protobuf-$(PROTOBUF_TAG)\\java\\core\\src\\main\\java && "$(JAVAC_BIN)" com\\google\\protobuf\\*java cd dependencies\\sources\\protobuf-$(PROTOBUF_TAG)\\java\\core\\src\\main\\java && "$(JAR_BIN)" cvf ..\\..\\..\\..\\..\\..\\..\\install\\lib\\protobuf.jar com\\google\\protobuf\\*class +################## +## ABSEIL-CPP ## +################## +# This uses abseil-cpp cmake-based build. +.PHONY: install_absl +install_absl: dependencies/install/lib/absl.lib + +dependencies/install/lib/absl.lib: dependencies/sources/abseil-cpp-$(ABSL_TAG) | dependencies/install + cd dependencies\sources\abseil-cpp-$(ABSL_TAG) && \ + $(SET_COMPILER) "$(CMAKE)" -H. -Bbuild_cmake \ + -DCMAKE_PREFIX_PATH=..\..\install \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_TESTING=OFF \ + -DCMAKE_INSTALL_PREFIX=..\..\install \ + -G "NMake Makefiles" && \ + "$(CMAKE)" --build build_cmake && \ + "$(CMAKE)" --build build_cmake --target install + +dependencies/sources/abseil-cpp-$(ABSL_TAG): | dependencies/sources + -$(DELREC) dependencies/sources/abseil-cpp-$(ABSL_TAG) + git clone --quiet -b $(ABSL_TAG) https://github.com/abseil/abseil-cpp.git dependencies\sources\abseil-cpp-$(ABSL_TAG) + cd dependencies\sources\abseil-cpp-$(ABSL_TAG) \ + && git reset --hard 45221cc + cd dependencies\sources\abseil-cpp-$(ABSL_TAG) \ + && git apply "$(OR_TOOLS_TOP)\patches\abseil-cpp-$(ABSL_TAG).patch" + $(COPY) patches\absl-config.cmake dependencies\sources\abseil-cpp-$(ABSL_TAG)\CMake + +ABSL_INC = /I"$(WINDOWS_ABSL_PATH)\\include" +ABSL_SWIG = -I"$(WINDOWS_ABSL_PATH)/include" +STATIC_ABSL_LNK = \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_bad_any_cast.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_bad_optional_access.lib"\ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_base.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_container.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_dynamic_annotations.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_examine_stack.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_failure_signal_handler.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_hash.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_int128.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_leak_check.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_malloc_internal.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_optional.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_spinlock_wait.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_stack_consumption.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_stacktrace.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_strings.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_str_format_extension_internal.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_str_format_internal.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_symbolize.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_synchronization.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_throw_delegate.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_time.lib" \ + "$(WINDOWS_ABSL_PATH)\\lib\\absl_variant.lib" +DYNAMIC_ABSL_LNK = $(STATIC_ABSL_LNK) + +ABSL_LNK = $(STATIC_ABSL_LNK) +DEPENDENCIES_LNK += $(ABSL_LNK) + ############ ## COIN ## ############ @@ -475,6 +543,7 @@ clean_third_party: remove_readonly_svn_attribs -$(DELREC) dependencies\sources\gflags* -$(DELREC) dependencies\sources\glog* -$(DELREC) dependencies\sources\protobuf* + -$(DELREC) dependencies\sources\abseil-cpp* -$(DELREC) dependencies\sources\Cbc-* -$(DELREC) dependencies\sources\google* -$(DELREC) dependencies\sources\glpk* diff --git a/makefiles/Makefile.unix.mk b/makefiles/Makefile.unix.mk index a413974e92a..aef7701b0fe 100644 --- a/makefiles/Makefile.unix.mk +++ b/makefiles/Makefile.unix.mk @@ -109,7 +109,7 @@ endif SWIG_INC = \ $(GFLAGS_SWIG) $(GLOG_SWIG) $(PROTOBUF_SWIG) $(COIN_SWIG) \ - -DUSE_GLOP -DUSE_BOP -DMUST_USE_RESULT \ + -DUSE_GLOP -DUSE_BOP -DABSL_MUST_USE_RESULT \ $(GLPK_SWIG) $(SCIP_SWIG) $(GUROBI_SWIG) $(CPLEX_SWIG) # Compilation flags diff --git a/makefiles/Makefile.win.mk b/makefiles/Makefile.win.mk index 0efac995ab9..83b92bd0490 100644 --- a/makefiles/Makefile.win.mk +++ b/makefiles/Makefile.win.mk @@ -147,7 +147,7 @@ endif SWIG_INC = \ $(ZLIB_SWIG) $(GFLAGS_SWIG) $(GLOG_SWIG) $(PROTOBUF_SWIG) $(CLP_SWIG) $(CBC_SWIG) \ - -DUSE_GLOP -DUSE_BOP -DMUST_USE_RESULT \ + -DUSE_GLOP -DUSE_BOP -DABSL_MUST_USE_RESULT \ $(GLPK_SWIG) $(SCIP_SWIG) $(GUROBI_SWIG) $(CPLEX_SWIG) SYS_LNK = psapi.lib ws2_32.lib shlwapi.lib @@ -163,7 +163,9 @@ DEPENDENCIES_INC = /I$(INC_DIR) /I$(GEN_DIR) \ /DUSE_GLOP /DUSE_BOP \ $(GLPK_INC) $(SCIP_INC) $(GUROBI_INC) $(CPLEX_INC) -CFLAGS = -nologo $(SYSCFLAGS) /D__WIN32__ /DPSAPI_VERSION=1 /DNOMINMAX $(DEBUG) $(DEPENDENCIES_INC) +CFLAGS = -nologo $(SYSCFLAGS) /D__WIN32__ /DPSAPI_VERSION=1 \ + /DNOMINMAX /DWIN32_LEAN_AND_MEAN=1 /D_CRT_SECURE_NO_WARNINGS \ + $(DEBUG) $(DEPENDENCIES_INC) JNIFLAGS=$(CFLAGS) $(DEPENDENCIES_INC) LDFLAGS = DEPENDENCIES_LNK = $(SYS_LNK) $(STATIC_GLPK_LNK) $(STATIC_SCIP_LNK) $(STATIC_GUROBI_LNK) $(STATIC_CPLEX_LNK) diff --git a/ortools/algorithms/CMakeLists.txt b/ortools/algorithms/CMakeLists.txt index 3016d151a54..867668a618e 100644 --- a/ortools/algorithms/CMakeLists.txt +++ b/ortools/algorithms/CMakeLists.txt @@ -2,7 +2,7 @@ file(GLOB _SRCS "*.h" "*.cc") list(REMOVE_ITEM _SRCS ${CMAKE_CURRENT_SOURCE_DIR}/hungarian_test.cc ${CMAKE_CURRENT_SOURCE_DIR}/hungarian_test.h - ) +) set(NAME ${PROJECT_NAME}_algorithms) @@ -14,36 +14,41 @@ set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF POSITION_INDEPENDENT_CODE ON - ) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE -# gflags::gflags -# glog::glog +# absl::memory absl::str_format +# gflags::gflags glog::glog # protobuf::libprotobuf -# Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + absl::memory absl::str_format + gflags::gflags glog::glog + protobuf::libprotobuf + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::algorithms ALIAS ${NAME}) diff --git a/ortools/algorithms/dynamic_partition.cc b/ortools/algorithms/dynamic_partition.cc index f2a47ab3419..d814230d69d 100644 --- a/ortools/algorithms/dynamic_partition.cc +++ b/ortools/algorithms/dynamic_partition.cc @@ -15,9 +15,9 @@ #include -#include "ortools/base/join.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/murmur.h" -#include "ortools/base/stringprintf.h" namespace operations_research { diff --git a/ortools/algorithms/find_graph_symmetries.cc b/ortools/algorithms/find_graph_symmetries.cc index fc42462afb0..57b7aa17ccd 100644 --- a/ortools/algorithms/find_graph_symmetries.cc +++ b/ortools/algorithms/find_graph_symmetries.cc @@ -17,16 +17,17 @@ #include #include +#include "absl/memory/memory.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" #include "ortools/algorithms/dense_doubly_linked_list.h" #include "ortools/algorithms/dynamic_partition.h" #include "ortools/algorithms/dynamic_permutation.h" #include "ortools/algorithms/sparse_permutation.h" #include "ortools/base/canonical_errors.h" #include "ortools/base/commandlineflags.h" -#include "ortools/base/join.h" -#include "ortools/base/memory.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/time_support.h" #include "ortools/graph/iterators.h" #include "ortools/graph/util.h" @@ -1017,7 +1018,7 @@ std::string GraphSymmetryFinder::SearchState::DebugString() const { " remaining_pruned_image_nodes=[%s]," " num_parts_before_trying_to_map_base_node=%d }", base_node, first_image_node, - absl::StrJoin(remaining_pruned_image_nodes, " ").c_str(), + absl::StrJoin(remaining_pruned_image_nodes, " "), num_parts_before_trying_to_map_base_node); } diff --git a/ortools/algorithms/find_graph_symmetries.h b/ortools/algorithms/find_graph_symmetries.h index 47c5725b8e0..c7d78f5a978 100644 --- a/ortools/algorithms/find_graph_symmetries.h +++ b/ortools/algorithms/find_graph_symmetries.h @@ -27,10 +27,10 @@ #include #include +#include "absl/time/time.h" #include "ortools/algorithms/dynamic_partition.h" #include "ortools/algorithms/dynamic_permutation.h" #include "ortools/base/status.h" -#include "ortools/base/time_support.h" #include "ortools/graph/graph.h" #include "ortools/graph/iterators.h" #include "ortools/util/stats.h" diff --git a/ortools/algorithms/hungarian.cc b/ortools/algorithms/hungarian.cc index 45abbf4b873..09f5c556168 100644 --- a/ortools/algorithms/hungarian.cc +++ b/ortools/algorithms/hungarian.cc @@ -641,8 +641,8 @@ void HungarianOptimizer::AugmentPath() { void MinimizeLinearAssignment( const std::vector >& cost, - std::unordered_map* direct_assignment, - std::unordered_map* reverse_assignment) { + absl::flat_hash_map* direct_assignment, + absl::flat_hash_map* reverse_assignment) { std::vector agent; std::vector task; HungarianOptimizer hungarian_optimizer(cost); @@ -655,8 +655,8 @@ void MinimizeLinearAssignment( void MaximizeLinearAssignment( const std::vector >& cost, - std::unordered_map* direct_assignment, - std::unordered_map* reverse_assignment) { + absl::flat_hash_map* direct_assignment, + absl::flat_hash_map* reverse_assignment) { std::vector agent; std::vector task; HungarianOptimizer hungarian_optimizer(cost); diff --git a/ortools/algorithms/hungarian.h b/ortools/algorithms/hungarian.h index 0d1e48aa996..3ec8c8a20d8 100644 --- a/ortools/algorithms/hungarian.h +++ b/ortools/algorithms/hungarian.h @@ -33,20 +33,23 @@ #ifndef OR_TOOLS_ALGORITHMS_HUNGARIAN_H_ #define OR_TOOLS_ALGORITHMS_HUNGARIAN_H_ -#include #include +#include "absl/container/flat_hash_map.h" namespace operations_research { // See IMPORTANT NOTE at the top of the file. -void MinimizeLinearAssignment(const std::vector >& cost, - std::unordered_map* direct_assignment, - std::unordered_map* reverse_assignment); +void MinimizeLinearAssignment( + const std::vector >& cost, + absl::flat_hash_map* direct_assignment, + absl::flat_hash_map* reverse_assignment); // See IMPORTANT NOTE at the top of the file. -void MaximizeLinearAssignment(const std::vector >& cost, - std::unordered_map* direct_assignment, - std::unordered_map* reverse_assignment); +void MaximizeLinearAssignment( + const std::vector >& cost, + absl::flat_hash_map* direct_assignment, + absl::flat_hash_map* reverse_assignment); + } // namespace operations_research #endif // OR_TOOLS_ALGORITHMS_HUNGARIAN_H_ diff --git a/ortools/algorithms/knapsack_solver.cc b/ortools/algorithms/knapsack_solver.cc index 9d43657081f..d8a2a08ae00 100644 --- a/ortools/algorithms/knapsack_solver.cc +++ b/ortools/algorithms/knapsack_solver.cc @@ -18,7 +18,7 @@ #include #include -#include "ortools/base/memory.h" +#include "absl/memory/memory.h" #include "ortools/base/stl_util.h" #include "ortools/linear_solver/linear_solver.h" #include "ortools/util/bitset.h" diff --git a/ortools/algorithms/knapsack_solver.h b/ortools/algorithms/knapsack_solver.h index 24eef379d11..dfdc729d9fb 100644 --- a/ortools/algorithms/knapsack_solver.h +++ b/ortools/algorithms/knapsack_solver.h @@ -62,11 +62,11 @@ #include #include +#include "absl/memory/memory.h" #include "ortools/base/basictypes.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/memory.h" #include "ortools/util/time_limit.h" namespace operations_research { diff --git a/ortools/algorithms/sparse_permutation.cc b/ortools/algorithms/sparse_permutation.cc index d39ccc0b13d..bf216c2190d 100644 --- a/ortools/algorithms/sparse_permutation.cc +++ b/ortools/algorithms/sparse_permutation.cc @@ -14,7 +14,7 @@ #include "ortools/algorithms/sparse_permutation.h" #include -#include "ortools/base/join.h" +#include "absl/strings/str_join.h" #include "ortools/base/logging.h" namespace operations_research { diff --git a/ortools/base/CMakeLists.txt b/ortools/base/CMakeLists.txt index 497711e9bdc..9a00672682f 100644 --- a/ortools/base/CMakeLists.txt +++ b/ortools/base/CMakeLists.txt @@ -4,43 +4,59 @@ set(NAME ${PROJECT_NAME}_base) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE # ZLIB::ZLIB -# gflags::gflags -# glog::glog +# absl::base absl::strings absl::str_format +# gflags::gflags glog::glog # protobuf::libprotobuf # Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - ZLIB::ZLIB protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + ZLIB::ZLIB + absl::base absl::strings absl::str_format + gflags::gflags glog::glog + protobuf::libprotobuf + Cbc::Cbc + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::base ALIAS ${NAME}) diff --git a/ortools/base/casts.h b/ortools/base/casts.h deleted file mode 100644 index 16c17d421b1..00000000000 --- a/ortools/base/casts.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file is provided for compatibility purposes. -// -#ifndef OR_TOOLS_BASE_CASTS_H_ -#define OR_TOOLS_BASE_CASTS_H_ - -#include - -namespace absl { -template -inline Dest bit_cast(const Source& source) { - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -} -} // namespace absl - -#endif // OR_TOOLS_BASE_CASTS_H_ diff --git a/ortools/base/commandlineflags.h b/ortools/base/commandlineflags.h index 717631942ee..1ba89c5c15b 100644 --- a/ortools/base/commandlineflags.h +++ b/ortools/base/commandlineflags.h @@ -16,7 +16,7 @@ #include "gflags/gflags.h" -namespace base { +namespace absl { template inline void SetFlag(T* flag, const T& value) { @@ -33,6 +33,14 @@ inline const T& GetFlag(T* flag) { return *flag; } -} // namespace base +template +inline const T& GetFlag(const T& flag) { + return flag; +} + +} // namespace absl + +#define ABSL_DECLARE_FLAG(t, n) DECLARE_##t(n) +#define ABSL_FLAG(t, n, d, h) DEFINE_##t(n, d, h) #endif // OR_TOOLS_BASE_COMMANDLINEFLAGS_H_ diff --git a/ortools/base/file.cc b/ortools/base/file.cc index 3ae44fcf973..1ed1945db57 100644 --- a/ortools/base/file.cc +++ b/ortools/base/file.cc @@ -13,7 +13,7 @@ #include #include -#include "ortools/base/join.h" +#include "absl/strings/str_cat.h" #if defined(_MSC_VER) #include #define access _access diff --git a/ortools/base/file.h b/ortools/base/file.h index 20c37a820c6..f18b80d5d5e 100644 --- a/ortools/base/file.h +++ b/ortools/base/file.h @@ -18,6 +18,7 @@ #include #include +#include "absl/strings/string_view.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/io/tokenizer.h" #include "google/protobuf/message.h" @@ -25,7 +26,6 @@ #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/status.h" -#include "ortools/base/string_view.h" // This file defines some IO interfaces for compatibility with Google // IO specifications. diff --git a/ortools/base/filelineiter.h b/ortools/base/filelineiter.h index b4a6243bc13..03906eac1bb 100644 --- a/ortools/base/filelineiter.h +++ b/ortools/base/filelineiter.h @@ -26,10 +26,9 @@ #ifndef OR_TOOLS_UTIL_FILELINEITER_H_ #define OR_TOOLS_UTIL_FILELINEITER_H_ +#include "absl/strings/match.h" #include "ortools/base/file.h" #include "ortools/base/logging.h" -#include "ortools/base/stringpiece_utils.h" -#include "ortools/base/strutil.h" // Implements the minimum interface for a range-based for loop iterator. class FileLineIterator { diff --git a/ortools/base/inlined_vector.h b/ortools/base/inlined_vector.h deleted file mode 100644 index 871c3e0491f..00000000000 --- a/ortools/base/inlined_vector.h +++ /dev/null @@ -1,726 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_INLINED_VECTOR_H_ -#define OR_TOOLS_BASE_INLINED_VECTOR_H_ - -// An InlinedVector is like a std::vector, except that storage -// for sequences of length <= N are provided inline without requiring -// any heap allocation. Typically N is very small (e.g., 4) so that -// sequences that are expected to be short do not require allocations. -// -// Only some of the std::vector<> operations are currently implemented. -// Other operations may be added as needed to facilitate migrating -// code that uses std::vector<> to InlinedVector<>. - -#include - -#include -#include -#include -#include -#include // NOLINT(build/include_order) -#include -#include -#include -#include - -#include "ortools/base/logging.h" - -namespace absl { - -template > -class InlinedVector { - public: - using allocator_type = A; - using value_type = typename allocator_type::value_type; - using pointer = typename allocator_type::pointer; - using const_pointer = typename allocator_type::const_pointer; - using reference = typename allocator_type::reference; - using const_reference = typename allocator_type::const_reference; - using size_type = typename allocator_type::size_type; - using difference_type = typename allocator_type::difference_type; - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - // Create an empty vector - InlinedVector(); - - // Create a vector with n copies of value_type(). - explicit InlinedVector(size_t n); - - // Create a vector with n copies of elem - InlinedVector(size_t n, const value_type& elem); - - // Create and initialize with the elements [range_start .. range_end). - // The unused enable_if argument restricts this constructor so that it is - // elided when value_type is an integral type. This prevents ambiguous - // interpretation between a call to this constructor with two integral - // arguments and a call to the preceding (n, elem) constructor. - template - InlinedVector( - InputIterator range_start, InputIterator range_end, - typename std::enable_if::value>::type* = - NULL) { - InitRep(); - AppendRange(range_start, range_end); - } - - InlinedVector(std::initializer_list init) { - InitRep(); - AppendRange(init.begin(), init.end()); - } - - InlinedVector(const InlinedVector& v); - - ~InlinedVector() { clear(); } - - InlinedVector& operator=(const InlinedVector& v) { - // Optimized to avoid reallocation. - // Prefer reassignment to copy construction for elements. - const size_t s = size(); - const size_t vs = v.size(); - if (s < vs) { // grow - reserve(vs); - if (s) std::copy(v.begin(), v.begin() + s, begin()); - std::copy(v.begin() + s, v.end(), std::back_inserter(*this)); - } else { // maybe shrink - erase(begin() + vs, end()); - std::copy(v.begin(), v.end(), begin()); - } - return *this; - } - - size_t size() const { return size_internal(); } - - bool empty() const { return (size() == 0); } - - // Return number of elements that can be stored in vector - // without requiring a reallocation of underlying memory - size_t capacity() const { - if (is_inline()) { - return kFit; - } else { - return static_cast(1) << u_.data[kSize - 2]; - } - } - - // Return a pointer to the underlying array. - // Only result[0,size()-1] are defined. - pointer data() { - if (is_inline()) { - return reinterpret_cast(u_.data); - } else { - return outofline_pointer(); - } - } - const_pointer data() const { - return const_cast*>(this)->data(); - } - - // Remove all elements - void clear() { - DiscardStorage(); - u_.data[kSize - 1] = 0; - } - - template - void assign( - InputIterator range_start, InputIterator range_end, - typename std::enable_if::value>::type* = - NULL) { - clear(); - AppendRange(range_start, range_end); - } - - // Return the ith element - // REQUIRES: 0 <= i < size() - const value_type& at(size_t i) const { - DCHECK_LT(i, size()); - return data()[i]; - } - const value_type& operator[](size_t i) const { - DCHECK_LT(i, size()); - return data()[i]; - } - - // Return a non-const reference to the ith element - // REQUIRES: 0 <= i < size() - value_type& at(size_t i) { - DCHECK_LT(i, size()); - return data()[i]; - } - value_type& operator[](size_t i) { - DCHECK_LT(i, size()); - return data()[i]; - } - - value_type& back() { - DCHECK(!empty()); - return at(size() - 1); - } - - const value_type& back() const { - DCHECK(!empty()); - return at(size() - 1); - } - - value_type& front() { - DCHECK(!empty()); - return at(0); - } - - const value_type& front() const { - DCHECK(!empty()); - return at(0); - } - - // Append a T constructed with args to the vector. - // Increases size() by one. - // Amortized complexity: O(1) - // Worst-case complexity: O(size()) - template - void emplace_back(Args&&... args) { - size_t s = size(); - DCHECK_LE(s, capacity()); - if (s < capacity()) { - new (data() + s) T(std::forward(args)...); - set_size_internal(s + 1); - } else { - EmplaceBackSlow(std::forward(args)...); - } - } - - // Append t to the vector. - // Increases size() by one. - // Amortized complexity: O(1) - // Worst-case complexity: O(size()) - void push_back(const value_type& t) { emplace_back(t); } - void push_back(value_type&& t) { emplace_back(std::move(t)); } - - inline void pop_back() { - DCHECK(!empty()); - const size_t s = size(); - Destroy(data() + s - 1, 1); - set_size_internal(s - 1); - } - - // Resizes the vector to contain "n" elements. - // If "n" is smaller than the initial size, extra elements are destroyed. - // If "n" is larger than the initial size, enough copies of "elem" - // are appended to increase the size to "n". If "elem" is omitted, - // new elements are value-initialized. - void resize(size_t n) { Resize(n, nullptr); } - void resize(size_t n, const value_type& elem) { Resize(n, &elem); } - - // InlinedVector::begin() - // - // Returns an iterator to the beginning of the inlined vector. - iterator begin() noexcept { return data(); } - - // Overload of InlinedVector::begin() for returning a const iterator to the - // beginning of the inlined vector. - const_iterator begin() const noexcept { return data(); } - - // InlinedVector::cbegin() - // - // Returns a const iterator to the beginning of the inlined vector. - const_iterator cbegin() const noexcept { return begin(); } - - // InlinedVector::end() - // - // Returns an iterator to the end of the inlined vector. - iterator end() noexcept { return data() + size(); } - - // Overload of InlinedVector::end() for returning a const iterator to the end - // of the inlined vector. - const_iterator end() const noexcept { return data() + size(); } - - // InlinedVector::cend() - // - // Returns a const iterator to the end of the inlined vector. - const_iterator cend() const noexcept { return end(); } - - // InlinedVector::rbegin() - // - // Returns a reverse iterator from the end of the inlined vector. - reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - - // Overload of InlinedVector::rbegin() for returning a const reverse iterator - // from the end of the inlined vector. - const_reverse_iterator rbegin() const noexcept { - return const_reverse_iterator(end()); - } - - // InlinedVector::crbegin() - // - // Returns a const reverse iterator from the end of the inlined vector. - const_reverse_iterator crbegin() const noexcept { return rbegin(); } - - // InlinedVector::rend() - // - // Returns a reverse iterator from the beginning of the inlined vector. - reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - - // Overload of InlinedVector::rend() for returning a const reverse iterator - // from the beginning of the inlined vector. - const_reverse_iterator rend() const noexcept { - return const_reverse_iterator(begin()); - } - - // InlinedVector::crend() - // - // Returns a reverse iterator from the beginning of the inlined vector. - const_reverse_iterator crend() const noexcept { return rend(); } - - iterator insert(iterator pos, const value_type& v); - - iterator erase(iterator pos) { - DCHECK_LT(pos, end()); - DCHECK_GE(pos, begin()); - std::copy(pos + 1, end(), pos); - pop_back(); - return pos; - } - - iterator erase(iterator first, iterator last); - - // Enlarges the underlying representation so it can hold at least - // "n" elements without reallocation. - // Does not change size() or the actual contents of the vector. - void reserve(size_t n) { - if (n > capacity()) { - // Make room for new elements - Grow(n); - } - } - - // Swap the contents of *this with other. - // REQUIRES: value_type is swappable and copyable. - void swap(InlinedVector& other); - - private: - // Representation can either be inlined or out-of-line. - // In either case, at least sizeof(void*) + 8 bytes are available. - // - // Inlined: - // Last byte holds the length. - // First (length*sizeof(T)) bytes stores the elements. - // Outlined: - // Last byte holds kSentinel. - // Second-last byte holds lg(capacity) - // Preceding 6 bytes hold size. - // First sizeof(T*) bytes hold pointer. - - // Compute rep size. - static const size_t kSizeUnaligned = N * sizeof(T) + 1; // Room for tag - static const size_t kSize = ((kSizeUnaligned + 15) / 16) * 16; // Align - - // See how many fit T we can fit inside kSize, but no more than 254 - // since 255 is used as sentinel tag for out-of-line allocation. - static const unsigned int kSentinel = 255; - static const size_t kFit1 = (kSize - 1) / sizeof(T); - static const size_t kFit = (kFit1 >= kSentinel) ? (kSentinel - 1) : kFit1; - - union { - unsigned char data[kSize]; - // Force data to be aligned enough for a pointer. - T* unused_aligner; - } u_; - - inline void InitRep() { u_.data[kSize - 1] = 0; } - inline bool is_inline() const { return u_.data[kSize - 1] != kSentinel; } - - inline T* outofline_pointer() const { - T* ptr; - memcpy(&ptr, &u_.data[0], sizeof(ptr)); - return ptr; - } - - inline void set_outofline_pointer(T* p) { - memcpy(&u_.data[0], &p, sizeof(p)); - } - - inline uint64_t outofline_word() const { - uint64_t word; - memcpy(&word, &u_.data[kSize - 8], sizeof(word)); - return word; - } - - inline void set_outofline_word(uint64_t w) { - memcpy(&u_.data[kSize - 8], &w, sizeof(w)); - } - - inline size_t size_internal() const { - uint8_t s = static_cast(u_.data[kSize - 1]); - if (s != kSentinel) { - return static_cast(s); - } else { - const uint64_t word = outofline_word(); - // The sentinel and capacity bits are most-significant bits in word. - return static_cast(word & 0xffffffffffffull); - } - } - - void set_size_internal(size_t n) { - if (is_inline()) { - DCHECK_LT(n, kSentinel); - u_.data[kSize - 1] = static_cast(n); - } else { - uint64_t word; - // The sentinel and capacity bits are most-significant bits in word. - word = (static_cast(n) | - (static_cast(u_.data[kSize - 2]) << 48) | - (static_cast(kSentinel) << 56)); - set_outofline_word(word); - DCHECK_EQ(u_.data[kSize - 1], kSentinel) << n; - } - } - - void DiscardStorage() { - T* base = data(); - size_t n = size(); - Destroy(base, n); - if (!is_inline()) { - free(base); - } - } - - template - void EmplaceBackSlow(Args&&... args) { - const size_t s = size(); - DCHECK_EQ(s, capacity()); - Grow(s + 1, std::forward(args)...); - set_size_internal(s + 1); - } - - // Movers for Grow - // Does nothing. - static void Nop(T* src, size_t n, T* dst) {} - - // Moves srcs[0,n-1] contents to dst[0,n-1]. - static void Move(T* src, size_t n, T* dst) { - for (size_t i = 0; i < n; i++) { - new (dst + i) T(std::move(*(src + i))); - } - } - - // Initializers for Resize. - // Initializes dst[0,n-1] with empty constructor. - static void ValueInit(const T*, size_t n, T* dst) { - for (size_t i = 0; i < n; i++) { - new (dst + i) T(); - } - } - - // Initializes dst[0,n-1] with copies of *src. - static void Fill(const T* src, size_t n, T* dst) { - for (size_t i = 0; i < n; i++) { - new (dst + i) T(*src); - } - } - - void Destroy(T* src, int n) { - if (!std::is_trivially_destructible::value) { - for (int i = 0; i < n; i++) { - (src + i)->~T(); - } - } - } - - // Initialization methods for Grow. - // 1) Leave uninitialized memory. - struct Uninitialized { - void operator()(T*) const {} - }; - // 2) Construct a T with args at not-yet-initialized memory pointed by dst. - struct Construct { - template - void operator()(T* dst, Args&&... args) const { - new (dst) T(std::forward(args)...); - } - }; - - // Grow so that capacity >= n. Uses Mover to move existing elements - // to new buffer, and possibly initialize the new element according - // to InitType. - // We pass the InitType and Mover as template arguments so that - // this code compiles even if T does not support copying or default - // construction. - template - void Grow(size_t n, Args&&... args) { - size_t s = size(); - DCHECK_LE(s, capacity()); - - // Compute new capacity by repeatedly doubling current capacity - size_t target = 1; - size_t target_lg = 0; - while (target < kFit || target < n) { - target_lg++; - target <<= 1; - } - - T* src = data(); - T* dst = static_cast(malloc(target * sizeof(T))); - - // Need to copy elem before discarding src since it might alias src. - InitType{}(dst + s, std::forward(args)...); - Mover(src, s, dst); - DiscardStorage(); - - u_.data[kSize - 1] = kSentinel; - u_.data[kSize - 2] = static_cast(target_lg); - set_size_internal(s); - DCHECK_EQ(capacity(), target); - set_outofline_pointer(dst); - } - - // Resize to size n. Any new elements are initialized by passing - // elem and the destination to Initializer. We pass the Initializer - // as a template argument so that this code compiles even if T does - // not support copying. - template - void Resize(size_t n, const T* elem) { - size_t s = size(); - if (n <= s) { - Destroy(data() + n, s - n); - set_size_internal(n); - return; - } - reserve(n); - DCHECK_GE(capacity(), n); - set_size_internal(n); - Initializer(elem, n - s, data() + s); - } - - template - void AppendRange(Iter first, Iter last, std::input_iterator_tag); - - // Faster path for forward iterators. - template - void AppendRange(Iter first, Iter last, std::forward_iterator_tag); - - template - void AppendRange(Iter first, Iter last); -}; - -// Provide linkage for constants. -template -const size_t InlinedVector::kSizeUnaligned; -template -const size_t InlinedVector::kSize; -template -const unsigned int InlinedVector::kSentinel; -template -const size_t InlinedVector::kFit1; -template -const size_t InlinedVector::kFit; - -template -inline void swap(InlinedVector& a, InlinedVector& b) { - a.swap(b); -} - -template -inline bool operator==(const InlinedVector& a, - const InlinedVector& b) { - return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); -} - -template -inline bool operator!=(const InlinedVector& a, - const InlinedVector& b) { - return !(a == b); -} - -template -inline bool operator<(const InlinedVector& a, - const InlinedVector& b) { - return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); -} - -template -inline bool operator>(const InlinedVector& a, - const InlinedVector& b) { - return b < a; -} - -template -inline bool operator<=(const InlinedVector& a, - const InlinedVector& b) { - return !(b < a); -} - -template -inline bool operator>=(const InlinedVector& a, - const InlinedVector& b) { - return !(a < b); -} - -// ======================================== -// Implementation - -template -inline InlinedVector::InlinedVector() { - InitRep(); -} - -template -inline InlinedVector::InlinedVector(size_t n) { - InitRep(); - if (n > capacity()) { - Grow(n); // Must use Nop in case T is not copyable - } - set_size_internal(n); - ValueInit(nullptr, n, data()); -} - -template -inline InlinedVector::InlinedVector(size_t n, const value_type& elem) { - InitRep(); - if (n > capacity()) { - Grow(n); // Can use Nop since we know we have nothing to copy - } - set_size_internal(n); - Fill(&elem, n, data()); -} - -template -inline InlinedVector::InlinedVector(const InlinedVector& v) { - InitRep(); - *this = v; -} - -template -typename InlinedVector::iterator InlinedVector::insert( - iterator pos, const value_type& v) { - DCHECK_GE(pos, begin()); - DCHECK_LE(pos, end()); - if (pos == end()) { - push_back(v); - return end() - 1; - } - size_t s = size(); - size_t idx = std::distance(begin(), pos); - if (s == capacity()) { - Grow(s + 1); - } - CHECK_LT(s, capacity()); - pos = begin() + idx; // Reset 'pos' into a post-enlarge iterator. - Fill(data() + s - 1, 1, data() + s); // data[s] = data[s-1] - std::copy_backward(pos, data() + s - 1, data() + s); - *pos = v; - - set_size_internal(s + 1); - return pos; -} - -template -typename InlinedVector::iterator InlinedVector::erase( - iterator first, iterator last) { - DCHECK_LE(begin(), first); - DCHECK_LE(first, last); - DCHECK_LE(last, end()); - - size_t s = size(); - ptrdiff_t erase_gap = std::distance(first, last); - std::copy(last, data() + s, first); - Destroy(data() + s - erase_gap, erase_gap); - set_size_internal(s - erase_gap); - return first; -} - -template -void InlinedVector::swap(InlinedVector& other) { - using std::swap; // Augment ADL with std::swap. - if (&other == this) { - return; - } - - InlinedVector* a = this; - InlinedVector* b = &other; - - const bool a_inline = a->is_inline(); - const bool b_inline = b->is_inline(); - - if (!a_inline && !b_inline) { - // Just swap the top-level representations. - T* aptr = a->outofline_pointer(); - T* bptr = b->outofline_pointer(); - a->set_outofline_pointer(bptr); - b->set_outofline_pointer(aptr); - - uint64_t aword = a->outofline_word(); - uint64_t bword = b->outofline_word(); - a->set_outofline_word(bword); - b->set_outofline_word(aword); - return; - } - - // Make a the larger of the two to reduce number of cases. - size_t a_size = a->size(); - size_t b_size = b->size(); - if (a->size() < b->size()) { - swap(a, b); - swap(a_size, b_size); - } - DCHECK_GE(a_size, b_size); - - if (b->capacity() < a_size) { - b->Grow(a_size); - } - - // One is inline and one is not. - // 'a' is larger. Swap the elements up to the smaller array size. - std::swap_ranges(a->data(), a->data() + b_size, b->data()); - std::uninitialized_copy(a->data() + b_size, a->data() + a_size, - b->data() + b_size); - Destroy(a->data() + b_size, a_size - b_size); - a->set_size_internal(b_size); - b->set_size_internal(a_size); - DCHECK_EQ(b->size(), a_size); - DCHECK_EQ(a->size(), b_size); -} - -template -template -inline void InlinedVector::AppendRange(Iter first, Iter last, - std::input_iterator_tag) { - std::copy(first, last, std::back_inserter(*this)); -} - -template -template -inline void InlinedVector::AppendRange(Iter first, Iter last, - std::forward_iterator_tag) { - typedef typename std::iterator_traits::difference_type Length; - Length length = std::distance(first, last); - size_t s = size(); - reserve(s + length); - std::uninitialized_copy_n(first, length, data() + s); - set_size_internal(s + length); -} - -template -template -inline void InlinedVector::AppendRange(Iter first, Iter last) { - typedef typename std::iterator_traits::iterator_category IterTag; - AppendRange(first, last, IterTag()); -} - -} // namespace absl - -#endif // OR_TOOLS_BASE_INLINED_VECTOR_H_ diff --git a/ortools/base/int_type.h b/ortools/base/int_type.h index 1be5b486fd3..9632d83b2a4 100644 --- a/ortools/base/int_type.h +++ b/ortools/base/int_type.h @@ -147,7 +147,6 @@ #include #include #include // NOLINT -#include #include "ortools/base/macros.h" diff --git a/ortools/base/join.cc b/ortools/base/join.cc deleted file mode 100644 index 566dc71d54b..00000000000 --- a/ortools/base/join.cc +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/base/join.h" -#include -#include "ortools/base/basictypes.h" -#include "ortools/base/string_view.h" -#include "ortools/base/stringprintf.h" - -namespace absl { - -void StrAppend(std::string* s, const AlphaNum& a) { - s->append(a.data(), a.size()); -} -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b) { - s->reserve(s->size() + a.size() + b.size()); - s->append(a.data(), a.size()); - s->append(b.data(), b.size()); -} -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c) { - s->reserve(s->size() + a.size() + b.size() + c.size()); - s->append(a.data(), a.size()); - s->append(b.data(), b.size()); - s->append(c.data(), c.size()); -} -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d) { - s->reserve(s->size() + a.size() + b.size() + c.size() + d.size()); - s->append(a.data(), a.size()); - s->append(b.data(), b.size()); - s->append(c.data(), c.size()); - s->append(d.data(), d.size()); -} -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e) { - StrAppend(s, a, b, c, d); - StrAppend(s, e); -} -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f) { - StrAppend(s, a, b, c, d); - StrAppend(s, e, f); -} -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g) { - StrAppend(s, a, b, c, d); - StrAppend(s, e, f, g); -} -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h) { - StrAppend(s, a, b, c, d); - StrAppend(s, e, f, g, h); -} - -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, - const AlphaNum& i) { - StrAppend(s, a, b, c, d); - StrAppend(s, e, f, g, h, i); -} - -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, - const AlphaNum& i, const AlphaNum& j) { - StrAppend(s, a, b, c, d); - StrAppend(s, e, f, g, h, i, j); -} - -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, - const AlphaNum& i, const AlphaNum& j, const AlphaNum& k) { - StrAppend(s, a, b, c, d); - StrAppend(s, e, f, g, h, i, j, k); -} - -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, - const AlphaNum& i, const AlphaNum& j, const AlphaNum& k, - const AlphaNum& l) { - StrAppend(s, a, b, c, d, e); - StrAppend(s, f, g, h, i, j, k, l); -} - -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, - const AlphaNum& i, const AlphaNum& j, const AlphaNum& k, - const AlphaNum& l, const AlphaNum& m) { - StrAppend(s, a, b, c, d, e, f); - StrAppend(s, g, h, i, j, k, l, m); -} - -std::string StrCat(const AlphaNum& a) { - return std::string(a.data(), a.size()); -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b) { - std::string out; - StrAppend(&out, a, b); - return out; -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) { - std::string out; - StrAppend(&out, a, b, c); - return out; -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d) { - std::string out; - StrAppend(&out, a, b, c, d); - return out; -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e) { - std::string out; - StrAppend(&out, a, b, c, d, e); - return out; -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f) { - std::string out; - StrAppend(&out, a, b, c, d, e, f); - return out; -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g) { - std::string out; - StrAppend(&out, a, b, c, d, e, f, g); - return out; -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h) { - std::string out; - StrAppend(&out, a, b, c, d, e, f, g, h); - return out; -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h, const AlphaNum& i) { - std::string out; - StrAppend(&out, a, b, c, d, e, f, g, h, i); - return out; -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, - const AlphaNum& j) { - std::string out; - StrAppend(&out, a, b, c, d, e, f, g, h, i, j); - return out; -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, - const AlphaNum& j, const AlphaNum& k) { - std::string out; - StrAppend(&out, a, b, c, d, e, f, g, h, i, j, k); - return out; -} - -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, - const AlphaNum& j, const AlphaNum& k, const AlphaNum& l) { - std::string out; - StrAppend(&out, a, b, c, d, e, f, g, h, i, j, k, l); - return out; -} -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, - const AlphaNum& j, const AlphaNum& k, const AlphaNum& l, - const AlphaNum& m) { - std::string out; - StrAppend(&out, a, b, c, d, e, f, g, h, i, j, k, l, m); - return out; -} - -} // namespace absl diff --git a/ortools/base/join.h b/ortools/base/join.h deleted file mode 100644 index 7ee79ae1368..00000000000 --- a/ortools/base/join.h +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_JOIN_H_ -#define OR_TOOLS_BASE_JOIN_H_ - -#include -#include - -#include "ortools/base/basictypes.h" -#include "ortools/base/integral_types.h" -#include "ortools/base/string_view.h" - -namespace absl { - -// A buffer size large enough for all FastToBuffer functions. -const int kFastToBufferSize = 32; - -// Writes output to the beginning of the given buffer. Returns a pointer to the -// end of the std::string (i.e. to the NUL char). Buffer must be at least 12 -// bytes. Not actually fast, but maybe someday! -template -char* NumToBuffer(T i, char* buffer) { - std::stringstream ss; - ss << i; - const std::string s = ss.str(); - strncpy(buffer, s.c_str(), kFastToBufferSize); // NOLINT - return buffer + s.size(); -} - -struct AlphaNum { - absl::string_view piece; - char digits[kFastToBufferSize]; - - // No bool ctor -- bools convert to an integral type. - // A bool ctor would also convert incoming pointers (bletch). - - AlphaNum(int32 i32) // NOLINT(runtime/explicit) - : piece(digits, NumToBuffer(i32, digits) - &digits[0]) {} - AlphaNum(uint32 u32) // NOLINT(runtime/explicit) - : piece(digits, NumToBuffer(u32, digits) - &digits[0]) {} - AlphaNum(long l) // NOLINT - : piece(digits, NumToBuffer(l, digits) - &digits[0]) {} - AlphaNum(unsigned long ul) // NOLINT - : piece(digits, NumToBuffer(ul, digits) - &digits[0]) {} - AlphaNum(int64 i64) // NOLINT(runtime/explicit) - : piece(digits, NumToBuffer(i64, digits) - &digits[0]) {} - AlphaNum(uint64 u64) // NOLINT(runtime/explicit) - : piece(digits, NumToBuffer(u64, digits) - &digits[0]) {} - AlphaNum(float f) // NOLINT(runtime/explicit) - : piece(digits, strlen(NumToBuffer(f, digits))) {} - AlphaNum(double f) { // NOLINT(runtime/explicit) - snprintf(digits, kFastToBufferSize, "%lf", f); - piece.set(digits); - } - AlphaNum(const char* c_str) : piece(c_str) {} // NOLINT(runtime/explicit) - AlphaNum(const absl::string_view& pc) - : piece(pc) {} // NOLINT(runtime/explicit) - AlphaNum(const std::string& s) : piece(s) {} // NOLINT(runtime/explicit) - - absl::string_view::size_type size() const { return piece.size(); } - const char* data() const { return piece.data(); } - - private: - // Use ":" not ':' - AlphaNum(char c); // NOLINT(runtime/explicit) -}; - -extern AlphaNum gEmptyAlphaNum; - -std::string StrCat(const AlphaNum& a); -std::string StrCat(const AlphaNum& a, const AlphaNum& b); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h, const AlphaNum& i); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, - const AlphaNum& j); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, - const AlphaNum& j, const AlphaNum& k); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, - const AlphaNum& j, const AlphaNum& k, const AlphaNum& l); -std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d, const AlphaNum& e, const AlphaNum& f, - const AlphaNum& g, const AlphaNum& h, const AlphaNum& i, - const AlphaNum& j, const AlphaNum& k, const AlphaNum& l, - const AlphaNum& m); - -void StrAppend(std::string* s, const AlphaNum& a); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, - const AlphaNum& i); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, - const AlphaNum& i, const AlphaNum& j); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, - const AlphaNum& i, const AlphaNum& j, const AlphaNum& k); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, - const AlphaNum& i, const AlphaNum& j, const AlphaNum& k, - const AlphaNum& l); -void StrAppend(std::string* s, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AlphaNum& f, const AlphaNum& g, const AlphaNum& h, - const AlphaNum& i, const AlphaNum& j, const AlphaNum& k, - const AlphaNum& l, const AlphaNum& m); - -template -std::string StrJoin(const Iterable& elements, const std::string& separator) { - std::string out; - for (const auto& e : elements) { - if (!out.empty()) out += separator; - StrAppend(&out, e); - } - return out; -} - -template -const T& LegacyPrecision(const T& t) { - return t; -} -} // namespace absl - -// Temporary aliases to support old code not using the absl:: namespace. -using absl::StrAppend; // NOLINT(readability/namespace) -using absl::StrCat; // NOLINT(readability/namespace) - -#endif // OR_TOOLS_BASE_JOIN_H_ diff --git a/ortools/base/macros.h b/ortools/base/macros.h index 752b91f82ac..5e6ba0def4f 100644 --- a/ortools/base/macros.h +++ b/ortools/base/macros.h @@ -16,12 +16,6 @@ #include // for size_t. -#if (defined(COMPILER_GCC3) || defined(OS_MACOSX)) && !defined(SWIG) -#define ATTRIBUTE_UNUSED __attribute__((__unused__)) -#else // GCC -#define ATTRIBUTE_UNUSED -#endif // GCC - #define COMPILE_ASSERT(x, msg) #ifdef NDEBUG @@ -36,18 +30,6 @@ const bool DEBUG_MODE = true; TypeName(const TypeName&); \ void operator=(const TypeName&) -// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through -// between switch labels. -#ifndef FALLTHROUGH_INTENDED -#define FALLTHROUGH_INTENDED \ - do { \ - } while (0) -#endif -// Alternate name. -#ifndef ABSL_FALLTHROUGH_INTENDED -#define ABSL_FALLTHROUGH_INTENDED FALLTHROUGH_INTENDED -#endif - template char (&ArraySizeHelper(T (&array)[N]))[N]; #ifndef COMPILER_MSVC diff --git a/ortools/base/match.h b/ortools/base/match.h deleted file mode 100644 index ce4cf71601b..00000000000 --- a/ortools/base/match.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_MATCH_H_ -#define OR_TOOLS_BASE_MATCH_H_ - -#include - -#include "ortools/base/string_view.h" - -namespace absl { -// Returns whether s begins with x. -inline bool StartsWith(string_view s, string_view x) { - return s.size() >= x.size() && memcmp(s.data(), x.data(), x.size()) == 0; -} - -// Returns whether s ends with x. -inline bool EndsWith(string_view s, string_view x) { - return s.size() >= x.size() && - memcmp(s.data() + (s.size() - x.size()), x.data(), x.size()) == 0; -} -} // namespace absl -#endif // OR_TOOLS_BASE_MATCH_H_ diff --git a/ortools/base/mathutil.h b/ortools/base/mathutil.h index bed0b136305..79691ec91ad 100644 --- a/ortools/base/mathutil.h +++ b/ortools/base/mathutil.h @@ -18,8 +18,8 @@ #include #include +#include "absl/base/casts.h" #include "ortools/base/basictypes.h" -#include "ortools/base/casts.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" diff --git a/ortools/base/memory.h b/ortools/base/memory.h deleted file mode 100644 index 6a75811a1ad..00000000000 --- a/ortools/base/memory.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_MEMORY_H_ -#define OR_TOOLS_BASE_MEMORY_H_ - -#include - -namespace absl { - -template -std::unique_ptr make_unique(Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); -} - -} // namespace absl - -#endif // OR_TOOLS_BASE_MEMORY_H_ diff --git a/ortools/base/mutex.cc b/ortools/base/mutex.cc deleted file mode 100644 index 039a57c7758..00000000000 --- a/ortools/base/mutex.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include // NOLINT - -#include "ortools/base/mutex.h" - -namespace absl { -Mutex::Mutex() {} -Mutex::~Mutex() {} -void Mutex::Lock() { real_mutex_.lock(); } -void Mutex::Unlock() { real_mutex_.unlock(); } -bool Mutex::TryLock() { return real_mutex_.try_lock(); } - -CondVar::CondVar() {} -CondVar::~CondVar() {} -void CondVar::Wait(Mutex* const mu) { - std::unique_lock mutex_lock(mu->real_mutex_); - real_condition_.wait(mutex_lock); -} -void CondVar::Signal() { real_condition_.notify_one(); } -void CondVar::SignalAll() { real_condition_.notify_all(); } -} // namespace absl diff --git a/ortools/base/mutex.h b/ortools/base/mutex.h deleted file mode 100644 index b9ece7face3..00000000000 --- a/ortools/base/mutex.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_MUTEX_H_ -#define OR_TOOLS_BASE_MUTEX_H_ - -#include // NOLINT -#include // NOLINT - -#include "ortools/base/macros.h" - -namespace absl { -class Mutex { - public: - Mutex(); - ~Mutex(); - void Lock(); - void Unlock(); - bool TryLock(); - - friend class CondVar; - - private: - std::mutex real_mutex_; - DISALLOW_COPY_AND_ASSIGN(Mutex); -}; - -class MutexLock { - public: - explicit MutexLock(Mutex* const mutex) : mutex_(mutex) { - this->mutex_->Lock(); - } - - ~MutexLock() { this->mutex_->Unlock(); } - - private: - Mutex* const mutex_; - DISALLOW_COPY_AND_ASSIGN(MutexLock); -}; - -class CondVar { - public: - CondVar(); - ~CondVar(); - void Wait(Mutex* const mu); - void Signal(); - void SignalAll(); - - private: - std::condition_variable real_condition_; - DISALLOW_COPY_AND_ASSIGN(CondVar); -}; - -// Checking macros. -#define EXCLUSIVE_LOCK_FUNCTION(x) -#define UNLOCK_FUNCTION(x) -#define LOCKS_EXCLUDED(x) -#define EXCLUSIVE_LOCKS_REQUIRED(x) -#define NO_THREAD_SAFETY_ANALYSIS -#define GUARDED_BY(x) -} // namespace absl -#endif // OR_TOOLS_BASE_MUTEX_H_ diff --git a/ortools/base/notification.cc b/ortools/base/notification.cc deleted file mode 100644 index 42e74ef1474..00000000000 --- a/ortools/base/notification.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/base/notification.h" - -#include -#include // NOLINT -#include // NOLINT - -namespace absl { -void Notification::Notify() { - std::unique_lock mutex_lock(mutex_); - notified_yet_.store(true, std::memory_order_release); - condition_.notify_all(); -} - -Notification::~Notification() { - // Make sure that the thread running Notify() exits before the object is - // destructed. - std::unique_lock mutex_lock(mutex_); -} - -static inline bool HasBeenNotifiedInternal( - const std::atomic *notified_yet) { - return notified_yet->load(std::memory_order_acquire); -} - -bool Notification::HasBeenNotified() const { - return HasBeenNotifiedInternal(&this->notified_yet_); -} - -void Notification::WaitForNotification() { - if (!HasBeenNotifiedInternal(&this->notified_yet_)) { - std::unique_lock mutex_lock(mutex_); - condition_.wait(mutex_lock); - } -} - -} // namespace absl diff --git a/ortools/base/notification.h b/ortools/base/notification.h deleted file mode 100644 index 508ccd79d8c..00000000000 --- a/ortools/base/notification.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_NOTIFICATION_H_ -#define OR_TOOLS_BASE_NOTIFICATION_H_ - -#include -#include // NOLINT -#include // NOLINT - -namespace absl { - -// ----------------------------------------------------------------------------- -// Notification -// ----------------------------------------------------------------------------- -class Notification { - public: - // Initializes the "notified" state to unnotified. - Notification() : notified_yet_(false) {} - Notification(const Notification&) = delete; - Notification& operator=(const Notification&) = delete; - ~Notification(); - - // Notification::HasBeenNotified() - // - // Returns the value of the notification's internal "notified" state. - bool HasBeenNotified() const; - - // Notification::WaitForNotification() - // - // Blocks the calling thread until the notification's "notified" state is - // `true`. Note that if `Notify()` has been previously called on this - // notification, this function will immediately return. - void WaitForNotification(); - - // Notification::Notify() - // - // Sets the "notified" state of this notification to `true` and wakes waiting - // threads. Note: do not call `Notify()` multiple times on the same - // `Notification`; calling `Notify()` more than once on the same notification - // results in undefined behavior. - void Notify(); - - private: - std::mutex mutex_; - std::condition_variable condition_; - std::atomic notified_yet_; // written under mutex_ -}; - -} // namespace absl - -#endif // OR_TOOLS_BASE_NOTIFICATION_H_ diff --git a/ortools/base/numbers.cc b/ortools/base/numbers.cc deleted file mode 100644 index 6ba5cffd4a7..00000000000 --- a/ortools/base/numbers.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/base/numbers.h" - -#include -#include - -#ifdef _MSC_VER -#define strtof strtod -#define strtoll _strtoi64 -#endif // _MSC_VER - -namespace strings { - -bool safe_strtof(const char* str, float* value) { - char* endptr; - *value = strtof(str, &endptr); - if (endptr != str) { - while (isspace(*endptr)) ++endptr; - } - // Ignore range errors from strtod/strtof. - // The values it returns on underflow and - // overflow are the right fallback in a - // robust setting. - return *str != '\0' && *endptr == '\0'; -} - -bool safe_strtod(const char* str, double* value) { - char* endptr; - *value = strtod(str, &endptr); - if (endptr != str) { - while (isspace(*endptr)) ++endptr; - } - // Ignore range errors from strtod. The values it - // returns on underflow and overflow are the right - // fallback in a robust setting. - return *str != '\0' && *endptr == '\0'; -} - -bool safe_strtof(const std::string& str, float* value) { - return safe_strtof(str.c_str(), value); -} - -bool safe_strtod(const std::string& str, double* value) { - return safe_strtod(str.c_str(), value); -} - -bool safe_strto64(const std::string& str, int64* value) { - if (str.empty()) return false; - char* endptr; - *value = strtoll(str.c_str(), &endptr, /*base=*/10); // NOLINT - return *endptr == '\0' && str[0] != '\0'; -} - -bool safe_strto32(const std::string& str, int* value) { - if (str.empty()) return false; - char* endptr; - *value = strtol(str.c_str(), &endptr, /*base=*/10); // NOLINT - return *endptr == '\0' && str[0] != '\0'; -} - -} // namespace strings -#undef strtof -#undef strtoll diff --git a/ortools/base/numbers.h b/ortools/base/numbers.h deleted file mode 100644 index 383a3799db3..00000000000 --- a/ortools/base/numbers.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_NUMBERS_H_ -#define OR_TOOLS_BASE_NUMBERS_H_ - -#include -#include "ortools/base/integral_types.h" -#include "ortools/base/join.h" - -namespace strings { - -// Convert strings to numerical values. -// Leading and trailing spaces are allowed. -// Values may be rounded on over- and underflow. -bool safe_strtof(const char* str, float* value); -bool safe_strtod(const char* str, double* value); -bool safe_strtof(const std::string& str, float* value); -bool safe_strtod(const std::string& str, double* value); -bool safe_strto64(const std::string& str, int64* value); -bool safe_strto32(const std::string& str, int* value); -// Converting int to std::string. -inline std::string SimpleItoa(int i) { return absl::StrCat(i); } - -} // namespace strings - -#endif // OR_TOOLS_BASE_NUMBERS_H_ diff --git a/ortools/base/optional.h b/ortools/base/optional.h deleted file mode 100644 index 501fe538ae3..00000000000 --- a/ortools/base/optional.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_OPTIONAL_H_ -#define OR_TOOLS_BASE_OPTIONAL_H_ - -namespace absl { - -template -class optional { - public: - optional() : has_(false) {} - optional& operator=(const T& t) { - has_ = true; - data_ = t; - return *this; - } - const T& data() const { return data_; } - constexpr const T& operator*() const& { return reference(); } - T& operator*() & { - assert(this->has_); - return reference(); - } - constexpr explicit operator bool() const noexcept { return has_; } - const T* operator->() const { - assert(this->has_); - return std::addressof(this->data_); - } - T* operator->() { - assert(this->has_); - return std::addressof(this->data_); - } - - private: - // Private accessors for internal storage viewed as reference to T. - constexpr const T& reference() const { return this->data_; } - T& reference() { return this->data_; } - bool has_; - T data_; -}; - -} // namespace absl - -#endif // OR_TOOLS_BASE_OPTIONAL_H_ diff --git a/ortools/base/port.h b/ortools/base/port.h deleted file mode 100644 index 8f09a0be30e..00000000000 --- a/ortools/base/port.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_PORT_H_ -#define OR_TOOLS_BASE_PORT_H_ - -// Tell the compiler to warn about unused return values for functions declared -// with this macro. The macro should be used on function declarations -// following the argument list: -// -// Sprocket* AllocateSprocket() MUST_USE_RESULT; -// -#if defined(SWIG) -#define MUST_USE_RESULT -#elif __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -#define MUST_USE_RESULT __attribute__((warn_unused_result)) -#else -#define MUST_USE_RESULT -#endif - -#define PREDICT_FALSE(x) x -#define PREDICT_TRUE(x) x - -#endif // OR_TOOLS_BASE_PORT_H_ diff --git a/ortools/base/protoutil.h b/ortools/base/protoutil.h index e612e7f80ba..773848121f3 100644 --- a/ortools/base/protoutil.h +++ b/ortools/base/protoutil.h @@ -14,30 +14,31 @@ #ifndef OR_TOOLS_BASE_PROTOUTIL_H_ #define OR_TOOLS_BASE_PROTOUTIL_H_ +#include "absl/time/time.h" #include "google/protobuf/duration.pb.h" #include "ortools/base/status.h" #include "ortools/base/statusor.h" -#include "ortools/base/time_support.h" namespace util_time { inline ::util::StatusOr EncodeGoogleApiProto( - base::Duration d) { + absl::Duration d) { google::protobuf::Duration proto; - proto.set_seconds(static_cast(d)); - proto.set_nanos(static_cast(d * 1e9) % 1000000000); + const int64 d_in_nano = ToInt64Nanoseconds(d); + proto.set_seconds(static_cast(d_in_nano / 1000000000)); + proto.set_nanos(static_cast(d_in_nano % 1000000000)); return proto; } -inline ::util::Status EncodeGoogleApiProto(base::Duration d, +inline ::util::Status EncodeGoogleApiProto(absl::Duration d, google::protobuf::Duration* proto) { *proto = EncodeGoogleApiProto(d).ValueOrDie(); return util::OkStatus(); } -inline ::util::StatusOr DecodeGoogleApiProto( +inline ::util::StatusOr DecodeGoogleApiProto( const google::protobuf::Duration& proto) { - return base::Seconds(proto.seconds() + 1e-9 * proto.nanos()); + return absl::Seconds(proto.seconds() + 1e-9 * proto.nanos()); } } // namespace util_time diff --git a/ortools/base/small_map.h b/ortools/base/small_map.h index 1c89745dafc..777f7e74404 100644 --- a/ortools/base/small_map.h +++ b/ortools/base/small_map.h @@ -14,7 +14,9 @@ #ifndef OR_TOOLS_BASE_SMALL_MAP_H_ #define OR_TOOLS_BASE_SMALL_MAP_H_ +namespace gtl { template class small_map : public T {}; +} // namespace gtl #endif // OR_TOOLS_BASE_SMALL_MAP_H_ diff --git a/ortools/base/small_ordered_set.h b/ortools/base/small_ordered_set.h index 4ba9e02e0b8..4a9c9567ee6 100644 --- a/ortools/base/small_ordered_set.h +++ b/ortools/base/small_ordered_set.h @@ -14,7 +14,9 @@ #ifndef OR_TOOLS_BASE_SMALL_ORDERED_SET_H_ #define OR_TOOLS_BASE_SMALL_ORDERED_SET_H_ +namespace gtl { template class small_ordered_set : public T {}; +} // namespace gtl #endif // OR_TOOLS_BASE_SMALL_ORDERED_SET_H_ diff --git a/ortools/base/span.h b/ortools/base/span.h deleted file mode 100644 index a3dc0e6d559..00000000000 --- a/ortools/base/span.h +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_SPAN_H_ -#define OR_TOOLS_BASE_SPAN_H_ - -// An Span represents an immutable array of elements of type -// T. It has a length "length", and a base pointer "ptr", and the -// array it represents contains the elements "ptr[0] .. ptr[len-1]". -// The backing store for the array is *not* owned by the Span -// object, and clients must arrange for the backing store to remain -// live while the Span object is in use. -// -// An Span is somewhat analogous to a StringPiece, but for -// array elements of type T. -// -// Implicit conversion operations are provided from types such as -// std::vector and absl::InlinedVector. Note that Span -// objects constructed from types in this way may be invalidated by -// any operations that mutate the underlying vector. -// -// One common use for Span is when passing arguments to a -// routine where you want to be able to accept a variety of array -// types (e.g. a vector, a absl::InlinedVector, a C-style array, -// etc.). The usual approach here is to have the client explicitly -// pass in a pointer and a length, as in: -// -// void MyRoutine(const int* elems, int N) { -// for (int i = 0; i < N; i++) { .. do something with elems[i] .. } -// } -// -// Unfortunately, this leads to ugly and error-prone code at the call site: -// -// std::vector my_vector; -// MyRoutine(vector_as_array(&my_vector), my_vector.size()); -// -// absl::InlinedVector my_inline_vector; -// MyRoutine(my_inline_vector.array(), my_inline_vector.size()); -// -// int my_array[10]; -// MyRoutine(my_array, 10); -// -// Instead, you can use an Span as the argument to the routine: -// -// void MyRoutine(Span a) { -// for (int i = 0; i < a.size(); i++) { .. do something with a[i] .. } -// } -// -// This makes the call sites cleaner, for the most part: -// -// std::vector my_vector; -// MyRoutine(my_vector); -// -// absl::InlinedVector my_inline_vector; -// MyRoutine(my_inline_vector); -// -// int my_array[10]; -// MyRoutine(my_array); -// -// int* my_array = new int[10]; -// MyRoutine(absl::Span(my_array, 10)); - -#include -#include -#include - -#include "ortools/base/inlined_vector.h" - -namespace absl { -namespace internal { - -// Template logic for generic constructors. - -// Wrappers whose Get() delegates to the appropriate method of a container, and -// is defined when this method exists. Delegates to the const method if C is a -// const type. -struct Data { - template - static decltype(std::declval().data()) Get(C* v) { - return v->data(); - } -}; - -struct Size { - template - static decltype(std::declval().size()) Get(C* v) { - return v->size(); - } -}; - -// Checks whether M::Get(C*) is defined and has a return type R such that -// Checker::valid()==true. -template -struct HasGetHelper : public M { - private: - struct None {}; - // M::Get is selected when it is viable. Get(...) is selected otherwise. - using M::Get; - static None Get(...); - - public: - static constexpr bool HasGet() { - using Result = decltype(Get(std::declval())); - return !std::is_same() && Checker::template valid(); - } -}; - -// Defines HasGet() for a particular method, container, and checker. If -// HasGet()==true, provides Get() that delegates to the method. -template ::HasGet()> -struct Wrapper { - static constexpr bool HasGet() { return false; } -}; - -template -struct Wrapper { - static constexpr bool HasGet() { return true; } - static decltype(M::Get(std::declval())) Get(C* v) { return M::Get(v); } -}; - -// Type checker for a method returning an integral value. -struct SizeChecker { - template - static constexpr bool valid() { - return std::is_integral::value; - } -}; - -// Type checker for a method returning either a pointer to T or a less const -// version of that. -template -struct DataChecker { - // We want to enable conversion from std::vector to Span - // but - // disable conversion from std::vector to Span. Here we - // use - // the fact that U** is convertible to Q* const* if and only if Q is the same - // type or a more cv-qualified version of U. - template - static constexpr bool valid() { - return std::is_convertible::value; - } -}; - -// Aliases to A if A::HasGet()==true, or to B otherwise. -template -using FirstWithGet = typename std::conditional::type; - -// Wraps C::data() const, returning a pointer to const data. -template -using ContainerData = Wrapper, const C>; - -// Wraps C::size() const. -template -using ContainerSize = Wrapper; - -// Implementation class for Span. T will be a const type. -template -class SpanImplBase { - public: - typedef T* pointer; - typedef const T* const_pointer; - typedef T& reference; - typedef const T& const_reference; - typedef pointer iterator; - typedef const_pointer const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - - static const size_type npos = static_cast(-1); - - SpanImplBase(pointer array, size_type length) - : ptr_(array), length_(length) {} - - // Substring of another Span. - // pos must be non-negative and <= x.length(). - // len must be non-negative and will be pinned to at most x.length() - pos. - SpanImplBase(const SpanImplBase& x, size_type pos, size_type len) - : ptr_(x.ptr_ + pos), length_(std::min(x.length_ - pos, len)) {} - - // Some of the const methods below return pointers and references to mutable - // data. This is only the case in this internal class; Span provides - // deep-constness. - - pointer data() const { return ptr_; } - size_type size() const { return length_; } - - void clear() { - ptr_ = nullptr; - length_ = 0; - } - - reference operator[](size_type i) const { return ptr_[i]; } - reference at(size_type i) const { - DCHECK_LT(i, length_); - return ptr_[i]; - } - reference front() const { - DCHECK_GT(length_, 0); - return ptr_[0]; - } - reference back() const { - DCHECK_GT(length_, 0); - return ptr_[length_ - 1]; - } - - void remove_prefix(size_type n) { - DCHECK_GE(length_, n); - ptr_ += n; - length_ -= n; - } - void remove_suffix(size_type n) { - DCHECK_GE(length_, n); - length_ -= n; - } - - iterator begin() const { return ptr_; } - iterator end() const { return ptr_ + length_; } - reverse_iterator rbegin() const { return reverse_iterator(end()); } - reverse_iterator rend() const { return reverse_iterator(begin()); } - - bool operator==(const SpanImplBase& other) const { - if (size() != other.size()) return false; - if (data() == other.data()) return true; - return std::equal(data(), data() + size(), other.data()); - } - bool operator!=(const SpanImplBase& other) const { return !(*this == other); } - - private: - pointer ptr_; - size_type length_; -}; - -template -class SpanImpl : public SpanImplBase { - public: - using SpanImplBase::SpanImplBase; - - // Defined iff the data and size accessors for the container C have been - // defined. - template - using EnableIfConvertibleFrom = - typename std::enable_if::HasGet() && - ContainerSize::HasGet()>::type; - - // Constructs from a container when EnableIfConvertibleFrom is - // defined. std::addressof handles types with overloaded operator&. - template - explicit SpanImpl(const C& v) - : SpanImplBase(ContainerData::Get(std::addressof(v)), - ContainerSize::Get(std::addressof(v))) {} -}; -} // namespace internal - -template -class Span { - private: - typedef internal::SpanImpl Impl; - - public: - typedef T value_type; - typedef typename Impl::pointer pointer; - typedef typename Impl::const_pointer const_pointer; - typedef typename Impl::reference reference; - typedef typename Impl::const_reference const_reference; - typedef typename Impl::iterator iterator; - typedef typename Impl::const_iterator const_iterator; - typedef typename Impl::reverse_iterator reverse_iterator; - typedef typename Impl::const_reverse_iterator const_reverse_iterator; - typedef typename Impl::size_type size_type; - typedef typename Impl::difference_type difference_type; - - static const size_type npos = Impl::npos; - - Span() : impl_(nullptr, 0) {} - Span(const_pointer array, size_type length) : impl_(array, length) {} - - // Implicit conversion constructors - Span(const std::vector& v) // NOLINT(runtime/explicit) - : impl_(v.data(), v.size()) {} - - template - Span(const value_type (&a)[N]) // NOLINT(runtime/explicit) - : impl_(a, N) {} - - template - Span(const InlinedVector& v) // NOLINT(runtime/explicit) - : impl_(v.data(), v.size()) {} - - // The constructor for any class supplying 'data() const' that returns either - // const T* or a less const-qualified version of it, and 'some_integral_type - // size() const'. google::protobuf::RepeatedField, std::string and (since - // C++11) std::vector and std::array are examples of this. See - // span_internal.h for details. - template > - Span(const V& v) // NOLINT(runtime/explicit) - : impl_(v) {} - - // Implicitly constructs an Span from an initializer list. This makes it - // possible to pass a brace-enclosed initializer list to a function expecting - // an Span: - // void Process(Span x); - // Process({1, 2, 3}); - // The data referenced by the initializer_list must outlive this - // Span. For example, "Span s={1,2};" and "return - // Span({3,4});" are errors, as the resulting Span may - // reference data that is no longer valid. - Span(std::initializer_list v) // NOLINT(runtime/explicit) - : impl_(v.begin(), v.size()) {} - - Span(const Span& x) : impl_(x.impl_) {} - - // Substring of another Span. - // pos must be non-negative and <= x.length(). - // len must be non-negative and will be pinned to at most x.length() - pos. - // If len==npos, the substring continues till the end of x. - Span(const Span& x, size_type pos, size_type len) - : impl_(x.impl_, pos, len) {} - - const_pointer data() const { return impl_.data(); } - size_type size() const { return impl_.size(); } - size_type length() const { return size(); } - bool empty() const { return size() == 0; } - - void clear() { impl_.clear(); } - - const_reference operator[](size_type i) const { return impl_[i]; } - const_reference at(size_type i) const { return impl_.at(i); } - const_reference front() const { return impl_.front(); } - const_reference back() const { return impl_.back(); } - - const_iterator begin() const { return impl_.begin(); } - const_iterator end() const { return impl_.end(); } - const_reverse_iterator rbegin() const { return impl_.rbegin(); } - const_reverse_iterator rend() const { return impl_.rend(); } - - void remove_prefix(size_type n) { impl_.remove_prefix(n); } - void remove_suffix(size_type n) { impl_.remove_suffix(n); } - void pop_back() { remove_suffix(1); } - void pop_front() { remove_prefix(1); } - - // These relational operators have the same semantics as the - // std::vector relational operators: they do deep (elementwise) - // comparisons. Array slices are equal iff their size is the same - // and all their elements are equal. - bool operator==(Span other) const { return impl_ == other.impl_; } - bool operator!=(Span other) const { return impl_ != other.impl_; } - - private: - Impl impl_; -}; - -template -const typename Span::size_type Span::npos; - -} // namespace absl - -#endif // OR_TOOLS_BASE_SPAN_H_ diff --git a/ortools/base/split.cc b/ortools/base/split.cc deleted file mode 100644 index 42aaab7fd51..00000000000 --- a/ortools/base/split.cc +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/base/split.h" - -#if defined(_MSC_VER) -#include -#endif // _MSC_VER -#include "ortools/base/logging.h" - -namespace absl { -namespace { - -// ---------------------------------------------------------------------- -// InternalSplitStringUsing() -// Split a std::string using a character delimiter. Append the components -// to 'result'. -// -// Note: For multi-character delimiters, this routine will split on *ANY* of -// the characters in the std::string, not the entire std::string as a single -// delimiter. -// ---------------------------------------------------------------------- -template -static inline void InternalSplitStringUsingChar(const std::string& full, char c, - ITR* result) { - const char* p = full.data(); - const char* end = p + full.size(); - while (p != end) { - if (*p == c) { - ++p; - } else { - const char* start = p; - while (++p != end && *p != c) { - } - result->emplace_back(start, p - start); - } - } - return; -} - -template -static inline void InternalSplitStringUsing(const std::string& full, - const char* delim, ITR* result) { - // Optimize the common case where delim is a single character. - if (delim[0] != '\0' && delim[1] == '\0') { - char c = delim[0]; - InternalSplitStringUsingChar(full, c, result); - return; - } - - std::string::size_type begin_index, end_index; - begin_index = full.find_first_not_of(delim); - while (begin_index != std::string::npos) { - end_index = full.find_first_of(delim, begin_index); - if (end_index == std::string::npos) { - end_index = full.size(); - result->emplace_back(full.c_str() + begin_index, end_index - begin_index); - return; - } - result->emplace_back(full.c_str() + begin_index, end_index - begin_index); - begin_index = full.find_first_not_of(delim, end_index); - } -} - -} // namespace - -std::vector StrSplit(const std::string& full, char delim, - int flags) { - CHECK_EQ(absl::SkipEmpty(), flags); - std::vector out; - InternalSplitStringUsingChar(full, delim, &out); - return out; -} - -std::vector StrSplit(const std::string& full, const char* delim, - int flags) { - CHECK_EQ(absl::SkipEmpty(), flags); - std::vector out; - InternalSplitStringUsing(full, delim, &out); - return out; -} - -std::vector StrSplit(const std::string& full, - const char* delim, int64 flags) { - CHECK_EQ(absl::SkipEmpty(), flags); - std::vector out; - InternalSplitStringUsing(full, delim, &out); - return out; -} - -} // namespace absl diff --git a/ortools/base/split.h b/ortools/base/split.h deleted file mode 100644 index 5e2116b7042..00000000000 --- a/ortools/base/split.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_SPLIT_H_ -#define OR_TOOLS_BASE_SPLIT_H_ - -#include -#include -#include -#include - -#include "ortools/base/integral_types.h" -#include "ortools/base/logging.h" -#include "ortools/base/string_view.h" - -namespace absl { -inline int SkipEmpty() { return 0xDEADBEEF; } - -std::vector StrSplit(const std::string& full, const char* delim, - int flags); - -std::vector StrSplit(const std::string& full, char delim, - int flags); - -namespace delimiter { -inline const char* AnyOf(const char* x) { return x; } -} // namespace delimiter -} // namespace absl - -// Split a std::string using a nul-terminated list of character -// delimiters. For each component, parse using the provided -// parsing function and if successful, append it to 'result'. -// Return true if and only if all components parse successfully. -// If there are consecutive delimiters, this function skips over -// all of them. This function will correctly handle parsing -// strings that have embedded \0s. -template -bool SplitStringAndParse(absl::string_view source, const std::string& delim, - bool (*parse)(const std::string& str, T* value), - std::vector* result); - -// We define here a very truncated version of the powerful absl::StrSplit() -// function. As of 2013-04, it can only be used like this: -// const char* separators = ...; -// std::vector result = absl::StrSplit( -// full, absl::delimiter::AnyOf(separators), absl::SkipEmpty()); -// -// TODO(user): The current interface has a really bug prone side effect because -// it can also be used without the AnyOf(). If separators contains only one -// character, this is fine, but if it contains more, then the meaning is -// different: Split() should interpret the whole std::string as a delimiter. Fix -// this. -// ###################### TEMPLATE INSTANTIATIONS BELOW ####################### -template -bool SplitStringAndParse(const std::string& source, const std::string& delim, - bool (*parse)(const std::string& str, T* value), - std::vector* result) { - CHECK(nullptr != parse); - CHECK(nullptr != result); - CHECK_GT(delim.size(), 0); - const std::vector pieces = - ::absl::StrSplit(source, absl::delimiter::AnyOf(delim.c_str()), - static_cast(absl::SkipEmpty())); - T t; - for (absl::string_view piece : pieces) { - if (!parse(piece.as_string(), &t)) return false; - result->push_back(t); - } - return true; -} - -#endif // OR_TOOLS_BASE_SPLIT_H_ diff --git a/ortools/base/status.h b/ortools/base/status.h index 1ca2562ea08..801d59790ac 100644 --- a/ortools/base/status.h +++ b/ortools/base/status.h @@ -15,7 +15,7 @@ #define OR_TOOLS_BASE_STATUS_H_ #include -#include "ortools/base/join.h" +#include "absl/strings/str_cat.h" #include "ortools/base/logging.h" namespace util { @@ -45,6 +45,7 @@ struct Status { } std::string error_message() const { return error_message_; } + std::string message() const { return error_message(); } void IgnoreError() const {} diff --git a/ortools/base/string_view.cc b/ortools/base/string_view.cc deleted file mode 100644 index 219682d083b..00000000000 --- a/ortools/base/string_view.cc +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/base/string_view.h" -#include // NOLINT -#include - -namespace absl { - -std::ostream& operator<<(std::ostream& o, const string_view& piece) { - o.write(piece.data(), piece.size()); - return o; -} - -bool operator==(const string_view& x, const string_view& y) { - int len = x.size(); - if (len != y.size()) { - return false; - } - const char* p = x.data(); - const char* p2 = y.data(); - // Test last byte in case strings share large common prefix - if ((len > 0) && (p[len - 1] != p2[len - 1])) return false; - const char* p_limit = p + len; - for (; p < p_limit; p++, p2++) { - if (*p != *p2) return false; - } - return true; -} - -bool operator<(const string_view& x, const string_view& y) { - const int r = memcmp(x.data(), y.data(), std::min(x.size(), y.size())); - return ((r < 0) || ((r == 0) && (x.size() < y.size()))); -} - -void string_view::CopyToString(std::string* target) const { - target->assign(ptr_, length_); -} - -int string_view::copy(char* buf, size_type n, size_type pos) const { - int ret = std::min(length_ - pos, n); - memcpy(buf, ptr_ + pos, ret); - return ret; -} - -int string_view::find(const string_view& s, size_type pos) const { - if (length_ < 0 || pos > static_cast(length_)) return npos; - - const char* result = - std::search(ptr_ + pos, ptr_ + length_, s.ptr_, s.ptr_ + s.length_); - const size_type xpos = result - ptr_; - return xpos + s.length_ <= length_ ? xpos : npos; -} - -int string_view::compare(const string_view& x) const { - int r = memcmp(ptr_, x.ptr_, std::min(length_, x.length_)); - if (r == 0) { - if (length_ < x.length_) - r = -1; - else if (length_ > x.length_) - r = +1; - } - return r; -} - -int string_view::find(char c, size_type pos) const { - if (length_ <= 0 || pos >= static_cast(length_)) { - return npos; - } - const char* result = std::find(ptr_ + pos, ptr_ + length_, c); - return result != ptr_ + length_ ? result - ptr_ : npos; -} - -int string_view::rfind(const string_view& s, size_type pos) const { - if (length_ < s.length_) return npos; - const size_t ulen = length_; - if (s.length_ == 0) return std::min(ulen, pos); - - const char* last = ptr_ + std::min(ulen - s.length_, pos) + s.length_; - const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_); - return result != last ? result - ptr_ : npos; -} - -int string_view::rfind(char c, size_type pos) const { - if (length_ <= 0) return npos; - for (int i = std::min(pos, static_cast(length_ - 1)); i >= 0; - --i) { - if (ptr_[i] == c) { - return i; - } - } - return npos; -} - -string_view string_view::substr(size_type pos, size_type n) const { - if (pos > length_) pos = length_; - if (n > length_ - pos) n = length_ - pos; - return string_view(ptr_ + pos, n); -} - -const string_view::size_type string_view::npos = size_type(-1); - -} // namespace absl diff --git a/ortools/base/string_view.h b/ortools/base/string_view.h deleted file mode 100644 index f4a0dd4c4fc..00000000000 --- a/ortools/base/string_view.h +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// A std::string-like object that points to a sized piece of memory. -// -// Functions or methods may use const string_view& parameters to accept either -// a "const char*" or a "std::string" value that will be implicitly converted to -// a string_view. The implicit conversion means that it is often appropriate -// to include this .h file in other files rather than forward-declaring -// string_view as would be appropriate for most other Google classes. -// -// Systematic usage of string_view is encouraged as it will reduce unnecessary -// conversions from "const char*" to "std::string" and back again. - -#ifndef OR_TOOLS_BASE_STRING_VIEW_H_ -#define OR_TOOLS_BASE_STRING_VIEW_H_ - -#include - -#include -#include -#include -#include - -namespace absl { - -class string_view { - private: - const char* ptr_; - int length_; - - public: - // We provide non-explicit singleton constructors so users can pass - // in a "const char*" or a "std::string" wherever a "string_view" is - // expected. - string_view() : ptr_(NULL), length_(0) {} - string_view(const char* str) // NOLINT - : ptr_(str), length_((str == NULL) ? 0 : static_cast(strlen(str))) {} - string_view(const std::string& str) // NOLINT - : ptr_(str.data()), length_(static_cast(str.size())) {} - string_view(const char* offset, int len) : ptr_(offset), length_(len) {} - - // data() may return a pointer to a buffer with embedded NULs, and the - // returned buffer may or may not be null terminated. Therefore it is - // typically a mistake to pass data() to a routine that expects a NUL - // terminated std::string. - const char* data() const { return ptr_; } - int size() const { return length_; } - int length() const { return length_; } - bool empty() const { return length_ == 0; } - - void clear() { - ptr_ = NULL; - length_ = 0; - } - void set(const char* data, int len) { - ptr_ = data; - length_ = len; - } - void set(const char* str) { - ptr_ = str; - if (str != NULL) - length_ = static_cast(strlen(str)); - else - length_ = 0; - } - void set(const void* data, int len) { - ptr_ = reinterpret_cast(data); - length_ = len; - } - - char operator[](int i) const { return ptr_[i]; } - - void remove_prefix(int n) { - ptr_ += n; - length_ -= n; - } - - void remove_suffix(int n) { length_ -= n; } - - int compare(const string_view& x) const; - - std::string as_string() const { return std::string(data(), size()); } - // We also define ToString() here, since many other std::string-like - // interfaces name the routine that converts to a C++ std::string - // "ToString", and it's confusing to have the method that does that - // for a string_view be called "as_string()". We also leave the - // "as_string()" method defined here for existing code. - std::string ToString() const { return std::string(data(), size()); } - - void CopyToString(std::string* target) const; - void AppendToString(std::string* target) const; - - // Does "this" start with "x"? - bool starts_with(const string_view& x) const { - return ((length_ >= x.length_) && (memcmp(ptr_, x.ptr_, x.length_) == 0)); - } - - // Does "this" end with "x"? - bool ends_with(const string_view& x) const { - return ((length_ >= x.length_) && - (memcmp(ptr_ + (length_ - x.length_), x.ptr_, x.length_) == 0)); - } - - // Standard STL container boilerplate. - typedef char value_type; - typedef const char* pointer; - typedef const char& reference; - typedef const char& const_reference; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - static const size_type npos; - typedef const char* const_iterator; - typedef const char* iterator; - typedef std::reverse_iterator const_reverse_iterator; - typedef std::reverse_iterator reverse_iterator; - iterator begin() const { return ptr_; } - iterator end() const { return ptr_ + length_; } - const_reverse_iterator rbegin() const { - return const_reverse_iterator(ptr_ + length_); - } - const_reverse_iterator rend() const { return const_reverse_iterator(ptr_); } - // STL says return size_type, but Google says return int. - int max_size() const { return length_; } - int capacity() const { return length_; } - - int copy(char* buf, size_type n, size_type pos = 0) const; - - int find(const string_view& s, size_type pos = 0) const; - int find(char c, size_type pos = 0) const; - int rfind(const string_view& s, size_type pos = npos) const; - int rfind(char c, size_type pos = npos) const; - - string_view substr(size_type pos, size_type n = npos) const; -}; - -bool operator==(const string_view& x, const string_view& y); - -inline bool operator!=(const string_view& x, const string_view& y) { - return !(x == y); -} - -bool operator<(const string_view& x, const string_view& y); - -inline bool operator>(const string_view& x, const string_view& y) { - return y < x; -} - -inline bool operator<=(const string_view& x, const string_view& y) { - return !(x > y); -} - -inline bool operator>=(const string_view& x, const string_view& y) { - return !(x < y); -} - -// Allow string_view to be logged. -extern std::ostream& operator<<(std::ostream& o, const string_view& piece); - -} // namespace absl - -#endif // OR_TOOLS_BASE_STRING_VIEW_H_ diff --git a/ortools/base/stringpiece_utils.h b/ortools/base/stringpiece_utils.h deleted file mode 100644 index 0ab8440faff..00000000000 --- a/ortools/base/stringpiece_utils.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_STRINGPIECE_UTILS_H_ -#define OR_TOOLS_BASE_STRINGPIECE_UTILS_H_ - -#include - -#include "ortools/base/string_view.h" - -namespace strings { -// Returns whether s begins with x. -inline bool StartsWith(absl::string_view s, absl::string_view x) { - return s.size() >= x.size() && memcmp(s.data(), x.data(), x.size()) == 0; -} - -// Returns whether s ends with x. -inline bool EndsWith(absl::string_view s, absl::string_view x) { - return s.size() >= x.size() && - memcmp(s.data() + (s.size() - x.size()), x.data(), x.size()) == 0; -} -} // namespace strings -#endif // OR_TOOLS_BASE_STRINGPIECE_UTILS_H_ diff --git a/ortools/base/stringprintf.cc b/ortools/base/stringprintf.cc deleted file mode 100644 index fa44a5b4d19..00000000000 --- a/ortools/base/stringprintf.cc +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/base/stringprintf.h" - -#include -#include - -#include - -#if !defined(_MSC_VER) -#ifndef va_copy -#ifdef __va_copy -#define va_copy(d, s) __va_copy((d), (s)) -#else -#define va_copy(d, s) memcpy(&(d), &(s), sizeof(va_list)) -#endif -#endif -#endif - -namespace operations_research { - -void StringAppendV(std::string* const dst, const char* const format, - va_list ap) { - // First try with a small fixed size buffer - char space[1024]; - - // It's possible for methods that use a va_list to invalidate - // the data in it upon use. The fix is to make a copy - // of the structure before using it and use that copy instead. - va_list backup_ap; -#if defined(_MSC_VER) - backup_ap = ap; -#else - va_copy(backup_ap, ap); -#endif - int result = vsnprintf(space, sizeof(space), format, backup_ap); - va_end(backup_ap); - - if ((result >= 0) && (result < sizeof(space))) { - // It fit - dst->append(space, result); - return; - } - - // Repeatedly increase buffer size until it fits - int length = sizeof(space); - while (true) { - if (result < 0) { - // Older behavior: just try doubling the buffer size - length *= 2; - } else { - // We need exactly "result+1" characters - length = result + 1; - } - char* const buf = new char[length]; - -// Restore the va_list before we use it again -#if defined(_MSC_VER) - backup_ap = ap; -#else - va_copy(backup_ap, ap); -#endif - result = vsnprintf(buf, length, format, backup_ap); - va_end(backup_ap); - - if ((result >= 0) && (result < length)) { - // It fit - dst->append(buf, result); - delete[] buf; - return; - } - delete[] buf; - } -} - -std::string StringPrintf(const char* const format, ...) { - va_list ap; - va_start(ap, format); - std::string result; - StringAppendV(&result, format, ap); - va_end(ap); - return result; -} - -void SStringPrintf(std::string* const dst, const char* const format, ...) { - va_list ap; - va_start(ap, format); - dst->clear(); - StringAppendV(dst, format, ap); - va_end(ap); -} - -void StringAppendF(std::string* const dst, const char* const format, ...) { - va_list ap; - va_start(ap, format); - StringAppendV(dst, format, ap); - va_end(ap); -} - -} // namespace operations_research - -namespace absl { -std::string StrFormat(const char* const format, ...) { - va_list ap; - va_start(ap, format); - std::string result; - ::operations_research::StringAppendV(&result, format, ap); - va_end(ap); - return result; -} - -void StrAppendFormat(std::string* const dst, const char* const format, ...) { - va_list ap; - va_start(ap, format); - ::operations_research::StringAppendV(dst, format, ap); - va_end(ap); -} -} // namespace absl diff --git a/ortools/base/strutil.h b/ortools/base/strutil.h index 18bdb43fd8f..7f3c2ce9425 100644 --- a/ortools/base/strutil.h +++ b/ortools/base/strutil.h @@ -15,7 +15,7 @@ #define OR_TOOLS_BASE_STRUTIL_H_ #include -#include "ortools/base/string_view.h" +#include "absl/strings/string_view.h" using std::string; diff --git a/ortools/base/synchronization.h b/ortools/base/synchronization.h deleted file mode 100644 index c54e3a72b4b..00000000000 --- a/ortools/base/synchronization.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_SYNCHRONIZATION_H_ -#define OR_TOOLS_BASE_SYNCHRONIZATION_H_ - -#include // NOLINT -#include // NOLINT - -#include "ortools/base/logging.h" - -namespace operations_research { -class Barrier { - public: - explicit Barrier(int num_threads) - : num_to_block_(num_threads), num_to_exit_(num_threads) {} - - bool Block() { - std::unique_lock mutex_lock(mutex_); - this->num_to_block_--; - CHECK_GE(this->num_to_block_, 0); - if (num_to_block_ > 0) { - while (num_to_block_ > 0) { - condition_.wait(mutex_lock); - } - } else { - condition_.notify_all(); - } - this->num_to_exit_--; - CHECK_GE(this->num_to_exit_, 0); - return this->num_to_exit_ == 0; - } - - private: - std::mutex mutex_; - std::condition_variable condition_; - int num_to_block_; - int num_to_exit_; - DISALLOW_COPY_AND_ASSIGN(Barrier); -}; -} // namespace operations_research -#endif // OR_TOOLS_BASE_SYNCHRONIZATION_H_ diff --git a/ortools/base/time_support.cc b/ortools/base/time_support.cc deleted file mode 100644 index 62c7f8aa8c5..00000000000 --- a/ortools/base/time_support.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/base/time_support.h" - -#if !defined(_MSC_VER) -#include -#else -#include -#endif -#if defined(__APPLE__) && defined(__GNUC__) -#include -#endif -#include - -namespace absl { - -int64 GetCurrentTimeNanos() { -#if defined(_MSC_VER) - const double kSecInNanoSeconds = 1000000000.0; - LARGE_INTEGER l_freq; - if (!QueryPerformanceFrequency(&l_freq)) { - return 0; - } - const double freq = static_cast(l_freq.QuadPart) / kSecInNanoSeconds; - LARGE_INTEGER now; - QueryPerformanceCounter(&now); - return static_cast(now.QuadPart / freq); -#elif defined(__APPLE__) && defined(__GNUC__) - int64 time = mach_absolute_time(); - mach_timebase_info_data_t info; - kern_return_t err = mach_timebase_info(&info); - return err == 0 ? time * info.numer / info.denom : 0; -#elif defined(__GNUC__) // Linux - const int64 kSecondInNanoSeconds = 1000000000; - struct timespec current; - clock_gettime(CLOCK_REALTIME, ¤t); - return current.tv_sec * kSecondInNanoSeconds + current.tv_nsec; -#endif -} - -} // namespace absl diff --git a/ortools/base/time_support.h b/ortools/base/time_support.h deleted file mode 100644 index e5e77e576b4..00000000000 --- a/ortools/base/time_support.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_TIME_SUPPORT_H_ -#define OR_TOOLS_BASE_TIME_SUPPORT_H_ - -#include "ortools/base/integral_types.h" - -namespace absl { - -// Ideally, this should be a super-fast implementation that isn't too -// unreliable: -// - It shouldn't take more than a few nanoseconds per call, on average. -// - It shouldn't be out of sync with the "real" time by more than a few -// milliseconds. -// It might be non-increasing though. This will depend on the implementation. -// -// The current implementation (below) is probably slower, and maybe not as -// reliable as that. -// TODO(user): implement it properly. -// One possible way to achieve this is to use the hardware cycle counter, -// coupled with automatic, periodic recalibration with the (reliable, but slow) -// system timer to avoid drifts. See -// http://en.wikipedia.org/wiki/Time_Stamp_Counter#Use. -int64 GetCurrentTimeNanos(); - -inline double WallTime_Now() { return GetCurrentTimeNanos() * 1e-9; } - -typedef double Duration; -typedef double Time; - -inline Time Now() { return WallTime_Now(); } - -inline Duration Seconds(double x) { return x; } -inline Duration Milliseconds(double x) { return x * 1e-3; } -inline double ToDoubleSeconds(Duration x) { return x; } -inline int64 ToInt64Milliseconds(Duration x) { return x * 1e3; } -inline Duration ZeroDuration() { return Duration(0); } - -} // namespace absl - -// Temporary support for the legacy "base::" namespace -namespace base { -using absl::Duration; // NOLINT(readability/namespace) -using absl::Milliseconds; // NOLINT(readability/namespace) -using absl::Seconds; // NOLINT(readability/namespace) -using absl::Time; // NOLINT(readability/namespace) -using absl::ToDoubleSeconds; // NOLINT(readability/namespace) -using absl::ToInt64Milliseconds; // NOLINT(readability/namespace) -using absl::ZeroDuration; // NOLINT(readability/namespace) -} // namespace base - -inline double ToWallTime(int64 nanos) { return 1e-9 * nanos; } - -#endif // OR_TOOLS_BASE_TIME_SUPPORT_H_ diff --git a/ortools/base/timer.h b/ortools/base/timer.h index 8b85f3b8958..95cc51a1b2c 100644 --- a/ortools/base/timer.h +++ b/ortools/base/timer.h @@ -14,10 +14,11 @@ #ifndef OR_TOOLS_BASE_TIMER_H_ #define OR_TOOLS_BASE_TIMER_H_ +#include "absl/time/clock.h" +#include "absl/time/time.h" #include "ortools/base/basictypes.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/time_support.h" class WallTimer { public: @@ -44,6 +45,9 @@ class WallTimer { double Get() const { return GetNanos() * 1e-9; } int64 GetInMs() const { return GetNanos() / 1000000; } int64 GetInUsec() const { return GetNanos() / 1000; } + inline absl::Duration GetDuration() const { + return absl::Nanoseconds(GetNanos()); + } protected: int64 GetNanos() const { @@ -72,6 +76,8 @@ class CycleTimer : public WallTimer { int64 GetCycles() const { return GetNanos(); } }; +typedef CycleTimer SimpleCycleTimer; + // Conversion routines between CycleTimer::GetCycles and actual times. class CycleTimerBase { public: diff --git a/ortools/bop/CMakeLists.txt b/ortools/bop/CMakeLists.txt index 0093ab41c94..1795e751cc1 100644 --- a/ortools/bop/CMakeLists.txt +++ b/ortools/bop/CMakeLists.txt @@ -4,39 +4,54 @@ set(NAME ${PROJECT_NAME}_bop) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE +# absl::memory absl::synchronization absl::str_format +# gflags::gflags glog::glog # protobuf::libprotobuf -# gflags::gflags -# glog::glog # Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + absl::memory absl::synchronization absl::str_format + gflags::gflags glog::glog + protobuf::libprotobuf + Cbc::Cbc + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::bop ALIAS ${NAME}) diff --git a/ortools/bop/bop_base.cc b/ortools/bop/bop_base.cc index 0488305c2e8..abed0a2277e 100644 --- a/ortools/bop/bop_base.cc +++ b/ortools/bop/bop_base.cc @@ -17,7 +17,7 @@ #include #include -#include "ortools/base/mutex.h" +#include "absl/synchronization/mutex.h" #include "ortools/sat/boolean_problem.h" namespace operations_research { diff --git a/ortools/bop/bop_base.h b/ortools/bop/bop_base.h index 8b5b92630ca..1cc79609c4e 100644 --- a/ortools/bop/bop_base.h +++ b/ortools/bop/bop_base.h @@ -16,8 +16,8 @@ #include +#include "absl/synchronization/mutex.h" #include "ortools/base/basictypes.h" -#include "ortools/base/mutex.h" #include "ortools/bop/bop_parameters.pb.h" #include "ortools/bop/bop_solution.h" #include "ortools/lp_data/lp_types.h" diff --git a/ortools/bop/bop_fs.cc b/ortools/bop/bop_fs.cc index d58cc92fd1b..641923e70b1 100644 --- a/ortools/bop/bop_fs.cc +++ b/ortools/bop/bop_fs.cc @@ -16,12 +16,12 @@ #include #include +#include "absl/memory/memory.h" +#include "absl/strings/str_format.h" #include "google/protobuf/text_format.h" #include "ortools/algorithms/sparse_permutation.h" #include "ortools/base/commandlineflags.h" -#include "ortools/base/memory.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/glop/lp_solver.h" #include "ortools/lp_data/lp_print_utils.h" #include "ortools/sat/boolean_problem.h" @@ -406,13 +406,13 @@ BopOptimizerBase::Status LinearRelaxation::SynchronizeIfNeeded( (clause.b.IsPositive() ? 0 : -1); const ColIndex col_a(clause.a.Variable().value()); const ColIndex col_b(clause.b.Variable().value()); + const std::string name_a = lp_model_.GetVariableName(col_a); + const std::string name_b = lp_model_.GetVariableName(col_b); + lp_model_.SetConstraintName( constraint_index, - absl::StrFormat((clause.a.IsPositive() ? "%s" : "not(%s)"), - lp_model_.GetVariableName(col_a).c_str()) + - " or " + - absl::StrFormat((clause.b.IsPositive() ? "%s" : "not(%s)"), - lp_model_.GetVariableName(col_b).c_str())); + (clause.a.IsPositive() ? name_a : "not(" + name_a + ")") + " or " + + (clause.b.IsPositive() ? name_b : "not(" + name_b + ")")); lp_model_.SetCoefficient(constraint_index, col_a, coefficient_a); lp_model_.SetCoefficient(constraint_index, col_b, coefficient_b); lp_model_.SetConstraintBounds(constraint_index, rhs, glop::kInfinity); diff --git a/ortools/bop/bop_lns.cc b/ortools/bop/bop_lns.cc index a9ce0c09b1e..fca227ce744 100644 --- a/ortools/bop/bop_lns.cc +++ b/ortools/bop/bop_lns.cc @@ -17,12 +17,11 @@ #include #include +#include "absl/memory/memory.h" #include "google/protobuf/text_format.h" #include "ortools/base/cleanup.h" #include "ortools/base/commandlineflags.h" -#include "ortools/base/memory.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/glop/lp_solver.h" #include "ortools/lp_data/lp_print_utils.h" #include "ortools/sat/boolean_problem.h" diff --git a/ortools/bop/bop_ls.cc b/ortools/bop/bop_ls.cc index cec03c5feef..5e12c35d2f1 100644 --- a/ortools/bop/bop_ls.cc +++ b/ortools/bop/bop_ls.cc @@ -13,8 +13,8 @@ #include "ortools/bop/bop_ls.h" -#include "ortools/base/memory.h" -#include "ortools/base/stringprintf.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_format.h" #include "ortools/bop/bop_util.h" #include "ortools/sat/boolean_problem.h" diff --git a/ortools/bop/bop_ls.h b/ortools/bop/bop_ls.h index 243fc3098b7..923ca12a686 100644 --- a/ortools/bop/bop_ls.h +++ b/ortools/bop/bop_ls.h @@ -28,8 +28,9 @@ #define OR_TOOLS_BOP_BOP_LS_H_ #include -#include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "ortools/base/hash.h" #include "ortools/base/random.h" #include "ortools/bop/bop_base.h" @@ -404,7 +405,7 @@ class AssignmentAndConstraintFeasibilityMaintainer { // Members used by PotentialOneFlipRepairs(). std::vector tmp_potential_repairs_; NonOrderedSetHasher constraint_set_hasher_; - std::unordered_map> + absl::flat_hash_map> hash_to_potential_repairs_; DISALLOW_COPY_AND_ASSIGN(AssignmentAndConstraintFeasibilityMaintainer); @@ -612,7 +613,7 @@ class LocalSearchAssignmentIterator { // Ideally, this should be related to the maximum number of decision in the // LS, but that requires templating the whole LS optimizer. bool use_transposition_table_; - std::unordered_set> + absl::flat_hash_set> transposition_table_; bool use_potential_one_flip_repairs_; diff --git a/ortools/bop/bop_portfolio.cc b/ortools/bop/bop_portfolio.cc index a9c8cfddcc4..af31f49c1a8 100644 --- a/ortools/bop/bop_portfolio.cc +++ b/ortools/bop/bop_portfolio.cc @@ -13,9 +13,9 @@ #include "ortools/bop/bop_portfolio.h" -#include "ortools/base/memory.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_format.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/bop/bop_fs.h" #include "ortools/bop/bop_lns.h" #include "ortools/bop/bop_ls.h" @@ -416,7 +416,7 @@ std::string OptimizerSelector::PrintStats( return absl::StrFormat( " %40s : %3d/%-3d (%6.2f%%) Total gain: %6d Total Dtime: %0.3f " "score: %f\n", - info.name.c_str(), info.num_successes, info.num_calls, + info.name, info.num_successes, info.num_calls, 100.0 * info.num_successes / info.num_calls, info.total_gain, info.time_spent, info.score); } diff --git a/ortools/bop/bop_solver.cc b/ortools/bop/bop_solver.cc index 74441e60cad..097036b0849 100644 --- a/ortools/bop/bop_solver.cc +++ b/ortools/bop/bop_solver.cc @@ -19,7 +19,6 @@ #include "google/protobuf/text_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/bop/bop_fs.h" #include "ortools/bop/bop_lns.h" #include "ortools/bop/bop_ls.h" diff --git a/ortools/bop/integral_solver.cc b/ortools/bop/integral_solver.cc index 99401ff7a1a..c9b7756ea4d 100644 --- a/ortools/bop/integral_solver.cc +++ b/ortools/bop/integral_solver.cc @@ -289,9 +289,9 @@ std::string IntegralVariable::DebugString() const { std::string str; CHECK_EQ(bits_.size(), weights_.size()); for (int i = 0; i < bits_.size(); ++i) { - str += absl::StrFormat("%lld [%lld] ", weights_[i], bits_[i].value()); + str += absl::StrFormat("%d [%d] ", weights_[i], bits_[i].value()); } - str += absl::StrFormat(" Offset: %lld", offset_); + str += absl::StrFormat(" Offset: %d", offset_); return str; } diff --git a/ortools/bop/integral_solver.h b/ortools/bop/integral_solver.h index c21a57914d1..83eb652aaa2 100644 --- a/ortools/bop/integral_solver.h +++ b/ortools/bop/integral_solver.h @@ -14,7 +14,6 @@ #ifndef OR_TOOLS_BOP_INTEGRAL_SOLVER_H_ #define OR_TOOLS_BOP_INTEGRAL_SOLVER_H_ -#include "ortools/base/port.h" #include "ortools/bop/bop_parameters.pb.h" #include "ortools/bop/bop_types.h" #include "ortools/lp_data/lp_data.h" @@ -38,21 +37,21 @@ class IntegralSolver { BopParameters parameters() const { return parameters_; } // Solves the given linear program and returns the solve status. - BopSolveStatus Solve(const glop::LinearProgram& linear_problem) - MUST_USE_RESULT; - BopSolveStatus SolveWithTimeLimit(const glop::LinearProgram& linear_problem, - TimeLimit* time_limit) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT BopSolveStatus + Solve(const glop::LinearProgram& linear_problem); + ABSL_MUST_USE_RESULT BopSolveStatus SolveWithTimeLimit( + const glop::LinearProgram& linear_problem, TimeLimit* time_limit); // Same as Solve() but starts from the given solution. // TODO(user): Change the API to accept a partial solution instead since the // underlying solver supports it. - BopSolveStatus Solve(const glop::LinearProgram& linear_problem, - const glop::DenseRow& user_provided_initial_solution) - MUST_USE_RESULT; - BopSolveStatus SolveWithTimeLimit( - const glop::LinearProgram& linear_problem, - const glop::DenseRow& user_provided_initial_solution, - TimeLimit* time_limit) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT BopSolveStatus + Solve(const glop::LinearProgram& linear_problem, + const glop::DenseRow& user_provided_initial_solution); + ABSL_MUST_USE_RESULT BopSolveStatus + SolveWithTimeLimit(const glop::LinearProgram& linear_problem, + const glop::DenseRow& user_provided_initial_solution, + TimeLimit* time_limit); // Returns the objective value of the solution with its offset. glop::Fractional objective_value() const { return objective_value_; } diff --git a/ortools/com/google/ortools/sat/CpModel.java b/ortools/com/google/ortools/sat/CpModel.java index 08b196a386a..474d1c283ec 100644 --- a/ortools/com/google/ortools/sat/CpModel.java +++ b/ortools/com/google/ortools/sat/CpModel.java @@ -717,8 +717,9 @@ public Constraint addReservoirConstraint( * @return an instance of the Constraint class * @throws MismatchedArrayLengths if times, demands, or actives have different length */ - public Constraint addReservoirConstraintWithActive(IntVar[] times, long[] demands, - IntVar[] actives, long minLevel, long maxLevel) throws MismatchedArrayLengths { + public Constraint addReservoirConstraintWithActive( + IntVar[] times, long[] demands, IntVar[] actives, long minLevel, long maxLevel) + throws MismatchedArrayLengths { if (times.length != demands.length) { throw new MismatchedArrayLengths("addReservoirConstraint", "times", "demands"); } @@ -747,8 +748,9 @@ public Constraint addReservoirConstraintWithActive(IntVar[] times, long[] demand * * @see #addReservoirConstraintWithActive(IntVar[], long[], actives, long, long) Reservoir */ - public Constraint addReservoirConstraintWithActive(IntVar[] times, int[] demands, - IntVar[] actives, long minLevel, long maxLevel) throws MismatchedArrayLengths { + public Constraint addReservoirConstraintWithActive( + IntVar[] times, int[] demands, IntVar[] actives, long minLevel, long maxLevel) + throws MismatchedArrayLengths { return addReservoirConstraintWithActive( times, toLongArray(demands), actives, minLevel, maxLevel); } @@ -1181,11 +1183,10 @@ public String modelStats() { return SatHelper.modelStats(model()); } - /** Returns a non empty string if the model is invalid. */ - public String validate() { - return SatHelper.validateModel(model()); - } - + /** Returns a non empty string explaining the issue if the model is invalid. */ + public String validate() { + return SatHelper.validateModel(model()); + } // Helpers diff --git a/ortools/constraint_solver/CMakeLists.txt b/ortools/constraint_solver/CMakeLists.txt index e1c5e0babe4..edca9098b13 100644 --- a/ortools/constraint_solver/CMakeLists.txt +++ b/ortools/constraint_solver/CMakeLists.txt @@ -4,43 +4,65 @@ set(NAME ${PROJECT_NAME}_constraint_solver) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE # ZLIB::ZLIB -# gflags::gflags -# glog::glog +# absl::base absl::memory absl::container absl::strings absl::str_format +# gflags::gflags glog::glog # protobuf::libprotobuf # Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - ZLIB::ZLIB protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + ZLIB::ZLIB + absl::base absl::memory absl::container absl::strings absl::str_format + gflags::gflags glog::glog + protobuf::libprotobuf + Cbc::Cbc + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::constraint_solver ALIAS ${NAME}) diff --git a/ortools/constraint_solver/ac4_mdd_reset_table.cc b/ortools/constraint_solver/ac4_mdd_reset_table.cc deleted file mode 100644 index 8ae84be143b..00000000000 --- a/ortools/constraint_solver/ac4_mdd_reset_table.cc +++ /dev/null @@ -1,1546 +0,0 @@ -// Copyright 2013-2014 Jean Charles Régin / Guillaume Perez -// 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. - -#include -#include -#include -#include "ortools/base/int_type.h" -#include "ortools/base/int_type_indexed_vector.h" -#include "ortools/base/integral_types.h" -#include "ortools/base/logging.h" -#include "ortools/base/macros.h" -#include "ortools/base/map_util.h" -#include "ortools/base/stl_util.h" -#include "ortools/constraint_solver/constraint_solver.h" -#include "ortools/constraint_solver/constraint_solveri.h" -#include "ortools/util/vector_map.h" - -namespace operations_research { -namespace { - -/** - * Creation of Mdd, from Yap & al, "An Mdd-based Generalized Arc Consistency - * Algorithm for Positive and Negative Table Constraints and Some Global - * Constraints" - */ - -// Mdd_compression -class Mdd { - int num_var_; - bool state_; - std::vector childs_; - // id for the VMREC algorithm - int id; - // for the eventual DFS ect (needed) - bool visited; - - public: - int64 num_state; - - int cptIn; - - Mdd* newVersion; - bool deleted; - - Mdd(int nb_values, int num_var, int nb_instance) - : num_var_(num_var), - state_(false), - id(nb_instance), - visited(false), - num_state(0), - cptIn(0), - newVersion(nullptr), - deleted(false) { - for (int i = 0; i < nb_values; i++) { - childs_.push_back(nullptr); - } - } - - ~Mdd() {} - - Mdd* operator[](int n) { return childs_[n]; } - - void set(int n, Mdd* mdd) { childs_[n] = mdd; } - - int size() { return childs_.size(); } - - bool getState() { return state_; } - void setState(bool state) { state_ = state; } - - int getNumVar() { return num_var_; } - int getID() { return id; } - - void setID(int id_) { id = id_; } - - bool isVisited() { return visited; } - void setVisited(bool v) { visited = v; } -}; - -class MddFactory { - class VmRec { - VectorMap mdd_; - std::vector vmr_; - Mdd* value_; - - public: - VmRec(Mdd* value = nullptr) : mdd_(), vmr_(), value_(value) {} - - ~VmRec() { gtl::STLDeleteElements(&vmr_); } - - VmRec* next(Mdd* mdd) { - if (mdd == nullptr) { - if (mdd_.Contains(0)) { - return vmr_[mdd_.Index(0)]; - } - return nullptr; - } - if (mdd_.Contains(mdd->getID())) { - return vmr_[mdd_.Index(mdd->getID())]; - } - return nullptr; - } - - void addMdd(Mdd* mdd) { - if (mdd == nullptr) { - mdd_.Add(0); - vmr_.push_back(new VmRec()); - } else { - mdd_.Add(mdd->getID()); - vmr_.push_back(new VmRec()); - } - } - - void setValue(Mdd* value) { value_ = value; } - - Mdd* getValue() { return value_; } - }; - - std::vector g_; - std::vector NumDifferentValuesInColumn; - Mdd* finalEdge_; - Mdd* Troot_; - Mdd** mddGrid; - int nb_instance; - VectorMap* alreadyToDelete; - - std::list toDelete; - - public: - std::vector > vm_; - MddFactory() : nb_instance(1) {} - - ~MddFactory() { gtl::STLDeleteElements(&g_); } - - int getNbInstance() { return nb_instance; } - - // the first-one with table unsorted - Mdd* mddify(const IntTupleSet& table) { -#ifdef MEMTABLETOMddDEBUG - for (int i = 0; i < table.NumTuples(); i++) { - for (int j = 0; j < table.Arity(); j++) { - std::cout << table.Value(i, j) << " "; - } - std::cout << "\n"; - } -#endif - - finalEdge_ = new Mdd(0, table.Arity(), nb_instance++); - finalEdge_->setState(true); - - for (int i = 0; i < table.Arity(); i++) { - vm_.push_back(VectorMap()); - g_.push_back(new VmRec()); - NumDifferentValuesInColumn.push_back(table.NumDifferentValuesInColumn(i)); - } - Mdd* Troot = new Mdd(NumDifferentValuesInColumn[0], 0, nb_instance++); - Mdd* T = nullptr; - for (int i = 0; i < table.NumTuples(); ++i) { - T = Troot; - for (int j = 0; j < table.Arity(); j++) { - if (!vm_[j].Contains(table.Value(i, j))) { - vm_[j].Add(table.Value(i, j)); - } - const int index = vm_[j].Index(table.Value(i, j)); - if ((*T)[index] == nullptr) { - if (j + 1 < table.Arity()) { - T->set(index, new Mdd(NumDifferentValuesInColumn[j + 1], j + 1, - nb_instance++)); - } else { // last - T->set(index, finalEdge_); - } - } - T = (*T)[index]; - } - } - return mddReduce(Troot); - } - - // - Mdd* mddReduce(Mdd* T) { - if (T == nullptr) { - return nullptr; - } - - if (T->getState()) { - return T; - } - int i; - - std::vector B(T->size(), nullptr); - bool BEmpty = true; - for (i = 0; i < T->size(); i++) { - Mdd* G = mddReduce((*T)[i]); - if (G != nullptr) { - B[i] = G; - BEmpty = false; - } - } - - if (BEmpty) { - delete (T); - return nullptr; - } - - for (int i = 0; i < T->size(); i++) { - T->set(i, B[i]); - } - Mdd* GP = gContains(T); - if (GP != nullptr) { // exist a G' identical to T... - delete (T); - return GP; - } else { - // add T to the global set of Mdd - gAdd(T); - return T; - } - } - - Mdd* gContains(Mdd* T) { - VmRec* tmp = g_[T->getNumVar()]; - - for (int i = 0; i < T->size(); i++) { - tmp = tmp->next((*T)[i]); - if (tmp == nullptr) { - return nullptr; - } - } - return tmp->getValue(); - } - - void gAdd(Mdd* T) { - VmRec* tmp = g_[T->getNumVar()]; - VmRec* tmp2 = tmp; - for (int i = 0; i < T->size(); i++) { - tmp2 = tmp; - tmp = tmp->next((*T)[i]); - if (tmp == nullptr) { - tmp2->addMdd((*T)[i]); - tmp = tmp2->next((*T)[i]); - } - } - tmp->setValue(T); - } - - Mdd* ReCount(Mdd* T) { - std::vector*v1, *v2, *tmp; - int nb = 0; - - std::vector visited(getNbInstance()); - v1 = new std::vector(); - v2 = new std::vector(); - v1->push_back(T); - while (v1->size() > 0) { - tmp = v2; - v2 = v1; - v1 = tmp; - while (v2->size()) { - v2->back()->setID(nb++); - for (int i = 0; i < v2->back()->size(); ++i) { - Mdd* fils = (*v2->back())[i]; - if (fils != nullptr) { - if (!visited[fils->getID()]) { - visited[fils->getID()] = true; - v1->push_back((*v2->back())[i]); - } - } - } - v2->pop_back(); - } - } - - nb_instance = nb; - return T; - } - - void Draw(Mdd* T) { - std::vector*v1, *v2, *tmp; - std::cout << "digraph G{\n"; - std::vector visited(getNbInstance()); - v1 = new std::vector(); - v2 = new std::vector(); - v1->push_back(T); - while (v1->size() > 0) { - tmp = v2; - v2 = v1; - v1 = tmp; - - while (v2->size()) { - for (int i = 0; i < v2->back()->size(); ++i) { - Mdd* fils = (*v2->back())[i]; - if (fils != nullptr) { - std::cout << v2->back()->getID() << " -> " << fils->getID() - << " [ label = " - << vm_[v2->back()->getNumVar()].Element(i) << " ]\n"; - if (!visited[fils->getID()]) { - visited[fils->getID()] = true; - v1->push_back((*v2->back())[i]); - } - } - } - v2->pop_back(); - } - } - std::cout << "}\n"; - } - - // regular new - - // new regular - Mdd* regular(const std::vector& variables, const IntTupleSet& tuples, - int64 initial_state, const std::vector& final_states) { - int numWord_ = tuples.NumDifferentValuesInColumn(1); - int numState = tuples.NumDifferentValuesInColumn(0); - - int size_ = variables.size(); - - finalEdge_ = new Mdd(0, size_, nb_instance++); - - finalEdge_->setState(true); - - for (int i = 0; i < size_; i++) { - vm_.push_back(VectorMap()); - g_.push_back(new VmRec()); - NumDifferentValuesInColumn.push_back(numWord_); - } - - Troot_ = new Mdd(NumDifferentValuesInColumn[0], 0, nb_instance++); - - // creer la grille - - int gSize = (size_ - 1) * numState; - - mddGrid = new Mdd*[gSize]; - - int indexIter = 0; - - for (int i = 0; i < (size_ - 1); ++i) { - for (int j = 0; j < numState; ++j) { - mddGrid[indexIter + j] = new Mdd(numWord_, 1 + i, nb_instance++); - } - indexIter += numState; - } - - VectorMap stateIndex; - VectorMap final_states_set; - - for (int i = 0; i < final_states.size(); ++i) { - final_states_set.Add(final_states[i]); - } - - for (int tuple = 0; tuple < tuples.NumTuples(); ++tuple) { - int64 start_state = tuples.Value(tuple, 0); - if (!stateIndex.Contains(start_state)) { - stateIndex.Add(start_state); - } - int64 end_state = tuples.Value(tuple, 2); - if (!stateIndex.Contains(end_state)) { - stateIndex.Add(end_state); - } - - int start_index = stateIndex.Index(start_state); - int end_index = stateIndex.Index(end_state); - - int64 value = tuples.Value(tuple, 1); - - if (!vm_[0].Contains(value)) { - for (int i = 0; i < size_; ++i) { - vm_[i].Add(value); - } - } - int indexValue = vm_[0].Index(value); - - // ajouter les arcs entre les bon noeud - // d'abord l'arc du noeud 0 au noeud i - - if (start_state == initial_state && (*Troot_)[indexValue] == nullptr) { - Troot_->set(indexValue, mddGrid[end_index]); - mddGrid[end_index]->cptIn++; - } - - // ensuite dans la grille - indexIter = 0; - for (int depth = 0; depth < size_ - 2; ++depth) { - mddGrid[start_index + indexIter]->set( - indexValue, mddGrid[end_index + indexIter + numState]); - mddGrid[end_index + indexIter + numState]->cptIn++; - indexIter += numState; - } - - if (final_states_set.Contains(end_state)) { - // puis la derniere i vers le final - mddGrid[start_index + indexIter]->set(indexValue, finalEdge_); - finalEdge_->cptIn++; - } - } - - // supprimer les noeuds qui n'ont pas de noeud precedent - for (int i = 0; i < gSize; ++i) { - if (mddGrid[i]->cptIn == 0) { - for (int j = 0; j < numWord_; ++j) { - if ((*mddGrid[i])[j]) { // si l'arc existe - (*mddGrid[i])[j]->cptIn--; - } - } - delete mddGrid[i]; - } - } - - delete[] mddGrid; - - Troot_ = mddReduceRegular(Troot_); - - while (toDelete.size()) { - delete toDelete.front(); - toDelete.pop_front(); - } - - return Troot_; - } - - // - // Constructor for regular - // - - Mdd* regularAncien(const std::vector& variables, - const IntTupleSet& tuples, int64 initial_state, - const std::vector& final_states) { - int nbValues = tuples.NumDifferentValuesInColumn(1); - Mdd* Troot = new Mdd(nbValues, 0, nb_instance++); - - finalEdge_ = new Mdd(0, variables.size(), nb_instance++); - - finalEdge_->setState(true); - alreadyToDelete = new VectorMap(); - - Troot->num_state = initial_state; - - Mdd* T = nullptr; - - VectorMap* v = new VectorMap(); - - std::vector mdds; - - VectorMap final_states_set; - - for (int e = 0; e < final_states.size(); e++) { - final_states_set.Add(final_states[e]); - } - - VectorMap stateIndex; - - std::vector > tuplesIndex; - - for (int i = 0; i < tuples.NumTuples(); i++) { - int64 state = tuples.Value(i, 0); - if (!stateIndex.Contains(state)) { - stateIndex.Add(state); - tuplesIndex.emplace_back(); - } - int index = stateIndex.Index(state); - tuplesIndex[index].push_back(i); - } - - std::queue l; - - l.push(Troot); - l.push(nullptr); - - for (int i = 0; i < variables.size(); i++) { - vm_.push_back(VectorMap()); - g_.push_back(new VmRec()); - } - - int lvl = 0; - - while (l.size() > 0) { - T = l.front(); - l.pop(); - if (T == nullptr) { - delete v; - v = new VectorMap(); - mdds.clear(); - lvl++; - if (lvl == variables.size() - 1) { - break; - } - l.push(nullptr); - continue; - } - int64 state = T->num_state; - int si = stateIndex.Index(state); - for (int t = 0; t < tuplesIndex[si].size(); t++) { - int i = tuplesIndex[si][t]; - - if (state == tuples.Value(i, 0)) { - int64 value = tuples.Value(i, 1); - if (!vm_[lvl].Contains(value)) { - vm_[lvl].Add(value); - } - - int index = vm_[lvl].Index(value); - int64 newState = tuples.Value(i, 2); - - if (!v->Contains(newState)) { - v->Add(newState); - mdds.push_back(new Mdd(nbValues, lvl + 1, nb_instance++)); - l.push(mdds[v->Index(newState)]); - mdds[v->Index(newState)]->num_state = newState; - } - - T->set(index, mdds[v->Index(newState)]); - } - } - } - - delete v; - - while (l.size() > 0) { - T = l.front(); - l.pop(); - - int64 state = T->num_state; - int si = stateIndex.Index(state); - for (int t = 0; t < tuplesIndex[si].size(); t++) { - int i = tuplesIndex[si][t]; - if (state == tuples.Value(i, 0)) { - int64 value = tuples.Value(i, 1); - if (!vm_[lvl].Contains(value)) { - vm_[lvl].Add(value); - } - - int index = vm_[lvl].Index(value); - int64 newState = tuples.Value(i, 2); - - if (final_states_set.Contains(newState)) { - T->set(index, finalEdge_); - } - } - } - } - Troot = mddReduceRegular(Troot); - - while (!toDelete.empty()) { - delete toDelete.front(); - toDelete.pop_front(); - } - - return Troot; - } - - Mdd* mddReduceRegular(Mdd* T) { - if (T == nullptr) { - return nullptr; - } - - if (T->deleted) { - return T->newVersion; - } - - if (T->getState()) { - return T; - } - int i; - - std::vector B(T->size(), nullptr); - bool BEmpty = true; - - for (i = 0; i < T->size(); i++) { - Mdd* G = mddReduceRegular((*T)[i]); - if (G != nullptr) { - B[i] = G; - BEmpty = false; - } - } - - if (BEmpty) { - T->deleted = true; - toDelete.push_back(T); - return nullptr; - } - - for (int i = 0; i < T->size(); i++) { - T->set(i, B[i]); - } - - Mdd* GP = gContains(T); - if (GP != nullptr) { // exist a G' identical to G... - // delete (T); - T->deleted = true; - toDelete.push_back(T); - T->newVersion = GP; - - return GP; - } else { - // add G to the global set of Mdd - gAdd(T); - T->deleted = true; - T->newVersion = T; - - return T; - } - } -}; - -class SparseRevSet : public NumericalRev { - public: - // size is the size of sparse - SparseRevSet(int size_, int* sparse_, int* dense_) - : NumericalRev(0), sparse(sparse_), dense(dense_), size(size_) { - for (int i = 0; i < size; ++i) { - sparse[i] = -1; - // dense_[i] = -1; - } - } - - ~SparseRevSet() { - delete sparse; - delete dense; - } - - int operator[](int i) { return dense[i]; } - - bool isMember(int k) { - int a = sparse[k]; - if (a < this->Value() && a >= 0 && dense[a] == k) { - return true; - } - return false; - } - - void addMember(int k, Solver* const s) { - int a = sparse[k]; - int b = this->Value(); - if (a >= b || a < 0 || dense[a] != k) { - sparse[k] = b; - dense[b] = k; - this->Incr(s); - } - } - void remove(int k, Solver* const s) { - int sk = sparse[k]; - int sL = this->Value() - 1; - dense[sk] = dense[sL]; - sparse[dense[sk]] = sk; - sparse[k] = sL; - dense[sL] = k; - this->Decr(s); - } - - void clear(Solver* const solver) { this->SetValue(solver, 0); } - - int nbValues() { return this->Value(); } - - void restore(int k, Solver* const s) { - int sk = sparse[k]; - int sL = this->Value(); - dense[sk] = dense[sL]; - sparse[dense[sk]] = sk; - sparse[k] = sL; - dense[sL] = k; - this->Incr(s); - } - - private: - int* sparse; - int* dense; - int size; -}; - -class MyMdd { - class Edge { - public: - Edge(int value, int start, int end, int id) - : start_(start), end_(end), id_(id), value_(value) {} - - inline int getValue() { return value_; } - - inline int getStart() { return start_; } - - void setStart(int start) { start_ = start; } - - inline int getEnd() { return end_; } - - void setEnd(int end) { end_ = end; } - - private: - int start_; - int end_; - int id_; - - int value_; - }; - - class Node { - public: - Node(int var, int* shared_in, int* shared_out, int nb_in, int nb_out, - int number_of_edges) - : in_(nb_in, shared_in, number_of_edges), - out_(nb_out, shared_out, number_of_edges), - var_(var) {} - - inline int getNumberOfEdgeIn() { return in_.Size(); } - inline int getNumberOfEdgeOut() { return out_.Size(); } - inline int getEdgeIn(int index) { return in_.Element(index); } - inline int getEdgeOut(int index) { return out_.Element(index); } - - inline int getVariable() { return var_; } - - void removeEdgeIn(int edge, Solver* solver) { in_.Remove(solver, edge); } - void removeEdgeOut(int edge, Solver* solver) { out_.Remove(solver, edge); } - - void InsertEdgeIn(int edge, Solver* solver) { in_.Insert(solver, edge); } - void InsertEdgeOut(int edge, Solver* solver) { out_.Insert(solver, edge); } - - // used for the reset - void ClearEdgeIn(Solver* solver) { in_.Clear(solver); } - - void ClearEdgeOut(Solver* solver) { out_.Clear(solver); } - - void RestoreEdgeIn(int edge, Solver* solver) { in_.Restore(solver, edge); } - - void RestoreEdgeOut(int edge, Solver* solver) { - out_.Restore(solver, edge); - } - - private: - RevIntSet in_; - RevIntSet out_; - - int var_; - }; - - public: - MyMdd(MddFactory* mf, Mdd* mdd, Solver* solver) - : vm_(), - number_of_edges_by_value(), - edges_(), - nodes_(), - shared_in_(nullptr), - shared_out_(nullptr), - shared_nodes(), - number_of_edge(0), - nodes_lvl(), - sizeBeforeReset(), - edges_lvl() { - // //Construct the Mdd - // MddFactory mf; - // Mdd* mdd = mf.mddify(tuples); - - // - std::list tmp; - number_of_edge = 0; - - // creation of the convertion table - for (int var = 0; var < mf->vm_.size(); var++) { - vm_.push_back(VectorMap()); - number_of_edges_by_value.push_back(std::vector()); - for (int val = 0; val < mf->vm_[var].size(); val++) { - vm_[var].Add(mf->vm_[var][val]); - number_of_edges_by_value[var].push_back(0); - } - } - - // will count how many arc are intering in a node - std::vector nb_in(mf->getNbInstance(), 0); - // will count how many arc will get out from a node - std::vector nb_out(mf->getNbInstance(), 0); - - std::vector num_var(mf->getNbInstance(), -1); - - std::vector indice; - - std::vector nb_nodes_lvl(mf->vm_.size() + 1, 0); - - tmp.push_back(mdd); - - num_var[mdd->getID()] = indice.size(); - indice.push_back(mdd->getNumVar()); - mdd->setVisited(true); - - while (tmp.size() > 0) { - mdd = tmp.front(); - tmp.pop_front(); - // count the number of node at each lvl - nb_nodes_lvl[mdd->getNumVar()]++; - for (int value = 0; value < mdd->size(); value++) { - if ((*mdd)[value] != nullptr) { - if (!(*mdd)[value]->isVisited()) { - num_var[(*mdd)[value]->getID()] = indice.size(); - indice.push_back((*mdd)[value]->getNumVar()); - - tmp.push_back((*mdd)[value]); - (*mdd)[value]->setVisited(true); - } - - edges_.push_back(new Edge(value, num_var[mdd->getID()], - num_var[(*mdd)[value]->getID()], - number_of_edge++)); - - nb_in[num_var[(*mdd)[value]->getID()]]++; - nb_out[num_var[mdd->getID()]]++; - - number_of_edges_by_value[mdd->getNumVar()][value]++; - } - } - } - - shared_in_ = new int[edges_.size()]; - shared_out_ = new int[edges_.size()]; - shared_nodes = new int[indice.size() + 1]; - - for (int lvl = 0; lvl < mf->vm_.size() + 1; ++lvl) { - nodes_lvl.push_back(new SparseRevSet(indice.size(), shared_nodes, - new int[nb_nodes_lvl[lvl]])); - - sizeBeforeReset.push_back(0); - - edges_lvl.push_back(new NumericalRev(0)); - } - - for (int n = 0; n < indice.size(); n++) { - nodes_.push_back(new Node(indice[n], shared_in_, shared_out_, nb_in[n], - nb_out[n], number_of_edge)); - - // add the nodes to the correct set - nodes_lvl[indice[n]]->addMember(n, solver); - } - - for (int edge = 0; edge < edges_.size(); edge++) { - nodes_[edges_[edge]->getStart()]->InsertEdgeOut(edge, solver); - edges_lvl[nodes_[edges_[edge]->getStart()]->getVariable()]->Incr(solver); - nodes_[edges_[edge]->getEnd()]->InsertEdgeIn(edge, solver); - } - - delete mdd; - } - - ~MyMdd() { - delete shared_in_; - delete shared_out_; - delete shared_nodes; - - for (int i = 0; i < edges_.size(); ++i) { - if (edges_[i] != nullptr) { - delete edges_[i]; - } - } - for (int i = 0; i < nodes_.size(); ++i) { - if (nodes_[i] != nullptr) { - delete nodes_[i]; - } - } - } - - inline int getIndexVal(int index, int64 val) const { - return vm_[index].Index(val); - } - - inline bool containValIndex(int index, int64 val) const { - return vm_[index].Contains(val); - } - - inline int64 getValForIndex(int index, int val) const { - return vm_[index].Element(val); - } - - void removeEdge(int edge, Solver* solver) { - nodes_[edges_[edge]->getStart()]->removeEdgeOut(edge, solver); - nodes_[edges_[edge]->getEnd()]->removeEdgeIn(edge, solver); - } - void removeEdgeUp(int edge, Solver* solver) { - nodes_[edges_[edge]->getStart()]->removeEdgeOut(edge, solver); - // nodes_[edges_[edge]->getEnd()]->removeEdgeIn(edge, solver); - } - void removeEdgeDown(int edge, Solver* solver) { - // nodes_[edges_[edge]->getStart()]->removeEdgeOut(edge, solver); - nodes_[edges_[edge]->getEnd()]->removeEdgeIn(edge, solver); - } - - void resetDeleteEdge(int edge, Solver* solver, int* cptUp, int* cptDown) { - resetDeleteEdgeUp(edge, solver, cptUp); - resetDeleteEdgeDown(edge, solver, cptDown); - } - - void resetDeleteEdgeUp(int edge, Solver* solver, int* cptUp) { - removeEdgeUp(edge, solver); - const int e_start = edges_[edge]->getStart(); - if (nodes_[e_start]->getNumberOfEdgeOut() == 0) { - *cptUp += nodes_[e_start]->getNumberOfEdgeIn(); - nodes_lvl[nodes_[e_start]->getVariable()]->remove(e_start, solver); - } - } - - void resetDeleteEdgeDown(int edge, Solver* solver, int* cptDown) { - removeEdgeDown(edge, solver); - const int e_end = edges_[edge]->getEnd(); - if (nodes_[e_end]->getNumberOfEdgeIn() == 0) { - *cptDown += nodes_[e_end]->getNumberOfEdgeOut(); - nodes_lvl[nodes_[e_end]->getVariable()]->remove(e_end, solver); - } - } - - void resetRestoreEdge(int edge, Solver* solver, int* cptUp, int* cptDown) { - resetRestoreEdgeUp(edge, solver, cptUp); - resetRestoreEdgeDown(edge, solver, cptDown); - } - void resetRestoreEdgeUp(int edge, Solver* solver, int* cptUp) { - const int e_start = edges_[edge]->getStart(); - if (!nodes_lvl[nodes_[e_start]->getVariable()]->isMember(e_start)) { - // we clear the node to delete the deleted edges - nodes_[e_start]->ClearEdgeOut(solver); - - nodes_lvl[nodes_[e_start]->getVariable()]->restore(e_start, solver); - *cptUp += nodes_[e_start]->getNumberOfEdgeIn(); - } - // and we restore only still valid edges - nodes_[e_start]->RestoreEdgeOut(edge, solver); - } - void resetRestoreEdgeDown(int edge, Solver* solver, int* cptDown) { - const int e_end = edges_[edge]->getEnd(); - if (!nodes_lvl[nodes_[e_end]->getVariable()]->isMember(e_end)) { - nodes_[e_end]->ClearEdgeIn(solver); - - nodes_lvl[nodes_[e_end]->getVariable()]->restore(e_end, solver); - *cptDown += nodes_[e_end]->getNumberOfEdgeOut(); - } - - nodes_[e_end]->RestoreEdgeIn(edge, solver); - } - - void clearNodesSet(int lvl, Solver* solver) { - sizeBeforeReset[lvl] = nodes_lvl[lvl]->nbValues(); - nodes_lvl[lvl]->clear(solver); - } - - void saveNodesSet(int lvl, Solver* solver) { - sizeBeforeReset[lvl] = nodes_lvl[lvl]->nbValues(); - } - - void resetGetEdgesToKeepUp(int lvl, std::vector* edges) { - edges->clear(); - for (int i = 0; i < nodes_lvl[lvl]->nbValues(); ++i) { - const int n = (*nodes_lvl[lvl])[i]; - for (int e = 0; e < nodes_[n]->getNumberOfEdgeIn(); ++e) { - edges->push_back(nodes_[n]->getEdgeIn(e)); - } - } - } - void resetGetEdgesToDeleteUp(int lvl, std::vector* edges) { - edges->clear(); - for (int i = nodes_lvl[lvl]->nbValues(); i < sizeBeforeReset[lvl]; ++i) { - const int n = (*nodes_lvl[lvl])[i]; - for (int e = 0; e < nodes_[n]->getNumberOfEdgeIn(); ++e) { - edges->push_back(nodes_[n]->getEdgeIn(e)); - } - } - } - - void resetGetEdgesToKeepDown(int lvl, std::vector* edges) { - edges->clear(); - for (int i = 0; i < nodes_lvl[lvl]->nbValues(); ++i) { - const int n = (*nodes_lvl[lvl])[i]; - for (int e = 0; e < nodes_[n]->getNumberOfEdgeOut(); ++e) { - edges->push_back(nodes_[n]->getEdgeOut(e)); - } - } - } - void resetGetEdgesToDeleteDown(int lvl, std::vector* edges) { - edges->clear(); - for (int i = nodes_lvl[lvl]->nbValues(); i < sizeBeforeReset[lvl]; ++i) { - const int n = (*nodes_lvl[lvl])[i]; - for (int e = 0; e < nodes_[n]->getNumberOfEdgeOut(); ++e) { - edges->push_back(nodes_[n]->getEdgeOut(e)); - } - } - } - - int getVarForEdge(int edge) { - return nodes_[edges_[edge]->getStart()]->getVariable(); - } - int getValueForEdge(int edge) { return edges_[edge]->getValue(); } - - int getNumberOfEdge() { return edges_.size(); } - - int getNumberOfNode() { return nodes_.size(); } - - int getNumberOfEdgesLvl(int lvl) { return edges_lvl[lvl]->Value(); } - void setNumberOfEdgesLvl(int lvl, int newValue, Solver* solver) { - edges_lvl[lvl]->SetValue(solver, newValue); - } - - std::vector > vm_; - std::vector > number_of_edges_by_value; - - private: - std::vector edges_; - std::vector nodes_; - - int* shared_in_; - int* shared_out_; - int* shared_nodes; - - int number_of_edge; - - std::vector nodes_lvl; - std::vector sizeBeforeReset; - - std::vector*> edges_lvl; -}; - -class MddTableVar { - public: - MddTableVar(Solver* const solver, IntVar* var, int index, - int number_of_different_value, int* shared_positions_edges, - int number_of_edges, - const std::vector& number_of_edges_by_value, - const MyMdd& mdd) - : solver_(solver), - edges_per_value_(number_of_different_value), - active_values_(number_of_different_value), - var_(var), - domain_iterator_(var->MakeDomainIterator(true)), - delta_domain_iterator_(var->MakeHoleIterator(true)), - index_(index), - mdd_(mdd), - firstTime(true) { - for (int value_index = 0; value_index < edges_per_value_.size(); - value_index++) { - edges_per_value_[value_index] = - new RevIntSet(number_of_edges_by_value[value_index], - shared_positions_edges, number_of_edges); - active_values_.Insert(solver_, value_index); - } - } - - ~MddTableVar() { gtl::STLDeleteElements(&edges_per_value_); } - - IntVar* Variable() const { return var_; } - - void CollectEdgesToRemove(const std::vector& delta, - std::vector* edges_to_remove) { - edges_to_remove->clear(); - for (int k = 0; k < delta.size(); k++) { - const int value = delta[k]; - for (int edge_index = 0; edge_index < edges_per_value_[value]->Size(); - edge_index++) { - const int edge = edges_per_value_[value]->Element(edge_index); - edges_to_remove->push_back(edge); - } - } - } - void CollectEdgesToKeep(std::vector* edges_to_keep) { - edges_to_keep->clear(); - for (domain_iterator_->Init(); domain_iterator_->Ok(); - domain_iterator_->Next()) { - const int value = mdd_.getIndexVal(index_, domain_iterator_->Value()); - for (int edge_index = 0; edge_index < edges_per_value_[value]->Size(); - edge_index++) { - const int edge = edges_per_value_[value]->Element(edge_index); - edges_to_keep->push_back(edge); - } - } - } - - int getNumberOfEdgeToRemove(const std::vector& delta) { - int total = 0; - for (int value : delta) { - total += edges_per_value_[value]->Size(); - } - return total; - } - - void removeEdgeForValue(int value, int edge) { - edges_per_value_[value]->Remove(solver_, edge); - if (edges_per_value_[value]->Size() == 0) { - var_->RemoveValue(mdd_.getValForIndex(index_, value)); - active_values_.Remove(solver_, value); - } - } - - void ComputeDeltaDomain(std::vector* delta) { - // nouvelle version, Pour Laurent, il utilise des bitset et - // recupère donc les valeurs supprimer / modifier en parcourant - // le plus petit ensemble des deux. - // Ici j'ai besoin pour voir directement itérer ensuite sur les - // Bon/Mauvais rapidement (en prenant donc le plus petit ensemble - // des deux) - // Idée 1) utilisé un sparseSet pour mon domain. il permetra - // de soit supprimer les valeurs supprimées, soit de reset - // et de remetre les valeur postive. - -#define NEWDELTA - - delta->clear(); - // we iterate the delta of the variable - // - // ATTENTION code for or-tools: the delta iterator does not - // include the values between oldmin and min and the values - // between max and oldmax. - // - // therefore we decompose the iteration into 3 parts - // - from oldmin to min - // - for the deleted values between min and max - // - from max to oldmax - const int64 old_min_domain = var_->OldMin(); - const int64 old_max_domain = var_->OldMax(); - const int64 min_domain = var_->Min(); - const int64 max_domain = var_->Max(); - - // pour le cas ou le Mdd contients des valeurs qui ne sont pas dans la - // variable - if (firstTime) { - firstTime = false; - for (int i = 0; i < active_values_.Size(); ++i) { - if (!var_->Contains( - mdd_.getValForIndex(index_, active_values_.Element(i)))) { - delta->push_back(active_values_.Element(i)); - } - } - } - -#ifdef NEWDELTA - - switch (var_->Size()) { - case 1: - for (int64 val = old_min_domain; val <= old_max_domain; ++val) { - const int index = mdd_.getIndexVal(index_, val); - if (index != -1 && min_domain != val) { - delta->push_back(index); - } - } - return; - break; - case 2: - for (int64 val = old_min_domain; val <= old_max_domain; ++val) { - const int index = mdd_.getIndexVal(index_, val); - if (index != -1 && min_domain != val && max_domain != val) { - delta->push_back(index); - } - } - return; - break; - default: - // Si c'est un interval - if (var_->Size() == max_domain - min_domain + 1) { - for (int64 val = old_min_domain; val < min_domain; ++val) { - const int index = mdd_.getIndexVal(index_, val); - if (index != -1) { - delta->push_back(index); - } - } - for (int64 val = max_domain + 1; val <= old_max_domain; ++val) { - const int index = mdd_.getIndexVal(index_, val); - if (index != -1) { - delta->push_back(index); - } - } - return; - } -#endif - - // First iteration: from old_min to min. - - for (int64 val = old_min_domain; val < min_domain; ++val) { - const int index = mdd_.getIndexVal(index_, val); - if (index != -1) { - delta->push_back(index); - } - } - // Second iteration: "delta" domain iteration. - IntVarIterator* const it = delta_domain_iterator_; - for (it->Init(); it->Ok(); it->Next()) { - const int64 value = it->Value(); - if (value > min_domain && value < max_domain) { - const int index = mdd_.getIndexVal(index_, value); - if (index != -1) { - delta->push_back(index); - } - } - } - // Third iteration: from max to old_max. - for (int64 val = max_domain + 1; val <= old_max_domain; ++val) { - const int index = mdd_.getIndexVal(index_, val); - if (index != -1) { - delta->push_back(index); - } - } - -#ifdef NEWDELTA - } -#endif - } - - void addEdge(int value, int edge) { - edges_per_value_[value]->Insert(solver_, edge); - } - - void ClearSupport() { - for (int k = 0; k < active_values_.Size(); k++) { - edges_per_value_[active_values_.Element(k)]->Clear(solver_); - } - } - - void RestoreEdge(int value, int edge) { - edges_per_value_[value]->Restore(solver_, edge); - } - - void RemoveUnsuportedValue() { - int count = 0; - for (int k = active_values_.Size() - 1; k >= 0; k--) { - const int64 value_index = active_values_.Element(k); - if (edges_per_value_[value_index]->Size() == 0) { - active_values_.Remove(solver_, value_index); - count++; - } - } - // Removed values have been inserted after the last active value. - for (int k = 0; k < count; ++k) { - const int64 value_index = active_values_.RemovedElement(k); - var_->RemoveValue(mdd_.getValForIndex(index_, value_index)); - } - } - - int GetNbActiveValues() { return var_->Size(); } - - void DeleteDeltaValues(const std::vector& delta) { - for (int value : delta) { - // protect of multiple proceeding ... - if (edges_per_value_[value]->Size() > 0) { - active_values_.Remove(solver_, value); - edges_per_value_[value]->Clear(solver_); - } - } - } - - void DeleteValuesNotBelongTheMdd() { - // maybe no need of the table? - std::vector toDel; - for (domain_iterator_->Init(); domain_iterator_->Ok(); - domain_iterator_->Next()) { - const int64 val = domain_iterator_->Value(); - if (!mdd_.containValIndex(index_, val)) { - toDel.push_back(val); - } - } - - for (int i = 0; i < toDel.size(); ++i) { - var_->RemoveValue(toDel[i]); - } - } - - private: - Solver* solver_; - std::vector*> edges_per_value_; - RevIntSet active_values_; - IntVar* const var_; - IntVarIterator* const domain_iterator_; - IntVarIterator* const delta_domain_iterator_; - - int index_; - const MyMdd& mdd_; - bool firstTime; -}; - -class Ac4MddTableConstraint : public Constraint { - public: - Ac4MddTableConstraint(Solver* const solver, MddFactory* mf, Mdd* mdd, - const std::vector& vars) - : Constraint(solver), - solver_(solver), - original_vars_(vars), - vars_(vars.size()), - num_variables_(vars.size()), - delta_of_value_indices_(0), - mdd_(mf, mdd, solver_), - shared_positions_edges_(nullptr), - up(new std::vector()), - down(new std::vector()), - edges(new std::vector()), - tmp(nullptr) { - shared_positions_edges_ = new int[mdd_.getNumberOfEdge()]; - for (int var_index = 0; var_index < vars.size(); var_index++) { - vars_[var_index] = new MddTableVar( - solver, vars[var_index], var_index, mdd_.vm_[var_index].size(), - shared_positions_edges_, mdd_.getNumberOfEdge(), - mdd_.number_of_edges_by_value[var_index], mdd_); - } - } - - ~Ac4MddTableConstraint() { - gtl::STLDeleteElements(&vars_); // delete all elements of a vector - delete shared_positions_edges_; - } - - void Post() { - for (int var_index = 0; var_index < num_variables_; var_index++) { - Demon* const demon = MakeConstraintDemon1( - solver(), this, &Ac4MddTableConstraint::FilterOneVariable, - "FilterOneVariable", var_index); - vars_[var_index]->Variable()->WhenDomain(demon); - } - } - - void InitialPropagate() { - // insert edges in correct set - int number_of_edge = mdd_.getNumberOfEdge(); - for (int edge = 0; edge < number_of_edge; edge++) { - vars_[mdd_.getVarForEdge(edge)]->addEdge(mdd_.getValueForEdge(edge), - edge); - } - - for (int var_index = 0; var_index < num_variables_; var_index++) { - vars_[var_index]->DeleteValuesNotBelongTheMdd(); - } - - for (int var_index = 0; var_index < num_variables_; var_index++) { - FilterOneVariable(var_index); - } - } - - void FilterOneVariable(int var_index) { - MddTableVar* const var = vars_[var_index]; - var->ComputeDeltaDomain(&delta_of_value_indices_); - - int cptUp = 0; - int cptDown = 0; - - // last action in this direction - bool ResetUp = false; - bool ResetDown = false; - - // index of the next var process - int lvlUp = var_index; - int lvlDown = var_index + 1; - - int nbToRemove = var->getNumberOfEdgeToRemove(delta_of_value_indices_); - - int nbToKeep = mdd_.getNumberOfEdgesLvl(var_index) - nbToRemove; - - mdd_.setNumberOfEdgesLvl(var_index, nbToKeep, solver_); - - if (nbToRemove < nbToKeep) { // we should do the normal deletation - mdd_.saveNodesSet(lvlDown, solver_); - mdd_.saveNodesSet(lvlUp, solver_); - - var->CollectEdgesToRemove(delta_of_value_indices_, &edges_to_remove_); - - // delete them from the Mdd and from the current support_list - for (int edge_indice = 0; edge_indice < edges_to_remove_.size(); - ++edge_indice) { - const int edge = edges_to_remove_[edge_indice]; - // delete in the Mdd - mdd_.resetDeleteEdge(edge, solver_, &cptUp, &cptDown); - } - - } else { // we should reset - ResetUp = true; - ResetDown = true; - - var->CollectEdgesToKeep(&edges_to_remove_); - var->ClearSupport(); - - mdd_.clearNodesSet(lvlDown, solver_); - mdd_.clearNodesSet(lvlUp, solver_); - - for (int edge_indice = 0; edge_indice < edges_to_remove_.size(); - ++edge_indice) { - const int edge = edges_to_remove_[edge_indice]; - // restoration in the Mdd - mdd_.resetRestoreEdge(edge, solver_, &cptUp, &cptDown); - // restoration in the support - vars_[var_index]->RestoreEdge(mdd_.getValueForEdge(edge), edge); - } - var->RemoveUnsuportedValue(); - } - - var->DeleteDeltaValues(delta_of_value_indices_); - - while (cptUp > 0) { - lvlUp--; - - if (ResetUp) { // if we was in reset mode - nbToRemove = mdd_.getNumberOfEdgesLvl(lvlUp) - cptUp; - nbToKeep = cptUp; - cptUp = 0; - } else { - nbToRemove = cptUp; - nbToKeep = mdd_.getNumberOfEdgesLvl(lvlUp) - cptUp; - cptUp = 0; - } - - mdd_.setNumberOfEdgesLvl(lvlUp, nbToKeep, solver_); - - if (nbToRemove < nbToKeep) { // we should delete - mdd_.saveNodesSet(lvlUp, solver_); - ResetUp = false; - mdd_.resetGetEdgesToDeleteUp(lvlUp + 1, &edges_to_remove_); - - for (int edge_indice = 0; edge_indice < edges_to_remove_.size(); - ++edge_indice) { - const int edge = edges_to_remove_[edge_indice]; - // delete in the Mdd - mdd_.resetDeleteEdgeUp(edge, solver_, &cptUp); - // delete in the support - vars_[lvlUp]->removeEdgeForValue(mdd_.getValueForEdge(edge), edge); - } - - } else { // we should reset - ResetUp = true; - // on reset le niveau dans lequel on va regarder - mdd_.clearNodesSet(lvlUp, solver_); - // on recupere les edge du niveau précédent - mdd_.resetGetEdgesToKeepUp(lvlUp + 1, &edges_to_remove_); - - vars_[lvlUp]->ClearSupport(); - - for (int edge_indice = 0; edge_indice < edges_to_remove_.size(); - ++edge_indice) { - const int edge = edges_to_remove_[edge_indice]; - // restoration in the Mdd - mdd_.resetRestoreEdgeUp(edge, solver_, &cptUp); - // restoration in the support - vars_[lvlUp]->RestoreEdge(mdd_.getValueForEdge(edge), edge); - } - vars_[lvlUp]->RemoveUnsuportedValue(); - } - } - - while (cptDown > 0) { - if (ResetDown) { // if we was in reset mode - nbToRemove = mdd_.getNumberOfEdgesLvl(lvlDown) - cptDown; - nbToKeep = cptDown; - cptDown = 0; - } else { - nbToRemove = cptDown; - nbToKeep = mdd_.getNumberOfEdgesLvl(lvlDown) - cptDown; - cptDown = 0; - } - - mdd_.setNumberOfEdgesLvl(lvlDown, nbToKeep, solver_); - - if (nbToRemove < nbToKeep) { // we should delete - mdd_.saveNodesSet(lvlDown + 1, solver_); - ResetDown = false; - mdd_.resetGetEdgesToDeleteDown(lvlDown, &edges_to_remove_); - - for (int edge_indice = 0; edge_indice < edges_to_remove_.size(); - ++edge_indice) { - const int edge = edges_to_remove_[edge_indice]; - // delete in the Mdd - mdd_.resetDeleteEdgeDown(edge, solver_, &cptDown); - // delete in the support - vars_[lvlDown]->removeEdgeForValue(mdd_.getValueForEdge(edge), edge); - } - - } else { // we should reset - ResetDown = true; - mdd_.clearNodesSet(lvlDown + 1, solver_); - - mdd_.resetGetEdgesToKeepDown(lvlDown, &edges_to_remove_); - - vars_[lvlDown]->ClearSupport(); - - for (int edge_indice = 0; edge_indice < edges_to_remove_.size(); - ++edge_indice) { - const int edge = edges_to_remove_[edge_indice]; - // restoration in the Mdd - mdd_.resetRestoreEdgeDown(edge, solver_, &cptDown); - // restoration in the support - vars_[lvlDown]->RestoreEdge(mdd_.getValueForEdge(edge), edge); - } - vars_[lvlDown]->RemoveUnsuportedValue(); - } - lvlDown++; - } - } - - virtual std::string DebugString() const { - return StringPrintf("Mdd4R(arity = %d)", num_variables_); - } - - virtual void Accept(ModelVisitor* const visitor) const { - visitor->BeginVisitConstraint(ModelVisitor::kAllowedAssignments, this); - visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument, - original_vars_); - visitor->EndVisitConstraint(ModelVisitor::kAllowedAssignments, this); - } - - private: - Solver* solver_; - std::vector original_vars_; - // Variables of the constraint. - std::vector vars_; - // Number of variables. - const int num_variables_; - // Temporary storage for delta of one variable. - std::vector delta_of_value_indices_; - // Mdd - MyMdd mdd_; - // temporary storage for deleted edges - std::vector edges_to_remove_; - - int* shared_positions_edges_; - - // for the reset - std::vector* up; - std::vector* down; - std::vector* edges; - std::vector* tmp; -}; -} // namespace - -// External API. -Constraint* BuildAc4MddResetTableConstraint(Solver* const solver, - const IntTupleSet& tuples, - const std::vector& vars) { - MddFactory mf; - Mdd* mdd = mf.mddify(tuples); - return solver->RevAlloc(new Ac4MddTableConstraint(solver, &mf, mdd, vars)); -} - -Constraint* BuildAc4MddResetRegularConstraint( - Solver* const solver, const std::vector& vars, - const IntTupleSet& tuples, int64 initial_state, - const std::vector& final_states) { - MddFactory mf; - Mdd* mdd = mf.regular(vars, tuples, initial_state, final_states); - return solver->RevAlloc(new Ac4MddTableConstraint(solver, &mf, mdd, vars)); -} - -Constraint* BuildAc4MddResetConstraint(Solver* const solver, - const std::vector& vars, - MddFactory* mf, Mdd* mdd) { - return solver->RevAlloc(new Ac4MddTableConstraint(solver, mf, mdd, vars)); -} - -} // namespace operations_research diff --git a/ortools/constraint_solver/ac4r_table.cc b/ortools/constraint_solver/ac4r_table.cc deleted file mode 100644 index c9767709a55..00000000000 --- a/ortools/constraint_solver/ac4r_table.cc +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright 2011-2012 Jean Charles Régin -// 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. - -#include "ortools/base/int_type.h" -#include "ortools/base/int_type_indexed_vector.h" -#include "ortools/base/integral_types.h" -#include "ortools/base/logging.h" -#include "ortools/base/macros.h" -#include "ortools/base/map_util.h" -#include "ortools/base/stl_util.h" -#include "ortools/constraint_solver/constraint_solver.h" -#include "ortools/constraint_solver/constraint_solveri.h" -#include "ortools/util/vector_map.h" - -namespace operations_research { -namespace { -// **************************************************************************** -// -// GAC-4 Revisited (c) Jean-Charles Régin 2012 -// -// **************************************************************************** -class Ac4TableConstraint; - -class IndexedTable { - public: - class Column { - public: - Column() : num_tuples_(0) {} - - void Init(const IntTupleSet& table, int var_index) { - num_tuples_ = table.NumTuples(); - column_of_value_indices_.resize(num_tuples_); - num_tuples_per_value_.resize(table.NumDifferentValuesInColumn(var_index), - 0); - for (int tuple_index = 0; tuple_index < num_tuples_; tuple_index++) { - const int64 val = table.Value(tuple_index, var_index); - if (!value_map_.Contains(val)) { - value_map_.Add(val); - } - const int index = value_map_.Index(val); - column_of_value_indices_[tuple_index] = index; - num_tuples_per_value_[index]++; - } - } - - int ValueIndex(int tuple_index) const { - return column_of_value_indices_[tuple_index]; - } - - int IndexFromValue(int64 value) const { return value_map_.Index(value); } - - int64 ValueFromIndex(int value_index) const { - return value_map_.Element(value_index); - } - - int NumTuplesContainingValueIndex(int value_index) const { - return num_tuples_per_value_[value_index]; - } - - int NumTuples() const { return num_tuples_; } - - int NumDifferentValues() const { return num_tuples_per_value_.size(); } - - private: - std::vector column_of_value_indices_; - VectorMap value_map_; - std::vector num_tuples_per_value_; - int num_tuples_; - }; - - IndexedTable(const IntTupleSet& tuple_set) - : tuple_set_(tuple_set), - arity_(tuple_set.Arity()), - num_tuples_(tuple_set.NumTuples()), - columns_(arity_) { - for (int i = 0; i < arity_; i++) { - columns_[i].Init(tuple_set, i); - } - } - - ~IndexedTable() {} - - int NumVars() const { return arity_; } - - const Column& column(int var_index) const { return columns_[var_index]; } - - int NumTuples() const { return num_tuples_; } - - const IntTupleSet& tuple_set() const { return tuple_set_; } - - private: - const IntTupleSet tuple_set_; - const int arity_; - const int num_tuples_; - std::vector columns_; -}; - -class TableVar { - public: - TableVar(Solver* const solver, IntVar* var, - const IndexedTable::Column& column) - : solver_(solver), - column_(column), - tuples_per_value_(column.NumDifferentValues()), - active_values_(column.NumDifferentValues()), - var_(var), - domain_iterator_(var->MakeDomainIterator(true)), - delta_domain_iterator_(var->MakeHoleIterator(true)), - shared_positions_(new int[column.NumTuples()]) { - for (int value_index = 0; value_index < tuples_per_value_.size(); - value_index++) { - tuples_per_value_[value_index] = - new RevIntSet(column.NumTuplesContainingValueIndex(value_index), - shared_positions_.get(), column.NumTuples()); - active_values_.Insert(solver_, value_index); - } - } - - ~TableVar() { - // delete all elements of a vector - gtl::STLDeleteElements(&tuples_per_value_); - } - - IntVar* Variable() const { return var_; } - - int NumTuplesPerValue(int value_index) const { - return tuples_per_value_[value_index]->Size(); - } - - // Checks whether we should do reset or not. - bool ShouldReset(const std::vector& delta) { - int num_deleted_tuples = 0; - for (int k = 0; k < delta.size(); k++) { - num_deleted_tuples += NumTuplesPerValue(delta[k]); - } - - if (num_deleted_tuples < 10) { - return false; - } - - int num_remaining_tuples = 0; - IntVarIterator* const it = domain_iterator_; - for (it->Init(); it->Ok(); it->Next()) { - const int value_index = column_.IndexFromValue(it->Value()); - num_remaining_tuples += NumTuplesPerValue(value_index); - } - return (num_remaining_tuples < num_deleted_tuples); - } - - void InitialPropagate(const std::vector& valid_tuples, - std::vector* const to_remove) { - // Insert tuples in correct maps. - for (int tuple_ref = 0; tuple_ref < valid_tuples.size(); ++tuple_ref) { - const int tuple_index = valid_tuples[tuple_ref]; - const int value_index = column_.ValueIndex(tuple_index); - tuples_per_value_[value_index]->Insert(solver_, tuple_index); - } - - // we remove from the domain the values that are not in the table, - // or that have no supporting tuples. - to_remove->clear(); - IntVarIterator* const it = domain_iterator_; - for (it->Init(); it->Ok(); it->Next()) { - const int64 value = it->Value(); - const int index = column_.IndexFromValue(value); - if (index == -1 || NumTuplesPerValue(index) == 0) { - to_remove->push_back(value); - } - } - var_->RemoveValues(*to_remove); - } - - void ComputeDeltaDomain(std::vector* delta) { - delta->clear(); - // we iterate the delta of the variable - // - // ATTENTION code for or-tools: the delta iterator does not - // include the values between oldmin and min and the values - // between max and oldmax. - // - // therefore we decompose the iteration into 3 parts - // - from oldmin to min - // - for the deleted values between min and max - // - from max to oldmax - - // First iteration: from old_min to min. - const int64 old_min_domain = var_->OldMin(); - const int64 min_domain = var_->Min(); - const int64 max_domain = var_->Max(); - for (int64 val = old_min_domain; val < min_domain; ++val) { - const int index = column_.IndexFromValue(val); - if (index != -1) { // -1 means not in column. - delta->push_back(index); - } - } - // Second iteration: "delta" domain iteration. - IntVarIterator* const it = delta_domain_iterator_; - for (it->Init(); it->Ok(); it->Next()) { - const int64 value = it->Value(); - if (value > min_domain && value < max_domain) { - const int index = column_.IndexFromValue(it->Value()); - if (index != -1) { // -1 means not in column. - delta->push_back(index); - } - } - } - // Third iteration: from max to old_max. - const int64 old_max_domain = var_->OldMax(); - for (int64 val = max_domain + 1; val <= old_max_domain; ++val) { - const int index = column_.IndexFromValue(val); - if (index != -1) { // -1 means not in column. - delta->push_back(index); - } - } - } - - void CollectTuplesToRemove(const std::vector& delta, - std::vector* const tuples_to_remove) { - tuples_to_remove->clear(); - const int delta_size = delta.size(); - for (int k = 0; k < delta_size; k++) { - RevIntSet* const active_tuples = tuples_per_value_[delta[k]]; - const int num_tuples_to_erase = active_tuples->Size(); - for (int index = 0; index < num_tuples_to_erase; index++) { - tuples_to_remove->push_back(active_tuples->Element(index)); - } - } - } - - void CollectTuplesToKeep(std::vector* const tuples_to_keep) const { - tuples_to_keep->clear(); - for (domain_iterator_->Init(); domain_iterator_->Ok(); - domain_iterator_->Next()) { - const int value_index = column_.IndexFromValue(domain_iterator_->Value()); - RevIntSet* const active_tuples = tuples_per_value_[value_index]; - const int num_tuples = active_tuples->Size(); - for (int j = 0; j < num_tuples; j++) { - tuples_to_keep->push_back(active_tuples->Element(j)); - } - } - } - - void RemoveTuples(const std::vector& tuples) { - for (int i = 0; i < tuples.size(); ++i) { - const int erased_tuple_index = tuples[i]; - const int value_index = column_.ValueIndex(erased_tuple_index); - RevIntSet* const active_tuples = tuples_per_value_[value_index]; - active_tuples->Remove(solver_, erased_tuple_index); - if (active_tuples->Size() == 0) { - var_->RemoveValue(column_.ValueFromIndex(value_index)); - active_values_.Remove(solver_, value_index); - } - } - } - - void OverwriteTuples(const std::vector& tuples) { - for (int k = 0; k < active_values_.Size(); k++) { - tuples_per_value_[active_values_.Element(k)]->Clear(solver_); - } - for (int i = 0; i < tuples.size(); ++i) { - const int tuple_index = tuples[i]; - const int value_index = column_.ValueIndex(tuple_index); - tuples_per_value_[value_index]->Restore(solver_, tuple_index); - } - - // We check for unsupported values and remove them. - int count = 0; - for (int k = active_values_.Size() - 1; k >= 0; k--) { - const int64 value_index = active_values_.Element(k); - if (tuples_per_value_[value_index]->Size() == 0) { - active_values_.Remove(solver_, value_index); - count++; - } - } - // Removed values have been inserted after the last active value. - for (int k = 0; k < count; ++k) { - const int64 value_index = active_values_.RemovedElement(k); - var_->RemoveValue(column_.ValueFromIndex(value_index)); - } - } - - private: - Solver* const solver_; - const IndexedTable::Column& column_; - // one LAA per value of the variable - std::vector*> tuples_per_value_; - // list of values: having a non empty tuple list - RevIntSet active_values_; - IntVar* const var_; - IntVarIterator* const domain_iterator_; - IntVarIterator* const delta_domain_iterator_; - std::unique_ptr shared_positions_; -}; - -class Ac4TableConstraint : public Constraint { - public: - Ac4TableConstraint(Solver* const solver, IndexedTable* const table, - bool delete_table, const std::vector& vars) - : Constraint(solver), - original_vars_(vars), - vars_(table->NumVars()), - table_(table), - delete_table_(delete_table), - tmp_tuples_(table->NumTuples()), - delta_of_value_indices_(table->NumTuples()), - num_variables_(table->NumVars()) { - for (int var_index = 0; var_index < table->NumVars(); var_index++) { - vars_[var_index] = - new TableVar(solver, vars[var_index], table->column(var_index)); - } - } - - ~Ac4TableConstraint() { - gtl::STLDeleteElements(&vars_); // delete all elements of a vector - if (delete_table_) { - delete table_; - } - } - - void Post() { - for (int var_index = 0; var_index < num_variables_; var_index++) { - Demon* const demon = MakeConstraintDemon1( - solver(), this, &Ac4TableConstraint::FilterOneVariable, - "FilterOneVariable", var_index); - vars_[var_index]->Variable()->WhenDomain(demon); - } - } - - bool IsTupleSupported(int tuple_index) { - for (int var_index = 0; var_index < num_variables_; ++var_index) { - if (!original_vars_[var_index]->Contains( - table_->tuple_set().Value(tuple_index, var_index))) { - return false; - } - } - return true; - } - - void InitialPropagate() { - std::vector valid_tuples; - for (int i = 0; i < table_->NumTuples(); ++i) { - if (IsTupleSupported(i)) { - valid_tuples.push_back(i); - } - } - if (valid_tuples.empty()) { - solver()->Fail(); - } - - std::vector to_remove; - for (int var_index = 0; var_index < num_variables_; var_index++) { - vars_[var_index]->InitialPropagate(valid_tuples, &to_remove); - } - } - - void FilterOneVariable(int var_index) { - TableVar* const var = vars_[var_index]; - var->ComputeDeltaDomain(&delta_of_value_indices_); - // We decide if we prefer to restart with the remaining set of - // tuples, or if we incrementaly remove unsupported tuples. - if (var->ShouldReset(delta_of_value_indices_)) { - var->CollectTuplesToKeep(&tmp_tuples_); - for (int i = 0; i < num_variables_; i++) { - vars_[i]->OverwriteTuples(tmp_tuples_); - } - } else { - var->CollectTuplesToRemove(delta_of_value_indices_, &tmp_tuples_); - for (int i = 0; i < num_variables_; ++i) { - vars_[i]->RemoveTuples(tmp_tuples_); - } - } - } - - virtual std::string DebugString() const { - return StringPrintf("AllowedAssignments(arity = %d, tuple_count = %d)", - table_->NumVars(), table_->NumTuples()); - } - - virtual void Accept(ModelVisitor* const visitor) const { - visitor->BeginVisitConstraint(ModelVisitor::kAllowedAssignments, this); - visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument, - original_vars_); - visitor->VisitIntegerMatrixArgument(ModelVisitor::kTuplesArgument, - table_->tuple_set()); - visitor->EndVisitConstraint(ModelVisitor::kAllowedAssignments, this); - } - - private: - std::vector original_vars_; - // Variables of the constraint. - std::vector vars_; - // Table. - IndexedTable* const table_; - const bool delete_table_; - // Temporary tuple array for delayed add or delete operations. - std::vector tmp_tuples_; - // Temporary storage for delta of one variable. - std::vector delta_of_value_indices_; - // Number of variables. - const int num_variables_; -}; -} // namespace - -// External API. -Constraint* BuildAc4TableConstraint(Solver* const solver, - const IntTupleSet& tuples, - const std::vector& vars) { - return solver->RevAlloc( - new Ac4TableConstraint(solver, new IndexedTable(tuples), true, vars)); -} - -Constraint* BuildAc4TableConstraint(Solver* const solver, - IndexedTable* const table, - const std::vector& vars) { - return solver->RevAlloc(new Ac4TableConstraint(solver, table, true, vars)); -} - -IndexedTable* BuildIndexedTable(const IntTupleSet& tuples) { - return new IndexedTable(tuples); -} -} // namespace operations_research diff --git a/ortools/constraint_solver/alldiff_cst.cc b/ortools/constraint_solver/alldiff_cst.cc index 0d40fa185aa..ffcac29b1dd 100644 --- a/ortools/constraint_solver/alldiff_cst.cc +++ b/ortools/constraint_solver/alldiff_cst.cc @@ -20,9 +20,9 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/string_array.h" @@ -36,8 +36,7 @@ class BaseAllDifferent : public Constraint { : Constraint(s), vars_(vars) {} ~BaseAllDifferent() override {} std::string DebugStringInternal(const std::string& name) const { - return StringPrintf("%s(%s)", name.c_str(), - JoinDebugStringPtr(vars_, ", ").c_str()); + return absl::StrFormat("%s(%s)", name, JoinDebugStringPtr(vars_, ", ")); } protected: @@ -496,9 +495,8 @@ class SortConstraint : public Constraint { } std::string DebugString() const override { - return StringPrintf("Sort(%s, %s)", - JoinDebugStringPtr(ovars_, ", ").c_str(), - JoinDebugStringPtr(svars_, ", ").c_str()); + return absl::StrFormat("Sort(%s, %s)", JoinDebugStringPtr(ovars_, ", "), + JoinDebugStringPtr(svars_, ", ")); } private: @@ -574,8 +572,8 @@ class AllDifferentExcept : public Constraint { } std::string DebugString() const override { - return StringPrintf("AllDifferentExcept([%s], %" GG_LL_FORMAT "d", - JoinDebugStringPtr(vars_, ", ").c_str(), escape_value_); + return absl::StrFormat("AllDifferentExcept([%s], %" GG_LL_FORMAT "d", + JoinDebugStringPtr(vars_, ", "), escape_value_); } void Accept(ModelVisitor* const visitor) const override { @@ -665,10 +663,10 @@ class NullIntersectArrayExcept : public Constraint { } std::string DebugString() const override { - return StringPrintf( + return absl::StrFormat( "NullIntersectArray([%s], [%s], escape = %" GG_LL_FORMAT "d", - JoinDebugStringPtr(first_vars_, ", ").c_str(), - JoinDebugStringPtr(second_vars_, ", ").c_str(), escape_value_); + JoinDebugStringPtr(first_vars_, ", "), + JoinDebugStringPtr(second_vars_, ", "), escape_value_); } void Accept(ModelVisitor* const visitor) const override { diff --git a/ortools/constraint_solver/assignment.cc b/ortools/constraint_solver/assignment.cc index 8a60ed377df..e1fad8ebf18 100644 --- a/ortools/constraint_solver/assignment.cc +++ b/ortools/constraint_solver/assignment.cc @@ -13,17 +13,17 @@ #include #include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/file.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" #include "ortools/base/recordio.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/assignment.pb.h" #include "ortools/constraint_solver/constraint_solver.h" @@ -96,10 +96,10 @@ void IntVarElement::WriteToProto( std::string IntVarElement::DebugString() const { if (Activated()) { if (min_ == max_) { - return StringPrintf("(%" GG_LL_FORMAT "d)", min_); + return absl::StrFormat("(%" GG_LL_FORMAT "d)", min_); } else { - return StringPrintf("(%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d)", min_, - max_); + return absl::StrFormat("(%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d)", min_, + max_); } } else { return "(...)"; @@ -201,17 +201,19 @@ void IntervalVarElement::WriteToProto( std::string IntervalVarElement::DebugString() const { if (Activated()) { std::string out; - SStringPrintf(&out, "(start = %" GG_LL_FORMAT "d", start_min_); + absl::StrAppendFormat(&out, "(start = %" GG_LL_FORMAT "d", start_min_); if (start_max_ != start_min_) { - StringAppendF(&out, "..%" GG_LL_FORMAT "d", start_max_); + absl::StrAppendFormat(&out, "..%" GG_LL_FORMAT "d", start_max_); } - StringAppendF(&out, ", duration = %" GG_LL_FORMAT "d", duration_min_); + absl::StrAppendFormat(&out, ", duration = %" GG_LL_FORMAT "d", + duration_min_); if (duration_max_ != duration_min_) { - StringAppendF(&out, "..%" GG_LL_FORMAT "d", duration_max_); + absl::StrAppendFormat(&out, "..%" GG_LL_FORMAT "d", duration_max_); } - StringAppendF(&out, ", status = %" GG_LL_FORMAT "d", performed_min_); + absl::StrAppendFormat(&out, ", status = %" GG_LL_FORMAT "d", + performed_min_); if (performed_max_ != performed_min_) { - StringAppendF(&out, "..%" GG_LL_FORMAT "d", performed_max_); + absl::StrAppendFormat(&out, "..%" GG_LL_FORMAT "d", performed_max_); } out.append(")"); return out; @@ -317,10 +319,10 @@ void SequenceVarElement::WriteToProto( std::string SequenceVarElement::DebugString() const { if (Activated()) { - return StringPrintf("[forward %s, backward %s, unperformed [%s]]", - absl::StrJoin(forward_sequence_, " -> ").c_str(), - absl::StrJoin(backward_sequence_, " -> ").c_str(), - absl::StrJoin(unperformed_, ", ").c_str()); + return absl::StrFormat("[forward %s, backward %s, unperformed [%s]]", + absl::StrJoin(forward_sequence_, " -> "), + absl::StrJoin(backward_sequence_, " -> "), + absl::StrJoin(unperformed_, ", ")); } else { return "(...)"; } @@ -379,7 +381,7 @@ void SequenceVarElement::SetUnperformed(const std::vector& unperformed) { } bool SequenceVarElement::CheckClassInvariants() { - std::unordered_set visited; + absl::flat_hash_set visited; for (const int forward_sequence : forward_sequence_) { if (gtl::ContainsKey(visited, forward_sequence)) { return false; @@ -443,7 +445,7 @@ namespace { template void IdToElementMap(AssignmentContainer* container, - std::unordered_map* id_to_element_map) { + absl::flat_hash_map* id_to_element_map) { CHECK(id_to_element_map != nullptr); id_to_element_map->clear(); for (int i = 0; i < container->Size(); ++i) { @@ -463,7 +465,7 @@ void IdToElementMap(AssignmentContainer* container, } template -void LoadElement(const std::unordered_map& id_to_element_map, +void LoadElement(const absl::flat_hash_map& id_to_element_map, const P& proto) { const std::string& var_id = proto.var_id(); CHECK(!var_id.empty()); @@ -515,7 +517,7 @@ void RealLoad(const AssignmentProto& assignment_proto, } } if (!fast_load) { - std::unordered_map id_to_element_map; + absl::flat_hash_map id_to_element_map; IdToElementMap(container, &id_to_element_map); for (int i = 0; i < (assignment_proto.*GetSize)(); ++i) { LoadElement(id_to_element_map, @@ -614,8 +616,8 @@ template void RealDebugString(const Container& container, std::string* const out) { for (const Element& element : container.elements()) { if (element.Var() != nullptr) { - StringAppendF(out, "%s %s | ", element.Var()->name().c_str(), - element.DebugString().c_str()); + absl::StrAppendFormat(out, "%s %s | ", element.Var()->name(), + element.DebugString()); } } } @@ -1000,7 +1002,9 @@ void Assignment::CopyIntersection(const Assignment* assignment) { int_var_container_.CopyIntersection(assignment->int_var_container_); interval_var_container_.CopyIntersection(assignment->interval_var_container_); sequence_var_container_.CopyIntersection(assignment->sequence_var_container_); - objective_element_ = assignment->objective_element_; + if (objective_element_.Var() == assignment->objective_element_.Var()) { + objective_element_ = assignment->objective_element_; + } } void Assignment::Copy(const Assignment* assignment) { diff --git a/ortools/constraint_solver/collect_variables.cc b/ortools/constraint_solver/collect_variables.cc index 0048986334f..d8e9ba2d97b 100644 --- a/ortools/constraint_solver/collect_variables.cc +++ b/ortools/constraint_solver/collect_variables.cc @@ -13,8 +13,8 @@ #include #include -#include #include +#include "absl/container/flat_hash_set.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" @@ -81,7 +81,7 @@ class CollectVariablesVisitor : public ModelParser { } else if (type_name.compare(ModelVisitor::kAllowedAssignments) == 0) { const IntTupleSet& matrix = Top()->FindIntegerMatrixArgumentOrDie(ModelVisitor::kTuplesArgument); - std::vector > counters(matrix.Arity()); + std::vector > counters(matrix.Arity()); for (int i = 0; i < matrix.NumTuples(); ++i) { for (int j = 0; j < matrix.Arity(); ++j) { counters[j].insert(matrix.Value(i, j)); @@ -203,13 +203,13 @@ class CollectVariablesVisitor : public ModelParser { std::vector* const secondaries_; std::vector* const sequences_; std::vector* const intervals_; - // These hash_set can't easily hold const IntVar*, because they - // ultimately serve as containers of mutable IntVar. - std::unordered_set primary_set_; - std::unordered_set secondary_set_; - std::unordered_set ignored_set_; - std::unordered_set sequence_set_; - std::unordered_set interval_set_; + // These hash_set can't easily hold const IntVar*, because they ultimately + // serve as containers of mutable IntVar. + absl::flat_hash_set primary_set_; + absl::flat_hash_set secondary_set_; + absl::flat_hash_set ignored_set_; + absl::flat_hash_set sequence_set_; + absl::flat_hash_set interval_set_; }; } // namespace diff --git a/ortools/constraint_solver/constraint_solver.cc b/ortools/constraint_solver/constraint_solver.cc index dd8fdb7d589..23b9cb3920e 100644 --- a/ortools/constraint_solver/constraint_solver.cc +++ b/ortools/constraint_solver/constraint_solver.cc @@ -23,6 +23,7 @@ #include #include #include +#include "absl/memory/memory.h" #include "ortools/base/random.h" #include "ortools/base/commandlineflags.h" @@ -33,9 +34,7 @@ #include "ortools/base/map_util.h" #include "ortools/base/recordio.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solveri.h" -#include "ortools/constraint_solver/model.pb.h" #include "ortools/util/tuple_set.h" #include "zlib.h" @@ -50,7 +49,6 @@ DEFINE_bool(cp_print_model, false, "use PrintModelVisitor on model before solving."); DEFINE_bool(cp_model_stats, false, "use StatisticsModelVisitor on model before solving."); -DEFINE_string(cp_export_file, "", "Export model to file using CpModel."); DEFINE_bool(cp_disable_solve, false, "Force failure at the beginning of a search."); DEFINE_string(cp_profile_file, "", "Export profiling overview to file."); @@ -59,16 +57,8 @@ DEFINE_bool(cp_print_local_search_profile, false, DEFINE_bool(cp_name_variables, false, "Force all variables to have names."); DEFINE_bool(cp_name_cast_variables, false, "Name variables casted from expressions"); -DEFINE_bool(cp_use_compact_table, true, - "Use compact table constraint when possible."); DEFINE_bool(cp_use_small_table, true, "Use small compact table constraint when possible."); -DEFINE_bool(cp_use_sat_table, false, - "If true, use a SAT constraint for all table constraints."); -DEFINE_int32(cp_ac4r_table_threshold, 2048, - "Above this size, allowed assignment constraints will use the " - "revised AC-4 implementation of the table constraint."); -DEFINE_bool(cp_use_mdd_table, false, "Use mdd table"); DEFINE_bool(cp_use_cumulative_edge_finder, true, "Use the O(n log n) cumulative edge finding algorithm described " "in 'Edge Finding Filtering Algorithm for Discrete Cumulative " @@ -131,15 +121,10 @@ ConstraintSolverParameters Solver::DefaultSolverParameters() { params.set_print_local_search_profile(FLAGS_cp_print_local_search_profile); params.set_print_model(FLAGS_cp_print_model); params.set_print_model_stats(FLAGS_cp_model_stats); - params.set_export_file(FLAGS_cp_export_file); params.set_disable_solve(FLAGS_cp_disable_solve); params.set_name_cast_variables(FLAGS_cp_name_cast_variables); params.set_print_added_constraints(FLAGS_cp_print_added_constraints); - params.set_use_compact_table(FLAGS_cp_use_compact_table); params.set_use_small_table(FLAGS_cp_use_small_table); - params.set_use_sat_table(FLAGS_cp_use_sat_table); - params.set_ac4r_table_threshold(FLAGS_cp_ac4r_table_threshold); - params.set_use_mdd_table(FLAGS_cp_use_mdd_table); params.set_use_cumulative_edge_finder(FLAGS_cp_use_cumulative_edge_finder); params.set_use_cumulative_time_table(FLAGS_cp_use_cumulative_time_table); params.set_use_cumulative_time_table_sync( @@ -609,7 +594,9 @@ class CompressedTrail { packer_.reset(new ZlibTrailPacker(block_size)); break; } - default: { LOG(ERROR) << "Should not be here"; } + default: { + LOG(ERROR) << "Should not be here"; + } } // We zero all memory used by addrval arrays. @@ -711,7 +698,7 @@ class CompressedTrail { // ----- Trail ----- -// Object are explicitely copied using the copy ctor instead of +// Object are explicitly copied using the copy ctor instead of // passing and storing a pointer. As objects are small, copying is // much faster than allocating (around 35% on a complete solve). @@ -831,7 +818,7 @@ struct Trail { target = m->rev_memory_index_; for (int curr = rev_memory_.size() - 1; curr >= target; --curr) { - // Explicitely call unsized delete + // Explicitly call unsized delete ::operator delete(reinterpret_cast(rev_memory_[curr])); // The previous cast is necessary to deallocate generic memory // described by a void* when passed to the RevAlloc procedure @@ -1041,6 +1028,10 @@ class Search { solver_->Fail(); } } + void set_search_context(const std::string& search_context) { + search_context_ = search_context; + } + std::string search_context() const { return search_context_; } friend class Solver; private: @@ -1066,6 +1057,7 @@ class Search { int sentinel_pushed_; bool jmpbuf_filled_; bool backtrack_at_the_end_of_the_search_; + std::string search_context_; }; // Backtrack is implemented using 3 primitives: @@ -1389,9 +1381,9 @@ Solver::Solver(const std::string& name) void Solver::Init() { CheckSolverParameters(parameters_); - queue_.reset(new Queue(this)); - trail_.reset( - new Trail(parameters_.trail_block_size(), parameters_.compress_trail())); + queue_ = absl::make_unique(this); + trail_ = absl::make_unique(parameters_.trail_block_size(), + parameters_.compress_trail()); state_ = OUTSIDE_SEARCH; branches_ = 0; fails_ = 0; @@ -1399,14 +1391,14 @@ void Solver::Init() { neighbors_ = 0; filtered_neighbors_ = 0; accepted_neighbors_ = 0; - timer_.reset(new ClockTimer); + timer_ = absl::make_unique(); searches_.assign(1, new Search(this, 0)); fail_stamp_ = GG_ULONGLONG(1); - balancing_decision_.reset(new BalancingDecision); + balancing_decision_ = absl::make_unique(); fail_intercept_ = nullptr; true_constraint_ = nullptr; false_constraint_ = nullptr; - fail_decision_.reset(new FailDecision()); + fail_decision_ = absl::make_unique(); constraint_index_ = 0; additional_constraint_index_ = 0; num_int_vars_ = 0; @@ -1423,7 +1415,6 @@ void Solver::Init() { PushSentinel(SOLVER_CTOR_SENTINEL); InitCachedIntConstants(); // to be called after the SENTINEL is set. InitCachedConstraint(); // Cache the true constraint. - InitBuilders(); timer_->Restart(); model_cache_.reset(BuildModelCache(this)); AddPropagationMonitor(reinterpret_cast(demon_profiler_)); @@ -1445,7 +1436,6 @@ Solver::~Solver() { gtl::STLDeleteElements(&searches_); DeleteDemonProfiler(demon_profiler_); DeleteLocalSearchProfiler(local_search_profiler_); - DeleteBuilders(); } std::string Solver::DebugString() const { @@ -1470,7 +1460,7 @@ std::string Solver::DebugString() const { out += "PROBLEM_INFEASIBLE"; break; } - StringAppendF( + absl::StrAppendFormat( &out, ", branches = %" GG_LL_FORMAT "d, fails = %" GG_LL_FORMAT "d, decisions = %" GG_LL_FORMAT "d, delayed demon runs = %" GG_LL_FORMAT @@ -1630,28 +1620,8 @@ void Solver::AddCastConstraint(CastConstraint* const constraint, } void Solver::Accept(ModelVisitor* const visitor) const { - std::vector monitors; - Accept(visitor, monitors, nullptr); -} - -void Solver::Accept(ModelVisitor* const visitor, - const std::vector& monitors) const { - Accept(visitor, monitors, nullptr); -} - -void Solver::Accept(ModelVisitor* const visitor, - const std::vector& monitors, - DecisionBuilder* const db) const { visitor->BeginVisitModel(name_); ForAll(constraints_list_, &Constraint::Accept, visitor); - if (state_ == IN_ROOT_NODE) { - TopLevelSearch()->Accept(visitor); - } else { - ForAll(monitors, &SearchMonitor::Accept, visitor); - } - if (db != nullptr) { - db->Accept(visitor); - } visitor->EndVisitModel(name_); } @@ -1666,19 +1636,6 @@ void Solver::ProcessConstraints() { ModelVisitor* const visitor = MakeStatisticsModelVisitor(); Accept(visitor); } - const std::string export_file = parameters_.export_file(); - if (!export_file.empty()) { - File* file; - if (!file::Open(export_file, "wb", &file, file::Defaults()).ok()) { - LOG(WARNING) << "Cannot open " << export_file; - } else { - CpModel export_proto = ExportModel(); - VLOG(1) << export_proto.DebugString(); - recordio::RecordWriter writer(file); - writer.WriteProtocolMessage(export_proto); - writer.Close(); - } - } if (parameters_.disable_solve()) { LOG(INFO) << "Forcing early failure"; @@ -2147,7 +2104,7 @@ bool Solver::NextSolution() { case SWITCH_BRANCHES: { d = RevAlloc(new ReverseDecision(d)); // We reverse the decision and fall through the normal code. - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; } case NO_CHANGE: { decisions_++; @@ -2328,8 +2285,8 @@ class AddConstraintDecisionBuilder : public DecisionBuilder { } std::string DebugString() const override { - return StringPrintf("AddConstraintDecisionBuilder(%s)", - constraint_->DebugString().c_str()); + return absl::StrFormat("AddConstraintDecisionBuilder(%s)", + constraint_->DebugString()); } private: @@ -2426,13 +2383,12 @@ std::string Solver::GetName(const PropagationBaseObject* object) { gtl::FindOrNull(cast_information_, object); if (cast_info != nullptr && cast_info->expression != nullptr) { if (cast_info->expression->HasName()) { - return StringPrintf("Var<%s>", cast_info->expression->name().c_str()); + return absl::StrFormat("Var<%s>", cast_info->expression->name()); } else if (parameters_.name_cast_variables()) { - return StringPrintf("Var<%s>", - cast_info->expression->DebugString().c_str()); + return absl::StrFormat("Var<%s>", cast_info->expression->DebugString()); } else { const std::string new_name = - StringPrintf("CastVar<%d>", anonymous_variable_index_++); + absl::StrFormat("CastVar<%d>", anonymous_variable_index_++); propagation_object_names_[object] = new_name; return new_name; } @@ -2440,7 +2396,7 @@ std::string Solver::GetName(const PropagationBaseObject* object) { const std::string base_name = object->BaseName(); if (parameters_.name_all_variables() && !base_name.empty()) { const std::string new_name = - StringPrintf("%s_%d", base_name.c_str(), anonymous_variable_index_++); + absl::StrFormat("%s_%d", base_name, anonymous_variable_index_++); propagation_object_names_[object] = new_name; return new_name; } @@ -3185,6 +3141,19 @@ LocalSearchMonitor* Solver::GetLocalSearchMonitor() const { return local_search_monitor_.get(); } +void Solver::SetSearchContext(Search* search, + const std::string& search_context) { + search->set_search_context(search_context); +} + +std::string Solver::SearchContext() const { + return ActiveSearch()->search_context(); +} + +std::string Solver::SearchContext(const Search* search) const { + return search->search_context(); +} + // ----------------- Constraint class ------------------- std::string Constraint::DebugString() const { return "Constraint"; } diff --git a/ortools/constraint_solver/constraint_solver.h b/ortools/constraint_solver/constraint_solver.h index 43c1feac96e..47cdad4e42e 100644 --- a/ortools/constraint_solver/constraint_solver.h +++ b/ortools/constraint_solver/constraint_solver.h @@ -68,11 +68,12 @@ #include #include #include -#include -#include #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" @@ -80,7 +81,6 @@ #include "ortools/base/macros.h" #include "ortools/base/map_util.h" #include "ortools/base/random.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/sysinfo.h" #include "ortools/base/timer.h" #include "ortools/constraint_solver/solver_parameters.pb.h" @@ -99,8 +99,6 @@ class CpArgument; class CpConstraint; class CpIntegerExpression; class CpIntervalVariable; -class CpModelLoader; -class CpModel; class CpSequenceVariable; class CastConstraint; class Constraint; @@ -127,8 +125,6 @@ class LocalSearchOperator; class LocalSearchPhaseParameters; class ModelCache; class ModelVisitor; -class NoGoodManager; -class NoGoodTerm; class OptimizeVar; class Pack; class PropagationBaseObject; @@ -178,11 +174,11 @@ struct DefaultPhaseParameters { // This parameter describes which value to select for a given var. ValueSelection value_selection_schema; - // Maximum number of intervals the initialization of impacts will scan + // Maximum number of intervals that the initialization of impacts will scan // per variable. int initialization_splits; - // The default phase will run heuristic periodically. This parameter + // The default phase will run heuristics periodically. This parameter // indicates if we should run all heuristics, or a randomly selected // one. bool run_all_heuristics; @@ -195,35 +191,21 @@ struct DefaultPhaseParameters { // The failure limit for each heuristic that we run. int heuristic_num_failures_limit; - // Whether to keep the impact from the first search for other searches + // Whether to keep the impact from the first search for other searches, // or to recompute the impact for each new search. bool persistent_impact; // Seed used to initialize the random part in some heuristics. int random_seed; - // Automatic Restart Size. When diving down, the size of the search - // space disminishes. We maintain the minimal log of the size of the search - // space with the following behavior: - // - A failure is ignored (no null size). - // - A solution has a size of 1 (and a log of 0). - // Then when backtracking, if the current log of the size of the search space - // is greater than the minimizal log size recorded + 'restart_log_size', then - // the search is restarted from scratch. A parameter < 0 means no restart. - // A parameter of 0 indicates that we restart after each failure. - double restart_log_size; - // This represents the amount of information displayed by the default search. // NONE means no display, VERBOSE means extra information. DisplayLevel display_level; - // Should we use Nogoods when restarting. The default is false. - bool use_no_goods; - // Should we use last conflict method. The default is false. bool use_last_conflict; - // When defined, this override the default impact based decision builder. + // When defined, this overrides the default impact based decision builder. DecisionBuilder* decision_builder; DefaultPhaseParameters(); @@ -233,21 +215,21 @@ struct DefaultPhaseParameters { // // Solver Class // -// A solver represent the main computation engine. It implements the whole +// A solver represents the main computation engine. It implements the entire // range of Constraint Programming protocols: // - Reversibility // - Propagation // - Search // -// Usually, a Constraint Programming code consists of +// Usually, Constraint Programming code consists of // - the creation of the Solver, // - the creation of the decision variables of the model, // - the creation of the constraints of the model and their addition to the // solver() through the AddConstraint() method, // - the creation of the main DecisionBuilder class, -// - the launch of the solve method with the above-created decision builder. +// - the launch of the solve() method with the decision builder. // -// For the time being, Solver is not MT_SAFE, nor MT_HOT. +// For the time being, Solver is neither MT_SAFE nor MT_HOT. ///////////////////////////////////////////////////////////////////// // class Solver { @@ -287,56 +269,56 @@ class Solver { CHOOSE_RANDOM, // Among unbound variables, select the variable with the smallest size, - // i.e. the smallest number of possible values. - // In case of tie, the selected variables is the one with the lowest min + // i.e., the smallest number of possible values. + // In case of a tie, the selected variables is the one with the lowest min // value. - // In case of tie, the first one is selected, first being defined by the + // In case of a tie, the first one is selected, first being defined by the // order in the vector of IntVars used to create the selector. CHOOSE_MIN_SIZE_LOWEST_MIN, // Among unbound variables, select the variable with the smallest size, - // i.e. the smallest number of possible values. - // In case of tie, the selected variables is the one with the highest min + // i.e., the smallest number of possible values. + // In case of a tie, the selected variable is the one with the highest min // value. - // In case of tie, the first one is selected, first being defined by the + // In case of a tie, the first one is selected, first being defined by the // order in the vector of IntVars used to create the selector. CHOOSE_MIN_SIZE_HIGHEST_MIN, // Among unbound variables, select the variable with the smallest size, - // i.e. the smallest number of possible values. - // In case of tie, the selected variables is the one with the lowest max + // i.e., the smallest number of possible values. + // In case of a tie, the selected variables is the one with the lowest max // value. - // In case of tie, the first one is selected, first being defined by the + // In case of a tie, the first one is selected, first being defined by the // order in the vector of IntVars used to create the selector. CHOOSE_MIN_SIZE_LOWEST_MAX, // Among unbound variables, select the variable with the smallest size, - // i.e. the smallest number of possible values. - // In case of tie, the selected variables is the one with the highest max + // i.e., the smallest number of possible values. + // In case of a tie, the selected variable is the one with the highest max // value. - // In case of tie, the first one is selected, first being defined by the + // In case of a tie, the first one is selected, first being defined by the // order in the vector of IntVars used to create the selector. CHOOSE_MIN_SIZE_HIGHEST_MAX, // Among unbound variables, select the variable with the smallest minimal // value. - // In case of tie, the first one is selected, first being defined by the + // In case of a tie, the first one is selected, "first" defined by the // order in the vector of IntVars used to create the selector. CHOOSE_LOWEST_MIN, // Among unbound variables, select the variable with the highest maximal // value. - // In case of tie, the first one is selected, first being defined by the + // In case of a tie, the first one is selected, first being defined by the // order in the vector of IntVars used to create the selector. CHOOSE_HIGHEST_MAX, // Among unbound variables, select the variable with the smallest size. - // In case of tie, the first one is selected, first being defined by the + // In case of a tie, the first one is selected, first being defined by the // order in the vector of IntVars used to create the selector. CHOOSE_MIN_SIZE, // Among unbound variables, select the variable with the highest size. - // In case of tie, the first one is selected, first being defined by the + // In case of a tie, the first one is selected, first being defined by the // order in the vector of IntVars used to create the selector. CHOOSE_MAX_SIZE, @@ -432,7 +414,7 @@ class Solver { // This enum is used in Solver::MakeOperator to specify the neighborhood to // create. enum LocalSearchOperators { - // Operator which reverves a sub-chain of a path. It is called TwoOpt + // Operator which reverses a sub-chain of a path. It is called TwoOpt // because it breaks two arcs on the path; resulting paths are called // two-optimal. // Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5 @@ -448,15 +430,15 @@ class Solver { // specified chain length is the fixed length of the chains being moved. // When this length is 1, the operator simply moves a node to another // position. - // Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5, for a chain length - // of 2 (where (1, 5) are first and last nodes of the path and can + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5, for a chain + // length of 2 (where (1, 5) are first and last nodes of the path and can // therefore not be moved): // 1 -> 4 -> [2 -> 3] -> 5 // 1 -> [3 -> 4] -> 2 -> 5 // - // Using Relocate with chain lengths of 1, 2 and 3 together is equivalent to - // the OrOpt operator on a path. The OrOpt operator is a limited version of - // 3Opt (breaks 3 arcs on a path). + // Using Relocate with chain lengths of 1, 2 and 3 together is equivalent + // to the OrOpt operator on a path. The OrOpt operator is a limited + // version of 3Opt (breaks 3 arcs on a path). OROPT, // Relocate neighborhood with length of 1 (see OROPT comment). @@ -491,15 +473,15 @@ class Solver { MAKEACTIVE, // Operator which makes path nodes inactive. - // Possible neighbors for the path 1 -> 2 -> 3 -> 4 (where 1 and 4 are first - // and last nodes of the path) are: + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 (where 1 and 4 are + // first and last nodes of the path) are: // 1 -> 3 -> 4 with 2 inactive // 1 -> 2 -> 4 with 3 inactive MAKEINACTIVE, // Operator which makes a "chain" of path nodes inactive. - // Possible neighbors for the path 1 -> 2 -> 3 -> 4 (where 1 and 4 are first - // and last nodes of the path) are: + // Possible neighbors for the path 1 -> 2 -> 3 -> 4 (where 1 and 4 are + // first and last nodes of the path) are: // 1 -> 3 -> 4 with 2 inactive // 1 -> 2 -> 4 with 3 inactive // 1 -> 4 with 2 and 3 inactive @@ -513,7 +495,7 @@ class Solver { SWAPACTIVE, // Operator which makes an inactive node active and an active one inactive. - // It is similar to SwapActiveOperator excepts that it tries to insert the + // It is similar to SwapActiveOperator except that it tries to insert the // inactive node in all possible positions instead of just the position of // the node made inactive. // Possible neighbors for the path 1 -> 2 -> 3 -> 4 with 5 inactive @@ -533,22 +515,23 @@ class Solver { // overlap. PATHLNS, - // Operator which relaxes one entire path and all unactive nodes, thus + // Operator which relaxes one entire path and all inactive nodes, thus // defining num_paths neighbors. FULLPATHLNS, // Operator which relaxes all inactive nodes and one sub-chain of six - // consecutive arcs. That way the path can be improve by inserting inactive - // nodes or swaping arcs. + // consecutive arcs. That way the path can be improved by inserting + // inactive nodes or swapping arcs. UNACTIVELNS, // Operator which defines one neighbor per variable. Each neighbor tries to // increment by one the value of the corresponding variable. When a new - // solution is found the neighborhood is rebuilt from scratch, i.e. tries + // solution is found the neighborhood is rebuilt from scratch, i.e., tries // to increment values in the variable order. - // Consider for instance variables x and y. x is incremented 1 by 1 to its - // max, and when it is not possible to increment x anymore, y is incremented - // once. If this is a solution, then next neighbor tries to increment x. + // Consider for instance variables x and y. x is incremented one by one to + // its max, and when it is not possible to increment x anymore, y is + // incremented once. If this is a solution, then next neighbor tries to + // increment x. INCREMENT, // Operator which defines a neighborhood to decrement values. @@ -569,7 +552,7 @@ class Solver { // This enum is used in Solver::MakeOperator associated with an evaluator // to specify the neighborhood to create. enum EvaluatorLocalSearchOperators { - // Lin-Kernighan local search. + // Lin–Kernighan local search. // While the accumulated local gain is positive, perform a 2opt or a 3opt // move followed by a series of 2opt moves. Return a neighbor for which the // global gain is positive. @@ -606,29 +589,9 @@ class Solver { EQ }; - // This enum is used in Solver::MakeLocalSearchObjectiveFilter. It specifies - // the operation used in the objective to build the corresponding filter. - enum LocalSearchOperation { - // The objective is the sum of the variables defined in - // Solver::MakeLocalSearchObjectiveFilter. - SUM, - - // The objective is the product of the variables defined in - // Solver::MakeLocalSearchObjectiveFilter. - PROD, - - // The objective is the max of the variables defined in - // Solver::MakeLocalSearchObjectiveFilter. - MAX, - - // The objective is the min of the variables defined in - // Solver::MakeLocalSearchObjectiveFilter. - MIN - }; - // This enum represents the three possible priorities for a demon in the // Solver queue. - // Note this is for advanced users only. + // Note: this is for advanced users only. enum DemonPriority { // DELAYED_PRIORITY is the lowest priority: Demons will be processed after // VAR_PRIORITY and NORMAL_PRIORITY demons. @@ -770,18 +733,6 @@ class Solver { typedef std::function Action; typedef std::function Closure; -// TODO(user): Remove all these SWIG protected code, move to .i. -#ifndef SWIG - typedef std::function - IntegerExpressionBuilder; - typedef std::function - ConstraintBuilder; - typedef std::function - IntervalVariableBuilder; - typedef std::function - SequenceVariableBuilder; -#endif // SWIG - // Solver API explicit Solver(const std::string& name); Solver(const std::string& name, const ConstraintSolverParameters& parameters); @@ -795,7 +746,7 @@ class Solver { // reversibility - // SaveValue() will save the value of the corresponding object. It must be + // SaveValue() saves the value of the corresponding object. It must be // called before modifying the object. The value will be restored upon // backtrack. template @@ -803,18 +754,18 @@ class Solver { InternalSaveValue(o); } - // Registers the given object as being reversible. By calling this method, the - // caller gives ownership of the object to the solver, which will delete it - // when there is a backtrack out of the current state. + // Registers the given object as being reversible. By calling this method, + // the caller gives ownership of the object to the solver, which will + // delete it when there is a backtrack out of the current state. // // Returns the argument for convenience: this way, the caller may directly // invoke a constructor in the argument, without having to store the pointer // first. // // This function is only for users that define their own subclasses of - // BaseObject: for all subclasses predefined in the library, the corresponding - // factory methods (e.g., MakeIntVar(...), MakeAllDifferent(...) already take - // care of the registration. + // BaseObject: for all subclasses predefined in the library, the + // corresponding factory methods (e.g., MakeIntVar(...), + // MakeAllDifferent(...) already take care of the registration. template T* RevAlloc(T* object) { return reinterpret_cast(SafeRevAlloc(object)); @@ -840,12 +791,12 @@ class Solver { // constraint in order to be considered feasible. There are two fairly // different use cases: // - // - the most common use case is modeling: the given constraint is really part - // of the problem that the user is trying to solve. In this use case, + // - the most common use case is modeling: the given constraint is really + // part of the problem that the user is trying to solve. In this use case, // AddConstraint is called outside of search (i.e., with state() == - // OUTSIDE_SEARCH). Most users should only use AddConstraint in this way. - // In this case, the constraint will belong to the model forever: it cannot - // not be removed by backtracking. + // OUTSIDE_SEARCH). Most users should only use AddConstraint in this + // way. In this case, the constraint will belong to the model forever: it + // cannot not be removed by backtracking. // // - a rarer use case is that 'c' is not a real constraint of the model. It // may be a constraint generated by a branching decision (a constraint whose @@ -853,22 +804,22 @@ class Solver { // constraint that does restrict the search space, but in a way that cannot // have an impact on the quality of the solutions in the subtree), or an // inferred constraint that, while having no semantic value to the model (it - // does not restrict the set of solutions), is worth having because we believe - // it may strengthen the propagation. In these cases, it happens that the - // constraint is added during the search (i.e., with state() == - // IN_SEARCH or state() == IN_ROOT_NODE). When a constraint is + // does not restrict the set of solutions), is worth having because we + // believe it may strengthen the propagation. In these cases, it happens + // that the constraint is added during the search (i.e., with state() == + // IN_SEARCH or state() == IN_ROOT_NODE). When a constraint is // added during a search, it applies only to the subtree of the search tree // rooted at the current node, and will be automatically removed by - // bracktracking. + // backtracking. // // This method does not take ownership of the constraint. If the constraint // has been created by any factory method (Solver::MakeXXX), it will // automatically be deleted. However, power users who implement their own - // constraints should do: solver.AddConstraint(solver.RevAlloc(new - // MyConstraint(...)); + // constraints should do: solver.AddConstraint(solver.RevAlloc(new + // MyConstraint(...)); void AddConstraint(Constraint* const c); - // Adds 'constraint' to the solver and marks it as a cast constraint, that is, - // a constraint created calling Var() on an expression. This is used + // Adds 'constraint' to the solver and marks it as a cast constraint, that + // is, a constraint created calling Var() on an expression. This is used // internally. void AddCastConstraint(CastConstraint* const constraint, IntVar* const target_var, IntExpr* const expr); @@ -880,14 +831,15 @@ class Solver { // These methods are the ones most users should use to search for a solution. // Note that the definition of 'solution' is subtle. A solution here is // defined as a leaf of the search tree with respect to the given decision - // builder for which there is no failure. What this means is that, contrary to - // intuition, a solution may not have all variables of the model bound. It is - // the responsibility of the decision builder to keep returning decisions - // until all variables are indeed bound. The most extreme counterexample is - // calling Solve with a trivial decision builder whose Next() method always - // returns nullptr. In this case, Solve immediately returns 'true', since not - // assigning any variable to any value is a solution, unless the root node - // propagation discovers that the model is infeasible. + // builder for which there is no failure. What this means is that, contrary + // to intuition, a solution may not have all variables of the model bound. + // It is the responsibility of the decision builder to keep returning + // decisions until all variables are indeed bound. The most extreme + // counterexample is calling Solve with a trivial decision builder whose + // Next() method always returns nullptr. In this case, Solve immediately + // returns 'true', since not assigning any variable to any value is a + // solution, unless the root node propagation discovers that the model is + // infeasible. // // This function must be called either from outside of search, // or from within the Next() method of a decision builder. @@ -900,9 +852,10 @@ class Solver { // // Upon search termination, there will be a series of backtracks all the way // to the top level. This means that a user cannot expect to inspect the - // solution by querying variables after a call to Solve(): all the information - // will be lost. In order to do something with the solution, the user must - // either: + // solution by querying variables after a call to Solve(): all the + // information will be lost. In order to do something with the solution, the + // user must either: + // // * Use a search monitor that can process such a leaf. See, in particular, // the SolutionCollector class. // * Do not use Solve. Instead, use the more fine-grained approach using @@ -968,7 +921,7 @@ class Solver { bool SolveAndCommit(DecisionBuilder* const db, SearchMonitor* const m1, SearchMonitor* const m2, SearchMonitor* const m3); - // Checks whether the given assignment satisfies all the relevant constraints. + // Checks whether the given assignment satisfies all relevant constraints. bool CheckAssignment(Assignment* const solution); // Checks whether adding this constraint will lead to an immediate @@ -982,30 +935,6 @@ class Solver { // Abandon the current branch in the search tree. A backtrack will follow. void Fail(); - // Exports the model to protobuf. This code will be called - // from inside the solver during the start of the search. - CpModel ExportModel() const; - // Exports the model to protobuf. Search monitors are useful to pass - // the objective and limits to the protobuf. - CpModel ExportModelWithSearchMonitors( - const std::vector& monitors) const; - // Exports the model to protobuf. Search monitors are useful to pass - // the objective and limits to the protobuf. - CpModel ExportModelWithSearchMonitorsAndDecisionBuilder( - const std::vector& monitors, - DecisionBuilder* const db) const; - // Loads the model into the solver, and returns true upon success. - bool LoadModel(const CpModel& model_proto); - // Loads the model into the solver, appends search monitors to monitors, - // and returns true upon success. - bool LoadModelWithSearchMonitors(const CpModel& model_proto, - std::vector* monitors); - // Upgrades the model to the latest version. - static bool UpgradeModel(CpModel* const proto); - // Get read only load context. LoadModel() or LoadModelWithSearchMonitors() - // must have beed called before. - const CpModelLoader* model_loader() const { return model_loader_.get(); } - #if !defined(SWIG) // Collects decision variables. // All decision variables will be collected in 4 groups: @@ -1026,14 +955,6 @@ class Solver { std::vector* const secondary_integer_variables, std::vector* const sequence_variables, std::vector* const interval_variables); - - ConstraintBuilder GetConstraintBuilder(const std::string& tag) const; - IntegerExpressionBuilder GetIntegerExpressionBuilder( - const std::string& tag) const; - IntervalVariableBuilder GetIntervalVariableBuilder( - const std::string& tag) const; - SequenceVariableBuilder GetSequenceVariableBuilder( - const std::string& tag) const; #endif // SWIG #if !defined(SWIG) @@ -1190,15 +1111,15 @@ class Solver { // values[index] IntExpr* MakeElement(const std::vector& values, IntVar* const index); - // Function-based element. The constraint takes ownership of - // callback The callback must be able to cope with any possible + // Function-based element. The constraint takes ownership of the + // callback. The callback must be able to cope with any possible // value in the domain of 'index' (potentially negative ones too). IntExpr* MakeElement(IndexEvaluator1 values, IntVar* const index); - // Function based element. The constraint takes ownership of + // Function based element. The constraint takes ownership of the // callback. The callback must be monotonic. It must be able to // cope with any possible value in the domain of 'index' // (potentially negative ones too). Furtermore, monotonicity is not - // checked. Thus giving a non monotonic function, or specifying an + // checked. Thus giving a non-monotonic function, or specifying an // incorrect increasing parameter will result in undefined behavior. IntExpr* MakeMonotonicElement(IndexEvaluator1 values, bool increasing, IntVar* const index); @@ -1427,7 +1348,7 @@ class Solver { // Creates the constraint abs(var) == abs_var. Constraint* MakeAbsEquality(IntVar* const var, IntVar* const abs_var); // This constraint is a special case of the element constraint with - // an array of integer variables where the variables are all + // an array of integer variables, where the variables are all // different and the index variable is constrained such that // vars[index] == target. Constraint* MakeIndexOfConstraint(const std::vector& vars, @@ -1581,7 +1502,7 @@ class Solver { // variables in vars, and so on: the value of sorted_vars[i] must be // equal to the i-th value of variables invars. // - // This constraint propagate in both directions: from "vars" to + // This constraint propagates in both directions: from "vars" to // "sorted_vars" and vice-versa. // // Behind the scenes, this constraint maintains that: @@ -1603,7 +1524,7 @@ class Solver { const std::vector& right); // Creates a constraint that enforces that left is lexicographically less - // or equal than right. + // than or equal to right. Constraint* MakeLexicalLessOrEqual(const std::vector& left, const std::vector& right); @@ -1625,9 +1546,9 @@ class Solver { IntVar* index, const std::vector& vars); // Creates a constraint that states that all variables in the first - // vector are different from all variables from the second + // vector are different from all variables in the second // group. Thus the set of values in the first vector does not - // intersect the set of values in the second vector. + // intersect with the set of values in the second vector. Constraint* MakeNullIntersect(const std::vector& first_vars, const std::vector& second_vars); @@ -1635,7 +1556,7 @@ class Solver { // vector are different from all variables from the second group, // unless they are assigned to the escape value. Thus the set of // values in the first vector minus the escape value does not - // intersect the set of values in the second vector. + // intersect with the set of values in the second vector. Constraint* MakeNullIntersectExcept(const std::vector& first_vars, const std::vector& second_vars, int64 escape_value); @@ -1643,15 +1564,15 @@ class Solver { // TODO(user): Implement MakeAllNullIntersect taking an array of // variable vectors. - // Prevent cycles, nexts variables representing the next in the chain. - // Active variables indicate if the corresponding next variable is active; + // Prevent cycles. The "nexts" variables represent the next in the chain. + // "active" variables indicate if the corresponding next variable is active; // this could be useful to model unperformed nodes in a routing problem. // A callback can be added to specify sink values (by default sink values // are values >= vars.size()). Ownership of the callback is passed to the // constraint. - // If assume_paths is either not specified or true, the constraint assumes the - // 'next' variables represent paths (and performs a faster propagation); - // otherwise the constraint assumes the 'next' variables represent a forest. + // If assume_paths is either not specified or true, the constraint assumes + // the "nexts" variables represent paths (and performs a faster propagation); + // otherwise the constraint assumes they represent a forest. Constraint* MakeNoCycle(const std::vector& nexts, const std::vector& active, IndexFilter1 sink_handler = nullptr); @@ -1659,10 +1580,10 @@ class Solver { const std::vector& active, IndexFilter1 sink_handler, bool assume_paths); - // Force the nexts() variable to create a complete hamiltonian path. + // Force the "nexts" variable to create a complete Hamiltonian path. Constraint* MakeCircuit(const std::vector& nexts); - // Force the nexts() variable to create a complete hamiltonian path + // Force the "nexts" variable to create a complete Hamiltonian path // for those that do not loop upon themselves. Constraint* MakeSubCircuit(const std::vector& nexts); @@ -1720,7 +1641,20 @@ class Solver { Constraint* MakePathPrecedenceConstraint( std::vector nexts, const std::vector>& precedences); - // Same precedence constraint as above but will force i to be before j if + // Same as MakePathPrecedenceConstraint but ensures precedence pairs on some + // paths follow a LIFO or FIFO order. + // LIFO order: given 2 pairs (a,b) and (c,d), if a is before c on the path + // then d must be before b or b must be before c. + // FIFO order: given 2 pairs (a,b) and (c,d), if a is before c on the path + // then b must be before d. + // LIFO (resp. FIFO) orders are enforced only on paths starting by indices in + // lifo_path_starts (resp. fifo_path_start). + Constraint* MakePathPrecedenceConstraint( + std::vector nexts, + const std::vector>& precedences, + const std::vector& lifo_path_starts, + const std::vector& fifo_path_starts); + // Same as MakePathPrecedenceConstraint but will force i to be before j if // the sum of transits on the path from i to j is strictly positive. Constraint* MakePathTransitPrecedenceConstraint( std::vector nexts, std::vector transits, @@ -1764,7 +1698,7 @@ class Solver { const std::vector& final_states); #if defined(SWIGPYTHON) - // Compatibility layer for python API. + // Compatibility layer for Python API. Constraint* MakeAllowedAssignments( const std::vector& vars, const std::vector>& raw_tuples) { @@ -1785,13 +1719,13 @@ class Solver { #endif // This constraint states that all the boxes must not overlap. - // The coordinates of box i are : + // The coordinates of box i are: // (x_vars[i], y_vars[i]), // (x_vars[i], y_vars[i] + y_size[i]), // (x_vars[i] + x_size[i], y_vars[i]), // (x_vars[i] + x_size[i], y_vars[i] + y_size[i]). - // The sizes must be non negative. Boxes with one zero dimension can be pushed - // like any box. + // The sizes must be non-negative. Boxes with a zero dimension can be + // pushed like any box. Constraint* MakeNonOverlappingBoxesConstraint( const std::vector& x_vars, const std::vector& y_vars, const std::vector& x_size, const std::vector& y_size); @@ -1803,13 +1737,13 @@ class Solver { const std::vector& x_size, const std::vector& y_size); // This constraint states that all the boxes must not overlap. - // The coordinates of box i are : + // The coordinates of box i are: // (x_vars[i], y_vars[i]), // (x_vars[i], y_vars[i] + y_size[i]), // (x_vars[i] + x_size[i], y_vars[i]), // (x_vars[i] + x_size[i], y_vars[i] + y_size[i]). // The sizes must be positive. - // Boxes with one zero dimension can be placed anywhere. + // Boxes with a zero dimension can be placed anywhere. Constraint* MakeNonOverlappingNonStrictBoxesConstraint( const std::vector& x_vars, const std::vector& y_vars, const std::vector& x_size, const std::vector& y_size); @@ -1825,7 +1759,7 @@ class Solver { // This constraint packs all variables onto 'number_of_bins' // variables. For any given variable, a value of 'number_of_bins' // indicates that the variable is not assigned to any bin. - // Dimensions, i.e. cumulative constraints on this packing can be + // Dimensions, i.e., cumulative constraints on this packing, can be // added directly from the pack class. Pack* MakePack(const std::vector& vars, int number_of_bins); @@ -1839,7 +1773,7 @@ class Solver { int64 duration, bool optional, const std::string& name); - // This method fills the vector with 'count' interval var built with + // This method fills the vector with 'count' interval variables built with // the corresponding parameters. void MakeFixedDurationIntervalVarArray( int count, int64 start_min, int64 start_max, int64 duration, @@ -1852,7 +1786,7 @@ class Solver { int64 duration, const std::string& name); - // Creates an interval var with a fixed duration, and performed var. + // Creates an interval var with a fixed duration, and performed_variable. // The duration must be greater than 0. IntervalVar* MakeFixedDurationIntervalVar(IntVar* const start_variable, int64 duration, @@ -1947,7 +1881,7 @@ class Solver { // Creates and returns an interval variable that wraps around the given one, // relaxing the min start and end. Relaxing means making unbounded when - // optional. If the variable is non optional, this methods returns + // optional. If the variable is non-optional, this method returns // interval_var. // // More precisely, such an interval variable behaves as follows: @@ -1966,7 +1900,7 @@ class Solver { // Creates and returns an interval variable that wraps around the given one, // relaxing the max start and end. Relaxing means making unbounded when - // optional. If the variable is non optional, this methods returns + // optional. If the variable is non optional, this method returns // interval_var. // // More precisely, such an interval variable behaves as follows: @@ -1976,10 +1910,10 @@ class Solver { // variable behaves like the underlying, except that it is unbounded on // the max side; // * When the underlying cannot be performed, the returned interval variable - // is of duration 0 and must be performed in an interval unbounded on both - // sides. + // is of duration 0 and must be performed in an interval unbounded on + // both sides. // - // This is very useful to implement propagators that may only modify + // This is very useful for implementing propagators that may only modify // the start min or end min. IntervalVar* MakeIntervalRelaxedMax(IntervalVar* const interval_var); @@ -2015,13 +1949,14 @@ class Solver { Constraint* MakeTemporalDisjunction(IntervalVar* const t1, IntervalVar* const t2); - // This constraint forces all interval vars into an non overlapping + // This constraint forces all interval vars into an non-overlapping // sequence. Intervals with zero duration can be scheduled anywhere. DisjunctiveConstraint* MakeDisjunctiveConstraint( const std::vector& intervals, const std::string& name); // This constraint forces all interval vars into an non-overlapping - // sequence. Intervals with zero durations cannot overlap with over intervals. + // sequence. Intervals with zero durations cannot overlap with over + // intervals. DisjunctiveConstraint* MakeStrictDisjunctiveConstraint( const std::vector& intervals, const std::string& name); @@ -2031,9 +1966,9 @@ class Solver { // // Intervals and demands should be vectors of equal size. // - // Demands should only contain non-negative values. Zero values are supported, - // and the corresponding intervals are filtered out, as they neither impact - // nor are impacted by this constraint. + // Demands should only contain non-negative values. Zero values are + // supported, and the corresponding intervals are filtered out, as they + // neither impact nor are impacted by this constraint. Constraint* MakeCumulative(const std::vector& intervals, const std::vector& demands, int64 capacity, const std::string& name); @@ -2057,22 +1992,22 @@ class Solver { // // Intervals and demands should be vectors of equal size. // - // Demands should only contain non-negative values. Zero values are supported, - // and the corresponding intervals are filtered out, as they neither impact - // nor are impacted by this constraint. + // Demands should only contain non-negative values. Zero values are + // supported, and the corresponding intervals are filtered out, as they + // neither impact nor are impacted by this constraint. Constraint* MakeCumulative(const std::vector& intervals, const std::vector& demands, IntVar* const capacity, const std::string& name); - // This constraint forces that, for any integer t, the sum of the demands + // This constraint enforces that, for any integer t, the sum of the demands // corresponding to an interval containing t does not exceed the given // capacity. // // Intervals and demands should be vectors of equal size. // - // Demands should only contain non-negative values. Zero values are supported, - // and the corresponding intervals are filtered out, as they neither impact - // nor are impacted by this constraint. + // Demands should only contain non-negative values. Zero values are + // supported, and the corresponding intervals are filtered out, as they + // neither impact nor are impacted by this constraint. Constraint* MakeCumulative(const std::vector& intervals, const std::vector& demands, IntVar* const capacity, const std::string& name); @@ -2224,7 +2159,7 @@ class Solver { // tenure). Only the variables passed to the method can enter the lists. // The tabu criterion is softened by the tabu factor which gives the number // of "tabu" violations which is tolerated; a factor of 1 means no violations - // allowed, a factor of 0 means all violations allowed. + // allowed; a factor of 0 means all violations are allowed. SearchMonitor* MakeTabuSearch(bool maximize, IntVar* const v, int64 step, const std::vector& vars, @@ -2319,14 +2254,6 @@ class Solver { // this happens at a leaf the corresponding solution will be rejected. SearchLimit* MakeCustomLimit(std::function limiter); - // ----- No Goods ----- - - // Creates a non-reversible nogood manager to store and use nogoods - // during search. Nogoods are defined by the NoGood class. It can be - // used during search with restart to avoid revisiting the same - // portion of the search tree. - NoGoodManager* MakeNoGoodManager(); - // ----- Tree Monitor ----- // Creates a tree monitor that outputs a detailed overview of the // decision phase in cpviz format. The XML data is written to files @@ -2415,7 +2342,7 @@ class Solver { #if !defined(SWIG) // Compute the number of constraints a variable is attached to. ModelVisitor* MakeVariableDegreeVisitor( - std::unordered_map* const map); + absl::flat_hash_map* const map); #endif // ----- Symmetry Breaking ----- @@ -2445,9 +2372,7 @@ class Solver { Decision* MakeAssignVariablesValues(const std::vector& vars, const std::vector& values); Decision* MakeFailDecision(); -#if !defined(SWIG) Decision* MakeDecision(Action apply, Action refute); -#endif // !defined(SWIG) // Creates a decision builder which sequentially composes decision builders. // At each leaf of a decision builder, the next decision builder is therefore @@ -2528,7 +2453,7 @@ class Solver { DecisionBuilder* MakeDefaultPhase(const std::vector& vars, const DefaultPhaseParameters& parameters); - // shortcuts for small arrays. + // Shortcuts for small arrays. DecisionBuilder* MakePhase(IntVar* const v0, IntVarStrategy var_str, IntValueStrategy val_str); DecisionBuilder* MakePhase(IntVar* const v0, IntVar* const v1, @@ -2629,9 +2554,9 @@ class Solver { // decision builder 'db' and a set of monitors and wrap it into a // single point. If there are no solutions to this nested tree, then // NestedOptimize will fail. If there are solutions, it will find - // the best as described by the mandatory objective in the solution, + // the best as described by the mandatory objective in the solution // as well as the optimization direction, instantiate all variables - // to this solution, and returns nullptr. + // to this solution, and return nullptr. DecisionBuilder* MakeNestedOptimize(DecisionBuilder* const db, Assignment* const solution, bool maximize, int64 step); @@ -2699,8 +2624,8 @@ class Solver { // Creates a local search operator that tries to move the assignment of some // variables toward a target. The target is given as an Assignment. This // operator generates neighbors in which the only difference compared to the - // current state is that one variable that belongs to the target assignment is - // set to its target value. + // current state is that one variable that belongs to the target assignment + // is set to its target value. LocalSearchOperator* MakeMoveTowardTargetOperator(const Assignment& target); // Creates a local search operator that tries to move the assignment of some @@ -2717,9 +2642,9 @@ class Solver { // Each operator from the vector is called sequentially. By default, when a // neighbor is found the neighborhood exploration restarts from the last // active operator (the one which produced the neighbor). - // This can be overriden by setting restart to true to force the exploration + // This can be overridden by setting restart to true to force the exploration // to start from the first operator in the vector. - // The default behavior can also be overriden using an evaluation callback to + // The default behavior can also be overridden using an evaluation callback to // set the order in which the operators are explored (the callback is called // in LocalSearchOperator::Start()). The first argument of the callback is // the index of the operator which produced the last move, the second @@ -2749,19 +2674,19 @@ class Solver { LocalSearchOperator* ConcatenateOperators( const std::vector& ops, std::function evaluator); - // Randomized version of local search concatenator; calls a random operator at - // each call to MakeNextNeighbor(). + // Randomized version of local search concatenator; calls a random operator + // at each call to MakeNextNeighbor(). LocalSearchOperator* RandomConcatenateOperators( const std::vector& ops); - // Randomized version of local search concatenator; calls a random operator at - // each call to MakeNextNeighbor(). The provided seed is used to init - // the random number generator. + // Randomized version of local search concatenator; calls a random operator + // at each call to MakeNextNeighbor(). The provided seed is used to + // initialize the random number generator. LocalSearchOperator* RandomConcatenateOperators( const std::vector& ops, int32 seed); // Creates a local search operator that wraps another local search - // operator and limits the number of neighbors explored (i.e. calls + // operator and limits the number of neighbors explored (i.e., calls // to MakeNextNeighbor from the current solution (between two calls // to Start()). When this limit is reached, MakeNextNeighbor() // returns false. The counter is cleared when Start() is called. @@ -2793,7 +2718,7 @@ class Solver { // as Guided Local Search, Tabu Search and Simulated Annealing. // // TODO(user): Make a variant which runs a local search after each - // solution found in a dfs + // solution found in a DFS. DecisionBuilder* MakeLocalSearchPhase( Assignment* const assignment, @@ -2834,30 +2759,26 @@ class Solver { // Local Search Filters LocalSearchFilter* MakeVariableDomainFilter(); - IntVarLocalSearchFilter* MakeLocalSearchObjectiveFilter( + IntVarLocalSearchFilter* MakeSumObjectiveFilter( const std::vector& vars, IndexEvaluator2 values, - IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum); - IntVarLocalSearchFilter* MakeLocalSearchObjectiveFilter( + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum); + IntVarLocalSearchFilter* MakeSumObjectiveFilter( const std::vector& vars, IndexEvaluator2 values, ObjectiveWatcher delta_objective_callback, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum); - IntVarLocalSearchFilter* MakeLocalSearchObjectiveFilter( + Solver::LocalSearchFilterBound filter_enum); + IntVarLocalSearchFilter* MakeSumObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 values, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum); - IntVarLocalSearchFilter* MakeLocalSearchObjectiveFilter( + Solver::LocalSearchFilterBound filter_enum); + IntVarLocalSearchFilter* MakeSumObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 values, ObjectiveWatcher delta_objective_callback, - IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum); + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum); - // Performs PeriodicCheck on the top-level search; can be called from a nested - // solve to check top-level limits for instance. + // Performs PeriodicCheck on the top-level search; for instance, can be + // called from a nested solve to check top-level limits. void TopPeriodicCheck(); // Returns a percentage representing the propress of the search before // reaching the limits of the top-level search (can be called from a nested @@ -2870,12 +2791,12 @@ class Solver { void PushState(); void PopState(); - // Gets the search depth of the current active search. Returns -1 in - // case there are no active search opened. + // Gets the search depth of the current active search. Returns -1 if + // there is no active search opened. int SearchDepth() const; - // Gets the search left depth of the current active search. Returns -1 in - // case there are no active search opened. + // Gets the search left depth of the current active search. Returns -1 if + // there is no active search opened. int SearchLeftDepth() const; // Gets the number of nested searches. It returns 0 outside search, @@ -2936,21 +2857,15 @@ class Solver { // Accepts the given model visitor. void Accept(ModelVisitor* const visitor) const; - // Accepts the given model visitor. - void Accept(ModelVisitor* const visitor, - const std::vector& monitors) const; - void Accept(ModelVisitor* const visitor, - const std::vector& monitors, - DecisionBuilder* const db) const; Decision* balancing_decision() const { return balancing_decision_.get(); } -// Internal + // Internal #if !defined(SWIG) void set_fail_intercept(std::function fail_intercept) { fail_intercept_ = std::move(fail_intercept); } -#endif // SWIG +#endif // !defined(SWIG) void clear_fail_intercept() { fail_intercept_ = nullptr; } // Access to demon profiler. DemonProfiler* demon_profiler() const { return demon_profiler_; } @@ -2989,9 +2904,12 @@ class Solver { void AddPropagationMonitor(PropagationMonitor* const monitor); // Returns the local search monitor. LocalSearchMonitor* GetLocalSearchMonitor() const; - // Adds the local search monitor to the solver. This is called internally when - // a propagation monitor is passed to the Solve() or NewSearch() method. + // Adds the local search monitor to the solver. This is called internally + // when a propagation monitor is passed to the Solve() or NewSearch() method. void AddLocalSearchMonitor(LocalSearchMonitor* monitor); + void SetSearchContext(Search* search, const std::string& search_context); + std::string SearchContext() const; + std::string SearchContext(const Search* search) const; // Unsafe temporary vector. It is used to avoid leaks in operations // that need storage and that may fail. See IntVar::SetValues() for @@ -3107,23 +3025,12 @@ class Solver { void InitCachedIntConstants(); void InitCachedConstraint(); - void InitBuilders(); - // Registers a constraint builder. - void RegisterBuilder(const std::string& tag, ConstraintBuilder builder); - // Registers an integer expression builder. - void RegisterBuilder(const std::string& tag, - IntegerExpressionBuilder builder); - // Registers an interval variable builder. - void RegisterBuilder(const std::string& tag, IntervalVariableBuilder builder); - // Registers a sequence variable builder. - void RegisterBuilder(const std::string& tag, SequenceVariableBuilder builder); - void DeleteBuilders(); - - // Returns the Search object that is at the bottom of the search stack. This - // is to be contrasted with ActiveSearch(), which returns the search at the + + // Returns the Search object that is at the bottom of the search stack. + // Contrast with ActiveSearch(), which returns the search at the // top of the stack. Search* TopLevelSearch() const { return searches_.at(1); } - // Returns the Search object which is the parent of the active search, i.e. + // Returns the Search object which is the parent of the active search, i.e., // the search below the top of the stack. If the active search is at the // bottom of the stack, returns the active search. Search* ParentSearch() const { @@ -3146,11 +3053,11 @@ class Solver { const std::string name_; const ConstraintSolverParameters parameters_; - std::unordered_map + absl::flat_hash_map propagation_object_names_; - std::unordered_map + absl::flat_hash_map cast_information_; - std::unordered_set cast_constraints_; + absl::flat_hash_set cast_constraints_; const std::string empty_name_; std::unique_ptr queue_; std::unique_ptr trail_; @@ -3190,14 +3097,6 @@ class Solver { int additional_constraint_index_; int num_int_vars_; - // Support for model loading. - std::unordered_map - expression_builders_; - std::unordered_map constraint_builders_; - std::unordered_map interval_builders_; - std::unordered_map sequence_builders_; - std::unique_ptr model_loader_; - std::unique_ptr model_cache_; std::unique_ptr propagation_monitor_; PropagationMonitor* print_trace_; @@ -3251,7 +3150,7 @@ class PropagationBaseObject : public BaseObject { if (name().empty()) { return "PropagationBaseObject"; } else { - return StringPrintf("PropagationBaseObject: %s", name().c_str()); + return absl::StrFormat("PropagationBaseObject: %s", name()); } } Solver* solver() const { return solver_; } @@ -3277,9 +3176,9 @@ class PropagationBaseObject : public BaseObject { void set_action_on_fail(Solver::Action a) { solver_->set_action_on_fail(std::move(a)); } -#endif // SWIG +#endif // !defined(SWIG) - // This methods clears the failure callback. + // This method clears the failure callback. void reset_action_on_fail() { solver_->reset_action_on_fail(); } // Shortcut for variable cleaner. @@ -3396,7 +3295,7 @@ class Demon : public BaseObject { // current position. void inhibit(Solver* const s); - // This method un-inhibits the demon that was inhibited. + // This method un-inhibits the demon that was previously inhibited. void desinhibit(Solver* const s); private: @@ -3839,10 +3738,10 @@ class NumericalRev : public Rev { }; // Reversible array of POD types. -// It contains the stamp optimization. i.e. the SaveValue call is done only +// It contains the stamp optimization. I.e., the SaveValue call is done only // once per node of the search tree. -// Please note that actual stamps always starts at 1, thus an initial value of -// 0 will always trigger the first SaveValue. +// Please note that actual stamp always starts at 1, thus an initial value of +// 0 always triggers the first SaveValue. template class RevArray { public: @@ -4129,14 +4028,14 @@ class IntVar : public IntExpr { // This method returns whether the value 'v' is in the domain of the variable. virtual bool Contains(int64 v) const = 0; - // Creates a hole iterator. When 'reversible' is false, the returned object is - // created on the normal C++ heap and the solver does NOT take ownership of - // the object. + // Creates a hole iterator. When 'reversible' is false, the returned + // object is created on the normal C++ heap and the solver does NOT + // take ownership of the object. virtual IntVarIterator* MakeHoleIterator(bool reversible) const = 0; - // Creates a domain iterator. When 'reversible' is false, the returned object - // is created on the normal C++ heap and the solver does NOT take ownership of - // the object. + // Creates a domain iterator. When 'reversible' is false, the + // returned object is created on the normal C++ heap and the solver + // does NOT take ownership of the object. virtual IntVarIterator* MakeDomainIterator(bool reversible) const = 0; // Returns the previous min. @@ -4168,12 +4067,13 @@ class IntVar : public IntExpr { // This class is the root class of all solution collectors. // It implements a basic query API to be used independently -// from the collector used. +// of the collector used. class SolutionCollector : public SearchMonitor { public: SolutionCollector(Solver* const solver, const Assignment* assignment); explicit SolutionCollector(Solver* const solver); ~SolutionCollector() override; + std::string DebugString() const override { return "SolutionCollector"; } // Add API. void Add(IntVar* const var); @@ -4206,30 +4106,30 @@ class SolutionCollector : public SearchMonitor { // Returns the objective value of the nth solution. int64 objective_value(int n) const; - // This is a short-cut to get the Value of 'var' in the nth solution. + // This is a shortcut to get the Value of 'var' in the nth solution. int64 Value(int n, IntVar* const var) const; - // This is a short-cut to get the StartValue of 'var' in the nth solution. + // This is a shortcut to get the StartValue of 'var' in the nth solution. int64 StartValue(int n, IntervalVar* const var) const; - // This is a short-cut to get the EndValue of 'var' in the nth solution. + // This is a shortcut to get the EndValue of 'var' in the nth solution. int64 EndValue(int n, IntervalVar* const var) const; - // This is a short-cut to get the DurationValue of 'var' in the nth solution. + // This is a shortcut to get the DurationValue of 'var' in the nth solution. int64 DurationValue(int n, IntervalVar* const var) const; - // This is a short-cut to get the PerformedValue of 'var' in the nth solution. + // This is a shortcut to get the PerformedValue of 'var' in the nth solution. int64 PerformedValue(int n, IntervalVar* const var) const; - // This is a short-cut to get the ForwardSequence of 'var' in the + // This is a shortcut to get the ForwardSequence of 'var' in the // nth solution. The forward sequence is the list of ranked interval // variables starting from the start of the sequence. const std::vector& ForwardSequence(int n, SequenceVar* const var) const; - // This is a short-cut to get the BackwardSequence of 'var' in the + // This is a shortcut to get the BackwardSequence of 'var' in the // nth solution. The backward sequence is the list of ranked interval // variables starting from the end of the sequence. const std::vector& BackwardSequence(int n, SequenceVar* const var) const; - // This is a short-cut to get the list of unperformed of 'var' in the + // This is a shortcut to get the list of unperformed of 'var' in the // nth solution. const std::vector& Unperformed(int n, SequenceVar* const var) const; @@ -4337,7 +4237,7 @@ class SearchLimit : public SearchMonitor { void PeriodicCheck() override; void RefuteDecision(Decision* const d) override; std::string DebugString() const override { - return StringPrintf("SearchLimit(crossed = %i)", crossed_); + return absl::StrFormat("SearchLimit(crossed = %i)", crossed_); } private: @@ -4347,77 +4247,6 @@ class SearchLimit : public SearchMonitor { DISALLOW_COPY_AND_ASSIGN(SearchLimit); }; -// ---------- NoGood Recorder ------ - -// Nogoods are used to store negative information collected during -// search. They are by definition non reversible. - -// ----- No Good ---- - -// A nogood is a conjunction of unary constraints that represents a -// state that must not be visited during search. For instance, if X -// and Y are variables, (X == 5) && (Y != 3) is a nogood that forbids -// all part of the search tree where X is 5 and Y is not 3. -class NoGood { - public: - ~NoGood(); - // Creates a term var == value. - void AddIntegerVariableEqualValueTerm(IntVar* const var, int64 value); - // Creates a term var != value. - void AddIntegerVariableNotEqualValueTerm(IntVar* const var, int64 value); - // Applies the nogood. That is if there is only one undecided term and - // all remaining terms are always true, then the opposite of this - // term is added to the solver. It returns true if the nogood is - // still active and needs to be reevaluated. - bool Apply(Solver* const solver); - // Pretty print. - std::string DebugString() const; - // TODO(user) : support interval variables and more types of constraints. - - private: - std::vector terms_; -}; - -// ----- Base class of no good manager ----- - -// A no good recorder is used to store a set of no goods in a non -// reversible way during search. It will actively propagate nogoods, -// that is if all its terms minus one are always true, then it will -// apply the reverse of this term during the search. -class NoGoodManager : public SearchMonitor { - public: - explicit NoGoodManager(Solver* const s) : SearchMonitor(s) {} - ~NoGoodManager() override {} - - // ----- User API ----- - - // Clear all stored nogoods. - virtual void Clear() = 0; - // NoGood factory. Create an empty nogood. - NoGood* MakeNoGood(); - // Add one nogood to the recorder. Ownership is transferred to the recorder. - virtual void AddNoGood(NoGood* const nogood) = 0; - // Returns the number of nogoods added to the recorder. - virtual int NoGoodCount() const = 0; - // Pretty Print. - std::string DebugString() const override = 0; - - // Internal methods that link search events to the recorder API. - void EnterSearch() override; - void BeginNextDecision(DecisionBuilder* const db) override; - bool AcceptSolution() override; - - private: - // ----- Implementor API ----- - - // Initialize data structures. - virtual void Init() = 0; - // Applies the nogood. - virtual void Apply() = 0; - - DISALLOW_COPY_AND_ASSIGN(NoGoodManager); -}; - // ---------- Interval Var ---------- // Interval variables are often used in scheduling. The main characteristics @@ -4442,7 +4271,7 @@ class IntervalVar : public PropagationBaseObject { } ~IntervalVar() override {} - // These methods query, set and watch the start position of the + // These methods query, set, and watch the start position of the // interval var. virtual int64 StartMin() const = 0; virtual int64 StartMax() const = 0; @@ -4470,7 +4299,7 @@ class IntervalVar : public PropagationBaseObject { } #endif // SWIG - // These methods query, set and watch the duration of the interval var. + // These methods query, set, and watch the duration of the interval var. virtual int64 DurationMin() const = 0; virtual int64 DurationMax() const = 0; virtual void SetDurationMin(int64 m) = 0; @@ -4497,7 +4326,7 @@ class IntervalVar : public PropagationBaseObject { } #endif // SWIG - // These methods query, set and watch the end position of the interval var. + // These methods query, set, and watch the end position of the interval var. virtual int64 EndMin() const = 0; virtual int64 EndMax() const = 0; virtual void SetEndMin(int64 m) = 0; @@ -4524,7 +4353,7 @@ class IntervalVar : public PropagationBaseObject { } #endif // SWIG - // These methods query, set and watches the performed status of the + // These methods query, set, and watch the performed status of the // interval var. virtual bool MustBePerformed() const = 0; virtual bool MayBePerformed() const = 0; @@ -4580,11 +4409,11 @@ class IntervalVar : public PropagationBaseObject { // ----- SequenceVar ----- -// A sequence variable is a variable which domain is a set of possible -// orderings of the interval variables. It allows ordering tasks. It -// has two sets of methods: ComputePossibleFirstsAndLasts() which -// returns the list of interval variables thant can be ranked first or -// lasts, and RankFirst/RankNotFirst/RankLast/RankNotLast which can be +// A sequence variable is a variable whose domain is a set of possible +// orderings of the interval variables. It allows ordering of tasks. It +// has two sets of methods: ComputePossibleFirstsAndLasts(), which +// returns the list of interval variables that can be ranked first or +// last; and RankFirst/RankNotFirst/RankLast/RankNotLast, which can be // used to create the search decision. class SequenceVar : public PropagationBaseObject { public: @@ -4621,11 +4450,11 @@ class SequenceVar : public PropagationBaseObject { // of all currently unranked interval vars. void RankNotFirst(int index); - // Ranks the index_th interval var last of all unranked interval + // Ranks the index_th interval var first of all unranked interval // vars. After that, it will no longer be considered ranked. void RankLast(int index); - // Indicates that the index_th interval var will not be ranked last + // Indicates that the index_th interval var will not be ranked first // of all currently unranked interval vars. void RankNotLast(int index); @@ -4723,7 +4552,7 @@ class IntVarElement : public AssignmentElement { void SetMax(int64 m) { max_ = m; } int64 Value() const { DCHECK_EQ(min_, max_); - // Getting the value from an unbound int var assignment element. + // Get the value from an unbound int var assignment element. return min_; } bool Bound() const { return (max_ == min_); } @@ -4930,7 +4759,7 @@ class AssignmentContainer { } void Clear() { elements_.clear(); - if (!elements_map_.empty()) { // 2x speedup on or-tools. + if (!elements_map_.empty()) { // 2x speedup on OR-tools. elements_map_.clear(); } } @@ -5026,9 +4855,9 @@ class AssignmentContainer { } // The == should be order-independent EnsureMapIsUpToDate(); - // Do not use the hash_map::== operator! It does not just compare content, - // but also how the map is hashed (e.g., number of buckets). This is not - // what we want. + // Do not use the hash_map::== operator! It + // compares both content and how the map is hashed (e.g., number of + // buckets). This is not what we want. for (const E& element : container.elements_) { const int position = gtl::FindWithDefault(elements_map_, element.Var(), -1); @@ -5044,8 +4873,8 @@ class AssignmentContainer { private: void EnsureMapIsUpToDate() const { - std::unordered_map* map = - const_cast*>(&elements_map_); + absl::flat_hash_map* map = + const_cast*>(&elements_map_); for (int i = map->size(); i < elements_.size(); ++i) { (*map)[elements_[i].Var()] = i; } @@ -5054,8 +4883,9 @@ class AssignmentContainer { // This threshold was determined from microbenchmarks on Nehalem platform. const size_t kMaxSizeForLinearAccess = 11; if (Size() <= kMaxSizeForLinearAccess) { - // Look for 'var' in the container by performing a linear search, avoiding - // the access to (and creation of) the elements hash table. + // Look for 'var' in the container by performing a linear + // search, avoiding the access to (and creation of) the elements + // hash table. for (int i = 0; i < elements_.size(); ++i) { if (var == elements_[i].Var()) { *index = i; @@ -5071,7 +4901,7 @@ class AssignmentContainer { } std::vector elements_; - std::unordered_map elements_map_; + absl::flat_hash_map elements_map_; }; // ----- Assignment ----- @@ -5178,7 +5008,7 @@ class Assignment : public PropagationBaseObject { SequenceVarElement* Add(SequenceVar* const var); void Add(const std::vector& vars); - // Adds without checking if variable has been previously added. + // Adds without checking if the variable had been previously added. SequenceVarElement* FastAdd(SequenceVar* const var); const std::vector& ForwardSequence(const SequenceVar* const var) const; const std::vector& BackwardSequence(const SequenceVar* const var) const; @@ -5221,7 +5051,7 @@ class Assignment : public PropagationBaseObject { // content. void Copy(const Assignment* assignment); - // TODO(user): Add iterators on elements to avoid exposing container class. + // TODO(user): Add element iterators to avoid exposing container class. const IntContainer& IntVarContainer() const { return int_var_container_; } IntContainer* MutableIntVarContainer() { return &int_var_container_; } const IntervalContainer& IntervalVarContainer() const { @@ -5308,7 +5138,8 @@ class Pack : public Constraint { const std::vector& loads); // This dimension imposes that for all bins b, the weighted sum - // (weights->Run(i, b)) of all objects i assigned to 'b' is equal to loads[b]. + // (weights->Run(i, b)) of all objects i assigned to 'b' is equal to + // loads[b]. void AddWeightedSumEqualVarDimension(Solver::IndexEvaluator2 weights, const std::vector& loads); @@ -5418,7 +5249,7 @@ class DisjunctiveConstraint : public Constraint { // ----- SolutionPool ----- // This class is used to manage a pool of solutions. It can transform -// a single point local search into a multi point local search. +// a single point local search into a multipoint local search. class SolutionPool : public BaseObject { public: SolutionPool() {} diff --git a/ortools/constraint_solver/constraint_solveri.h b/ortools/constraint_solver/constraint_solveri.h index 5f8ff1dc895..28fadcd62c1 100644 --- a/ortools/constraint_solver/constraint_solveri.h +++ b/ortools/constraint_solver/constraint_solveri.h @@ -55,19 +55,20 @@ #include #include #include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" #include "ortools/base/sysinfo.h" #include "ortools/base/timer.h" #include "ortools/constraint_solver/constraint_solver.h" -#include "ortools/constraint_solver/model.pb.h" #include "ortools/util/bitset.h" #include "ortools/util/tuple_set.h" #include "ortools/util/vector_map.h" @@ -87,8 +88,7 @@ class CPIntervalVariableProto; // and subclasses of BaseIntExpr. Variables are stateful objects that // provide a rich API (remove values, WhenBound...). On the other hand, // subclasses of BaseIntExpr represent range-only stateless objects. -// That is, std::min(A + B) is recomputed each time as std::min(A) + -// std::min(B). +// That is, min(A + B) is recomputed each time as min(A) + min(B). // // Furthermore, sometimes, the propagation on an expression is not complete, // and Min(), Max() are not monotonic with respect to SetMin() and SetMax(). @@ -251,9 +251,9 @@ inline uint64 Hash1(int value) { return Hash1(static_cast(value)); } inline uint64 Hash1(void* const ptr) { #if defined(ARCH_K8) || defined(__powerpc64__) || defined(__aarch64__) - return Hash1(absl::bit_cast(ptr)); + return Hash1(reinterpret_cast(ptr)); #else - return Hash1(absl::bit_cast(ptr)); + return Hash1(reinterpret_cast(ptr)); #endif } @@ -318,7 +318,7 @@ class RevImmutableMultiMap { return false; } - // Returns one value attached to 'key', or 'defaut_value' if 'key' + // Returns one value attached to 'key', or 'default_value' if 'key' // is not in the multi-map. The actual value returned if more than one // values is attached to the same key is not specified. const V& FindWithDefault(const K& key, const V& default_value) const { @@ -816,6 +816,7 @@ class LocalSearchOperator : public BaseObject { ~LocalSearchOperator() override {} virtual bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) = 0; virtual void Start(const Assignment* assignment) = 0; + virtual void Reset() {} }; // ----- Base operator class for operators manipulating variables ----- @@ -840,6 +841,7 @@ class VarLocalSearchOperator : public LocalSearchOperator { activated_.Set(i, var_handler_.ValueFromAssignent(*assignment, vars_[i], i, &values_[i])); } + prev_values_ = old_values_; old_values_ = values_; was_activated_.SetContentFromBitsetOfSameSize(activated_); OnStart(); @@ -905,6 +907,7 @@ class VarLocalSearchOperator : public LocalSearchOperator { const int64 size = Size(); values_.resize(size); old_values_.resize(size); + prev_values_.resize(size); activated_.Resize(size); was_activated_.Resize(size); changes_.ClearAndResize(size); @@ -929,6 +932,7 @@ class VarLocalSearchOperator : public LocalSearchOperator { std::vector vars_; std::vector values_; std::vector old_values_; + std::vector prev_values_; Bitset64<> activated_; Bitset64<> was_activated_; SparseBitset<> changes_; @@ -1233,9 +1237,11 @@ class PathOperator : public IntVarLocalSearchOperator { // removed. PathOperator(const std::vector& next_vars, const std::vector& path_vars, int number_of_base_nodes, + bool skip_locally_optimal_paths, std::function start_empty_path_class); ~PathOperator() override {} virtual bool MakeNeighbor() = 0; + void Reset() override; // TODO(user): Make the following methods protected. bool SkipUnchanged(int index) const override; @@ -1350,11 +1356,13 @@ class PathOperator : public IntVarLocalSearchOperator { const int number_of_nexts_; const bool ignore_path_vars_; int next_base_to_increment_; + int num_paths_ = 0; + std::vector start_to_path_; private: void OnStart() override; // Called by OnStart() after initializing node information. Should be - // overriden instead of OnStart() to avoid calling PathOperator::OnStart + // overridden instead of OnStart() to avoid calling PathOperator::OnStart // explicitly. virtual void OnNodeInitialization() {} // Returns true if two nodes are on the same path in the current assignment. @@ -1385,6 +1393,10 @@ class PathOperator : public IntVarLocalSearchOperator { bool just_started_; bool first_start_; std::function start_empty_path_class_; + bool skip_locally_optimal_paths_; + bool optimal_paths_enabled_; + std::vector path_basis_; + std::vector optimal_paths_; }; // Simple PathOperator wrapper that also stores the current previous nodes, @@ -1460,7 +1472,52 @@ class LocalSearchFilter : public BaseObject { virtual void Synchronize(const Assignment* assignment, const Assignment* delta) = 0; virtual bool IsIncremental() const { return false; } + + // DO NOT USE. Objective value from last time Synchronize() was called. + virtual int64 GetSynchronizedObjectiveValue() const { return 0LL; } + // DO NOT USE. Objective value from the last time Accept() was called and + // returned true. If the last Accept() call returned false, returns an + // undefined value. + virtual int64 GetAcceptedObjectiveValue() const { return 0LL; } +}; + +#if !defined(SWIG) +// Filter manager: when a move is made, filters are executed to decide whether +// the solution is feasible and compute parts of the new cost. This class +// schedules filter execution and composes costs as a sum. +class LocalSearchFilterManager : public LocalSearchFilter { + public: + LocalSearchFilterManager(const std::vector& filters, + IntVar* objective); + std::string DebugString() const override { + return "LocalSearchFilterManager"; + } + // Returns true iff all filters return true, and the sum of their accepted + // objectives is smaller or equal to the target objective. This target + // objective is: + // (objective_ == nullptr) ? + // kint64max : + // ((objective_ != delta->Objective()) ? + // objective_.Max() : + // min(objective_.Max(), delta->ObjectiveMax())) + bool Accept(const Assignment* delta, const Assignment* deltadelta) override; + // Synchronizes all filters to assignment. + void Synchronize(const Assignment* assignment, + const Assignment* delta) override; + bool IsIncremental() const override { return is_incremental_; } + int64 GetSynchronizedObjectiveValue() const override { + return synchronized_value_; + } + int64 GetAcceptedObjectiveValue() const override { return accepted_value_; } + + private: + std::vector filters_; + IntVar* const objective_; + bool is_incremental_; + int64 synchronized_value_; + int64 accepted_value_; }; +#endif // ----- IntVarLocalSearchFilter ----- @@ -1527,6 +1584,7 @@ class PropagationMonitor : public SearchMonitor { public: explicit PropagationMonitor(Solver* const solver); ~PropagationMonitor() override; + std::string DebugString() const override { return "PropagationMonitor"; } // Propagation events. virtual void BeginConstraintInitialPropagation( @@ -1593,6 +1651,7 @@ class LocalSearchMonitor : public SearchMonitor { public: explicit LocalSearchMonitor(Solver* const solver); ~LocalSearchMonitor() override; + std::string DebugString() const override { return "LocalSearchMonitor"; } // Local search operator events. virtual void BeginOperatorStart() = 0; @@ -2025,17 +2084,17 @@ class ArgumentHolder { private: std::string type_name_; - std::unordered_map integer_argument_; - std::unordered_map > integer_array_argument_; - std::unordered_map matrix_argument_; - std::unordered_map integer_expression_argument_; - std::unordered_map interval_argument_; - std::unordered_map sequence_argument_; - std::unordered_map > + absl::flat_hash_map integer_argument_; + absl::flat_hash_map > integer_array_argument_; + absl::flat_hash_map matrix_argument_; + absl::flat_hash_map integer_expression_argument_; + absl::flat_hash_map interval_argument_; + absl::flat_hash_map sequence_argument_; + absl::flat_hash_map > integer_variable_array_argument_; - std::unordered_map > + absl::flat_hash_map > interval_array_argument_; - std::unordered_map > + absl::flat_hash_map > sequence_array_argument_; }; @@ -2100,99 +2159,7 @@ class ModelParser : public ModelVisitor { private: std::vector holders_; }; -#endif // SWIG - -// ---------- CpModelLoader ----------- - -// The class CpModelLoader is responsible for reading a protocol -// buffer representing a CP model and creating the corresponding CP -// model with the expressions and constraints. It should not be used directly. -class CpModelLoader { - public: - explicit CpModelLoader(Solver* const solver) : solver_(solver) {} - ~CpModelLoader() {} - - Solver* solver() const { return solver_; } - - // Returns stored integer expression. - IntExpr* IntegerExpression(int index) const; - // Returns the number of stored integer expressions. - int NumIntegerExpressions() const { return expressions_.size(); } - // Returns stored interval variable. - IntervalVar* IntervalVariable(int index) const; - // Returns the number of stored interval variables. - int NumIntervalVariables() const { return intervals_.size(); } - -#if !defined(SWIG) - // Internal, do not use. - - // Builds integer expression from proto and stores it. It returns - // true upon success. - bool BuildFromProto(const CpIntegerExpression& proto); - // Builds constraint from proto and returns it. - Constraint* BuildFromProto(const CpConstraint& proto); - // Builds interval variable from proto and stores it. It returns - // true upon success. - bool BuildFromProto(const CpIntervalVariable& proto); - // Builds sequence variable from proto and stores it. It returns - // true upon success. - bool BuildFromProto(const CpSequenceVariable& proto); - - bool ScanOneArgument(int type_index, const CpArgument& arg_proto, - int64* to_fill); - - bool ScanOneArgument(int type_index, const CpArgument& arg_proto, - IntExpr** to_fill); - - bool ScanOneArgument(int type_index, const CpArgument& arg_proto, - std::vector* to_fill); - - bool ScanOneArgument(int type_index, const CpArgument& arg_proto, - IntTupleSet* to_fill); - - bool ScanOneArgument(int type_index, const CpArgument& arg_proto, - std::vector* to_fill); - - bool ScanOneArgument(int type_index, const CpArgument& arg_proto, - IntervalVar** to_fill); - - bool ScanOneArgument(int type_index, const CpArgument& arg_proto, - std::vector* to_fill); - - bool ScanOneArgument(int type_index, const CpArgument& arg_proto, - SequenceVar** to_fill); - - bool ScanOneArgument(int type_index, const CpArgument& arg_proto, - std::vector* to_fill); - - template - bool ScanArguments(const std::string& type, const P& proto, A* to_fill) { - const int index = tags_.Index(type); - for (int i = 0; i < proto.arguments_size(); ++i) { - if (ScanOneArgument(index, proto.arguments(i), to_fill)) { - return true; - } - } - return false; - } - - int TagIndex(const std::string& tag) const { return tags_.Index(tag); } - - void AddTag(const std::string& tag) { tags_.Add(tag); } - - // TODO(user): Use. - void SetSequenceVariable(int index, SequenceVar* const var) {} -#endif // !defined(SWIG) - - private: - Solver* const solver_; - std::vector expressions_; - std::vector intervals_; - std::vector sequences_; - VectorMap tags_; -}; -#if !defined(SWIG) // ----- Utility Class for Callbacks ----- template @@ -2226,26 +2193,6 @@ class ArrayWithOffset : public BaseObject { const int64 index_max_; std::unique_ptr values_; }; - -template -std::function MakeFunctionFromProto(CpModelLoader* const builder, - const CpExtension& proto, - int tag_index) { - DCHECK_EQ(tag_index, proto.type_index()); - Solver* const solver = builder->solver(); - int64 index_min = 0; - CHECK(builder->ScanArguments(ModelVisitor::kMinArgument, proto, &index_min)); - int64 index_max = 0; - CHECK(builder->ScanArguments(ModelVisitor::kMaxArgument, proto, &index_max)); - std::vector values; - CHECK(builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)); - ArrayWithOffset* const array = - solver->RevAlloc(new ArrayWithOffset(index_min, index_max)); - for (int i = index_min; i <= index_max; ++i) { - array->SetValue(i, values[i - index_min]); - } - return [array](int64 index) { return array->Evaluate(index); }; -} #endif // SWIG // This class is a reversible growing array. In can grow in both @@ -2562,7 +2509,7 @@ class RevPartialSequence { // This class represents a reversible bitset. It is meant to represent a set of // active bits. It does not offer direct access, but just methods that can -// reversibly substract another bitset, or check if the current active bitset +// reversibly subtract another bitset, or check if the current active bitset // intersects with another bitset. class UnsortedNullableRevBitset { public: @@ -2575,7 +2522,7 @@ class UnsortedNullableRevBitset { // be called only once. void Init(Solver* const solver, const std::vector& mask); - // This method substracts the mask from the active bitset. It returns true if + // This method subtracts the mask from the active bitset. It returns true if // the active bitset was changed in the process. bool RevSubtract(Solver* const solver, const std::vector& mask); diff --git a/ortools/constraint_solver/constraints.cc b/ortools/constraint_solver/constraints.cc index 611117324d2..ec3a5e07b38 100644 --- a/ortools/constraint_solver/constraints.cc +++ b/ortools/constraint_solver/constraints.cc @@ -18,10 +18,10 @@ #include #include +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/saturated_arithmetic.h" @@ -176,7 +176,7 @@ class MapDomain : public Constraint { const int64 vmin = var_->Min(); const int64 vmax = var_->Max(); const int64 size = actives_.size(); - for (int64 j = std::max(oldmin, 0LL); j < std::min(vmin, size); ++j) { + for (int64 j = std::max(oldmin, int64{0}); j < std::min(vmin, size); ++j) { actives_[j]->SetValue(0); } for (const int64 j : InitAndGetValues(holes_)) { @@ -184,8 +184,8 @@ class MapDomain : public Constraint { actives_[j]->SetValue(0); } } - for (int64 j = std::max(vmax + 1LL, 0LL); j <= std::min(oldmax, size - 1LL); - ++j) { + for (int64 j = std::max(vmax + int64{1}, int64{0}); + j <= std::min(oldmax, size - int64{1}); ++j) { actives_[j]->SetValue(0LL); } } @@ -197,8 +197,8 @@ class MapDomain : public Constraint { } } std::string DebugString() const override { - return StringPrintf("MapDomain(%s, [%s])", var_->DebugString().c_str(), - JoinDebugStringPtr(actives_, ", ").c_str()); + return absl::StrFormat("MapDomain(%s, [%s])", var_->DebugString(), + JoinDebugStringPtr(actives_, ", ")); } void Accept(ModelVisitor* const visitor) const override { @@ -271,10 +271,9 @@ class LexicalLess : public Constraint { } std::string DebugString() const override { - return StringPrintf("%s([%s], [%s])", - strict_ ? "LexicalLess" : "LexicalLessOrEqual", - JoinDebugStringPtr(left_, ", ").c_str(), - JoinDebugStringPtr(right_, ", ").c_str()); + return absl::StrFormat( + "%s([%s], [%s])", strict_ ? "LexicalLess" : "LexicalLessOrEqual", + JoinDebugStringPtr(left_, ", "), JoinDebugStringPtr(right_, ", ")); } void Accept(ModelVisitor* const visitor) const override { @@ -370,9 +369,9 @@ class InversePermutationConstraint : public Constraint { } std::string DebugString() const override { - return StringPrintf("InversePermutationConstraint([%s], [%s])", - JoinDebugStringPtr(left_, ", ").c_str(), - JoinDebugStringPtr(right_, ", ").c_str()); + return absl::StrFormat("InversePermutationConstraint([%s], [%s])", + JoinDebugStringPtr(left_, ", "), + JoinDebugStringPtr(right_, ", ")); } void Accept(ModelVisitor* const visitor) const override { @@ -388,7 +387,7 @@ class InversePermutationConstraint : public Constraint { // See PropagateHolesOfLeftVarToRight() and PropagateHolesOfRightVarToLeft(). void PropagateHoles(int index, IntVar* const var, IntVarIterator* const holes, const std::vector& inverse) { - const int64 oldmin = std::max(var->OldMin(), 0LL); + const int64 oldmin = std::max(var->OldMin(), int64{0}); const int64 oldmax = std::min(var->OldMax(), static_cast(left_.size() - 1)); const int64 vmin = var->Min(); @@ -455,7 +454,7 @@ class IndexOfFirstMaxValue : public Constraint { void InitialPropagate() override { const int64 vsize = vars_.size(); - const int64 imin = std::max(0LL, index_->Min()); + const int64 imin = std::max(int64{0}, index_->Min()); const int64 imax = std::min(vsize - 1, index_->Max()); int64 max_max = kint64min; int64 max_min = kint64min; @@ -488,8 +487,8 @@ class IndexOfFirstMaxValue : public Constraint { } std::string DebugString() const override { - return StringPrintf("IndexMax(%s, [%s])", index_->DebugString().c_str(), - JoinDebugStringPtr(vars_, ", ").c_str()); + return absl::StrFormat("IndexMax(%s, [%s])", index_->DebugString(), + JoinDebugStringPtr(vars_, ", ")); } void Accept(ModelVisitor* const visitor) const override { diff --git a/ortools/constraint_solver/count_cst.cc b/ortools/constraint_solver/count_cst.cc index 1e8d6dd53b8..f95574dc1b9 100644 --- a/ortools/constraint_solver/count_cst.cc +++ b/ortools/constraint_solver/count_cst.cc @@ -18,10 +18,10 @@ #include #include +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/string_array.h" @@ -126,9 +126,8 @@ class AtMost : public Constraint { } std::string DebugString() const override { - return StringPrintf("AtMost(%s, %" GG_LL_FORMAT "d, %" GG_LL_FORMAT "d)", - JoinDebugStringPtr(vars_, ", ").c_str(), value_, - max_count_); + return absl::StrFormat("AtMost(%s, %" GG_LL_FORMAT "d, %" GG_LL_FORMAT "d)", + JoinDebugStringPtr(vars_, ", "), value_, max_count_); } void Accept(ModelVisitor* const visitor) const override { @@ -170,10 +169,10 @@ class Distribute : public Constraint { void CardMin(int cindex); void CardMax(int cindex); std::string DebugString() const override { - return StringPrintf("Distribute(vars = [%s], values = [%s], cards = [%s])", - JoinDebugStringPtr(vars_, ", ").c_str(), - absl::StrJoin(values_, ", ").c_str(), - JoinDebugStringPtr(cards_, ", ").c_str()); + return absl::StrFormat( + "Distribute(vars = [%s], values = [%s], cards = [%s])", + JoinDebugStringPtr(vars_, ", "), absl::StrJoin(values_, ", "), + JoinDebugStringPtr(cards_, ", ")); } void Accept(ModelVisitor* const visitor) const override { @@ -390,9 +389,9 @@ FastDistribute::FastDistribute(Solver* const s, } std::string FastDistribute::DebugString() const { - return StringPrintf("FastDistribute(vars = [%s], cards = [%s])", - JoinDebugStringPtr(vars_, ", ").c_str(), - JoinDebugStringPtr(cards_, ", ").c_str()); + return absl::StrFormat("FastDistribute(vars = [%s], cards = [%s])", + JoinDebugStringPtr(vars_, ", "), + JoinDebugStringPtr(cards_, ", ")); } void FastDistribute::Post() { @@ -456,7 +455,7 @@ void FastDistribute::OneDomain(int index) { const int64 oldmax = var->OldMax(); const int64 vmin = var->Min(); const int64 vmax = var->Max(); - for (int64 card_index = std::max(oldmin, 0LL); + for (int64 card_index = std::max(oldmin, int64{0}); card_index < std::min(vmin, card_size()); ++card_index) { if (undecided_.IsSet(index, card_index)) { SetRevCannotContribute(index, card_index); @@ -468,7 +467,7 @@ void FastDistribute::OneDomain(int index) { SetRevCannotContribute(index, card_index); } } - for (int64 card_index = std::max(vmax + 1, 0LL); + for (int64 card_index = std::max(vmax + 1, int64{0}); card_index <= std::min(oldmax, card_size() - 1); ++card_index) { if (undecided_.IsSet(index, card_index)) { SetRevCannotContribute(index, card_index); @@ -589,13 +588,11 @@ BoundedDistribute::BoundedDistribute(Solver* const s, } std::string BoundedDistribute::DebugString() const { - return StringPrintf( + return absl::StrFormat( "BoundedDistribute([%s], values = [%s], card_min = [%s], card_max = " "[%s]", - JoinDebugStringPtr(vars_, ", ").c_str(), - absl::StrJoin(values_, ", ").c_str(), - absl::StrJoin(card_min_, ", ").c_str(), - absl::StrJoin(card_max_, ", ").c_str()); + JoinDebugStringPtr(vars_, ", "), absl::StrJoin(values_, ", "), + absl::StrJoin(card_min_, ", "), absl::StrJoin(card_max_, ", ")); } void BoundedDistribute::Post() { @@ -789,11 +786,10 @@ BoundedFastDistribute::BoundedFastDistribute(Solver* const s, } std::string BoundedFastDistribute::DebugString() const { - return StringPrintf( + return absl::StrFormat( "BoundedFastDistribute([%s], card_min = [%s], card_max = [%s]", - JoinDebugStringPtr(vars_, ", ").c_str(), - absl::StrJoin(card_min_, ", ").c_str(), - absl::StrJoin(card_max_, ", ").c_str()); + JoinDebugStringPtr(vars_, ", "), absl::StrJoin(card_min_, ", "), + absl::StrJoin(card_max_, ", ")); } void BoundedFastDistribute::Post() { @@ -872,7 +868,7 @@ void BoundedFastDistribute::OneDomain(int index) { const int64 oldmax = var->OldMax(); const int64 vmin = var->Min(); const int64 vmax = var->Max(); - for (int64 card_index = std::max(oldmin, 0LL); + for (int64 card_index = std::max(oldmin, int64{0}); card_index < std::min(vmin, card_size()); ++card_index) { if (undecided_.IsSet(index, card_index)) { SetRevCannotContribute(index, card_index); @@ -884,7 +880,7 @@ void BoundedFastDistribute::OneDomain(int index) { SetRevCannotContribute(index, card_index); } } - for (int64 card_index = std::max(vmax + 1, 0LL); + for (int64 card_index = std::max(vmax + 1, int64{0}); card_index <= std::min(oldmax, card_size() - 1); ++card_index) { if (undecided_.IsSet(index, card_index)) { SetRevCannotContribute(index, card_index); diff --git a/ortools/constraint_solver/csharp/constraint_solver.i b/ortools/constraint_solver/csharp/constraint_solver.i index 03c5ce71c06..14685eb1d76 100644 --- a/ortools/constraint_solver/csharp/constraint_solver.i +++ b/ortools/constraint_solver/csharp/constraint_solver.i @@ -70,10 +70,6 @@ class LocalSearchPhaseParameters { }; } // namespace operations_research -namespace swig_util { - class NodeEvaluator2; -} - struct FailureProtect { jmp_buf exception_buffer; void JumpBack() { @@ -145,9 +141,6 @@ PROTECT_FROM_FAILURE(Solver::Fail(), arg1); %template(CpIntVectorVector) std::vector >; %template(CpInt64VectorVector) std::vector >; -// This needs to be declared here as the camel case rename rule will cause collisions in the C# NodeEvaluator2Vector class. -%template(NodeEvaluator2Vector) std::vector<::swig_util::NodeEvaluator2*>; - %define CS_TYPEMAP_STDVECTOR_OBJECT(CTYPE, TYPE) SWIG_STD_VECTOR_ENHANCED(operations_research::CTYPE*); %template(TYPE ## Vector) std::vector; @@ -408,25 +401,21 @@ namespace operations_research { const std::vector& secondary_vars, IndexEvaluator3 evaluator, EvaluatorLocalSearchOperators op); -%ignore Solver::MakeLocalSearchObjectiveFilter( +%ignore Solver::MakeSumObjectiveFilter( const std::vector& vars, IndexEvaluator2 values, - IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum); -%ignore Solver::MakeLocalSearchObjectiveFilter( + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum); +%ignore Solver::MakeSumObjectiveFilter( const std::vector& vars, IndexEvaluator2 values, ObjectiveWatcher delta_objective_callback, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum); -%ignore Solver::MakeLocalSearchObjectiveFilter( + Solver::LocalSearchFilterBound filter_enum); +%ignore Solver::MakeSumObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 values, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum); -%ignore Solver::MakeLocalSearchObjectiveFilter( + Solver::LocalSearchFilterBound filter_enum); +%ignore Solver::MakeSumObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 values, ObjectiveWatcher delta_objective_callback, - IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum); + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum); %ignore Solver::ConcatenateOperators( const std::vector& ops, std::function evaluator); @@ -585,48 +574,44 @@ namespace operations_research { [evaluator](int64 i, int64 j, int64 k) { return evaluator->Run(i, j, k); }, op); } - LocalSearchFilter* MakeLocalSearchObjectiveFilter( + LocalSearchFilter* MakeSumObjectiveFilter( const std::vector& vars, swig_util::LongLongToLong* values, - IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum) { - return $self->MakeLocalSearchObjectiveFilter( + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum) { + return $self->MakeSumObjectiveFilter( vars, [values](int64 i, int64 j) { return values->Run(i, j); }, - objective, filter_enum, op_enum); + objective, filter_enum); } - LocalSearchFilter* MakeLocalSearchObjectiveFilter( + LocalSearchFilter* MakeSumObjectiveFilter( const std::vector& vars, swig_util::LongLongToLong* values, swig_util::LongToVoid* delta_objective_callback, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum) { - return $self->MakeLocalSearchObjectiveFilter( + Solver::LocalSearchFilterBound filter_enum) { + return $self->MakeSumObjectiveFilter( vars, [values](int64 i, int64 j) { return values->Run(i, j); }, [delta_objective_callback](int64 i) { return delta_objective_callback->Run(i); }, - objective, filter_enum, op_enum); + objective, filter_enum); } - LocalSearchFilter* MakeLocalSearchObjectiveFilter( + LocalSearchFilter* MakeSumObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, swig_util::LongLongLongToLong* values, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum) { - return $self->MakeLocalSearchObjectiveFilter( + Solver::LocalSearchFilterBound filter_enum) { + return $self->MakeSumObjectiveFilter( vars, secondary_vars, [values](int64 i, int64 j, int64 k) { return values->Run(i, j, k); }, - objective, filter_enum, op_enum); + objective, filter_enum); } - LocalSearchFilter* MakeLocalSearchObjectiveFilter( + LocalSearchFilter* MakeSumObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, swig_util::LongLongLongToLong* values, swig_util::LongToVoid* delta_objective_callback, - IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum) { - return $self->MakeLocalSearchObjectiveFilter( + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum) { + return $self->MakeSumObjectiveFilter( vars, secondary_vars, [values](int64 i, int64 j, int64 k) { return values->Run(i, j, k); }, [delta_objective_callback](int64 i) { return delta_objective_callback->Run(i); }, - objective, filter_enum, op_enum); + objective, filter_enum); } LocalSearchOperator* ConcatenateOperators( const std::vector& ops, @@ -883,7 +868,7 @@ PROTO2_RETURN(operations_research::SearchLimitParameters, PROTO_INPUT(operations_research::CpModel, Google.OrTools.ConstraintSolver.CpModel, - model_proto) + proto) PROTO2_RETURN(operations_research::CpModel, Google.OrTools.ConstraintSolver.CpModel) diff --git a/ortools/constraint_solver/csharp/routing.i b/ortools/constraint_solver/csharp/routing.i index 852fb6f9662..08d5e705c0d 100644 --- a/ortools/constraint_solver/csharp/routing.i +++ b/ortools/constraint_solver/csharp/routing.i @@ -1,4 +1,4 @@ -// Copyright 2010-2014 Google +// Copyright 2010-2018 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -13,6 +13,9 @@ // TODO(user): Refactor this file to adhere to the SWIG style guide. %include "ortools/constraint_solver/csharp/constraint_solver.i" +%include "ortools/constraint_solver/csharp/routing_types.i" +%include "ortools/constraint_solver/csharp/routing_index_manager.i" +%include "ortools/util/csharp/functions.i" // We need to forward-declare the proto here, so that PROTO_INPUT involving it // works correctly. The order matters very much: this declaration needs to be @@ -25,184 +28,13 @@ class RoutingSearchParameters; // Include the file we want to wrap a first time. %{ #include "ortools/constraint_solver/routing.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" +#include "ortools/constraint_solver/routing_types.h" %} %module(directors="1") operations_research; -%feature("director") swig_util::NodeEvaluator2; - -// Convert RoutingModel::NodeIndex to (32-bit signed) integers. -%typemap(ctype) operations_research::RoutingModel::NodeIndex "int" -%typemap(imtype) operations_research::RoutingModel::NodeIndex "int" -%typemap(cstype) operations_research::RoutingModel::NodeIndex "int" -%typemap(csin) operations_research::RoutingModel::NodeIndex "$csinput" -%typemap(csout) operations_research::RoutingModel::NodeIndex { - return $imcall; -} -%typemap(in) operations_research::RoutingModel::NodeIndex { - $1 = operations_research::RoutingModel::NodeIndex($input); -} -%typemap(out) operations_research::RoutingModel::NodeIndex { - $result = $1.value(); -} -%typemap(csvarin) operations_research::RoutingModel::NodeIndex -%{ - set { $imcall; } -%} -%typemap(csvarout, excode=SWIGEXCODE) operations_research::RoutingModel::NodeIndex -%{ - get { - return $imcall; - } -%} - -// This typemap maps C# TypeA[] arrays to C++ std::vector, where -// TypeA and TypeB are strictly equivalent, bit-wise (like an IntType -// and an int, for example). This is done via an intermediate mapping -// to pairs (int length, TypeA*). -// -// We need to redefine it ourselves it because std_vector.i will only -// allow these conversions when TypeA == TypeB. -%define CS_TYPEMAP_STDVECTOR(TYPE, CTYPE, CSHARPTYPE) -%typemap(ctype) const std::vector& %{ int length$argnum, CTYPE* %} -%typemap(imtype) const std::vector& %{ int length$argnum, CSHARPTYPE[] %} -%typemap(cstype) const std::vector& %{ CSHARPTYPE[] %} -%typemap(csin) const std::vector& "$csinput.Length, $csinput" -%typemap(freearg) const std::vector& { delete $1; } -%typemap(in) const std::vector& %{ - $1 = new std::vector; - $1->reserve(length$argnum); - for(int i = 0; i < length$argnum; ++i) { - $1->emplace_back($input[i]); - } -%} -%enddef // CS_TYPEMAP_STDVECTOR - -CS_TYPEMAP_STDVECTOR(operations_research::RoutingModel::NodeIndex, int, int); - -// Ditto, for bi-dimensional arrays: map C# TypeA[][] to C++ -// std::vector>. -%define CS_TYPEMAP_STDVECTOR_IN1(TYPE, CTYPE, CSHARPTYPE) -%typemap(ctype) const std::vector >& %{ - int len$argnum_1, int len$argnum_2[], CTYPE* -%} -%typemap(imtype) const std::vector >& %{ - int len$argnum_1, int[] len$argnum_2, CSHARPTYPE[] -%} -%typemap(cstype) const std::vector >& %{ CSHARPTYPE[][] %} -%typemap(csin) const std::vector >& "$csinput.GetLength(0), NestedArrayHelper.GetArraySecondSize($csinput), NestedArrayHelper.GetFlatArray($csinput)" -%typemap(in) const std::vector >& (std::vector > result) %{ - - result.clear(); - result.resize(len$argnum_1); - - TYPE* inner_array = reinterpret_cast($input); - - int actualIndex = 0; - for (int index1 = 0; index1 < len$argnum_1; ++index1) { - result[index1].reserve(len$argnum_2[index1]); - for (int index2 = 0; index2 < len$argnum_2[index1]; ++index2) { - const TYPE value = inner_array[actualIndex]; - result[index1].emplace_back(value); - actualIndex++; - } - } - - $1 = &result; -%} -%enddef // CS_TYPEMAP_STDVECTOR_IN1 - -CS_TYPEMAP_STDVECTOR_IN1(operations_research::RoutingModel::NodeIndex, int, int); - -// Create input mapping for NodeEvaluator2 callback wrapping. -// TODO(user): split out the callback code; it creates another file since -// it uses a different module. -%{ -namespace swig_util { -class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 { - public: - NodeEvaluator2() : used_as_permanent_handler_(false) {} - - virtual ~NodeEvaluator2() {} - - virtual int64 Run(int i, int j) = 0; - - operations_research::RoutingModel::NodeEvaluator2* GetPermanentCallback() { - CHECK(!used_as_permanent_handler_); - used_as_permanent_handler_ = true; - return this; - } - - private: - virtual bool IsRepeatable() const { return true; } - - virtual int64 Run(operations_research::RoutingModel::NodeIndex i, - operations_research::RoutingModel::NodeIndex j) { - return Run(i.value(), j.value()); - } - - bool used_as_permanent_handler_; -}; -} // namespace swig_util -%} - -// We prefer keeping this C# extension here (instead of moving it to a dedicated -// C# file, using partial classes) because it uses SWIG's $descriptor(). -%typemap(cscode) swig_util::NodeEvaluator2 %{ - public $descriptor(operations_research::RoutingModel::NodeEvaluator2*) DisownAndGetPermanentCallback() { - swigCMemOwn = false; - GC.SuppressFinalize(this); - return GetPermanentCallback(); - } -%} - -namespace swig_util { -class NodeEvaluator2 : private ::operations_research::RoutingModel::NodeEvaluator2 { - public: - NodeEvaluator2(); - virtual int64 Run(int i, int j) = 0; - ::operations_research::RoutingModel::NodeEvaluator2* GetPermanentCallback(); - virtual ~NodeEvaluator2(); - - private: - virtual bool IsRepeatable() const; - virtual int64 Run(::operations_research::RoutingModel::NodeIndex i, - ::operations_research::RoutingModel::NodeIndex j); - bool used_as_permanent_handler_; -}; -} // namespace swig_util - - -%typemap(cscode) operations_research::RoutingModel %{ - private System.Collections.Generic.List pinned = - new System.Collections.Generic.List(); - - private HandleRef TakeOwnershipAndAddReference(NodeEvaluator2 value) - { - var handle = $descriptor(ResultCallback2*).getCPtr(value.DisownAndGetPermanentCallback()); - this.pinned.Add(value); - return handle; - } - - private HandleRef TakeOwnershipAndAddReference(NodeEvaluator2[] values) - { - NodeEvaluator2Vector vector = new NodeEvaluator2Vector(values); - foreach (NodeEvaluator2 value in values) - { - value.DisownAndGetPermanentCallback(); - this.pinned.Add(value); - } - - return NodeEvaluator2Vector.getCPtr(vector); - } -%} - -// Typemaps for NodeEvaluator2 callbacks in csharp. -%typemap(cstype) operations_research::RoutingModel::NodeEvaluator2* "NodeEvaluator2"; -%typemap(csin) operations_research::RoutingModel::NodeEvaluator2* "this.TakeOwnershipAndAddReference($csinput)"; - -// Typemaps for NodeEvaluator2 arrays in csharp. -%typemap(cstype) std::vector*>& "NodeEvaluator2[]"; -%typemap(csin) std::vector*>& %{ this.TakeOwnershipAndAddReference($csinput) %} %rename (RoutingModelStatus) operations_research::RoutingModel::Status; @@ -216,10 +48,27 @@ class NodeEvaluator2 : private ::operations_research::RoutingModel::NodeEvaluato int64 capacity, const std::string& name); +%ignore operations_research::RoutingModel::RegisterStateDependentTransitCallback; +%ignore operations_research::RoutingModel::StateDependentTransitCallback; %ignore operations_research::RoutingModel::MakeStateDependentTransit; %ignore operations_research::RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity; +%ignore operations_research::RoutingModel::RegisterTransitCallback( + operations_research::TransitCallback2); +%ignore operations_research::RoutingModel::RegisterUnaryTransitCallback( + operations_research::TransitCallback1); + %extend operations_research::RoutingModel { + int RegisterTransitCallback(swig_util::IntIntToLong* callback) { + return $self->RegisterTransitCallback([callback](int i, int j) { + return callback->Run(i, j); + }); + } + int RegisterUnaryTransitCallback(swig_util::IntToLong* callback) { + return $self->RegisterUnaryTransitCallback([callback](int i) { + return callback->Run(i); + }); + } void AddVectorDimension(const std::vector& values, int64 capacity, bool fix_start_cumul_to_zero, @@ -230,13 +79,6 @@ class NodeEvaluator2 : private ::operations_research::RoutingModel::NodeEvaluato } } -%ignore operations_research::RoutingModel::WrapIndexEvaluator( - Solver::IndexEvaluator2* evaluator); - -%ignore operations_research::RoutingModel::RoutingModel( - int nodes, int vehicles, - const std::vector >& start_end); - %rename("%(camelcase)s", %$isfunction) ""; // Protobuf support @@ -252,5 +94,5 @@ PROTO2_RETURN(operations_research::RoutingModelParameters, Google.OrTools.ConstraintSolver.RoutingModelParameters) -%include "ortools/constraint_solver/routing_types.h" +%include "ortools/constraint_solver/routing_parameters.h" %include "ortools/constraint_solver/routing.h" diff --git a/ortools/constraint_solver/csharp/routing_index_manager.i b/ortools/constraint_solver/csharp/routing_index_manager.i new file mode 100644 index 00000000000..3930a8feb17 --- /dev/null +++ b/ortools/constraint_solver/csharp/routing_index_manager.i @@ -0,0 +1,39 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Wrapper for RoutingIndexManager. + +%include "ortools/base/base.i" +%include "ortools/util/csharp/vector.i" + +%{ +#include "ortools/constraint_solver/routing_index_manager.h" +%} + +DEFINE_INDEX_TYPE_TYPEDEF(operations_research::RoutingNodeIndex, + operations_research::RoutingIndexManager::NodeIndex); + +%ignoreall + +%unignore operations_research; +%unignore operations_research::RoutingIndexManager; +%unignore operations_research::RoutingIndexManager::IndexToNode; +%unignore operations_research::RoutingIndexManager::NodeToIndex; +%unignore operations_research::RoutingIndexManager::NodesToIndices; +%unignore operations_research::RoutingIndexManager::RoutingIndexManager(int, int, NodeIndex); +%unignore operations_research::RoutingIndexManager::RoutingIndexManager(int, int, const std::vector&, const std::vector&); +%unignore operations_research::RoutingIndexManager::~RoutingIndexManager; + +%include "ortools/constraint_solver/routing_index_manager.h" + +%unignoreall diff --git a/ortools/constraint_solver/csharp/routing_types.i b/ortools/constraint_solver/csharp/routing_types.i new file mode 100644 index 00000000000..af0e2ae9852 --- /dev/null +++ b/ortools/constraint_solver/csharp/routing_types.i @@ -0,0 +1,79 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Typemaps for Routing Index Types. This does not define any type wrappings, +// because these index types are are never exposed to the target language. +// Instead, indices are manipulated as native target language types (e.g. C# +// int). +// This file is to be %included when wrapped objects need to use these typemaps. + +%include "ortools/base/base.i" +%import "ortools/util/csharp/vector.i" + +%{ +#include "ortools/constraint_solver/routing_types.h" +%} + +// This macro defines typemaps for IndexT, std::vector and +// std::vector>. +%define DEFINE_INDEX_TYPE(IndexT) + +// Convert IndexT to (32-bit signed) integers. +%typemap(ctype) IndexT "int" +%typemap(imtype) IndexT "int" +%typemap(cstype) IndexT "int" +%typemap(csin) IndexT "$csinput" +%typemap(csout) IndexT { + return $imcall; +} +%typemap(in) IndexT { + $1 = IndexT($input); +} +%typemap(out) IndexT { + $result = $1.value(); +} +%typemap(csvarin) IndexT +%{ + set { $imcall; } +%} +%typemap(csvarout, excode=SWIGEXCODE) IndexT +%{ + get { + return $imcall; + } +%} + +// Convert std::vector to/from int arrays. +VECTOR_AS_CSHARP_ARRAY(IndexT, int, int); +MATRIX_AS_CSHARP_ARRAY(IndexT, int, int); + +%enddef // DEFINE_INDEX_TYPE + +// This macro applies all typemaps for a given index type to a typedef. +// Normally we should not need that as SWIG is supposed to automatically apply +// all typemaps to typedef definitions (http://www.swig.org/Doc2.0/SWIGDocumentation.html#Typemaps_typedef_reductions), +// but this is not actually the case. +%define DEFINE_INDEX_TYPE_TYPEDEF(IndexT, NewIndexT) +%apply IndexT { NewIndexT }; +%apply std::vector { std::vector }; +%apply const std::vector& { std::vector& }; +%apply const std::vector >& { const std::vector >& }; +%enddef // DEFINE_INDEX_TYPE_TYPEDEF + +DEFINE_INDEX_TYPE(operations_research::RoutingNodeIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingCostClassIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingDimensionIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingDisjunctionIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingVehicleClassIndex); + +%include "ortools/constraint_solver/routing_types.h" diff --git a/ortools/constraint_solver/default_search.cc b/ortools/constraint_solver/default_search.cc index 322355b3395..12586b38881 100644 --- a/ortools/constraint_solver/default_search.cc +++ b/ortools/constraint_solver/default_search.cc @@ -16,16 +16,15 @@ #include #include #include -#include #include #include +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/stringprintf.h" - #include "ortools/base/random.h" #include "ortools/base/stl_util.h" #include "ortools/constraint_solver/constraint_solver.h" @@ -37,16 +36,12 @@ DEFINE_int32(cp_impact_divider, 10, "Divider for continuous update."); namespace operations_research { -class NoGoodManager; - namespace { // Default constants for search phase parameters. const int kDefaultNumberOfSplits = 100; const int kDefaultHeuristicPeriod = 100; const int kDefaultHeuristicNumFailuresLimit = 30; const int kDefaultSeed = 0; -const double kDefaultRestartLogSize = -1.0; -const bool kDefaultUseNoGoods = true; const bool kDefaultUseLastConflict = true; } // namespace @@ -59,9 +54,7 @@ DefaultPhaseParameters::DefaultPhaseParameters() heuristic_num_failures_limit(kDefaultHeuristicNumFailuresLimit), persistent_impact(true), random_seed(kDefaultSeed), - restart_log_size(kDefaultRestartLogSize), display_level(DefaultPhaseParameters::NORMAL), - use_no_goods(kDefaultUseNoGoods), use_last_conflict(kDefaultUseLastConflict), decision_builder(nullptr) {} @@ -154,7 +147,7 @@ class FindVar : public DecisionVisitor { Operation operation_; }; -// ----- Auxilliary decision builders to init impacts ----- +// ----- Auxiliary decision builders to init impacts ----- // This class initialize impacts by scanning each value of the domain // of the variable. @@ -648,7 +641,7 @@ class ImpactRecorder : public SearchMonitor { int current_var_; int64 current_value_; FindVar find_var_; - std::unordered_map var_map_; + absl::flat_hash_map var_map_; bool init_done_; DISALLOW_COPY_AND_ASSIGN(ImpactRecorder); @@ -660,15 +653,6 @@ const double ImpactRecorder::kFailureImpact = 1.0; const double ImpactRecorder::kInitFailureImpact = 2.0; const int ImpactRecorder::kUninitializedVarIndex = -1; -// ----- Restart ----- - -int64 ComputeBranchRestart(int64 log) { - if (log <= 0 || log > 63) { - return kint64max; - } - return GG_ULONGLONG(1) << log; -} - // This structure stores 'var[index] (left?==:!=) value'. class ChoiceInfo { public: @@ -678,8 +662,8 @@ class ChoiceInfo { : value_(value), var_(var), left_(left) {} std::string DebugString() const { - return StringPrintf("%s %s %lld", var_->name().c_str(), - (left_ ? "==" : "!="), value_); + return absl::StrFormat("%s %s %d", var_->name(), (left_ ? "==" : "!="), + value_); } IntVar* var() const { return var_; } @@ -696,256 +680,6 @@ class ChoiceInfo { bool left_; }; -// Hook on the search to check restart before the refutation of a decision. -class RestartMonitor : public SearchMonitor { - public: - RestartMonitor(Solver* const solver, DefaultPhaseParameters parameters, - DomainWatcher* const domain_watcher) - : SearchMonitor(solver), - parameters_(parameters), - domain_watcher_(domain_watcher), - min_log_search_space_(std::numeric_limits::infinity()), - no_good_manager_(parameters_.restart_log_size >= 0 && - parameters_.use_no_goods - ? solver->MakeNoGoodManager() - : nullptr), - branches_between_restarts_(0), - min_restart_period_(ComputeBranchRestart(parameters_.restart_log_size)), - maximum_restart_depth_(kint64max), - num_restarts_(0) {} - - ~RestartMonitor() override {} - - void ApplyDecision(Decision* const d) override { - Solver* const s = solver(); - branches_between_restarts_++; - d->Accept(&find_var_); - if (find_var_.operation() == FindVar::ASSIGN) { - choices_.Push(solver(), - ChoiceInfo(find_var_.var(), find_var_.value(), true)); - if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) { - VLOG(2) << "adding no good = " << choices_.Last()->DebugString() - << " at depth " << s->SearchDepth(); - } - } - } - - void RefuteDecision(Decision* const d) override { - CHECK(d != nullptr); - Solver* const s = solver(); - branches_between_restarts_++; - d->Accept(&find_var_); - if (find_var_.operation() == FindVar::ASSIGN) { - choices_.Push(solver(), - ChoiceInfo(find_var_.var(), find_var_.value(), false)); - if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) { - VLOG(2) << "adding no good = " << choices_.Last()->DebugString() - << " at depth " << s->SearchDepth(); - } - if (ChecksRestartOnRefute(s)) { - if (AddsNoGood(s)) { - if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) { - VLOG(2) << "restarting"; - } - num_restarts_++; - s->RestartCurrentSearch(); - s->Fail(); - } - } - } - } - - void ExitSearch() override { - if (parameters_.display_level != DefaultPhaseParameters::NONE && - no_good_manager_ != nullptr) { - LOG(INFO) << "Default search has generated " - << no_good_manager_->NoGoodCount() << " no goods, and " - << num_restarts_ << " restarts"; - } - } - - bool AtSolution() override { - if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) { - VLOG(2) << "Found a solution after the following decisions:"; - for (SimpleRevFIFO::Iterator it(&choices_); it.ok(); ++it) { - VLOG(2) << " " << (*it).DebugString(); - } - } - return false; - } - - void BeginFail() override { - if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) { - VLOG(2) << "-- Failure"; - } - } - - void Install() override { - SearchMonitor::Install(); - if (no_good_manager_ != nullptr) { - no_good_manager_->Install(); - } - } - - int num_restarts() const { return num_restarts_; } - - std::string DebugString() const override { return "RestartMonitor"; } - - private: - // Called before applying the refutation of the decision. This - // method must decide if we need to restart or not. The main - // decision is based on the restart_log_size and the current search - // space size. If, the current search space size is greater than the - // min search space size encountered since the last restart + - // restart_log_size, this means that we have just finished a search - // tree of size at least restart_log_size. If the search tree is - // very sparse, then we may have visited close to restart_log_size - // nodes since the last restart (instead of the maximum of - // 2^restart_log_size). To fight this degenerate case, we also count - // the number of branches explored since the last restart and decide - // to postpone restart until we finish a sub-search tree and have - // visited enough branches (2^restart_log_size). To enforce this, - // when we postpone a restart, we store the current search depth -1 - // in maximum_restart_depth_ and will restart as soon as we reach a - // node above the current one, with enough visited branches. - bool ChecksRestartOnRefute(Solver* const solver) { - // We do nothing if restart_log_size is < 0. - if (parameters_.restart_log_size >= 0) { - const int search_depth = solver->SearchDepth(); - const double log_search_space_size = - domain_watcher_->LogSearchSpaceSize(); - if (min_log_search_space_ > log_search_space_size) { - min_log_search_space_ = log_search_space_size; - } - - // Some verbose display. - if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) { - VLOG(2) << "search_depth = " << search_depth - << ", branches between restarts = " - << branches_between_restarts_ - << ", log_search_space_size = " << log_search_space_size - << ", min_log_search_space = " << min_log_search_space_; - } - bool all_rights = true; - for (SimpleRevFIFO::Iterator it(&choices_); it.ok(); ++it) { - if ((*it).left()) { - all_rights = false; - break; - } - } - if (all_rights) { - if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) { - VLOG(2) << " - finished a left subtree, forcing a nogood"; - } - return true; - } - if (search_depth > maximum_restart_depth_ || search_depth == 0) { - // We are deeper than maximum_restart_depth_, we should not restart - // because we have not finished a sub-tree of sufficient size. - return false; - } - // We may restart either because of the search space criteria, - // or the search depth is less than maximum_restart_depth_. - if (min_log_search_space_ + parameters_.restart_log_size < - log_search_space_size || - (search_depth <= maximum_restart_depth_ && - maximum_restart_depth_ != kint64max)) { - // If we have not visited enough branches, we postpone the - // restart and force to check at least at the parent of the - // current search node. - if (branches_between_restarts_ < min_restart_period_) { - maximum_restart_depth_ = search_depth - 1; - if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) { - VLOG(2) << "Postpone restarting until depth <= " - << maximum_restart_depth_ - << ", visited nodes = " << branches_between_restarts_ - << " / " << min_restart_period_; - } - return false; - } - return true; - } - } - return false; - } - - // Performs the restart. It resets various counters and adds a - // non-reversible nogood if need be. It returns true if we should - // restart after having added the nogood. - bool AddsNoGood(Solver* const solver) { - min_log_search_space_ = std::numeric_limits::infinity(); - branches_between_restarts_ = 0; - maximum_restart_depth_ = kint64max; - // Creates nogood. - if (parameters_.use_no_goods) { - bool all_rights = true; - for (SimpleRevFIFO::Iterator it(&choices_); it.ok(); ++it) { - const ChoiceInfo& choice = *it; - if (choice.left()) { - all_rights = false; - } - } - DCHECK(no_good_manager_ != nullptr); - - // Reverse the last no good if need be. If we have finished the - // apply branch, then the subtree below the left branch is - // completely explored. The choice list contains the refute - // branch. This one should be reverted. - // - // It can also happen that we are failing in the apply - // branch. In that case, we do not need to revert it. - DCHECK(!choices_.Last()->left()); - const bool last_left = choices_.Last()->left(); - choices_.MutableLast()->set_left(true); - NoGood* const nogood = no_good_manager_->MakeNoGood(); - - // if the nogood contains both x == 3 and x != 4, we can simplify - // to keep only x == 3. - std::unordered_set positive_variable; - for (SimpleRevFIFO::Iterator it(&choices_); it.ok(); ++it) { - const ChoiceInfo& choice = *it; - if (choice.left()) { - positive_variable.insert(choice.var()); - } - } - // We fill the nogood structure. - for (SimpleRevFIFO::Iterator it(&choices_); it.ok(); ++it) { - const ChoiceInfo& choice = *it; - IntVar* const var = choice.var(); - const int64 value = choice.value(); - if (choice.left()) { - nogood->AddIntegerVariableEqualValueTerm(var, value); - } else if (!gtl::ContainsKey(positive_variable, choice.var())) { - nogood->AddIntegerVariableNotEqualValueTerm(var, value); - } - } - if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) { - VLOG(2) << "Adding no good no " << no_good_manager_->NoGoodCount() - << ": " << nogood->DebugString(); - } - // Adds the nogood to the nogood manager. - no_good_manager_->AddNoGood(nogood); - // Revert the modification on the last. - choices_.MutableLast()->set_left(last_left); - // If the nogood is only right branches, there is no need to - // restart as we would end up in the same place. - return !all_rights; - } - return true; - } - - const DefaultPhaseParameters parameters_; - DomainWatcher* const domain_watcher_; - double min_log_search_space_; - NoGoodManager* no_good_manager_; - int64 branches_between_restarts_; - const int64 min_restart_period_; - int64 maximum_restart_depth_; - SimpleRevFIFO choices_; - FindVar find_var_; - int num_restarts_; -}; - // ---------- Heuristics ---------- class RunHeuristicsAsDives : public Decision { @@ -1107,7 +841,6 @@ class DefaultIntegerSearch : public DecisionBuilder { parameters_.run_all_heuristics, parameters_.random_seed, parameters_.heuristic_period, parameters_.heuristic_num_failures_limit), - restart_monitor_(solver, parameters_, &domain_watcher_), find_var_(), last_int_var_(nullptr), last_int_value_(0), @@ -1185,7 +918,9 @@ class DefaultIntegerSearch : public DecisionBuilder { } break; } - default: { break; } + default: { + break; + } } } @@ -1215,9 +950,6 @@ class DefaultIntegerSearch : public DecisionBuilder { if (parameters_.decision_builder == nullptr) { extras->push_back(&impact_recorder_); } - if (parameters_.restart_log_size >= 0) { - extras->push_back(&restart_monitor_); - } } void Accept(ModelVisitor* const visitor) const override { @@ -1242,15 +974,8 @@ class DefaultIntegerSearch : public DecisionBuilder { } std::string StatString() const { - const int restarts = restart_monitor_.num_restarts(); const int runs = heuristics_.heuristic_runs(); std::string result; - if (restarts == 1) { - result.append("1 restart"); - } else if (restarts > 1) { - StringAppendF(&result, "%d restarts", restarts); - } - if (runs > 0) { if (!result.empty()) { result.append(", "); @@ -1258,7 +983,7 @@ class DefaultIntegerSearch : public DecisionBuilder { if (runs == 1) { result.append("1 heuristic run"); } else { - StringAppendF(&result, "%d heuristic runs", runs); + absl::StrAppendFormat(&result, "%d heuristic runs", runs); } } if (last_conflict_count_ > 0) { @@ -1268,7 +993,8 @@ class DefaultIntegerSearch : public DecisionBuilder { if (last_conflict_count_ == 1) { result.append("1 last conflict hint"); } else { - StringAppendF(&result, "%d last conflict hints", last_conflict_count_); + absl::StrAppendFormat(&result, "%d last conflict hints", + last_conflict_count_); } } return result; @@ -1315,8 +1041,8 @@ class DefaultIntegerSearch : public DecisionBuilder { << " variables, initialization splits = " << parameters_.initialization_splits << ", heuristic_period = " << parameters_.heuristic_period - << ", run_all_heuristics = " << parameters_.run_all_heuristics - << ", restart_log_size = " << parameters_.restart_log_size; + << ", run_all_heuristics = " + << parameters_.run_all_heuristics; } // Init the impacts. impact_recorder_.FirstRun(parameters_.initialization_splits); @@ -1364,7 +1090,6 @@ class DefaultIntegerSearch : public DecisionBuilder { DomainWatcher domain_watcher_; ImpactRecorder impact_recorder_; RunHeuristicsAsDives heuristics_; - RestartMonitor restart_monitor_; FindVar find_var_; IntVar* last_int_var_; int64 last_int_value_; diff --git a/ortools/constraint_solver/demon_profiler.cc b/ortools/constraint_solver/demon_profiler.cc index 36128de9969..4f2a79fc93e 100644 --- a/ortools/constraint_solver/demon_profiler.cc +++ b/ortools/constraint_solver/demon_profiler.cc @@ -15,18 +15,20 @@ #include #include #include -#include #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_format.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" #include "ortools/base/file.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" +#include "ortools/base/mathutil.h" #include "ortools/base/status.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/time_support.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/constraint_solver/demon_profiler.pb.h" @@ -264,12 +266,12 @@ class DemonProfiler : public PropagationMonitor { "d us, [average=%.2lf, median=%.2lf, stddev=%.2lf]\n"; File* file; const std::string model = - StringPrintf("Model %s:\n", solver->model_name().c_str()); + absl::StrFormat("Model %s:\n", solver->model_name()); if (file::Open(filename, "w", &file, file::Defaults()).ok()) { file::WriteString(file, model, file::Defaults()).IgnoreError(); std::vector to_sort; - for (std::unordered_map::const_iterator it = + for (absl::flat_hash_map::const_iterator it = constraint_map_.begin(); it != constraint_map_.end(); ++it) { const Constraint* const ct = it->first; @@ -297,9 +299,9 @@ class DemonProfiler : public PropagationMonitor { &demon_invocations, &total_demon_runtime, &demon_count); const std::string constraint_message = - StringPrintf(kConstraintFormat, ct->DebugString().c_str(), fails, - initial_propagation_runtime, demon_count, - demon_invocations, total_demon_runtime); + absl::StrFormat(kConstraintFormat, ct->DebugString(), fails, + initial_propagation_runtime, demon_count, + demon_invocations, total_demon_runtime); file::WriteString(file, constraint_message, file::Defaults()) .IgnoreError(); const std::vector& demons = demons_per_constraint_[ct]; @@ -315,9 +317,9 @@ class DemonProfiler : public PropagationMonitor { ExportInformation(demon_runs, &invocations, &fails, &runtime, &mean_runtime, &median_runtime, &standard_deviation); - const std::string runs = StringPrintf( - kDemonFormat, demon_runs->demon_id().c_str(), invocations, fails, - runtime, mean_runtime, median_runtime, standard_deviation); + const std::string runs = absl::StrFormat( + kDemonFormat, demon_runs->demon_id(), invocations, fails, runtime, + mean_runtime, median_runtime, standard_deviation); file::WriteString(file, runs, file::Defaults()).IgnoreError(); } } @@ -421,9 +423,9 @@ class DemonProfiler : public PropagationMonitor { Constraint* active_constraint_; Demon* active_demon_; const int64 start_time_ns_; - std::unordered_map constraint_map_; - std::unordered_map demon_map_; - std::unordered_map > + absl::flat_hash_map constraint_map_; + absl::flat_hash_map demon_map_; + absl::flat_hash_map > demons_per_constraint_; }; diff --git a/ortools/constraint_solver/deviation.cc b/ortools/constraint_solver/deviation.cc index b3a15b15213..aea93bc264d 100644 --- a/ortools/constraint_solver/deviation.cc +++ b/ortools/constraint_solver/deviation.cc @@ -17,10 +17,10 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/mathutil.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/util/string_array.h" @@ -71,9 +71,9 @@ class Deviation : public Constraint { } std::string DebugString() const override { - return StringPrintf("Deviation([%s], deviation_var = %s, sum = %lld)", - JoinDebugStringPtr(vars_, ", ").c_str(), - deviation_var_->DebugString().c_str(), total_sum_); + return absl::StrFormat("Deviation([%s], deviation_var = %s, sum = %d)", + JoinDebugStringPtr(vars_, ", "), + deviation_var_->DebugString(), total_sum_); } void Accept(ModelVisitor* const visitor) const override { diff --git a/ortools/constraint_solver/diffn.cc b/ortools/constraint_solver/diffn.cc index 7ab5bf81610..e7a9434b951 100644 --- a/ortools/constraint_solver/diffn.cc +++ b/ortools/constraint_solver/diffn.cc @@ -15,12 +15,12 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/hash.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/string_array.h" @@ -116,11 +116,10 @@ class Diffn : public Constraint { } std::string DebugString() const override { - return StringPrintf("Diffn(x = [%s], y = [%s], dx = [%s], dy = [%s]))", - JoinDebugStringPtr(x_, ", ").c_str(), - JoinDebugStringPtr(y_, ", ").c_str(), - JoinDebugStringPtr(dx_, ", ").c_str(), - JoinDebugStringPtr(dy_, ", ").c_str()); + return absl::StrFormat( + "Diffn(x = [%s], y = [%s], dx = [%s], dy = [%s]))", + JoinDebugStringPtr(x_, ", "), JoinDebugStringPtr(y_, ", "), + JoinDebugStringPtr(dx_, ", "), JoinDebugStringPtr(dy_, ", ")); } void Accept(ModelVisitor* const visitor) const override { @@ -267,7 +266,9 @@ class Diffn : public Constraint { dy_[other]->SetMax(y_[box]->Max() - y_[other]->Min()); break; } - default: { break; } + default: { + break; + } } } @@ -288,7 +289,7 @@ class Diffn : public Constraint { const bool strict_; const int64 size_; Demon* delayed_demon_; - std::unordered_set to_propagate_; + absl::flat_hash_set to_propagate_; std::vector neighbors_; uint64 fail_stamp_; }; diff --git a/ortools/constraint_solver/element.cc b/ortools/constraint_solver/element.cc index ecbbe0c4834..266bb8d1a93 100644 --- a/ortools/constraint_solver/element.cc +++ b/ortools/constraint_solver/element.cc @@ -18,10 +18,10 @@ #include #include +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/range_minimum_query.h" @@ -41,7 +41,7 @@ template class VectorLess { public: explicit VectorLess(const std::vector* values) : values_(values) {} - bool operator()(const T& x, const T& y) { + bool operator()(const T& x, const T& y) const { return (*values_)[x] < (*values_)[y]; } @@ -53,7 +53,7 @@ template class VectorGreater { public: explicit VectorGreater(const std::vector* values) : values_(values) {} - bool operator()(const T& x, const T& y) { + bool operator()(const T& x, const T& y) const { return (*values_)[x] > (*values_)[y]; } @@ -258,10 +258,9 @@ class IntElementConstraint : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("IntElementConstraint(%s, %s, %s)", - absl::StrJoin(values_, ", ").c_str(), - index_->DebugString().c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("IntElementConstraint(%s, %s, %s)", + absl::StrJoin(values_, ", "), index_->DebugString(), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -296,24 +295,22 @@ class IntExprElement : public BaseIntExprElement { std::string name() const override { const int size = values_.size(); if (size > 10) { - return StringPrintf("IntElement(array of size %d, %s)", size, - expr_->name().c_str()); + return absl::StrFormat("IntElement(array of size %d, %s)", size, + expr_->name()); } else { - return StringPrintf("IntElement(%s, %s)", - absl::StrJoin(values_, ", ").c_str(), - expr_->name().c_str()); + return absl::StrFormat("IntElement(%s, %s)", absl::StrJoin(values_, ", "), + expr_->name()); } } std::string DebugString() const override { const int size = values_.size(); if (size > 10) { - return StringPrintf("IntElement(array of size %d, %s)", size, - expr_->DebugString().c_str()); + return absl::StrFormat("IntElement(array of size %d, %s)", size, + expr_->DebugString()); } else { - return StringPrintf("IntElement(%s, %s)", - absl::StrJoin(values_, ", ").c_str(), - expr_->DebugString().c_str()); + return absl::StrFormat("IntElement(%s, %s)", absl::StrJoin(values_, ", "), + expr_->DebugString()); } } @@ -339,7 +336,7 @@ class IntExprElement : public BaseIntExprElement { DCHECK_LT(index, values_.size()); return values_[index]; } - int64 ExprMin() const override { return std::max(0LL, expr_->Min()); } + int64 ExprMin() const override { return std::max(int64{0}, expr_->Min()); } int64 ExprMax() const override { return std::min(static_cast(values_.size()) - 1, expr_->Max()); } @@ -384,7 +381,7 @@ class RangeMinimumQueryExprElement : public BaseIntExpr { } private: - int64 IndexMin() const { return std::max(0LL, index_->Min()); } + int64 IndexMin() const { return std::max(int64{0}, index_->Min()); } int64 IndexMax() const { return std::min(static_cast(min_rmq_.array().size()) - 1, index_->Max()); @@ -469,14 +466,12 @@ class IncreasingIntExprElement : public BaseIntExpr { bool Bound() const override { return (index_->Bound()); } // TODO(user) : improve me, the previous test is not always true std::string name() const override { - return StringPrintf("IntElement(%s, %s)", - absl::StrJoin(values_, ", ").c_str(), - index_->name().c_str()); + return absl::StrFormat("IntElement(%s, %s)", absl::StrJoin(values_, ", "), + index_->name()); } std::string DebugString() const override { - return StringPrintf("IntElement(%s, %s)", - absl::StrJoin(values_, ", ").c_str(), - index_->DebugString().c_str()); + return absl::StrFormat("IntElement(%s, %s)", absl::StrJoin(values_, ", "), + index_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -509,13 +504,13 @@ IncreasingIntExprElement::IncreasingIntExprElement( } int64 IncreasingIntExprElement::Min() const { - const int64 expression_min = std::max(0LL, index_->Min()); + const int64 expression_min = std::max(int64{0}, index_->Min()); return (expression_min < values_.size() ? values_[expression_min] : kint64max); } void IncreasingIntExprElement::SetMin(int64 m) { - const int64 index_min = std::max(0LL, index_->Min()); + const int64 index_min = std::max(int64{0}, index_->Min()); const int64 index_max = std::min(static_cast(values_.size()) - 1LL, index_->Max()); @@ -536,7 +531,7 @@ int64 IncreasingIntExprElement::Max() const { } void IncreasingIntExprElement::SetMax(int64 m) { - int64 index_min = std::max(0LL, index_->Min()); + int64 index_min = std::max(int64{0}, index_->Min()); if (m < values_[index_min]) { solver()->Fail(); } @@ -551,7 +546,7 @@ void IncreasingIntExprElement::SetRange(int64 mi, int64 ma) { if (mi > ma) { solver()->Fail(); } - const int64 index_min = std::max(0LL, index_->Min()); + const int64 index_min = std::max(int64{0}, index_->Min()); const int64 index_max = std::min(static_cast(values_.size()) - 1LL, index_->Max()); @@ -681,11 +676,11 @@ class IntExprFunctionElement : public BaseIntExprElement { ~IntExprFunctionElement() override; std::string name() const override { - return StringPrintf("IntFunctionElement(%s)", expr_->name().c_str()); + return absl::StrFormat("IntFunctionElement(%s)", expr_->name()); } std::string DebugString() const override { - return StringPrintf("IntFunctionElement(%s)", expr_->DebugString().c_str()); + return absl::StrFormat("IntFunctionElement(%s)", expr_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -693,12 +688,7 @@ class IntExprFunctionElement : public BaseIntExprElement { visitor->BeginVisitIntegerExpression(ModelVisitor::kElement, this); visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndexArgument, expr_); - if (expr_->Min() == 0) { - visitor->VisitInt64ToInt64AsArray(values_, ModelVisitor::kValuesArgument, - expr_->Max()); - } else { - visitor->VisitInt64ToInt64Extension(values_, expr_->Min(), expr_->Max()); - } + visitor->VisitInt64ToInt64Extension(values_, expr_->Min(), expr_->Max()); visitor->EndVisitIntegerExpression(ModelVisitor::kElement, this); } @@ -779,13 +769,13 @@ class IncreasingIntExprFunctionElement : public BaseIntExpr { } std::string name() const override { - return StringPrintf("IncreasingIntExprFunctionElement(values, %s)", - index_->name().c_str()); + return absl::StrFormat("IncreasingIntExprFunctionElement(values, %s)", + index_->name()); } std::string DebugString() const override { - return StringPrintf("IncreasingIntExprFunctionElement(values, %s)", - index_->DebugString().c_str()); + return absl::StrFormat("IncreasingIntExprFunctionElement(values, %s)", + index_->DebugString()); } void WhenRange(Demon* d) override { index_->WhenRange(d); } @@ -894,9 +884,8 @@ class IntIntExprFunctionElement : public BaseIntExpr { IntVar* const expr1, IntVar* const expr2); ~IntIntExprFunctionElement() override; std::string DebugString() const override { - return StringPrintf("IntIntFunctionElement(%s,%s)", - expr1_->DebugString().c_str(), - expr2_->DebugString().c_str()); + return absl::StrFormat("IntIntFunctionElement(%s,%s)", + expr1_->DebugString(), expr2_->DebugString()); } int64 Min() const override; int64 Max() const override; @@ -1179,10 +1168,9 @@ class IfThenElseCt : public CastConstraint { } std::string DebugString() const override { - return StringPrintf( - "(%s ? %s : %s) == %s", condition_->DebugString().c_str(), - one_->DebugString().c_str(), zero_->DebugString().c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("(%s ? %s : %s) == %s", condition_->DebugString(), + one_->DebugString(), zero_->DebugString(), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override {} @@ -1335,8 +1323,8 @@ std::string StringifyEvaluatorBare(const Solver::Int64ToIntVar& evaluator, if (i != range_start) { out += ", "; } - out += StringPrintf("%" GG_LL_FORMAT "d -> %s", i, - evaluator(i)->DebugString().c_str()); + out += absl::StrFormat("%" GG_LL_FORMAT "d -> %s", i, + evaluator(i)->DebugString()); } return out; } @@ -1345,14 +1333,14 @@ std::string StringifyInt64ToIntVar(const Solver::Int64ToIntVar& evaluator, int64 range_begin, int64 range_end) { std::string out; if (range_end - range_begin > 10) { - out = StringPrintf( + out = absl::StrFormat( "IntToIntVar(%s, ...%s)", - StringifyEvaluatorBare(evaluator, range_begin, range_begin + 5).c_str(), - StringifyEvaluatorBare(evaluator, range_end - 5, range_end).c_str()); + StringifyEvaluatorBare(evaluator, range_begin, range_begin + 5), + StringifyEvaluatorBare(evaluator, range_end - 5, range_end)); } else { - out = StringPrintf( + out = absl::StrFormat( "IntToIntVar(%s)", - StringifyEvaluatorBare(evaluator, range_begin, range_end).c_str()); + StringifyEvaluatorBare(evaluator, range_begin, range_end)); } return out; } @@ -1393,22 +1381,21 @@ IntExprArrayElementCt::IntExprArrayElementCt(Solver* const s, std::vector vars, IntVar* const index, IntVar* const target_var) - : IntExprEvaluatorElementCt(s, [this](int64 idx) { return vars_[idx]; }, 0, - vars.size(), index, target_var), + : IntExprEvaluatorElementCt( + s, [this](int64 idx) { return vars_[idx]; }, 0, vars.size(), index, + target_var), vars_(std::move(vars)) {} std::string IntExprArrayElementCt::DebugString() const { int64 size = vars_.size(); if (size > 10) { - return StringPrintf("IntExprArrayElement(var array of size %" GG_LL_FORMAT - "d, %s) == %s", - size, index_->DebugString().c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat( + "IntExprArrayElement(var array of size %" GG_LL_FORMAT "d, %s) == %s", + size, index_->DebugString(), target_var_->DebugString()); } else { - return StringPrintf("IntExprArrayElement([%s], %s) == %s", - JoinDebugStringPtr(vars_, ", ").c_str(), - index_->DebugString().c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("IntExprArrayElement([%s], %s) == %s", + JoinDebugStringPtr(vars_, ", "), + index_->DebugString(), target_var_->DebugString()); } } @@ -1471,9 +1458,9 @@ class IntExprArrayElementCstCt : public Constraint { } std::string DebugString() const override { - return StringPrintf("IntExprArrayElement([%s], %s) == %" GG_LL_FORMAT "d", - JoinDebugStringPtr(vars_, ", ").c_str(), - index_->DebugString().c_str(), target_); + return absl::StrFormat( + "IntExprArrayElement([%s], %s) == %" GG_LL_FORMAT "d", + JoinDebugStringPtr(vars_, ", "), index_->DebugString(), target_); } void Accept(ModelVisitor* const visitor) const override { @@ -1564,9 +1551,9 @@ class IntExprIndexOfCt : public Constraint { } std::string DebugString() const override { - return StringPrintf("IntExprIndexOf([%s], %s) == %" GG_LL_FORMAT "d", - JoinDebugStringPtr(vars_, ", ").c_str(), - index_->DebugString().c_str(), target_); + return absl::StrFormat("IntExprIndexOf([%s], %s) == %" GG_LL_FORMAT "d", + JoinDebugStringPtr(vars_, ", "), + index_->DebugString(), target_); } void Accept(ModelVisitor* const visitor) const override { @@ -1637,9 +1624,8 @@ IntExpr* Solver::MakeElement(const std::vector& vars, IntVar* const scaled_index = MakeSum(index, -index->Min())->Var(); IntVar* const zero = vars[index->Min()]; IntVar* const one = vars[index->Max()]; - const std::string name = - StringPrintf("ElementVar([%s], %s)", JoinNamePtr(vars, ", ").c_str(), - index->name().c_str()); + const std::string name = absl::StrFormat( + "ElementVar([%s], %s)", JoinNamePtr(vars, ", "), index->name()); IntVar* const target = MakeIntVar(std::min(zero->Min(), one->Min()), std::max(zero->Max(), one->Max()), name); AddConstraint( @@ -1656,11 +1642,10 @@ IntExpr* Solver::MakeElement(const std::vector& vars, } } const std::string vname = - size > 10 ? StringPrintf("ElementVar(var array of size %d, %s)", size, - index->DebugString().c_str()) - : StringPrintf("ElementVar([%s], %s)", - JoinNamePtr(vars, ", ").c_str(), - index->name().c_str()); + size > 10 ? absl::StrFormat("ElementVar(var array of size %d, %s)", size, + index->DebugString()) + : absl::StrFormat("ElementVar([%s], %s)", + JoinNamePtr(vars, ", "), index->name()); IntVar* const element_var = MakeIntVar(emin, emax, vname); AddConstraint( RevAlloc(new IntExprArrayElementCt(this, vars, index, element_var))); @@ -1671,10 +1656,9 @@ IntExpr* Solver::MakeElement(Int64ToIntVar vars, int64 range_start, int64 range_end, IntVar* argument) { const std::string index_name = !argument->name().empty() ? argument->name() : argument->DebugString(); - const std::string vname = - StringPrintf("ElementVar(%s, %s)", - StringifyInt64ToIntVar(vars, range_start, range_end).c_str(), - index_name.c_str()); + const std::string vname = absl::StrFormat( + "ElementVar(%s, %s)", + StringifyInt64ToIntVar(vars, range_start, range_end), index_name); IntVar* const element_var = MakeIntVar(kint64min, kint64max, vname); IntExprEvaluatorElementCt* evaluation_ct = new IntExprEvaluatorElementCt( this, std::move(vars), range_start, range_end, argument, element_var); @@ -1768,9 +1752,8 @@ IntExpr* Solver::MakeIndexExpression(const std::vector& vars, if (cache != nullptr) { return cache->Var(); } else { - const std::string name = - StringPrintf("Index(%s, %" GG_LL_FORMAT "d)", - JoinNamePtr(vars, ", ").c_str(), value); + const std::string name = absl::StrFormat("Index(%s, %" GG_LL_FORMAT "d)", + JoinNamePtr(vars, ", "), value); IntVar* const index = MakeIntVar(0, vars.size() - 1, name); AddConstraint(MakeIndexOfConstraint(vars, index, value)); model_cache_->InsertVarArrayConstantExpression( diff --git a/ortools/constraint_solver/expr_array.cc b/ortools/constraint_solver/expr_array.cc index 1480f4bbcc5..fc9b6f80d0a 100644 --- a/ortools/constraint_solver/expr_array.cc +++ b/ortools/constraint_solver/expr_array.cc @@ -19,15 +19,13 @@ #include #include +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/mathutil.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" - -#include "ortools/base/join.h" #include "ortools/util/saturated_arithmetic.h" #include "ortools/util/string_array.h" @@ -58,9 +56,9 @@ class TreeArrayConstraint : public CastConstraint { } std::string DebugStringInternal(const std::string& name) const { - return StringPrintf("%s(%s) == %s", name.c_str(), - JoinDebugStringPtr(vars_, ", ").c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("%s(%s) == %s", name, + JoinDebugStringPtr(vars_, ", "), + target_var_->DebugString()); } void AcceptInternal(const std::string& name, @@ -395,9 +393,9 @@ class SmallSumConstraint : public Constraint { } std::string DebugString() const override { - return StringPrintf("SmallSum(%s) == %s", - JoinDebugStringPtr(vars_, ", ").c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("SmallSum(%s) == %s", + JoinDebugStringPtr(vars_, ", "), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -635,7 +633,7 @@ class SafeSumConstraint : public TreeArrayConstraint { // ---------- Min Array ---------- -// This constraint implements std::min(vars) == min_var. +// This constraint implements min(vars) == min_var. class MinConstraint : public TreeArrayConstraint { public: MinConstraint(Solver* const solver, const std::vector& vars, @@ -833,9 +831,9 @@ class SmallMinConstraint : public Constraint { } std::string DebugString() const override { - return StringPrintf("SmallMin(%s) == %s", - JoinDebugStringPtr(vars_, ", ").c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("SmallMin(%s) == %s", + JoinDebugStringPtr(vars_, ", "), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -916,7 +914,7 @@ class SmallMinConstraint : public Constraint { // ---------- Max Array ---------- -// This constraint implements std::max(vars) == max_var. +// This constraint implements max(vars) == max_var. class MaxConstraint : public TreeArrayConstraint { public: MaxConstraint(Solver* const solver, const std::vector& vars, @@ -1113,9 +1111,9 @@ class SmallMaxConstraint : public Constraint { } std::string DebugString() const override { - return StringPrintf("SmallMax(%s) == %s", - JoinDebugStringPtr(vars_, ", ").c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("SmallMax(%s) == %s", + JoinDebugStringPtr(vars_, ", "), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -1286,9 +1284,8 @@ class ArrayBoolAndEq : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("And(%s) == %s", - JoinDebugStringPtr(vars_, ", ").c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("And(%s) == %s", JoinDebugStringPtr(vars_, ", "), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -1417,8 +1414,8 @@ class ArrayBoolOrEq : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("Or(%s) == %s", JoinDebugStringPtr(vars_, ", ").c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("Or(%s) == %s", JoinDebugStringPtr(vars_, ", "), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -1467,8 +1464,7 @@ class BaseSumBooleanConstraint : public Constraint { protected: std::string DebugStringInternal(const std::string& name) const { - return StringPrintf("%s(%s)", name.c_str(), - JoinDebugStringPtr(vars_, ", ").c_str()); + return absl::StrFormat("%s(%s)", name, JoinDebugStringPtr(vars_, ", ")); } const std::vector vars_; @@ -1835,8 +1831,8 @@ class SumBooleanEqualToVar : public BaseSumBooleanConstraint { } std::string DebugString() const override { - return StringPrintf("%s == %s", DebugStringInternal("SumBoolean").c_str(), - sum_var_->DebugString().c_str()); + return absl::StrFormat("%s == %s", DebugStringInternal("SumBoolean"), + sum_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -1989,9 +1985,9 @@ class BooleanScalProdLessConstant : public Constraint { } std::string DebugString() const override { - return StringPrintf("BooleanScalProd([%s], [%s]) <= %" GG_LL_FORMAT "d)", - JoinDebugStringPtr(vars_, ", ").c_str(), - absl::StrJoin(coefs_, ", ").c_str(), upper_bound_); + return absl::StrFormat("BooleanScalProd([%s], [%s]) <= %" GG_LL_FORMAT "d)", + JoinDebugStringPtr(vars_, ", "), + absl::StrJoin(coefs_, ", "), upper_bound_); } void Accept(ModelVisitor* const visitor) const override { @@ -2110,10 +2106,10 @@ class PositiveBooleanScalProdEqVar : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("PositiveBooleanScal([%s], [%s]) == %s", - JoinDebugStringPtr(vars_, ", ").c_str(), - absl::StrJoin(coefs_, ", ").c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("PositiveBooleanScal([%s], [%s]) == %s", + JoinDebugStringPtr(vars_, ", "), + absl::StrJoin(coefs_, ", "), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -2225,9 +2221,9 @@ class PositiveBooleanScalProd : public BaseIntExpr { } std::string DebugString() const override { - return StringPrintf("PositiveBooleanScalProd([%s], [%s])", - JoinDebugStringPtr(vars_, ", ").c_str(), - absl::StrJoin(coefs_, ", ").c_str()); + return absl::StrFormat("PositiveBooleanScalProd([%s], [%s])", + JoinDebugStringPtr(vars_, ", "), + absl::StrJoin(coefs_, ", ")); } void WhenRange(Demon* d) override { @@ -2355,10 +2351,10 @@ class PositiveBooleanScalProdEqCst : public Constraint { } std::string DebugString() const override { - return StringPrintf("PositiveBooleanScalProd([%s], [%s]) == %" GG_LL_FORMAT - "d", - JoinDebugStringPtr(vars_, ", ").c_str(), - absl::StrJoin(coefs_, ", ").c_str(), constant_); + return absl::StrFormat( + "PositiveBooleanScalProd([%s], [%s]) == %" GG_LL_FORMAT "d", + JoinDebugStringPtr(vars_, ", "), absl::StrJoin(coefs_, ", "), + constant_); } void Accept(ModelVisitor* const visitor) const override { @@ -2388,7 +2384,7 @@ class PositiveBooleanScalProdEqCst : public Constraint { class ExprLinearizer : public ModelParser { public: explicit ExprLinearizer( - std::unordered_map* const variables_to_coefficients) + absl::flat_hash_map* const variables_to_coefficients) : variables_to_coefficients_(variables_to_coefficients), constant_(0) {} ~ExprLinearizer() override {} @@ -2650,7 +2646,7 @@ class ExprLinearizer : public ModelParser { // We do need a IntVar* as key, and not const IntVar*, because clients of this // class typically iterate over the map keys and use them as mutable IntVar*. - std::unordered_map* const variables_to_coefficients_; + absl::flat_hash_map* const variables_to_coefficients_; std::vector multipliers_; int64 constant_; }; @@ -2688,7 +2684,7 @@ void DeepLinearize(Solver* const solver, const std::vector& pre_vars, } if (need_linearization) { // Instrospect the variables to simplify the sum. - std::unordered_map variables_to_coefficients; + absl::flat_hash_map variables_to_coefficients; ExprLinearizer linearizer(&variables_to_coefficients); for (int i = 0; i < pre_vars.size(); ++i) { linearizer.Visit(pre_vars[i], pre_coefs[i]); @@ -3062,7 +3058,7 @@ IntExpr* MakeSumArrayAux(Solver* const solver, const std::vector& vars, return solver->MakeSum(cache, constant); } else { const std::string name = - StringPrintf("Sum([%s])", JoinNamePtr(vars, ", ").c_str()); + absl::StrFormat("Sum([%s])", JoinNamePtr(vars, ", ")); IntVar* const sum_var = solver->MakeIntVar(new_min, new_max, name); if (AreAllBooleans(vars)) { solver->AddConstraint( @@ -3212,7 +3208,7 @@ IntExpr* MakeScalProdFct(Solver* solver, const std::vector& pre_vars, } IntExpr* MakeSumFct(Solver* solver, const std::vector& pre_vars) { - std::unordered_map variables_to_coefficients; + absl::flat_hash_map variables_to_coefficients; ExprLinearizer linearizer(&variables_to_coefficients); for (int i = 0; i < pre_vars.size(); ++i) { linearizer.Visit(pre_vars[i], 1); @@ -3260,7 +3256,7 @@ IntExpr* Solver::MakeSum(const std::vector& vars) { const bool all_booleans = AreAllBooleans(vars); if (all_booleans) { const std::string name = - StringPrintf("BooleanSum([%s])", JoinNamePtr(vars, ", ").c_str()); + absl::StrFormat("BooleanSum([%s])", JoinNamePtr(vars, ", ")); sum_expr = MakeIntVar(new_min, new_max, name); AddConstraint( RevAlloc(new SumBooleanEqualToVar(this, vars, sum_expr->Var()))); @@ -3268,7 +3264,7 @@ IntExpr* Solver::MakeSum(const std::vector& vars) { sum_expr = MakeSumFct(this, vars); } else { const std::string name = - StringPrintf("Sum([%s])", JoinNamePtr(vars, ", ").c_str()); + absl::StrFormat("Sum([%s])", JoinNamePtr(vars, ", ")); sum_expr = MakeIntVar(new_min, new_max, name); AddConstraint( RevAlloc(new SafeSumConstraint(this, vars, sum_expr->Var()))); diff --git a/ortools/constraint_solver/expr_cst.cc b/ortools/constraint_solver/expr_cst.cc index d7e57c7e599..aef0716391f 100644 --- a/ortools/constraint_solver/expr_cst.cc +++ b/ortools/constraint_solver/expr_cst.cc @@ -19,12 +19,12 @@ #include #include +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/saturated_arithmetic.h" @@ -77,8 +77,8 @@ void EqualityExprCst::Post() { void EqualityExprCst::InitialPropagate() { expr_->SetValue(value_); } std::string EqualityExprCst::DebugString() const { - return StringPrintf("(%s == %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), value_); + return absl::StrFormat("(%s == %" GG_LL_FORMAT "d)", expr_->DebugString(), + value_); } } // namespace @@ -162,8 +162,8 @@ void GreaterEqExprCst::InitialPropagate() { } std::string GreaterEqExprCst::DebugString() const { - return StringPrintf("(%s >= %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), value_); + return absl::StrFormat("(%s >= %" GG_LL_FORMAT "d)", expr_->DebugString(), + value_); } } // namespace @@ -260,8 +260,8 @@ void LessEqExprCst::InitialPropagate() { } std::string LessEqExprCst::DebugString() const { - return StringPrintf("(%s <= %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), value_); + return absl::StrFormat("(%s <= %" GG_LL_FORMAT "d)", expr_->DebugString(), + value_); } } // namespace @@ -367,8 +367,8 @@ void DiffCst::BoundPropagate() { } std::string DiffCst::DebugString() const { - return StringPrintf("(%s != %" GG_LL_FORMAT "d)", var_->DebugString().c_str(), - value_); + return absl::StrFormat("(%s != %" GG_LL_FORMAT "d)", var_->DebugString(), + value_); } } // namespace @@ -434,9 +434,9 @@ class IsEqualCstCt : public CastConstraint { } } std::string DebugString() const override { - return StringPrintf("IsEqualCstCt(%s, %" GG_LL_FORMAT "d, %s)", - var_->DebugString().c_str(), cst_, - target_var_->DebugString().c_str()); + return absl::StrFormat("IsEqualCstCt(%s, %" GG_LL_FORMAT "d, %s)", + var_->DebugString(), cst_, + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -474,8 +474,8 @@ IntVar* Solver::MakeIsEqualCstVar(IntExpr* const var, int64 value) { if (var->IsVar()) { return var->Var()->IsEqual(value); } else { - IntVar* const boolvar = MakeBoolVar(StringPrintf( - "Is(%s == %" GG_LL_FORMAT "d)", var->DebugString().c_str(), value)); + IntVar* const boolvar = MakeBoolVar(absl::StrFormat( + "Is(%s == %" GG_LL_FORMAT "d)", var->DebugString(), value)); AddConstraint(MakeIsEqualCstCt(var, value, boolvar)); return boolvar; } @@ -553,9 +553,9 @@ class IsDiffCstCt : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("IsDiffCstCt(%s, %" GG_LL_FORMAT "d, %s)", - var_->DebugString().c_str(), cst_, - target_var_->DebugString().c_str()); + return absl::StrFormat("IsDiffCstCt(%s, %" GG_LL_FORMAT "d, %s)", + var_->DebugString(), cst_, + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -652,9 +652,9 @@ class IsGreaterEqualCstCt : public CastConstraint { } } std::string DebugString() const override { - return StringPrintf("IsGreaterEqualCstCt(%s, %" GG_LL_FORMAT "d, %s)", - expr_->DebugString().c_str(), cst_, - target_var_->DebugString().c_str()); + return absl::StrFormat("IsGreaterEqualCstCt(%s, %" GG_LL_FORMAT "d, %s)", + expr_->DebugString(), cst_, + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -684,8 +684,8 @@ IntVar* Solver::MakeIsGreaterOrEqualCstVar(IntExpr* const var, int64 value) { if (var->IsVar()) { return var->Var()->IsGreaterOrEqual(value); } else { - IntVar* const boolvar = MakeBoolVar(StringPrintf( - "Is(%s >= %" GG_LL_FORMAT "d)", var->DebugString().c_str(), value)); + IntVar* const boolvar = MakeBoolVar(absl::StrFormat( + "Is(%s >= %" GG_LL_FORMAT "d)", var->DebugString(), value)); AddConstraint(MakeIsGreaterOrEqualCstCt(var, value, boolvar)); return boolvar; } @@ -752,9 +752,9 @@ class IsLessEqualCstCt : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("IsLessEqualCstCt(%s, %" GG_LL_FORMAT "d, %s)", - expr_->DebugString().c_str(), cst_, - target_var_->DebugString().c_str()); + return absl::StrFormat("IsLessEqualCstCt(%s, %" GG_LL_FORMAT "d, %s)", + expr_->DebugString(), cst_, + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -784,8 +784,8 @@ IntVar* Solver::MakeIsLessOrEqualCstVar(IntExpr* const var, int64 value) { if (var->IsVar()) { return var->Var()->IsLessOrEqual(value); } else { - IntVar* const boolvar = MakeBoolVar(StringPrintf( - "Is(%s <= %" GG_LL_FORMAT "d)", var->DebugString().c_str(), value)); + IntVar* const boolvar = MakeBoolVar(absl::StrFormat( + "Is(%s <= %" GG_LL_FORMAT "d)", var->DebugString(), value)); AddConstraint(MakeIsLessOrEqualCstCt(var, value, boolvar)); return boolvar; } @@ -842,8 +842,9 @@ class BetweenCt : public Constraint { } std::string DebugString() const override { - return StringPrintf("BetweenCt(%s, %" GG_LL_FORMAT "d, %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), min_, max_); + return absl::StrFormat("BetweenCt(%s, %" GG_LL_FORMAT "d, %" GG_LL_FORMAT + "d)", + expr_->DebugString(), min_, max_); } void Accept(ModelVisitor* const visitor) const override { @@ -890,9 +891,9 @@ class NotBetweenCt : public Constraint { } std::string DebugString() const override { - return StringPrintf("NotBetweenCt(%s, %" GG_LL_FORMAT "d, %" GG_LL_FORMAT - "d)", - expr_->DebugString().c_str(), min_, max_); + return absl::StrFormat("NotBetweenCt(%s, %" GG_LL_FORMAT "d, %" GG_LL_FORMAT + "d)", + expr_->DebugString(), min_, max_); } void Accept(ModelVisitor* const visitor) const override { @@ -1023,10 +1024,9 @@ class IsBetweenCt : public Constraint { } std::string DebugString() const override { - return StringPrintf("IsBetweenCt(%s, %" GG_LL_FORMAT "d, %" GG_LL_FORMAT - "d, %s)", - expr_->DebugString().c_str(), min_, max_, - boolvar_->DebugString().c_str()); + return absl::StrFormat( + "IsBetweenCt(%s, %" GG_LL_FORMAT "d, %" GG_LL_FORMAT "d, %s)", + expr_->DebugString(), min_, max_, boolvar_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -1112,8 +1112,8 @@ class MemberCt : public Constraint { void InitialPropagate() override { var_->SetValues(values_); } std::string DebugString() const override { - return StringPrintf("Member(%s, %s)", var_->DebugString().c_str(), - absl::StrJoin(values_, ", ").c_str()); + return absl::StrFormat("Member(%s, %s)", var_->DebugString(), + absl::StrJoin(values_, ", ")); } void Accept(ModelVisitor* const visitor) const override { @@ -1143,8 +1143,8 @@ class NotMemberCt : public Constraint { void InitialPropagate() override { var_->RemoveValues(values_); } std::string DebugString() const override { - return StringPrintf("NotMember(%s, %s)", var_->DebugString().c_str(), - absl::StrJoin(values_, ", ").c_str()); + return absl::StrFormat("NotMember(%s, %s)", var_->DebugString(), + absl::StrJoin(values_, ", ")); } void Accept(ModelVisitor* const visitor) const override { @@ -1345,9 +1345,9 @@ class IsMemberCt : public Constraint { } std::string DebugString() const override { - return StringPrintf("IsMemberCt(%s, %s, %s)", var_->DebugString().c_str(), - absl::StrJoin(values_, ", ").c_str(), - boolvar_->DebugString().c_str()); + return absl::StrFormat("IsMemberCt(%s, %s, %s)", var_->DebugString(), + absl::StrJoin(values_, ", "), + boolvar_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -1411,7 +1411,7 @@ class IsMemberCt : public Constraint { } IntVar* const var_; - std::unordered_set values_as_set_; + absl::flat_hash_set values_as_set_; std::vector values_; IntVar* const boolvar_; int support_; @@ -1547,9 +1547,8 @@ class SortedDisjointForbiddenIntervalsConstraint : public Constraint { } std::string DebugString() const override { - return StringPrintf("ForbiddenIntervalCt(%s, %s)", - var_->DebugString().c_str(), - intervals_.DebugString().c_str()); + return absl::StrFormat("ForbiddenIntervalCt(%s, %s)", var_->DebugString(), + intervals_.DebugString()); } void Accept(ModelVisitor* const visitor) const override { diff --git a/ortools/constraint_solver/expressions.cc b/ortools/constraint_solver/expressions.cc index 4a03a56d17e..a84507462ea 100644 --- a/ortools/constraint_solver/expressions.cc +++ b/ortools/constraint_solver/expressions.cc @@ -15,18 +15,18 @@ #include #include #include -#include #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" #include "ortools/base/mathutil.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/bitset.h" @@ -290,7 +290,7 @@ class DomainIntVar : public IntVar { return Solver::VAR_PRIORITY; } std::string DebugString() const override { - return StringPrintf("Handler(%s)", var_->DebugString().c_str()); + return absl::StrFormat("Handler(%s)", var_->DebugString()); } private: @@ -450,8 +450,8 @@ class DomainIntVar : public IntVar { const std::string vname = variable_->HasName() ? variable_->name() : variable_->DebugString(); - const std::string bname = StringPrintf( - "Watch<%s == %" GG_LL_FORMAT "d>", vname.c_str(), value); + const std::string bname = + absl::StrFormat("Watch<%s == %" GG_LL_FORMAT "d>", vname, value); IntVar* const boolvar = solver()->MakeBoolVar(bname); watchers_.UnsafeRevInsert(value, boolvar); if (posted_.Switched()) { @@ -615,7 +615,7 @@ class DomainIntVar : public IntVar { } std::string DebugString() const override { - return StringPrintf("ValueWatcher(%s)", variable_->DebugString().c_str()); + return absl::StrFormat("ValueWatcher(%s)", variable_->DebugString()); } private: @@ -684,8 +684,8 @@ class DomainIntVar : public IntVar { const std::string vname = variable_->HasName() ? variable_->name() : variable_->DebugString(); - const std::string bname = StringPrintf( - "Watch<%s == %" GG_LL_FORMAT "d>", vname.c_str(), value); + const std::string bname = + absl::StrFormat("Watch<%s == %" GG_LL_FORMAT "d>", vname, value); IntVar* const boolvar = solver()->MakeBoolVar(bname); RevInsert(index, boolvar); if (posted_.Switched()) { @@ -858,8 +858,7 @@ class DomainIntVar : public IntVar { } std::string DebugString() const override { - return StringPrintf("DenseValueWatcher(%s)", - variable_->DebugString().c_str()); + return absl::StrFormat("DenseValueWatcher(%s)", variable_->DebugString()); } private: @@ -940,8 +939,8 @@ class DomainIntVar : public IntVar { const std::string vname = variable_->HasName() ? variable_->name() : variable_->DebugString(); - const std::string bname = StringPrintf( - "Watch<%s >= %" GG_LL_FORMAT "d>", vname.c_str(), value); + const std::string bname = + absl::StrFormat("Watch<%s >= %" GG_LL_FORMAT "d>", vname, value); IntVar* const boolvar = solver()->MakeBoolVar(bname); watchers_.UnsafeRevInsert(value, boolvar); if (posted_.Switched()) { @@ -1063,8 +1062,7 @@ class DomainIntVar : public IntVar { } std::string DebugString() const override { - return StringPrintf("UpperBoundWatcher(%s)", - variable_->DebugString().c_str()); + return absl::StrFormat("UpperBoundWatcher(%s)", variable_->DebugString()); } private: @@ -1181,8 +1179,8 @@ class DomainIntVar : public IntVar { const std::string vname = variable_->HasName() ? variable_->name() : variable_->DebugString(); - const std::string bname = StringPrintf( - "Watch<%s >= %" GG_LL_FORMAT "d>", vname.c_str(), value); + const std::string bname = + absl::StrFormat("Watch<%s >= %" GG_LL_FORMAT "d>", vname, value); IntVar* const boolvar = solver()->MakeBoolVar(bname); RevInsert(value - offset_, boolvar); if (posted_.Switched()) { @@ -1311,8 +1309,8 @@ class DomainIntVar : public IntVar { } std::string DebugString() const override { - return StringPrintf("DenseUpperBoundWatcher(%s)", - variable_->DebugString().c_str()); + return absl::StrFormat("DenseUpperBoundWatcher(%s)", + variable_->DebugString()); } private: @@ -1727,11 +1725,11 @@ class SimpleBitSet : public DomainIntVar::BitSet { std::string DebugString() const override { std::string out; - SStringPrintf( + absl::StrAppendFormat( &out, "SimpleBitSet(%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d : ", omin_, omax_); for (int i = 0; i < bsize_; ++i) { - StringAppendF(&out, "%llx", bits_[i]); + absl::StrAppendFormat(&out, "%x", bits_[i]); } out += ")"; return out; @@ -1765,13 +1763,15 @@ class SimpleBitSet : public DomainIntVar::BitSet { } else { if (cumul) { if (v == start_cumul + 1) { - StringAppendF(&out, "%" GG_LL_FORMAT "d ", start_cumul); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d ", start_cumul); } else if (v == start_cumul + 2) { - StringAppendF(&out, "%" GG_LL_FORMAT "d %" GG_LL_FORMAT "d ", - start_cumul, v - 1); + absl::StrAppendFormat(&out, + "%" GG_LL_FORMAT "d %" GG_LL_FORMAT "d ", + start_cumul, v - 1); } else { - StringAppendF(&out, "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d ", - start_cumul, v - 1); + absl::StrAppendFormat(&out, + "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d ", + start_cumul, v - 1); } cumul = false; } @@ -1779,17 +1779,17 @@ class SimpleBitSet : public DomainIntVar::BitSet { } if (cumul) { if (max == start_cumul + 1) { - StringAppendF(&out, "%" GG_LL_FORMAT "d %" GG_LL_FORMAT "d", - start_cumul, max); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d %" GG_LL_FORMAT "d", + start_cumul, max); } else { - StringAppendF(&out, "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d", - start_cumul, max); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d", + start_cumul, max); } } else { - StringAppendF(&out, "%" GG_LL_FORMAT "d", max); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d", max); } } else { - StringAppendF(&out, "%" GG_LL_FORMAT "d", min); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d", min); } return out; } @@ -1943,9 +1943,9 @@ class SmallBitSet : public DomainIntVar::BitSet { uint64 Size() const override { return size_.Value(); } std::string DebugString() const override { - return StringPrintf("SmallBitSet(%" GG_LL_FORMAT "d..%" GG_LL_FORMAT - "d : %llx)", - omin_, omax_, bits_); + return absl::StrFormat("SmallBitSet(%" GG_LL_FORMAT "d..%" GG_LL_FORMAT + "d : %llx)", + omin_, omax_, bits_); } void DelayRemoveValue(int64 val) override { @@ -1980,13 +1980,15 @@ class SmallBitSet : public DomainIntVar::BitSet { } else { if (cumul) { if (v == start_cumul + 1) { - StringAppendF(&out, "%" GG_LL_FORMAT "d ", start_cumul); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d ", start_cumul); } else if (v == start_cumul + 2) { - StringAppendF(&out, "%" GG_LL_FORMAT "d %" GG_LL_FORMAT "d ", - start_cumul, v - 1); + absl::StrAppendFormat(&out, + "%" GG_LL_FORMAT "d %" GG_LL_FORMAT "d ", + start_cumul, v - 1); } else { - StringAppendF(&out, "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d ", - start_cumul, v - 1); + absl::StrAppendFormat(&out, + "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d ", + start_cumul, v - 1); } cumul = false; } @@ -1994,17 +1996,17 @@ class SmallBitSet : public DomainIntVar::BitSet { } if (cumul) { if (max == start_cumul + 1) { - StringAppendF(&out, "%" GG_LL_FORMAT "d %" GG_LL_FORMAT "d", - start_cumul, max); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d %" GG_LL_FORMAT "d", + start_cumul, max); } else { - StringAppendF(&out, "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d", - start_cumul, max); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d", + start_cumul, max); } } else { - StringAppendF(&out, "%" GG_LL_FORMAT "d", max); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d", max); } } else { - StringAppendF(&out, "%" GG_LL_FORMAT "d", min); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d", min); } return out; } @@ -2491,12 +2493,12 @@ std::string DomainIntVar::DebugString() const { out = "DomainIntVar("; } if (min_.Value() == max_.Value()) { - StringAppendF(&out, "%" GG_LL_FORMAT "d", min_.Value()); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d", min_.Value()); } else if (bits_ != nullptr) { out.append(bits_->pretty_DebugString(min_.Value(), max_.Value())); } else { - StringAppendF(&out, "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d", min_.Value(), - max_.Value()); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d", + min_.Value(), max_.Value()); } out += ")"; return out; @@ -2520,7 +2522,7 @@ class ConcreteBooleanVar : public BooleanVar { return Solver::VAR_PRIORITY; } std::string DebugString() const override { - return StringPrintf("Handler(%s)", var_->DebugString().c_str()); + return absl::StrFormat("Handler(%s)", var_->DebugString()); } private: @@ -2622,9 +2624,9 @@ class IntConst : public IntVar { std::string out; if (solver()->HasName(this)) { const std::string& var_name = name(); - SStringPrintf(&out, "%s(%" GG_LL_FORMAT "d)", var_name.c_str(), value_); + absl::StrAppendFormat(&out, "%s(%" GG_LL_FORMAT "d)", var_name, value_); } else { - SStringPrintf(&out, "IntConst(%" GG_LL_FORMAT "d)", value_); + absl::StrAppendFormat(&out, "IntConst(%" GG_LL_FORMAT "d)", value_); } return out; } @@ -2688,11 +2690,11 @@ class PlusCstVar : public IntVar { std::string DebugString() const override { if (HasName()) { - return StringPrintf("%s(%s + %" GG_LL_FORMAT "d)", name().c_str(), - var_->DebugString().c_str(), cst_); + return absl::StrFormat("%s(%s + %" GG_LL_FORMAT "d)", name(), + var_->DebugString(), cst_); } else { - return StringPrintf("(%s + %" GG_LL_FORMAT "d)", - var_->DebugString().c_str(), cst_); + return absl::StrFormat("(%s + %" GG_LL_FORMAT "d)", var_->DebugString(), + cst_); } } @@ -3001,10 +3003,10 @@ bool SubCstIntVar::Contains(int64 v) const { return var_->Contains(cst_ - v); } std::string SubCstIntVar::DebugString() const { if (cst_ == 1 && var_->VarType() == BOOLEAN_VAR) { - return StringPrintf("Not(%s)", var_->DebugString().c_str()); + return absl::StrFormat("Not(%s)", var_->DebugString()); } else { - return StringPrintf("(%" GG_LL_FORMAT "d - %s)", cst_, - var_->DebugString().c_str()); + return absl::StrFormat("(%" GG_LL_FORMAT "d - %s)", cst_, + var_->DebugString()); } } @@ -3012,10 +3014,9 @@ std::string SubCstIntVar::name() const { if (solver()->HasName(this)) { return PropagationBaseObject::name(); } else if (cst_ == 1 && var_->VarType() == BOOLEAN_VAR) { - return StringPrintf("Not(%s)", var_->name().c_str()); + return absl::StrFormat("Not(%s)", var_->name()); } else { - return StringPrintf("(%" GG_LL_FORMAT "d - %s)", cst_, - var_->name().c_str()); + return absl::StrFormat("(%" GG_LL_FORMAT "d - %s)", cst_, var_->name()); } } @@ -3127,7 +3128,7 @@ uint64 OppIntVar::Size() const { return var_->Size(); } bool OppIntVar::Contains(int64 v) const { return var_->Contains(-v); } std::string OppIntVar::DebugString() const { - return StringPrintf("-(%s)", var_->DebugString().c_str()); + return absl::StrFormat("-(%s)", var_->DebugString()); } // ----- Utility functions ----- @@ -3181,8 +3182,8 @@ class TimesCstIntVar : public IntVar { } std::string DebugString() const override { - return StringPrintf("(%s * %" GG_LL_FORMAT "d)", - var_->DebugString().c_str(), cst_); + return absl::StrFormat("(%s * %" GG_LL_FORMAT "d)", var_->DebugString(), + cst_); } int VarType() const override { return VAR_TIMES_CST; } @@ -3613,13 +3614,12 @@ class PlusIntExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf("(%s + %s)", left_->name().c_str(), - right_->name().c_str()); + return absl::StrFormat("(%s + %s)", left_->name(), right_->name()); } std::string DebugString() const override { - return StringPrintf("(%s + %s)", left_->DebugString().c_str(), - right_->DebugString().c_str()); + return absl::StrFormat("(%s + %s)", left_->DebugString(), + right_->DebugString()); } void WhenRange(Demon* d) override { @@ -3706,13 +3706,12 @@ class SafePlusIntExpr : public BaseIntExpr { bool Bound() const override { return (left_->Bound() && right_->Bound()); } std::string name() const override { - return StringPrintf("(%s + %s)", left_->name().c_str(), - right_->name().c_str()); + return absl::StrFormat("(%s + %s)", left_->name(), right_->name()); } std::string DebugString() const override { - return StringPrintf("(%s + %s)", left_->DebugString().c_str(), - right_->DebugString().c_str()); + return absl::StrFormat("(%s + %s)", left_->DebugString(), + right_->DebugString()); } void WhenRange(Demon* d) override { @@ -3746,12 +3745,11 @@ class PlusIntCstExpr : public BaseIntExpr { void SetMax(int64 m) override { expr_->SetMax(CapSub(m, value_)); } bool Bound() const override { return (expr_->Bound()); } std::string name() const override { - return StringPrintf("(%s + %" GG_LL_FORMAT "d)", expr_->name().c_str(), - value_); + return absl::StrFormat("(%s + %" GG_LL_FORMAT "d)", expr_->name(), value_); } std::string DebugString() const override { - return StringPrintf("(%s + %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), value_); + return absl::StrFormat("(%s + %" GG_LL_FORMAT "d)", expr_->DebugString(), + value_); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } IntVar* CastToVar() override; @@ -3835,13 +3833,12 @@ class SubIntExpr : public BaseIntExpr { bool Bound() const override { return (left_->Bound() && right_->Bound()); } std::string name() const override { - return StringPrintf("(%s - %s)", left_->name().c_str(), - right_->name().c_str()); + return absl::StrFormat("(%s - %s)", left_->name(), right_->name()); } std::string DebugString() const override { - return StringPrintf("(%s - %s)", left_->DebugString().c_str(), - right_->DebugString().c_str()); + return absl::StrFormat("(%s - %s)", left_->DebugString(), + right_->DebugString()); } void WhenRange(Demon* d) override { @@ -3922,12 +3919,11 @@ class SubIntCstExpr : public BaseIntExpr { void SetMax(int64 m) override { expr_->SetMin(CapSub(value_, m)); } bool Bound() const override { return (expr_->Bound()); } std::string name() const override { - return StringPrintf("(%" GG_LL_FORMAT "d - %s)", value_, - expr_->name().c_str()); + return absl::StrFormat("(%" GG_LL_FORMAT "d - %s)", value_, expr_->name()); } std::string DebugString() const override { - return StringPrintf("(%" GG_LL_FORMAT "d - %s)", value_, - expr_->DebugString().c_str()); + return absl::StrFormat("(%" GG_LL_FORMAT "d - %s)", value_, + expr_->DebugString()); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } IntVar* CastToVar() override; @@ -3968,10 +3964,10 @@ class OppIntExpr : public BaseIntExpr { void SetMax(int64 m) override { expr_->SetMin(-m); } bool Bound() const override { return (expr_->Bound()); } std::string name() const override { - return StringPrintf("(-%s)", expr_->name().c_str()); + return absl::StrFormat("(-%s)", expr_->name()); } std::string DebugString() const override { - return StringPrintf("(-%s)", expr_->DebugString().c_str()); + return absl::StrFormat("(-%s)", expr_->DebugString()); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } IntVar* CastToVar() override; @@ -4006,13 +4002,12 @@ class TimesIntCstExpr : public BaseIntExpr { bool Bound() const override { return (expr_->Bound()); } std::string name() const override { - return StringPrintf("(%s * %" GG_LL_FORMAT "d)", expr_->name().c_str(), - value_); + return absl::StrFormat("(%s * %" GG_LL_FORMAT "d)", expr_->name(), value_); } std::string DebugString() const override { - return StringPrintf("(%s * %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), value_); + return absl::StrFormat("(%s * %" GG_LL_FORMAT "d)", expr_->DebugString(), + value_); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } @@ -4300,12 +4295,11 @@ class TimesIntExpr : public BaseIntExpr { void SetMax(int64 m) override; bool Bound() const override; std::string name() const override { - return StringPrintf("(%s * %s)", left_->name().c_str(), - right_->name().c_str()); + return absl::StrFormat("(%s * %s)", left_->name(), right_->name()); } std::string DebugString() const override { - return StringPrintf("(%s * %s)", left_->DebugString().c_str(), - right_->DebugString().c_str()); + return absl::StrFormat("(%s * %s)", left_->DebugString(), + right_->DebugString()); } void WhenRange(Demon* d) override { left_->WhenRange(d); @@ -4359,12 +4353,11 @@ class TimesPosIntExpr : public BaseIntExpr { void SetMax(int64 m) override; bool Bound() const override; std::string name() const override { - return StringPrintf("(%s * %s)", left_->name().c_str(), - right_->name().c_str()); + return absl::StrFormat("(%s * %s)", left_->name(), right_->name()); } std::string DebugString() const override { - return StringPrintf("(%s * %s)", left_->DebugString().c_str(), - right_->DebugString().c_str()); + return absl::StrFormat("(%s * %s)", left_->DebugString(), + right_->DebugString()); } void WhenRange(Demon* d) override { left_->WhenRange(d); @@ -4417,12 +4410,11 @@ class SafeTimesPosIntExpr : public BaseIntExpr { (left_->Bound() && right_->Bound())); } std::string name() const override { - return StringPrintf("(%s * %s)", left_->name().c_str(), - right_->name().c_str()); + return absl::StrFormat("(%s * %s)", left_->name(), right_->name()); } std::string DebugString() const override { - return StringPrintf("(%s * %s)", left_->DebugString().c_str(), - right_->DebugString().c_str()); + return absl::StrFormat("(%s * %s)", left_->DebugString(), + right_->DebugString()); } void WhenRange(Demon* d) override { left_->WhenRange(d); @@ -4461,12 +4453,11 @@ class TimesBooleanPosIntExpr : public BaseIntExpr { void SetRange(int64 mi, int64 ma) override; bool Bound() const override; std::string name() const override { - return StringPrintf("(%s * %s)", boolvar_->name().c_str(), - expr_->name().c_str()); + return absl::StrFormat("(%s * %s)", boolvar_->name(), expr_->name()); } std::string DebugString() const override { - return StringPrintf("(%s * %s)", boolvar_->DebugString().c_str(), - expr_->DebugString().c_str()); + return absl::StrFormat("(%s * %s)", boolvar_->DebugString(), + expr_->DebugString()); } void WhenRange(Demon* d) override { boolvar_->WhenRange(d); @@ -4558,7 +4549,7 @@ class TimesBooleanIntExpr : public BaseIntExpr { } default: { DCHECK_EQ(BooleanVar::kUnboundBooleanVarValue, boolvar_->RawValue()); - return std::min(0LL, expr_->Min()); + return std::min(int64{0}, expr_->Min()); } } } @@ -4573,7 +4564,7 @@ class TimesBooleanIntExpr : public BaseIntExpr { } default: { DCHECK_EQ(BooleanVar::kUnboundBooleanVarValue, boolvar_->RawValue()); - return std::max(0LL, expr_->Max()); + return std::max(int64{0}, expr_->Max()); } } } @@ -4582,12 +4573,11 @@ class TimesBooleanIntExpr : public BaseIntExpr { void SetRange(int64 mi, int64 ma) override; bool Bound() const override; std::string name() const override { - return StringPrintf("(%s * %s)", boolvar_->name().c_str(), - expr_->name().c_str()); + return absl::StrFormat("(%s * %s)", boolvar_->name(), expr_->name()); } std::string DebugString() const override { - return StringPrintf("(%s * %s)", boolvar_->DebugString().c_str(), - expr_->DebugString().c_str()); + return absl::StrFormat("(%s * %s)", boolvar_->DebugString(), + expr_->DebugString()); } void WhenRange(Demon* d) override { boolvar_->WhenRange(d); @@ -4670,8 +4660,8 @@ void TimesBooleanIntExpr::Range(int64* mi, int64* ma) { } default: { DCHECK_EQ(BooleanVar::kUnboundBooleanVarValue, boolvar_->RawValue()); - *mi = std::min(0LL, expr_->Min()); - *ma = std::max(0LL, expr_->Max()); + *mi = std::min(int64{0}, expr_->Min()); + *ma = std::max(int64{0}, expr_->Max()); break; } } @@ -4748,13 +4738,13 @@ class DivPosIntCstExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf("(%s div %" GG_LL_FORMAT "d)", expr_->name().c_str(), - value_); + return absl::StrFormat("(%s div %" GG_LL_FORMAT "d)", expr_->name(), + value_); } std::string DebugString() const override { - return StringPrintf("(%s div %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), value_); + return absl::StrFormat("(%s div %" GG_LL_FORMAT "d)", expr_->DebugString(), + value_); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } @@ -4824,12 +4814,11 @@ class DivPosIntExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf("(%s div %s)", num_->name().c_str(), - denom_->name().c_str()); + return absl::StrFormat("(%s div %s)", num_->name(), denom_->name()); } std::string DebugString() const override { - return StringPrintf("(%s div %s)", num_->DebugString().c_str(), - denom_->DebugString().c_str()); + return absl::StrFormat("(%s div %s)", num_->DebugString(), + denom_->DebugString()); } void WhenRange(Demon* d) override { num_->WhenRange(d); @@ -4889,13 +4878,12 @@ class DivPosPosIntExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf("(%s div %s)", num_->name().c_str(), - denom_->name().c_str()); + return absl::StrFormat("(%s div %s)", num_->name(), denom_->name()); } std::string DebugString() const override { - return StringPrintf("(%s div %s)", num_->DebugString().c_str(), - denom_->DebugString().c_str()); + return absl::StrFormat("(%s div %s)", num_->DebugString(), + denom_->DebugString()); } void WhenRange(Demon* d) override { @@ -4928,52 +4916,50 @@ class DivIntExpr : public BaseIntExpr { ~DivIntExpr() override {} - // Due to VS 2015 cl.exe limitation, it is impossible to create locals - // (e.g. denom_min, denom_max) - // Once VS 2015 will be not supported please remove this comment and the - // associated commit. int64 Min() const override { - if (denom_->Min() == 0 && denom_->Max() == 0) { - return kint64max; // TODO(user): Check this convention. - } else if (denom_->Min() >= 0) { // Denominator strictly positive. - DCHECK_GT(denom_->Max(), 0); - const int64 num_min = num_->Min(); - const int64 adjusted_denom_min = (denom_->Min() == 0) ? 1 : denom_->Min(); - return num_min >= 0 ? num_min / denom_->Max() - : num_min / adjusted_denom_min; - } else if (denom_->Max() <= 0) { // Denominator strictly negative. - DCHECK_LT(denom_->Min(), 0); - const int64 num_max = num_->Max(); - const int64 adjusted_denom_max = - (denom_->Max() == 0) ? -1 : denom_->Max(); - return num_max >= 0 ? num_max / adjusted_denom_max - : num_max / denom_->Min(); + const int64 num_min = num_->Min(); + const int64 num_max = num_->Max(); + const int64 denom_min = denom_->Min(); + const int64 denom_max = denom_->Max(); + + if (denom_min == 0 && denom_max == 0) { + return kint64max; // TODO(user): Check this convention. + } + + if (denom_min >= 0) { // Denominator strictly positive. + DCHECK_GT(denom_max, 0); + const int64 adjusted_denom_min = denom_min == 0 ? 1 : denom_min; + return num_min >= 0 ? num_min / denom_max : num_min / adjusted_denom_min; + } else if (denom_max <= 0) { // Denominator strictly negative. + DCHECK_LT(denom_min, 0); + const int64 adjusted_denom_max = denom_max == 0 ? -1 : denom_max; + return num_max >= 0 ? num_max / adjusted_denom_max : num_max / denom_min; } else { // Denominator across 0. - return std::min(num_->Min(), -num_->Max()); + return std::min(num_min, -num_max); } } - // Due to VS 2015 cl.exe limitation, it is impossible to create locals - // (e.g. denom_min, denom_max) - // Once VS 2015 will be not supported please remove this comment and the - // associated commit. int64 Max() const override { - if (denom_->Min() == 0 && denom_->Max() == 0) { - return kint64min; // TODO(user): Check this convention. - } else if (denom_->Min() >= 0) { // Denominator strictly positive. - DCHECK_GT(denom_->Max(), 0); - const int64 num_max = num_->Max(); - const int64 adjusted_denom_min = denom_->Min() == 0 ? 1 : denom_->Min(); - return num_max >= 0 ? num_max / adjusted_denom_min - : num_max / denom_->Max(); - } else if (denom_->Max() <= 0) { // Denominator strictly negative. - DCHECK_LT(denom_->Min(), 0); - const int64 num_min = num_->Min(); - const int64 adjusted_denom_max = denom_->Max() == 0 ? -1 : denom_->Max(); - return num_min >= 0 ? num_min / denom_->Min() + const int64 num_min = num_->Min(); + const int64 num_max = num_->Max(); + const int64 denom_min = denom_->Min(); + const int64 denom_max = denom_->Max(); + + if (denom_min == 0 && denom_max == 0) { + return kint64min; // TODO(user): Check this convention. + } + + if (denom_min >= 0) { // Denominator strictly positive. + DCHECK_GT(denom_max, 0); + const int64 adjusted_denom_min = denom_min == 0 ? 1 : denom_min; + return num_max >= 0 ? num_max / adjusted_denom_min : num_max / denom_max; + } else if (denom_max <= 0) { // Denominator strictly negative. + DCHECK_LT(denom_min, 0); + const int64 adjusted_denom_max = denom_max == 0 ? -1 : denom_max; + return num_min >= 0 ? num_min / denom_min : -num_min / -adjusted_denom_max; } else { // Denominator across 0. - return std::max(num_->Max(), -num_->Min()); + return std::max(num_max, -num_min); } } @@ -5062,12 +5048,11 @@ class DivIntExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf("(%s div %s)", num_->name().c_str(), - denom_->name().c_str()); + return absl::StrFormat("(%s div %s)", num_->name(), denom_->name()); } std::string DebugString() const override { - return StringPrintf("(%s div %s)", num_->DebugString().c_str(), - denom_->DebugString().c_str()); + return absl::StrFormat("(%s div %s)", num_->DebugString(), + denom_->DebugString()); } void WhenRange(Demon* d) override { num_->WhenRange(d); @@ -5137,8 +5122,8 @@ class IntAbsConstraint : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("IntAbsConstraint(%s, %s)", sub_->DebugString().c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("IntAbsConstraint(%s, %s)", sub_->DebugString(), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -5230,11 +5215,11 @@ class IntAbs : public BaseIntExpr { void WhenRange(Demon* d) override { expr_->WhenRange(d); } std::string name() const override { - return StringPrintf("IntAbs(%s)", expr_->name().c_str()); + return absl::StrFormat("IntAbs(%s)", expr_->name()); } std::string DebugString() const override { - return StringPrintf("IntAbs(%s)", expr_->DebugString().c_str()); + return absl::StrFormat("IntAbs(%s)", expr_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -5249,7 +5234,7 @@ class IntAbs : public BaseIntExpr { int64 max_value = 0; Range(&min_value, &max_value); Solver* const s = solver(); - const std::string name = StringPrintf("AbsVar(%s)", expr_->name().c_str()); + const std::string name = absl::StrFormat("AbsVar(%s)", expr_->name()); IntVar* const target = s->MakeIntVar(min_value, max_value, name); CastConstraint* const ct = s->RevAlloc(new IntAbsConstraint(s, expr_->Var(), target)); @@ -5317,10 +5302,10 @@ class IntSquare : public BaseIntExpr { bool Bound() const override { return expr_->Bound(); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } std::string name() const override { - return StringPrintf("IntSquare(%s)", expr_->name().c_str()); + return absl::StrFormat("IntSquare(%s)", expr_->name()); } std::string DebugString() const override { - return StringPrintf("IntSquare(%s)", expr_->DebugString().c_str()); + return absl::StrFormat("IntSquare(%s)", expr_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -5402,13 +5387,13 @@ class BasePower : public BaseIntExpr { void WhenRange(Demon* d) override { expr_->WhenRange(d); } std::string name() const override { - return StringPrintf("IntPower(%s, %" GG_LL_FORMAT "d)", - expr_->name().c_str(), pow_); + return absl::StrFormat("IntPower(%s, %" GG_LL_FORMAT "d)", expr_->name(), + pow_); } std::string DebugString() const override { - return StringPrintf("IntPower(%s, %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), pow_); + return absl::StrFormat("IntPower(%s, %" GG_LL_FORMAT "d)", + expr_->DebugString(), pow_); } void Accept(ModelVisitor* const visitor) const override { @@ -5620,12 +5605,11 @@ class MinIntExpr : public BaseIntExpr { } } std::string name() const override { - return StringPrintf("MinIntExpr(%s, %s)", left_->name().c_str(), - right_->name().c_str()); + return absl::StrFormat("MinIntExpr(%s, %s)", left_->name(), right_->name()); } std::string DebugString() const override { - return StringPrintf("MinIntExpr(%s, %s)", left_->DebugString().c_str(), - right_->DebugString().c_str()); + return absl::StrFormat("MinIntExpr(%s, %s)", left_->DebugString(), + right_->DebugString()); } void WhenRange(Demon* d) override { left_->WhenRange(d); @@ -5676,13 +5660,13 @@ class MinCstIntExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf("MinCstIntExpr(%s, %" GG_LL_FORMAT "d)", - expr_->name().c_str(), value_); + return absl::StrFormat("MinCstIntExpr(%s, %" GG_LL_FORMAT "d)", + expr_->name(), value_); } std::string DebugString() const override { - return StringPrintf("MinCstIntExpr(%s, %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), value_); + return absl::StrFormat("MinCstIntExpr(%s, %" GG_LL_FORMAT "d)", + expr_->DebugString(), value_); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } @@ -5729,13 +5713,12 @@ class MaxIntExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf("MaxIntExpr(%s, %s)", left_->name().c_str(), - right_->name().c_str()); + return absl::StrFormat("MaxIntExpr(%s, %s)", left_->name(), right_->name()); } std::string DebugString() const override { - return StringPrintf("MaxIntExpr(%s, %s)", left_->DebugString().c_str(), - right_->DebugString().c_str()); + return absl::StrFormat("MaxIntExpr(%s, %s)", left_->DebugString(), + right_->DebugString()); } void WhenRange(Demon* d) override { @@ -5787,13 +5770,13 @@ class MaxCstIntExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf("MaxCstIntExpr(%s, %" GG_LL_FORMAT "d)", - expr_->name().c_str(), value_); + return absl::StrFormat("MaxCstIntExpr(%s, %" GG_LL_FORMAT "d)", + expr_->name(), value_); } std::string DebugString() const override { - return StringPrintf("MaxCstIntExpr(%s, %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), value_); + return absl::StrFormat("MaxCstIntExpr(%s, %" GG_LL_FORMAT "d)", + expr_->DebugString(), value_); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } @@ -5900,19 +5883,17 @@ class SimpleConvexPiecewiseExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf("ConvexPiecewiseExpr(%s, ec = %" GG_LL_FORMAT - "d, ed = %" GG_LL_FORMAT "d, ld = %" GG_LL_FORMAT - "d, lc = %" GG_LL_FORMAT "d)", - expr_->name().c_str(), early_cost_, early_date_, - late_date_, late_cost_); + return absl::StrFormat( + "ConvexPiecewiseExpr(%s, ec = %" GG_LL_FORMAT "d, ed = %" GG_LL_FORMAT + "d, ld = %" GG_LL_FORMAT "d, lc = %" GG_LL_FORMAT "d)", + expr_->name(), early_cost_, early_date_, late_date_, late_cost_); } std::string DebugString() const override { - return StringPrintf("ConvexPiecewiseExpr(%s, ec = %" GG_LL_FORMAT - "d, ed = %" GG_LL_FORMAT "d, ld = %" GG_LL_FORMAT - "d, lc = %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), early_cost_, early_date_, - late_date_, late_cost_); + return absl::StrFormat( + "ConvexPiecewiseExpr(%s, ec = %" GG_LL_FORMAT "d, ed = %" GG_LL_FORMAT + "d, ld = %" GG_LL_FORMAT "d, lc = %" GG_LL_FORMAT "d)", + expr_->DebugString(), early_cost_, early_date_, late_date_, late_cost_); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } @@ -5988,15 +5969,15 @@ class SemiContinuousExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf("SemiContinuous(%s, fixed_charge = %" GG_LL_FORMAT - "d, step = %" GG_LL_FORMAT "d)", - expr_->name().c_str(), fixed_charge_, step_); + return absl::StrFormat("SemiContinuous(%s, fixed_charge = %" GG_LL_FORMAT + "d, step = %" GG_LL_FORMAT "d)", + expr_->name(), fixed_charge_, step_); } std::string DebugString() const override { - return StringPrintf("SemiContinuous(%s, fixed_charge = %" GG_LL_FORMAT - "d, step = %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), fixed_charge_, step_); + return absl::StrFormat("SemiContinuous(%s, fixed_charge = %" GG_LL_FORMAT + "d, step = %" GG_LL_FORMAT "d)", + expr_->DebugString(), fixed_charge_, step_); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } @@ -6059,15 +6040,15 @@ class SemiContinuousStepOneExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf( + return absl::StrFormat( "SemiContinuousStepOne(%s, fixed_charge = %" GG_LL_FORMAT "d)", - expr_->name().c_str(), fixed_charge_); + expr_->name(), fixed_charge_); } std::string DebugString() const override { - return StringPrintf( + return absl::StrFormat( "SemiContinuousStepOne(%s, fixed_charge = %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), fixed_charge_); + expr_->DebugString(), fixed_charge_); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } @@ -6127,15 +6108,15 @@ class SemiContinuousStepZeroExpr : public BaseIntExpr { } std::string name() const override { - return StringPrintf( + return absl::StrFormat( "SemiContinuousStepZero(%s, fixed_charge = %" GG_LL_FORMAT "d)", - expr_->name().c_str(), fixed_charge_); + expr_->name(), fixed_charge_); } std::string DebugString() const override { - return StringPrintf( + return absl::StrFormat( "SemiContinuousStepZero(%s, fixed_charge = %" GG_LL_FORMAT "d)", - expr_->DebugString().c_str(), fixed_charge_); + expr_->DebugString(), fixed_charge_); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } @@ -6178,8 +6159,8 @@ class LinkExprAndVar : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("cast(%s, %s)", expr_->DebugString().c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("cast(%s, %s)", expr_->DebugString(), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -6282,9 +6263,9 @@ class ExprWithEscapeValue : public BaseIntExpr { } std::string DebugString() const override { - return StringPrintf("ConditionExpr(%s, %s, %" GG_LL_FORMAT "d)", - condition_->DebugString().c_str(), - expression_->DebugString().c_str(), unperformed_value_); + return absl::StrFormat("ConditionExpr(%s, %s, %" GG_LL_FORMAT "d)", + condition_->DebugString(), + expression_->DebugString(), unperformed_value_); } void Accept(ModelVisitor* const visitor) const override { @@ -6347,8 +6328,8 @@ class LinkExprAndDomainIntVar : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("cast(%s, %s)", expr_->DebugString().c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("cast(%s, %s)", expr_->DebugString(), + target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -6525,7 +6506,7 @@ std::string IndexedName(const std::string& prefix, int index, int max_index) { #else const int digits = max_index > 0 ? static_cast(log10(max_index)) + 1: 1; #endif - return StringPrintf("%s%0*d", prefix.c_str(), digits, index); + return absl::StrFormat("%s%0*d", prefix, digits, index); #else return absl::StrCat(prefix, index); #endif @@ -7241,13 +7222,13 @@ class PiecewiseLinearExpr : public BaseIntExpr { expr_->SetRange(range.first, range.second); } std::string name() const override { - return StringPrintf("PiecewiseLinear(%s, f = %s)", expr_->name().c_str(), - f_.DebugString().c_str()); + return absl::StrFormat("PiecewiseLinear(%s, f = %s)", expr_->name(), + f_.DebugString()); } std::string DebugString() const override { - return StringPrintf("PiecewiseLinear(%s, f = %s)", - expr_->DebugString().c_str(), f_.DebugString().c_str()); + return absl::StrFormat("PiecewiseLinear(%s, f = %s)", expr_->DebugString(), + f_.DebugString()); } void WhenRange(Demon* d) override { expr_->WhenRange(d); } diff --git a/ortools/constraint_solver/gcc.cc b/ortools/constraint_solver/gcc.cc deleted file mode 100644 index 9f13dd2cec0..00000000000 --- a/ortools/constraint_solver/gcc.cc +++ /dev/null @@ -1,731 +0,0 @@ -// Copyright 2011-2012 Claude-Guy Quimper -// 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. - -// This is an implementation of : -// Alejandro López-Ortiz, Claude-Guy Quimper, John Tromp, and Peter -// van Beek. A fast and simple algorithm for bounds consistency of the -// alldifferent constraint. In Proceedings of the 18th International -// Joint Conference on Artificial Intelligence (IJCAI 03), Acapulco, -// Mexico, pages 245-250, 2003. - -#include "ortools/base/int_type.h" -#include "ortools/base/int_type_indexed_vector.h" -#include "ortools/base/integral_types.h" -#include "ortools/base/join.h" -#include "ortools/base/logging.h" -#include "ortools/base/macros.h" -#include "ortools/base/map_util.h" -#include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" -#include "ortools/constraint_solver/constraint_solver.h" -#include "ortools/constraint_solver/constraint_solveri.h" -#include "ortools/util/vector_map.h" - -namespace operations_research { -namespace { -DEFINE_INT_TYPE(Index, int); - -struct Interval { - Interval() : min_value(0), max_value(0), min_rank(0), max_rank(0) {} - int64 min_value; - int64 max_value; // start, end of Interval - int64 min_rank; - int64 max_rank; // rank of min & max in bounds_[] - std::string DebugString() const { - return StringPrintf("Interval(value = [%lld, %lld], rank = [%lld, %lld])", - min_value, max_value, min_rank, max_rank); - } -}; - -struct CompareIntervalMin { - inline bool operator()(Interval* const i1, Interval* const i2) { - return (i1->min_value < i2->min_value); - } -}; - -struct CompareIntervalMax { - inline bool operator()(Interval* const i1, Interval* const i2) { - return (i1->max_value < i2->max_value); - } -}; - -class PartialSum { - public: - // Create a partial sum data structure adapted to the - // filterLower{Min,Max} and filterUpper{Min,Max} functions. - // Two elements before and after the element list will be added - // with a weight of 1. - PartialSum(int64 offset, int64 count, const std::vector& elements) - : offset_(offset - 3), // We add 3 elements at the beginning. - last_value_(offset + count + 1), - sum_(count + 5), - ds_(count + 5) { - sum_[0] = 0; - sum_[1] = 1; - sum_[2] = 2; - for (int64 i = 0; i < count; i++) { - sum_[i + 3] = sum_[i + 2] + elements[i]; - } - sum_[count + 3] = sum_[count + 2] + 1; - sum_[count + 4] = sum_[count + 3] + 1; - - int64 i = count + 3; - int64 j = count + 4; - while (i > 0) { - while (sum_[i] == sum_[i - 1]) { - ds_[i--] = j; - } - ds_[j] = i--; - j = ds_[j]; - } - ds_[j] = 0; - } - - PartialSum(int64 offset, int64 count, const std::vector& elements) - : offset_(offset - 3), - last_value_(offset + count + 1), - sum_(count + 5), - ds_(count + 5) { - sum_[0] = 0; - sum_[1] = 1; - sum_[2] = 2; - for (int64 i = 0; i < count; i++) { - sum_[i + 3] = sum_[i + 2] + elements[i]; - } - sum_[count + 3] = sum_[count + 2] + 1; - sum_[count + 4] = sum_[count + 3] + 1; - - int64 i = count + 3; - int64 j = count + 4; - while (i > 0) { - while (sum_[i] == sum_[i - 1]) { - ds_[i--] = j; - } - ds_[j] = i--; - j = ds_[j]; - } - ds_[j] = 0; - } - - int64 MinValue() const { return offset_ + 3; } - - int64 MaxValue() const { return last_value_ - 2; } - - int64 SkipNonNullElementsRight(int64 value) { - value -= offset_; - return (ds_[value] < value ? value : ds_[value]) + offset_; - } - - int64 SkipNonNullElementsLeft(int64 value) { - value -= offset_; - return (ds_[value] > value ? ds_[ds_[value]] : value) + offset_; - } - - int64 Sum(int64 from, int64 to) const { - if (from <= to) { - DCHECK((offset_ <= from) && (to <= last_value_)); - return sum_[to - offset_] - sum_[from - 1 - offset_]; - } else { - DCHECK((offset_ <= to) && (from <= last_value_)); - return sum_[to - 1 - offset_] - sum_[from - offset_]; - } - } - - int64 offset() const { return offset_; } - - int64 last_value() const { return last_value_; } - - std::string DebugString() const { - return StringPrintf( - "PartialSum(offset=%lld, last_value = %lld, sum = %s, ds = %s)", - offset_, last_value_, absl::StrJoin(sum_, ", ").c_str(), - absl::StrJoin(ds_, ", ").c_str()); - } - - private: - int64 offset_; - int64 last_value_; - std::vector sum_; - std::vector ds_; -}; - -// A value "v" must be assigned to at least -// min_occurrences[v - firstDomainValue] variables and at most -// max_occurrences[v - firstDomainValue] variables -class GccConstraint : public Constraint { - public: - GccConstraint(Solver* const solver, const std::vector& vars, - int64 first_domain_value, int64 number_of_values, - const std::vector& min_occurrences, - const std::vector& max_occurrences) - : Constraint(solver), - variables_(vars.begin(), vars.end()), - size_(vars.size()), - max_occurrences_(number_of_values + 1, 0), - first_domain_value_(first_domain_value), - tree_(2 * size_ + 2), - diffs_(2 * size_ + 2), - hall_(2 * size_ + 2), - stable_intervals_(2 * size_ + 2), - potential_stable_sets_(2 * size_ + 2), - new_min_(size_), - intervals_(size_), - sorted_by_min_(size_), - sorted_by_max_(size_), - bounds_(2 * size_ + 2), - active_size_(0), - lower_sum_(first_domain_value, number_of_values, min_occurrences), - upper_sum_(first_domain_value, number_of_values, max_occurrences) { - for (int64 i = 0; i < number_of_values; i++) { - max_occurrences_.SetValue(solver, i, max_occurrences[i]); - } - - for (Index i(0); i < size_; i++) { - sorted_by_min_[i.value()] = &intervals_[i]; - sorted_by_max_[i.value()] = &intervals_[i]; - } - } - - GccConstraint(Solver* const solver, const std::vector& vars, - int64 first_domain_value, int64 number_of_values, - const std::vector& min_occurrences, - const std::vector& max_occurrences) - : Constraint(solver), - variables_(vars.begin(), vars.end()), - size_(vars.size()), - max_occurrences_(number_of_values, 0), - first_domain_value_(first_domain_value), - tree_(2 * size_ + 2), - diffs_(2 * size_ + 2), - hall_(2 * size_ + 2), - stable_intervals_(2 * size_ + 2), - potential_stable_sets_(2 * size_ + 2), - new_min_(size_), - intervals_(size_), - sorted_by_min_(size_), - sorted_by_max_(size_), - bounds_(2 * size_ + 2), - active_size_(0), - lower_sum_(first_domain_value, number_of_values, min_occurrences), - upper_sum_(first_domain_value, number_of_values, max_occurrences) { - for (int64 i = 0; i < number_of_values; i++) { - max_occurrences_.SetValue(solver, i, max_occurrences[i]); - } - - for (Index i(0); i < size_; i++) { - sorted_by_min_[i.value()] = &intervals_[i]; - sorted_by_max_[i.value()] = &intervals_[i]; - } - } - - virtual ~GccConstraint() {} - - virtual void Post() { - for (Index i(0); i < size_; ++i) { - Demon* const bound_demon = - MakeConstraintDemon1(solver(), this, &GccConstraint::PropagateValue, - "PropagateValue", i.value()); - - variables_[i]->WhenBound(bound_demon); - } - Demon* const demon = MakeDelayedConstraintDemon0( - solver(), this, &GccConstraint::PropagateRange, "PropagateRange"); - for (Index i(0); i < size_; ++i) { - variables_[i]->WhenRange(demon); - } - } - - virtual void InitialPropagate() { - // Sets the range. - for (Index i(0); i < size_; ++i) { - variables_[i]->SetRange( - first_domain_value_, - first_domain_value_ + max_occurrences_.size() - 1); - } - // Removes value with max card = 0; - std::vector to_remove; - for (int64 i = 0; i < max_occurrences_.size(); ++i) { - if (max_occurrences_[i] == 0) { - to_remove.push_back(first_domain_value_ + i); - } - } - if (!to_remove.empty()) { - for (Index i(0); i < size_; ++i) { - variables_[i]->RemoveValues(to_remove); - } - } - PropagateRange(); - } - - void PropagateRange() { - bool has_changed = false; - - for (Index i(0); i < size_; ++i) { - intervals_[i].min_value = variables_[i]->Min(); - intervals_[i].max_value = variables_[i]->Max(); - } - - SortIntervals(); - - // The variable domains must be inside the domain defined by - // the lower bounds_ (l) and the upper bounds_ (u). - // assert(MinValue(l) == MinValue(u)); - // assert(MaxValue(l) == MaxValue(u)); - // assert(MinValue(l) <= sorted_by_min_[0]->min); - // assert(sorted_by_max_[n-1]->max <= MaxValue(u)); - - // Checks if there are values that must be assigned before the - // smallest interval or after the last interval. If this is - // the case, there is no solution to the problem - // This is not an optimization since - // filterLower{Min,Max} and - // filterUpper{Min,Max} do not check for this case. - if ((lower_sum_.Sum(lower_sum_.MinValue(), - sorted_by_min_[0]->min_value - 1) > 0) || - (lower_sum_.Sum(sorted_by_max_[size_ - 1]->max_value + 1, - lower_sum_.MaxValue()) > 0)) { - solver()->Fail(); - } - - has_changed = FilterLowerMax(); - has_changed = FilterLowerMin() || has_changed; - has_changed = FilterUpperMax() || has_changed; - has_changed = FilterUpperMin() || has_changed; - - if (has_changed) { - for (Index i(0); i < size_; ++i) { - variables_[i]->SetRange(intervals_[i].min_value, - intervals_[i].max_value); - } - } - } - - void PropagateValue(int index) { - const int64 value = variables_[Index(index)]->Value(); - const int vindex = value - first_domain_value_; - const int64 cap = max_occurrences_.Value(vindex) - 1; - max_occurrences_.SetValue(solver(), vindex, cap); - - if (cap == 0) { - for (Index j(0); j < size_; j++) { - if (!variables_[j]->Bound()) { - variables_[j]->RemoveValue(value); - } - } - } - } - - virtual std::string DebugString() const { - // TODO(user): Improve me. - return "GccConstraint"; - } - - void Accept(ModelVisitor* const visitor) const { - LOG(FATAL) << "Not yet implemented"; - // TODO(user): IMPLEMENT ME. - } - - private: - void PathSet(std::vector* const tree, int64 start, int64 end, - int64 to) { - int64 l = start; - while (l != end) { - int64 k = l; - l = (*tree)[k]; - (*tree)[k] = to; - } - } - - int64 PathMin(const std::vector& tree, int64 index) { - int64 i = index; - while (tree[i] < i) { - i = tree[i]; - } - return i; - } - - int64 PathMax(const std::vector& tree, int64 index) { - int64 i = index; - while (tree[i] > i) { - i = tree[i]; - } - return i; - } - - void SortIntervals() { - std::sort(sorted_by_min_.begin(), sorted_by_min_.end(), - CompareIntervalMin()); - std::sort(sorted_by_max_.begin(), sorted_by_max_.end(), - CompareIntervalMax()); - - int64 min = sorted_by_min_[0]->min_value; - int64 max = sorted_by_max_[0]->max_value + 1; - int64 last = lower_sum_.offset() + 1; - // MODIFIED: bounds_[0] = last = min - 2; - bounds_[0] = last; - - // merge sorted_by_min_[] and sorted_by_max_[] into bounds_[] - int64 min_index = 0; - int64 max_index = 0; - int64 active_index = 0; - // merge minsorted[] and maxsorted[] into bounds[] - for (;;) { - // make sure sorted_by_min_ exhausted first - if (min_index < size_ && min <= max) { - if (min != last) { - bounds_[++active_index] = min; - last = min; - } - sorted_by_min_[min_index]->min_rank = active_index; - if (++min_index < size_) { - min = sorted_by_min_[min_index]->min_value; - } - } else { - if (max != last) { - bounds_[++active_index] = max; - last = max; - } - sorted_by_max_[max_index]->max_rank = active_index; - if (++max_index == size_) { - break; - } - max = sorted_by_max_[max_index]->max_value + 1; - } - } - active_size_ = active_index; - // MODIFIED: bounds_[active_index+1] = bounds_[active_index] + 2; - bounds_[active_index + 1] = upper_sum_.last_value() + 1; - } - - // Shrink the lower bounds_ for the max occurrences problem. - - bool FilterLowerMax() { - bool changed = false; - - for (int64 i = 1; i <= active_size_ + 1; i++) { - tree_[i] = i - 1; - hall_[i] = i - 1; - diffs_[i] = upper_sum_.Sum(bounds_[i - 1], bounds_[i] - 1); - } - // visit intervals in increasing max order - for (int64 i = 0; i < size_; i++) { - // get interval bounds_ - const int64 x = sorted_by_max_[i]->min_rank; - const int64 y = sorted_by_max_[i]->max_rank; - int64 z = PathMax(tree_, x + 1); - const int64 j = tree_[z]; - if (--diffs_[z] == 0) { - tree_[z] = z + 1; - z = PathMax(tree_, z + 1); - tree_[z] = j; - } - PathSet(&tree_, x + 1, z, z); - if (diffs_[z] < upper_sum_.Sum(bounds_[y], bounds_[z] - 1)) { - solver()->Fail(); - } - if (hall_[x] > x) { - const int64 w = PathMax(hall_, hall_[x]); - sorted_by_max_[i]->min_value = bounds_[w]; - PathSet(&hall_, x, w, w); - changed = true; - } - if (diffs_[z] == upper_sum_.Sum(bounds_[y], bounds_[z] - 1)) { - PathSet(&hall_, hall_[y], j - 1, y); // mark hall interval - // hall interval [bounds_[j], bounds_[y]] - hall_[y] = j - 1; - } - } - return changed; - } - - // Shrink the upper bounds_ for the max occurrences problem. - bool FilterUpperMax() { - // Assertion: FilterLowerMax returns true - bool changed = false; - - for (int64 i = 0; i <= active_size_; i++) { - hall_[i] = i + 1; - tree_[i] = i + 1; - diffs_[i] = upper_sum_.Sum(bounds_[i], bounds_[i + 1] - 1); - } - - // Visits intervals in decreasing min order. - for (int64 i = size_ - 1; i >= 0; --i) { - // Gets interval bounds. - const int64 x = sorted_by_min_[i]->max_rank; - const int64 y = sorted_by_min_[i]->min_rank; - int64 z = PathMin(tree_, x - 1); - const int64 j = tree_[z]; - if (--diffs_[z] == 0) { - tree_[z] = z - 1; - z = PathMin(tree_, z - 1); - tree_[z] = j; - } - PathSet(&tree_, x - 1, z, z); - if (diffs_[z] < upper_sum_.Sum(bounds_[z], bounds_[y] - 1)) { - solver()->Fail(); - } - if (hall_[x] < x) { - const int64 w = PathMin(hall_, hall_[x]); - sorted_by_min_[i]->max_value = bounds_[w] - 1; - PathSet(&hall_, x, w, w); - changed = true; - } - if (diffs_[z] == upper_sum_.Sum(bounds_[z], bounds_[y] - 1)) { - PathSet(&hall_, hall_[y], j + 1, y); - hall_[y] = j + 1; - } - } - return changed; - } - - // Shrink the lower bounds_ for the min occurrences problem. called - // as: FilterLowerMin(t, d, h, stable_intervals_, potential_stable_sets_, - // new_min_); - // - bool FilterLowerMin() { - bool changed = false; - int64 w = active_size_ + 1; - for (int64 i = active_size_ + 1; i > 0; i--) { - // diffs_[i] = Sum(l, bounds_[potential_stable_sets_[i] = - // stable_intervals_[i]=i-1], bounds_[i]-1); - potential_stable_sets_[i] = stable_intervals_[i] = i - 1; - diffs_[i] = lower_sum_.Sum(bounds_[i - 1], bounds_[i] - 1); - // If the capacity between both bounds_ is zero, we have - // an unstable set between these two bounds_. - if (diffs_[i] == 0) { - hall_[i - 1] = w; - } else { - w = hall_[w] = i - 1; - } - } - - w = active_size_ + 1; - for (int64 i = active_size_ + 1; i >= 0; i--) { - if (diffs_[i] == 0) { - tree_[i] = w; - } else { - w = tree_[w] = i; - } - } - - // visit intervals in increasing max order - for (int64 i = 0; i < size_; i++) { - // Get interval bounds_ - const int64 x = sorted_by_max_[i]->min_rank; - int64 y = sorted_by_max_[i]->max_rank; - int64 z = PathMax(tree_, x + 1); - const int64 j = tree_[z]; - if (z != x + 1) { - // if bounds_[z] - 1 belongs to a stable set, - // [bounds_[x], bounds_[z]) is a sub set of this stable set - w = PathMax(potential_stable_sets_, x + 1); - int64 v = potential_stable_sets_[w]; - PathSet(&potential_stable_sets_, x + 1, w, w); // path compression - w = std::min(y, z); - PathSet(&potential_stable_sets_, potential_stable_sets_[w], v, w); - potential_stable_sets_[w] = v; - } - - if (diffs_[z] <= lower_sum_.Sum(bounds_[y], bounds_[z] - 1)) { - // (potential_stable_sets_[y], y] is a stable set - w = PathMax(stable_intervals_, potential_stable_sets_[y]); - // Path compression - PathSet(&stable_intervals_, potential_stable_sets_[y], w, w); - int64 v = stable_intervals_[w]; - PathSet(&stable_intervals_, stable_intervals_[y], v, y); - stable_intervals_[y] = v; - } else { - // Decrease the capacity between the two bounds_ - if (--diffs_[z] == 0) { - tree_[z] = z + 1; - z = PathMax(tree_, z + 1); - tree_[z] = j; - } - - // If the lower bound belongs to an unstable or a stable set, - // remind the new value we might assigned to the lower bound - // in case the variable doesn't belong to a stable set. - if (hall_[x] > x) { - w = PathMax(hall_, x); - new_min_[i] = w; - PathSet(&hall_, x, w, w); // path compression - } else { - new_min_[i] = x; // Do not shrink the variable - } - - // If an unstable set is discovered - if (diffs_[z] == lower_sum_.Sum(bounds_[y], bounds_[z] - 1)) { - // Consider stable and unstable sets beyong y - if (hall_[y] > y) { - // Equivalent to PathMax since the path is fully compressed. - y = hall_[y]; - } - PathSet(&hall_, hall_[y], j - 1, y); // mark the new unstable set - hall_[y] = j - 1; - } - } - PathSet(&tree_, x + 1, z, z); // path compression - } - - // If there is a failure set - if (hall_[active_size_] != 0) { - solver()->Fail(); - } - - // Perform path compression over all elements in - // the stable interval data structure. This data - // structure will no longer be modified and will be - // accessed n or 2n times. Therefore, we can afford - // a linear time compression. - for (int64 i = active_size_ + 1; i > 0; i--) { - if (stable_intervals_[i] > i) - stable_intervals_[i] = w; - else - w = i; - } - - // For all variables that are not a subset of a stable set, shrink - // the lower bound - for (int64 i = size_ - 1; i >= 0; i--) { - const int64 x = sorted_by_max_[i]->min_rank; - const int64 y = sorted_by_max_[i]->max_rank; - if ((stable_intervals_[x] <= x) || (y > stable_intervals_[x])) { - sorted_by_max_[i]->min_value = - lower_sum_.SkipNonNullElementsRight(bounds_[new_min_[i]]); - changed = true; - } - } - - return changed; - } - - // - // Shrink the upper bounds_ for the min occurrences problem. - // called as: FilterUpperMin(t, d, h, stable_intervals_, new_min_); - // - bool FilterUpperMin() { - // ASSERTION: FilterLowerMin returns true - bool changed = false; - int64 w = 0; - for (int64 i = 0; i <= active_size_; i++) { - // diffs_[i] = bounds_[tree_[i]=hall_[i]=i+1] - bounds_[i]; - diffs_[i] = lower_sum_.Sum(bounds_[i], bounds_[i + 1] - 1); - if (diffs_[i] == 0) { - tree_[i] = w; - } else { - tree_[w] = i; - w = i; - } - } - tree_[w] = active_size_ + 1; - w = 0; - for (int64 i = 1; i <= active_size_; i++) { - if (diffs_[i - 1] == 0) { - hall_[i] = w; - } else { - hall_[w] = i; - w = i; - } - } - hall_[w] = active_size_ + 1; - - for (int64 i = size_; - --i >= 0;) { // visit intervals in decreasing min order - // Get interval bounds_ - const int64 x = sorted_by_min_[i]->max_rank; - int64 y = sorted_by_min_[i]->min_rank; - int64 z = PathMin(tree_, x - 1); - // Solve the lower bound problem - const int64 j = tree_[z]; - - // If the variable is not in a discovered stable set Possible - // optimization: Use the array stable_intervals_ to perform this - // test - if (diffs_[z] > lower_sum_.Sum(bounds_[z], bounds_[y] - 1)) { - if (--diffs_[z] == 0) { - tree_[z = PathMin(tree_, tree_[z] = z - 1)] = j; - } - if (hall_[x] < x) { - w = PathMin(hall_, hall_[x]); - new_min_[i] = w; - PathSet(&hall_, x, w, w); // path compression - } else { - new_min_[i] = x; - } - if (diffs_[z] == lower_sum_.Sum(bounds_[z], bounds_[y] - 1)) { - if (hall_[y] < y) { - y = hall_[y]; - } - PathSet(&hall_, hall_[y], j + 1, y); - hall_[y] = j + 1; - } - } - PathSet(&tree_, x - 1, z, z); - } - - // For all variables that are not subsets of a stable set, shrink - // the lower bound - for (int64 i = size_ - 1; i >= 0; i--) { - const int64 x = sorted_by_min_[i]->min_rank; - const int64 y = sorted_by_min_[i]->max_rank; - if ((stable_intervals_[x] <= x) || (y > stable_intervals_[x])) - sorted_by_min_[i]->max_value = - lower_sum_.SkipNonNullElementsLeft(bounds_[new_min_[i]] - 1); - changed = true; - } - - return changed; - } - - gtl::ITIVector variables_; - const int64 size_; - // NumericalRev current_level_; - // int last_level_; - NumericalRevArray max_occurrences_; - const int64 first_domain_value_; - std::vector tree_; // tree links - std::vector diffs_; // diffs between critical capacities - std::vector hall_; // hall interval links - std::vector stable_intervals_; - std::vector potential_stable_sets_; - std::vector new_min_; - gtl::ITIVector intervals_; - std::vector sorted_by_min_; - std::vector sorted_by_max_; - // bounds_[1..active_size_] hold set of min & max of the n intervals - std::vector bounds_; - // while bounds_[0] and bounds_[active_size_+1] allow sentinels - int64 active_size_; - PartialSum lower_sum_; - PartialSum upper_sum_; -}; -} // namespace - -Constraint* MakeGcc(Solver* const solver, const std::vector& vars, - int64 first_domain_value, - const std::vector& min_occurrences, - const std::vector& max_occurrences) { - return solver->RevAlloc(new GccConstraint(solver, vars, first_domain_value, - min_occurrences.size(), - min_occurrences, max_occurrences)); -} - -Constraint* MakeGcc(Solver* const solver, const std::vector& vars, - int64 offset, const std::vector& min_occurrences, - const std::vector& max_occurrences) { - return solver->RevAlloc(new GccConstraint(solver, vars, offset, - min_occurrences.size(), - min_occurrences, max_occurrences)); -} -} // namespace operations_research diff --git a/ortools/constraint_solver/graph_constraints.cc b/ortools/constraint_solver/graph_constraints.cc index abca9825c08..0fdaf6ae3d7 100644 --- a/ortools/constraint_solver/graph_constraints.cc +++ b/ortools/constraint_solver/graph_constraints.cc @@ -12,16 +12,18 @@ // limitations under the License. #include +#include #include #include -#include #include #include +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/saturated_arithmetic.h" @@ -351,7 +353,7 @@ void NoCycle::ComputeSupport(int index) { } std::string NoCycle::DebugString() const { - return StringPrintf("NoCycle(%s)", JoinDebugStringPtr(nexts_, ", ").c_str()); + return absl::StrFormat("NoCycle(%s)", JoinDebugStringPtr(nexts_, ", ")); } // ----- Circuit constraint ----- @@ -427,8 +429,8 @@ class Circuit : public Constraint { } std::string DebugString() const override { - return StringPrintf("%sCircuit(%s)", sub_circuit_ ? "Sub" : "", - JoinDebugStringPtr(nexts_, " ").c_str()); + return absl::StrFormat("%sCircuit(%s)", sub_circuit_ ? "Sub" : "", + JoinDebugStringPtr(nexts_, " ")); } void Accept(ModelVisitor* const visitor) const override { @@ -1434,14 +1436,21 @@ Constraint* Solver::MakePathConnected(std::vector nexts, namespace { class PathTransitPrecedenceConstraint : public Constraint { public: + enum PrecedenceType { + ANY, + LIFO, + FIFO, + }; PathTransitPrecedenceConstraint( Solver* solver, std::vector nexts, std::vector transits, - const std::vector>& precedences) + const std::vector>& precedences, + absl::flat_hash_map precedence_types) : Constraint(solver), nexts_(std::move(nexts)), transits_(std::move(transits)), predecessors_(nexts_.size()), successors_(nexts_.size()), + precedence_types_(std::move(precedence_types)), starts_(nexts_.size(), -1), ends_(nexts_.size(), -1), transit_cumuls_(nexts_.size(), 0) { @@ -1505,13 +1514,32 @@ class PathTransitPrecedenceConstraint : public Constraint { if (end < nexts_.size()) starts_.SetValue(solver(), end, start); ends_.SetValue(solver(), start, end); int current = start; + PrecedenceType type = ANY; + auto it = precedence_types_.find(start); + if (it != precedence_types_.end()) { + type = it->second; + } forbidden_.clear(); marked_.clear(); + pushed_.clear(); int64 transit_cumul = 0; const bool has_transits = !transits_.empty(); while (current < nexts_.size() && current != end) { transit_cumuls_[current] = transit_cumul; marked_.insert(current); + // If current has predecessors and we are in LIFO/FIFO mode. + if (!predecessors_[current].empty() && !pushed_.empty()) { + bool found = false; + // One of the predecessors must be at the top of the stack. + for (const int predecessor : predecessors_[current]) { + if (pushed_.back() == predecessor) { + found = true; + break; + } + } + if (!found) solver()->Fail(); + pushed_.pop_back(); + } if (forbidden_.find(current) != forbidden_.end()) { for (const int successor : successors_[current]) { if (marked_.find(successor) != marked_.end()) { @@ -1522,6 +1550,18 @@ class PathTransitPrecedenceConstraint : public Constraint { } } } + if (!successors_[current].empty()) { + switch (type) { + case LIFO: + pushed_.push_back(current); + break; + case FIFO: + pushed_.push_front(current); + break; + case ANY: + break; + } + } for (const int predecessor : predecessors_[current]) { forbidden_.insert(predecessor); } @@ -1546,12 +1586,28 @@ class PathTransitPrecedenceConstraint : public Constraint { const std::vector transits_; std::vector> predecessors_; std::vector> successors_; + const absl::flat_hash_map precedence_types_; RevArray starts_; RevArray ends_; - std::unordered_set forbidden_; - std::unordered_set marked_; + absl::flat_hash_set forbidden_; + absl::flat_hash_set marked_; + std::deque pushed_; std::vector transit_cumuls_; }; + +Constraint* MakePathTransitTypedPrecedenceConstraint( + Solver* solver, std::vector nexts, std::vector transits, + const std::vector>& precedences, + absl::flat_hash_map + precedence_types) { + if (precedences.empty()) { + return solver->MakeTrueConstraint(); + } + return solver->RevAlloc(new PathTransitPrecedenceConstraint( + solver, std::move(nexts), std::move(transits), precedences, + std::move(precedence_types))); +} + } // namespace Constraint* Solver::MakePathPrecedenceConstraint( @@ -1560,13 +1616,27 @@ Constraint* Solver::MakePathPrecedenceConstraint( return MakePathTransitPrecedenceConstraint(std::move(nexts), {}, precedences); } +Constraint* Solver::MakePathPrecedenceConstraint( + std::vector nexts, + const std::vector>& precedences, + const std::vector& lifo_path_starts, + const std::vector& fifo_path_starts) { + absl::flat_hash_map + precedence_types; + for (int start : lifo_path_starts) { + precedence_types[start] = PathTransitPrecedenceConstraint::LIFO; + } + for (int start : fifo_path_starts) { + precedence_types[start] = PathTransitPrecedenceConstraint::FIFO; + } + return MakePathTransitTypedPrecedenceConstraint( + this, std::move(nexts), {}, precedences, std::move(precedence_types)); +} + Constraint* Solver::MakePathTransitPrecedenceConstraint( std::vector nexts, std::vector transits, const std::vector>& precedences) { - if (precedences.empty()) { - return MakeTrueConstraint(); - } - return RevAlloc(new PathTransitPrecedenceConstraint( - this, std::move(nexts), std::move(transits), precedences)); + return MakePathTransitTypedPrecedenceConstraint( + this, std::move(nexts), std::move(transits), precedences, {{}}); } } // namespace operations_research diff --git a/ortools/constraint_solver/hybrid.cc b/ortools/constraint_solver/hybrid.cc deleted file mode 100644 index 4f8da798fe0..00000000000 --- a/ortools/constraint_solver/hybrid.cc +++ /dev/null @@ -1,624 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/constraint_solver/hybrid.h" - -#include -#include -#include -#include -#include "ortools/base/callback.h" -#include "ortools/base/commandlineflags.h" -#include "ortools/base/integral_types.h" -#include "ortools/base/macros.h" -#include "ortools/base/stl_util.h" -#include "ortools/constraint_solver/constraint_solver.h" -#include "ortools/constraint_solver/constraint_solveri.h" -#include "ortools/linear_solver/linear_solver.h" -#include "ortools/util/string_array.h" - -DEFINE_int32(simplex_cleanup_frequency, 0, - "frequency to cleanup the simplex after each call, 0: no cleanup"); -DEFINE_bool(verbose_simplex_call, false, - "Do not suppress output of the simplex"); - -namespace operations_research { -namespace { -inline MPSolver::OptimizationProblemType LpSolverForCp() { -#if defined(USE_GUROBI) - return MPSolver::GUROBI_LINEAR_PROGRAMMING; -#elif defined(USE_CLP) - return MPSolver::CLP_LINEAR_PROGRAMMING; -#elif defined(USE_GLPK) - return MPSolver::CLP_LINEAR_PROGRAMMING; -#elif defined(USE_SULUM) - return MPSolver::SULUM_LINEAR_PROGRAMMING; -#else - LOG(FATAL) << "No suitable linear programming solver available."; -#endif -} - -class SimplexConnection : public SearchMonitor { - public: - SimplexConnection(Solver* const solver, - std::function builder, - std::function modifier, - std::function runner, - int simplex_frequency) - : SearchMonitor(solver), - builder_(std::move(builder)), - modifier_(std::move(modifier)), - runner_(std::move(runner)), - mp_solver_("InSearchSimplex", LpSolverForCp()), - counter_(0LL), - simplex_frequency_(simplex_frequency) { - if (!FLAGS_verbose_simplex_call) { - mp_solver_.SuppressOutput(); - } - } - - void EndInitialPropagation() override { - mp_solver_.Clear(); - if (builder_) { - builder_(&mp_solver_); - } - RunOptim(); - } - - void BeginNextDecision(DecisionBuilder* const b) override { - if (++counter_ % simplex_frequency_ == 0) { - const int cleanup = FLAGS_simplex_cleanup_frequency * simplex_frequency_; - if (FLAGS_simplex_cleanup_frequency != 0 && counter_ % cleanup == 0) { - mp_solver_.Clear(); - if (builder_) { - builder_(&mp_solver_); - } - } - RunOptim(); - } - } - - void RunOptim() { - if (modifier_) { - modifier_(&mp_solver_); - } - if (runner_) { - runner_(&mp_solver_); - } - } - - std::string DebugString() const override { return "SimplexConnection"; } - - private: - std::function builder_; - std::function modifier_; - std::function runner_; - MPSolver mp_solver_; - int64 counter_; - const int simplex_frequency_; - DISALLOW_COPY_AND_ASSIGN(SimplexConnection); -}; - -// ---------- Automatic Linearization ---------- - -// ----- Useful typedefs ----- - -typedef std::unordered_map ExprTranslation; - -#define IS_TYPE(type, tag) type.compare(ModelVisitor::tag) == 0 - -// ----- Actual Linearization ----- - -class Linearizer : public ModelParser { - public: - Linearizer(MPSolver* const mp_solver, ExprTranslation* tr, IntVar** objective, - bool* maximize) - : mp_solver_(mp_solver), - translation_(tr), - objective_(objective), - maximize_(maximize) {} - - ~Linearizer() override {} - - // Begin/End visit element. - void BeginVisitModel(const std::string& solver_name) override { - BeginVisit(true); - } - - void EndVisitModel(const std::string& solver_name) override { EndVisit(); } - - void BeginVisitConstraint(const std::string& type_name, - const Constraint* const constraint) override { - if (!constraint->IsCastConstraint() && - (IS_TYPE(type_name, kEquality) || IS_TYPE(type_name, kLessOrEqual) || - IS_TYPE(type_name, kGreaterOrEqual) || - IS_TYPE(type_name, kScalProdLessOrEqual))) { - BeginVisit(true); - } else { - BeginVisit(false); - } - } - - void EndVisitConstraint(const std::string& type_name, - const Constraint* const constraint) override { - if (!constraint->IsCastConstraint()) { - if (IS_TYPE(type_name, kEquality)) { - VisitEquality(); - } else if (IS_TYPE(type_name, kLessOrEqual)) { - VisitLessOrEqual(); - } else if (IS_TYPE(type_name, kGreaterOrEqual)) { - VisitGreaterOrEqual(); - } else if (IS_TYPE(type_name, kScalProdLessOrEqual)) { - VisitScalProdLessOrEqual(); - } - } - EndVisit(); - } - void BeginVisitExtension(const std::string& type) override { - BeginVisit(true); - } - void EndVisitExtension(const std::string& type) override { - if (IS_TYPE(type, kObjectiveExtension)) { - VisitObjective(); - } - EndVisit(); - } - void BeginVisitIntegerExpression(const std::string& type_name, - const IntExpr* const expr) override { - BeginVisit(true); - } - void EndVisitIntegerExpression(const std::string& type_name, - const IntExpr* const expr) override { - if (IS_TYPE(type_name, kSum)) { - VisitSum(expr); - } else if (IS_TYPE(type_name, kScalProd)) { - VisitScalProd(expr); - } else if (IS_TYPE(type_name, kDifference)) { - VisitDifference(expr); - } else if (IS_TYPE(type_name, kOpposite)) { - VisitOpposite(expr); - } else if (IS_TYPE(type_name, kProduct)) { - VisitProduct(expr); - } else { - VisitIntegerExpression(expr); - } - EndVisit(); - } - - void VisitIntegerVariable(const IntVar* const variable, - const std::string& operation, int64 value, - IntVar* const delegate) override { - RegisterExpression(variable); - RegisterExpression(delegate); - if (operation == ModelVisitor::kSumOperation) { - MPConstraint* const ct = mp_solver_->MakeRowConstraint(value, value); - ct->SetCoefficient(Translated(variable), 1.0); - ct->SetCoefficient(Translated(delegate), -1.0); - } else if (operation == ModelVisitor::kDifferenceOperation) { - MPConstraint* const ct = mp_solver_->MakeRowConstraint(value, value); - ct->SetCoefficient(Translated(variable), 1.0); - ct->SetCoefficient(Translated(delegate), 1.0); - } else if (operation == ModelVisitor::kProductOperation) { - MPConstraint* const ct = mp_solver_->MakeRowConstraint(0.0, 0.0); - ct->SetCoefficient(Translated(variable), 1.0); - ct->SetCoefficient(Translated(delegate), -value); - } - } - - void VisitIntegerVariable(const IntVar* const variable, - IntExpr* const delegate) override { - RegisterExpression(variable); - if (delegate != nullptr) { - VisitSubExpression(delegate); - AddMpEquality(variable, delegate); - } - } - - void VisitIntervalVariable(const IntervalVar* const variable, - const std::string& operation, int64 value, - IntervalVar* const delegate) override {} - - // Visit integer arguments. - void VisitIntegerArgument(const std::string& arg_name, int64 value) override { - if (DoVisit()) { - Top()->SetIntegerArgument(arg_name, value); - } - } - - void VisitIntegerArrayArgument(const std::string& arg_name, - const std::vector& values) override { - if (DoVisit()) { - Top()->SetIntegerArrayArgument(arg_name, values); - } - } - - void VisitIntegerMatrixArgument(const std::string& arg_name, - const IntTupleSet& values) override { - if (DoVisit()) { - Top()->SetIntegerMatrixArgument(arg_name, values); - } - } - - // Visit integer expression argument. - void VisitIntegerExpressionArgument(const std::string& arg_name, - IntExpr* const argument) override { - if (DoVisit()) { - Top()->SetIntegerExpressionArgument(arg_name, argument); - VisitSubExpression(argument); - } - } - - void VisitIntegerVariableArrayArgument( - const std::string& arg_name, - const std::vector& arguments) override { - if (DoVisit()) { - Top()->SetIntegerVariableArrayArgument(arg_name, arguments); - for (int i = 0; i < arguments.size(); ++i) { - VisitSubExpression(arguments[i]); - } - } - } - - // Visit interval argument. - void VisitIntervalArgument(const std::string& arg_name, - IntervalVar* const argument) override {} - - void VisitIntervalArrayArgument( - const std::string& arg_name, - const std::vector& argument) override {} - - std::string DebugString() const override { return "Linearizer"; } - - private: - void BeginVisit(bool active) { - PushActive(active); - PushArgumentHolder(); - } - - void EndVisit() { - PopArgumentHolder(); - PopActive(); - } - - bool DoVisit() const { return actives_.back(); } - - void PushActive(bool active) { actives_.push_back(active); } - - void PopActive() { actives_.pop_back(); } - - void RegisterExpression(const IntExpr* const cp_expr) { - if (!gtl::ContainsKey(*translation_, cp_expr)) { - MPVariable* const mp_var = - mp_solver_->MakeIntVar(cp_expr->Min(), cp_expr->Max(), ""); - (*translation_)[cp_expr] = mp_var; - } - } - - void VisitSubExpression(IntExpr* const cp_expr) { - if (!gtl::ContainsKey(*translation_, cp_expr)) { - cp_expr->Accept(this); - } - } - - void AddMpEquality(const IntExpr* const left, const IntExpr* const right) { - MPConstraint* const ct = mp_solver_->MakeRowConstraint(0.0, 0.0); - ct->SetCoefficient(Translated(left), 1.0); - ct->SetCoefficient(Translated(right), -1.0); - } - - MPVariable* Translated(const IntExpr* const cp_expr) { - return gtl::FindOrDie((*translation_), cp_expr); - } - - void VisitBinaryRowConstraint(double lhs, double rhs) { - MPConstraint* const ct = mp_solver_->MakeRowConstraint(lhs, rhs); - const IntExpr* const left = - Top()->FindIntegerExpressionArgumentOrDie(ModelVisitor::kLeftArgument); - const IntExpr* const right = - Top()->FindIntegerExpressionArgumentOrDie(ModelVisitor::kRightArgument); - ct->SetCoefficient(Translated(left), 1.0); - ct->SetCoefficient(Translated(right), -1.0); - } - - void VisitUnaryRowConstraint(double lhs, double rhs) { - const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie( - ModelVisitor::kExpressionArgument); - MPConstraint* const ct = mp_solver_->MakeRowConstraint(lhs, rhs); - ct->SetCoefficient(Translated(expr), 1.0); - } - - void VisitEquality() { - if (Top()->HasIntegerExpressionArgument(ModelVisitor::kLeftArgument)) { - VisitBinaryRowConstraint(0.0, 0.0); - } else { - const int64 value = - Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument); - VisitUnaryRowConstraint(value, value); - } - } - - void VisitLessOrEqual() { - if (Top()->HasIntegerExpressionArgument(ModelVisitor::kLeftArgument)) { - VisitBinaryRowConstraint(-mp_solver_->infinity(), 0.0); - } else { - const int64 value = - Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument); - VisitUnaryRowConstraint(-mp_solver_->infinity(), value); - } - } - - void VisitGreaterOrEqual() { - if (Top()->HasIntegerExpressionArgument(ModelVisitor::kLeftArgument)) { - VisitBinaryRowConstraint(0.0, mp_solver_->infinity()); - } else { - const int64 value = - Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument); - VisitUnaryRowConstraint(value, mp_solver_->infinity()); - } - } - - void VisitScalProdLessOrEqual() { - const std::vector& cp_vars = - Top()->FindIntegerVariableArrayArgumentOrDie( - ModelVisitor::kVarsArgument); - const std::vector& cp_coefficients = - Top()->FindIntegerArrayArgumentOrDie( - ModelVisitor::kCoefficientsArgument); - const int64 constant = - Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument); - MPConstraint* const ct = - mp_solver_->MakeRowConstraint(-mp_solver_->infinity(), constant); - CHECK_EQ(cp_vars.size(), cp_coefficients.size()); - for (int i = 0; i < cp_coefficients.size(); ++i) { - MPVariable* const mp_var = Translated(cp_vars[i]); - ct->SetCoefficient(mp_var, - cp_coefficients[i] + ct->GetCoefficient(mp_var)); - } - } - - void VisitSum(const IntExpr* const cp_expr) { - if (Top()->HasIntegerVariableArrayArgument(ModelVisitor::kVarsArgument)) { - MPConstraint* const ct = mp_solver_->MakeRowConstraint(0.0, 0.0); - const std::vector& cp_vars = - Top()->FindIntegerVariableArrayArgumentOrDie( - ModelVisitor::kVarsArgument); - for (int i = 0; i < cp_vars.size(); ++i) { - MPVariable* const mp_var = Translated(cp_vars[i]); - ct->SetCoefficient(mp_var, 1.0 + ct->GetCoefficient(mp_var)); - } - RegisterExpression(cp_expr); - ct->SetCoefficient(Translated(cp_expr), -1.0); - } else if (Top()->HasIntegerExpressionArgument( - ModelVisitor::kLeftArgument)) { - MPConstraint* const ct = mp_solver_->MakeRowConstraint(0.0, 0.0); - const IntExpr* const left = Top()->FindIntegerExpressionArgumentOrDie( - ModelVisitor::kLeftArgument); - const IntExpr* const right = Top()->FindIntegerExpressionArgumentOrDie( - ModelVisitor::kRightArgument); - if (left != right) { - ct->SetCoefficient(Translated(left), 1.0); - ct->SetCoefficient(Translated(right), 1.0); - } else { - ct->SetCoefficient(Translated(left), 2.0); - } - RegisterExpression(cp_expr); - ct->SetCoefficient(Translated(cp_expr), -1.0); - } else { - const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie( - ModelVisitor::kExpressionArgument); - const int64 value = - Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument); - MPConstraint* const ct = mp_solver_->MakeRowConstraint(-value, -value); - ct->SetCoefficient(Translated(expr), 1.0); - RegisterExpression(cp_expr); - ct->SetCoefficient(Translated(cp_expr), -1.0); - } - } - - void VisitScalProd(const IntExpr* const cp_expr) { - const std::vector& cp_vars = - Top()->FindIntegerVariableArrayArgumentOrDie( - ModelVisitor::kVarsArgument); - const std::vector& cp_coefficients = - Top()->FindIntegerArrayArgumentOrDie( - ModelVisitor::kCoefficientsArgument); - CHECK_EQ(cp_vars.size(), cp_coefficients.size()); - MPConstraint* const ct = mp_solver_->MakeRowConstraint(0.0, 0.0); - for (int i = 0; i < cp_vars.size(); ++i) { - MPVariable* const mp_var = Translated(cp_vars[i]); - const int64 coefficient = cp_coefficients[i]; - ct->SetCoefficient(mp_var, coefficient + ct->GetCoefficient(mp_var)); - } - RegisterExpression(cp_expr); - ct->SetCoefficient(Translated(cp_expr), -1.0); - } - - void VisitDifference(const IntExpr* const cp_expr) { - if (Top()->HasIntegerExpressionArgument(ModelVisitor::kLeftArgument)) { - MPConstraint* const ct = mp_solver_->MakeRowConstraint(0.0, 0.0); - const IntExpr* const left = Top()->FindIntegerExpressionArgumentOrDie( - ModelVisitor::kLeftArgument); - const IntExpr* const right = Top()->FindIntegerExpressionArgumentOrDie( - ModelVisitor::kRightArgument); - ct->SetCoefficient(Translated(left), 1.0); - ct->SetCoefficient(Translated(right), -1.0); - RegisterExpression(cp_expr); - ct->SetCoefficient(Translated(cp_expr), -1.0); - } else { - const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie( - ModelVisitor::kExpressionArgument); - const int64 value = - Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument); - MPConstraint* const ct = mp_solver_->MakeRowConstraint(value, value); - ct->SetCoefficient(Translated(expr), 1.0); - RegisterExpression(cp_expr); - ct->SetCoefficient(Translated(cp_expr), 1.0); - } - } - - void VisitOpposite(const IntExpr* const cp_expr) { - const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie( - ModelVisitor::kExpressionArgument); - MPConstraint* const ct = mp_solver_->MakeRowConstraint(0.0, 0.0); - ct->SetCoefficient(Translated(expr), 1.0); - RegisterExpression(cp_expr); - ct->SetCoefficient(Translated(cp_expr), -1.0); - } - - void VisitProduct(const IntExpr* const cp_expr) { - if (Top()->HasIntegerExpressionArgument( - ModelVisitor::kExpressionArgument)) { - const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie( - ModelVisitor::kExpressionArgument); - const int64 value = - Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument); - MPConstraint* const ct = mp_solver_->MakeRowConstraint(0.0, 0.0); - ct->SetCoefficient(Translated(expr), value); - RegisterExpression(cp_expr); - ct->SetCoefficient(Translated(cp_expr), -1.0); - } else { - RegisterExpression(cp_expr); - } - } - - void VisitIntegerExpression(const IntExpr* const cp_expr) { - RegisterExpression(cp_expr); - } - - void VisitObjective() { - *maximize_ = - Top()->FindIntegerArgumentOrDie(ModelVisitor::kMaximizeArgument); - *objective_ = - const_cast(Top()->FindIntegerExpressionArgumentOrDie( - ModelVisitor::kExpressionArgument)) - ->Var(); - MPObjective* const objective = mp_solver_->MutableObjective(); - objective->SetCoefficient(Translated(*objective_), 1.0); - objective->SetOptimizationDirection(*maximize_); - } - - MPSolver* const mp_solver_; - ExprTranslation* const translation_; - IntVar** objective_; - bool* maximize_; - std::vector actives_; -}; - -#undef IS_TYPE - -// ----- Search Monitor ----- - -class AutomaticLinearization : public SearchMonitor { - public: - AutomaticLinearization(Solver* const solver, int frequency) - : SearchMonitor(solver), - mp_solver_("InSearchSimplex", LpSolverForCp()), - counter_(0), - simplex_frequency_(frequency), - objective_(nullptr), - maximize_(false) {} - - ~AutomaticLinearization() override {} - - void BeginInitialPropagation() override { - mp_solver_.Clear(); - translation_.clear(); - BuildModel(); - } - - void EndInitialPropagation() override { RunOptim(); } - - void BeginNextDecision(DecisionBuilder* const b) override { - if (++counter_ % simplex_frequency_ == 0) { - RunOptim(); - } - } - - void RunOptim() { - AssignVariables(); - SolveProblem(); - } - - void BuildModel() { - Linearizer linearizer(&mp_solver_, &translation_, &objective_, &maximize_); - solver()->Accept(&linearizer); - } - - void AssignVariables() { - for (const auto& it : translation_) { - it.second->SetBounds(it.first->Min(), it.first->Max()); - } - } - - void SolveProblem() { - if (objective_ != nullptr) { - switch (mp_solver_.Solve()) { - case MPSolver::OPTIMAL: { - const double obj_value = mp_solver_.Objective().Value(); - if (maximize_) { - const int64 int_obj_value = static_cast(ceil(obj_value)); - objective_->SetMax(int_obj_value); - } else { - const int64 int_obj_value = static_cast(floor(obj_value)); - objective_->SetMin(int_obj_value); - } - break; - } - case MPSolver::FEASIBLE: - break; - case MPSolver::INFEASIBLE: - solver()->Fail(); - break; - case MPSolver::UNBOUNDED: - LOG(INFO) << "Error: unbounded LP status."; - break; - case MPSolver::ABNORMAL: - LOG(INFO) << "Error: abnormal LP status."; - break; - default: - LOG(FATAL) << "Error: Unknown LP status."; - break; - } - } - } - - std::string DebugString() const override { return "AutomaticLinearization"; } - - private: - MPSolver mp_solver_; - int64 counter_; - const int simplex_frequency_; - ExprTranslation translation_; - IntVar* objective_; - bool maximize_; -}; -} // namespace - -// ---------- API ---------- - -SearchMonitor* MakeSimplexConnection(Solver* const solver, - std::function builder, - std::function modifier, - std::function runner, - int frequency) { - return solver->RevAlloc(new SimplexConnection(solver, std::move(builder), - std::move(modifier), - std::move(runner), frequency)); -} - -SearchMonitor* MakeSimplexConstraint(Solver* const solver, - int simplex_frequency) { - return solver->RevAlloc( - new AutomaticLinearization(solver, simplex_frequency)); -} -} // namespace operations_research diff --git a/ortools/constraint_solver/hybrid.h b/ortools/constraint_solver/hybrid.h deleted file mode 100644 index bd6891a7f44..00000000000 --- a/ortools/constraint_solver/hybrid.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_CONSTRAINT_SOLVER_HYBRID_H_ -#define OR_TOOLS_CONSTRAINT_SOLVER_HYBRID_H_ - -#include "ortools/constraint_solver/constraint_solver.h" - -namespace operations_research { -class MPSolver; - -// ----- Simplex Connection ----- -SearchMonitor* MakeSimplexConnection(Solver* const solver, - std::function builder, - std::function modifier, - std::function runner, - int frequency); - -// ----- Linear Relaxation Constraint ----- - -// Creates a search monitor that will maintain a linear relaxation -// of the problem. Every 'simplex_frequency' nodes explored in the -// search tree, this linear relaxation will be called and the -// resulting optimal solution found by the simplex will be used to -// prune the objective of the constraint programming model. -SearchMonitor* MakeSimplexConstraint(Solver* const solver, - int simplex_frequency); -} // namespace operations_research - -#endif // OR_TOOLS_CONSTRAINT_SOLVER_HYBRID_H_ diff --git a/ortools/constraint_solver/interval.cc b/ortools/constraint_solver/interval.cc index bb645f3bc4b..c47f34948ee 100644 --- a/ortools/constraint_solver/interval.cc +++ b/ortools/constraint_solver/interval.cc @@ -14,11 +14,11 @@ #include #include +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/saturated_arithmetic.h" @@ -110,7 +110,7 @@ class MirrorIntervalVar : public IntervalVar { } std::string DebugString() const override { - return StringPrintf("MirrorInterval(%s)", t_->DebugString().c_str()); + return absl::StrFormat("MirrorInterval(%s)", t_->DebugString()); } IntExpr* StartExpr() override { @@ -159,7 +159,7 @@ class AlwaysPerformedIntervalVarWrapper : public IntervalVar { public: explicit AlwaysPerformedIntervalVarWrapper(IntervalVar* const t) : IntervalVar(t->solver(), - StringPrintf("AlwaysPerformed<%s>", t->name().c_str())), + absl::StrFormat("AlwaysPerformed<%s>", t->name())), t_(t), start_expr_(nullptr), duration_expr_(nullptr), @@ -322,8 +322,8 @@ class IntervalVarRelaxedMax : public AlwaysPerformedIntervalVarWrapper { } std::string DebugString() const override { - return StringPrintf("IntervalVarRelaxedMax(%s)", - underlying()->DebugString().c_str()); + return absl::StrFormat("IntervalVarRelaxedMax(%s)", + underlying()->DebugString()); } }; @@ -372,8 +372,8 @@ class IntervalVarRelaxedMin : public AlwaysPerformedIntervalVarWrapper { } std::string DebugString() const override { - return StringPrintf("IntervalVarRelaxedMin(%s)", - underlying()->DebugString().c_str()); + return absl::StrFormat("IntervalVarRelaxedMin(%s)", + underlying()->DebugString()); } }; @@ -390,7 +390,7 @@ class BaseIntervalVar : public IntervalVar { return Solver::VAR_PRIORITY; } std::string DebugString() const override { - return StringPrintf("Handler(%s)", var_->DebugString().c_str()); + return absl::StrFormat("Handler(%s)", var_->DebugString()); } private: @@ -605,7 +605,7 @@ class RangeVar : public IntExpr { std::string DebugString() const override { std::string out = absl::StrCat(min_.Value()); if (!Bound()) { - StringAppendF(&out, " .. %" GG_LL_FORMAT "d", max_.Value()); + absl::StrAppendFormat(&out, " .. %" GG_LL_FORMAT "d", max_.Value()); } return out; } @@ -952,7 +952,7 @@ std::string FixedDurationIntervalVar::DebugString() const { const std::string& var_name = name(); if (performed_.Max() == 0) { if (!var_name.empty()) { - return StringPrintf("%s(performed = false)", var_name.c_str()); + return absl::StrFormat("%s(performed = false)", var_name); } else { return "IntervalVar(performed = false)"; } @@ -963,9 +963,9 @@ std::string FixedDurationIntervalVar::DebugString() const { } else { out = "IntervalVar(start = "; } - StringAppendF(&out, "%s, duration = %" GG_LL_FORMAT "d, performed = %s)", - start_.DebugString().c_str(), duration_, - performed_.DebugString().c_str()); + absl::StrAppendFormat( + &out, "%s, duration = %" GG_LL_FORMAT "d, performed = %s)", + start_.DebugString(), duration_, performed_.DebugString()); return out; } } @@ -1157,8 +1157,9 @@ std::string FixedDurationPerformedIntervalVar::DebugString() const { } else { out = "IntervalVar(start = "; } - StringAppendF(&out, "%s, duration = %" GG_LL_FORMAT "d, performed = true)", - start_.DebugString().c_str(), duration_); + absl::StrAppendFormat(&out, + "%s, duration = %" GG_LL_FORMAT "d, performed = true)", + start_.DebugString(), duration_); return out; } @@ -1314,13 +1315,13 @@ std::string StartVarPerformedIntervalVar::DebugString() const { } else { out = "IntervalVar(start = "; } - StringAppendF(&out, "%" GG_LL_FORMAT "d", start_var_->Min()); + absl::StrAppendFormat(&out, "%" GG_LL_FORMAT "d", start_var_->Min()); if (!start_var_->Bound()) { - StringAppendF(&out, " .. %" GG_LL_FORMAT "d", start_var_->Max()); + absl::StrAppendFormat(&out, " .. %" GG_LL_FORMAT "d", start_var_->Max()); } - StringAppendF(&out, ", duration = %" GG_LL_FORMAT "d, performed = true)", - duration_); + absl::StrAppendFormat( + &out, ", duration = %" GG_LL_FORMAT "d, performed = true)", duration_); return out; } @@ -1540,7 +1541,7 @@ std::string StartVarIntervalVar::DebugString() const { const std::string& var_name = name(); if (performed_->Max() == 0) { if (!var_name.empty()) { - return StringPrintf("%s(performed = false)", var_name.c_str()); + return absl::StrFormat("%s(performed = false)", var_name); } else { return "IntervalVar(performed = false)"; } @@ -1551,9 +1552,9 @@ std::string StartVarIntervalVar::DebugString() const { } else { out = "IntervalVar(start = "; } - StringAppendF(&out, "%s, duration = %" GG_LL_FORMAT "d, performed = %s)", - start_->DebugString().c_str(), duration_, - performed_->DebugString().c_str()); + absl::StrAppendFormat( + &out, "%s, duration = %" GG_LL_FORMAT "d, performed = %s)", + start_->DebugString(), duration_, performed_->DebugString()); return out; } } @@ -1735,10 +1736,10 @@ std::string FixedInterval::DebugString() const { } else { out = "IntervalVar(start = "; } - StringAppendF(&out, - "%" GG_LL_FORMAT "d, duration = %" GG_LL_FORMAT - "d, performed = true)", - start_, duration_); + absl::StrAppendFormat(&out, + "%" GG_LL_FORMAT "d, duration = %" GG_LL_FORMAT + "d, performed = true)", + start_, duration_); return out; } @@ -1960,7 +1961,7 @@ class VariableDurationIntervalVar : public BaseIntervalVar { const std::string& var_name = name(); if (performed_.Max() != 1) { if (!var_name.empty()) { - return StringPrintf("%s(performed = false)", var_name.c_str()); + return absl::StrFormat("%s(performed = false)", var_name); } else { return "IntervalVar(performed = false)"; } @@ -1972,10 +1973,10 @@ class VariableDurationIntervalVar : public BaseIntervalVar { out = "IntervalVar(start = "; } - StringAppendF(&out, "%s, duration = %s, end = %s, performed = %s)", - start_.DebugString().c_str(), - duration_.DebugString().c_str(), end_.DebugString().c_str(), - performed_.DebugString().c_str()); + absl::StrAppendFormat(&out, + "%s, duration = %s, end = %s, performed = %s)", + start_.DebugString(), duration_.DebugString(), + end_.DebugString(), performed_.DebugString()); return out; } } @@ -2091,10 +2092,10 @@ class FixedDurationIntervalVarStartSyncedOnStart int64 duration, int64 offset) : FixedDurationSyncedIntervalVar( t, duration, offset, - StringPrintf( + absl::StrFormat( "IntervalStartSyncedOnStart(%s, duration = %" GG_LL_FORMAT "d, offset = %" GG_LL_FORMAT "d)", - t->name().c_str(), duration, offset)) {} + t->name(), duration, offset)) {} ~FixedDurationIntervalVarStartSyncedOnStart() override {} int64 StartMin() const override { return CapAdd(t_->StartMin(), offset_); } int64 StartMax() const override { return CapAdd(t_->StartMax(), offset_); } @@ -2136,10 +2137,10 @@ class FixedDurationIntervalVarStartSyncedOnStart this, ModelVisitor::kStartSyncOnStartOperation, offset_, t_); } std::string DebugString() const override { - return StringPrintf( + return absl::StrFormat( "IntervalStartSyncedOnStart(%s, duration = %" GG_LL_FORMAT "d, offset = %" GG_LL_FORMAT "d)", - t_->DebugString().c_str(), duration_, offset_); + t_->DebugString(), duration_, offset_); } }; @@ -2152,10 +2153,10 @@ class FixedDurationIntervalVarStartSyncedOnEnd int64 offset) : FixedDurationSyncedIntervalVar( t, duration, offset, - StringPrintf( + absl::StrFormat( "IntervalStartSyncedOnEnd(%s, duration = %" GG_LL_FORMAT "d, offset = %" GG_LL_FORMAT "d)", - t->name().c_str(), duration, offset)) {} + t->name(), duration, offset)) {} ~FixedDurationIntervalVarStartSyncedOnEnd() override {} int64 StartMin() const override { return CapAdd(t_->EndMin(), offset_); } int64 StartMax() const override { return CapAdd(t_->EndMax(), offset_); } @@ -2198,9 +2199,10 @@ class FixedDurationIntervalVarStartSyncedOnEnd offset_, t_); } std::string DebugString() const override { - return StringPrintf("IntervalStartSyncedOnEnd(%s, duration = %" GG_LL_FORMAT - "d, offset = %" GG_LL_FORMAT "d)", - t_->DebugString().c_str(), duration_, offset_); + return absl::StrFormat( + "IntervalStartSyncedOnEnd(%s, duration = %" GG_LL_FORMAT + "d, offset = %" GG_LL_FORMAT "d)", + t_->DebugString(), duration_, offset_); } }; } // namespace diff --git a/ortools/constraint_solver/io.cc b/ortools/constraint_solver/io.cc deleted file mode 100644 index 5773597edd7..00000000000 --- a/ortools/constraint_solver/io.cc +++ /dev/null @@ -1,2678 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ortools/base/callback.h" -#include "ortools/base/hash.h" -#include "ortools/base/integral_types.h" -#include "ortools/base/logging.h" -#include "ortools/base/map_util.h" -#include "ortools/base/stl_util.h" -#include "ortools/constraint_solver/constraint_solver.h" -#include "ortools/constraint_solver/constraint_solveri.h" -#include "ortools/constraint_solver/model.pb.h" -#include "ortools/constraint_solver/search_limit.pb.h" -#include "ortools/util/tuple_set.h" -#include "ortools/util/vector_map.h" - -namespace operations_research { - -Constraint* SetIsEqual(IntVar* const var, const std::vector& values, - const std::vector& vars); - -Constraint* SetIsGreaterOrEqual(IntVar* const var, - const std::vector& values, - const std::vector& vars); - -namespace { -// ---------- Model Protobuf Writers ----------- - -// ----- First Pass visitor ----- - -// This visitor collects all constraints and expressions. It sorts the -// expressions, such that we can build them in sequence using -// previously created expressions. -class FirstPassVisitor : public ModelVisitor { - public: - FirstPassVisitor() {} // Needed for Visual Studio. - ~FirstPassVisitor() override {} - - std::string DebugString() const override { return "FirstPassVisitor"; } - - // Begin/End visit element. - void BeginVisitModel(const std::string& solver_name) override { - // Reset statistics. - expression_map_.clear(); - delegate_map_.clear(); - expression_list_.clear(); - constraint_list_.clear(); - interval_list_.clear(); - sequence_list_.clear(); - } - - void EndVisitConstraint(const std::string& type_name, - const Constraint* const constraint) override { - Register(constraint); - } - - void EndVisitIntegerExpression(const std::string& type_name, - const IntExpr* const expression) override { - Register(expression); - } - - void VisitIntegerVariable(const IntVar* const variable, - IntExpr* const delegate) override { - if (delegate != nullptr) { - delegate->Accept(this); - delegate_map_[variable] = delegate; - } - Register(variable); - } - - void VisitIntegerVariable(const IntVar* const variable, - const std::string& operation, int64 value, - IntVar* const delegate) override { - delegate->Accept(this); - delegate_map_[variable] = delegate; - Register(variable); - } - - void VisitIntervalVariable(const IntervalVar* const variable, - const std::string& operation, int64 value, - IntervalVar* const delegate) override { - if (delegate != nullptr) { - delegate->Accept(this); - } - Register(variable); - } - - void VisitSequenceVariable(const SequenceVar* const sequence) override { - for (int i = 0; i < sequence->size(); ++i) { - sequence->Interval(i)->Accept(this); - } - Register(sequence); - } - - // Visit integer expression argument. - void VisitIntegerExpressionArgument(const std::string& arg_name, - IntExpr* const argument) override { - VisitSubArgument(argument); - } - - void VisitIntegerVariableArrayArgument( - const std::string& arg_name, - const std::vector& arguments) override { - for (int i = 0; i < arguments.size(); ++i) { - VisitSubArgument(arguments[i]); - } - } - - // Visit interval argument. - void VisitIntervalArgument(const std::string& arg_name, - IntervalVar* const argument) override { - VisitSubArgument(argument); - } - - void VisitIntervalArrayArgument( - const std::string& arg_name, - const std::vector& arguments) override { - for (int i = 0; i < arguments.size(); ++i) { - VisitSubArgument(arguments[i]); - } - } - - // Visit sequence argument. - void VisitSequenceArgument(const std::string& arg_name, - SequenceVar* const argument) override { - VisitSubArgument(argument); - } - - void VisitSequenceArrayArgument( - const std::string& arg_name, - const std::vector& arguments) override { - for (int i = 0; i < arguments.size(); ++i) { - VisitSubArgument(arguments[i]); - } - } - - // Export - const std::unordered_map& expression_map() const { - return expression_map_; - } - const std::unordered_map& interval_map() const { - return interval_map_; - } - const std::unordered_map& sequence_map() const { - return sequence_map_; - } - const std::unordered_map& delegate_map() const { - return delegate_map_; - } - const std::vector& expression_list() const { - return expression_list_; - } - const std::vector& constraint_list() const { - return constraint_list_; - } - const std::vector& interval_list() const { - return interval_list_; - } - const std::vector& sequence_list() const { - return sequence_list_; - } - - private: - void Register(const IntExpr* const expression) { - if (!gtl::ContainsKey(expression_map_, expression)) { - const int index = expression_map_.size(); - CHECK_EQ(index, expression_list_.size()); - expression_map_[expression] = index; - expression_list_.push_back(expression); - } - } - - void Register(const Constraint* const constraint) { - constraint_list_.push_back(constraint); - } - - void Register(const IntervalVar* const interval) { - if (!gtl::ContainsKey(interval_map_, interval)) { - const int index = interval_map_.size(); - CHECK_EQ(index, interval_list_.size()); - interval_map_[interval] = index; - interval_list_.push_back(interval); - } - } - - void Register(const SequenceVar* const sequence) { - if (!gtl::ContainsKey(sequence_map_, sequence)) { - const int index = sequence_map_.size(); - CHECK_EQ(index, sequence_list_.size()); - sequence_map_[sequence] = index; - sequence_list_.push_back(sequence); - } - } - - void VisitSubArgument(IntExpr* const expression) { - if (!gtl::ContainsKey(expression_map_, expression)) { - expression->Accept(this); - } - } - - void VisitSubArgument(IntervalVar* const interval) { - if (!gtl::ContainsKey(interval_map_, interval)) { - interval->Accept(this); - } - } - - void VisitSubArgument(SequenceVar* const sequence) { - if (!gtl::ContainsKey(sequence_map_, sequence)) { - sequence->Accept(this); - } - } - - const std::string filename_; - std::unordered_map expression_map_; - std::unordered_map interval_map_; - std::unordered_map sequence_map_; - std::unordered_map delegate_map_; - std::vector expression_list_; - std::vector constraint_list_; - std::vector interval_list_; - std::vector sequence_list_; -}; - -// ----- Argument Holder ----- - -class ArgumentHolder { - public: - template - void ExportToProto(VectorMap* const tags, P* const proto) const { - for (const auto& it : integer_argument_) { - CpArgument* const arg_proto = proto->add_arguments(); - arg_proto->set_argument_index(tags->Add(it.first)); - arg_proto->set_integer_value(it.second); - arg_proto->set_type(CpArgument::INTEGER_VALUE); - } - - for (const auto& it : integer_array_argument_) { - CpArgument* const arg_proto = proto->add_arguments(); - arg_proto->set_argument_index(tags->Add(it.first)); - for (int64 value : it.second) { - arg_proto->add_integer_array(value); - } - arg_proto->set_type(CpArgument::INTEGER_ARRAY); - } - - for (const auto& it : integer_matrix_argument_) { - CpArgument* const arg_proto = proto->add_arguments(); - arg_proto->set_argument_index(tags->Add(it.first)); - CpIntegerMatrix* const matrix_proto = arg_proto->mutable_integer_matrix(); - const int columns = it.second.first; - CHECK_GT(columns, 0); - const int rows = it.second.second.size() / columns; - matrix_proto->set_rows(rows); - matrix_proto->set_columns(columns); - for (int64 value : it.second.second) { - matrix_proto->add_values(value); - } - arg_proto->set_type(CpArgument::INTEGER_MATRIX); - } - - for (const auto& it : integer_expression_argument_) { - CpArgument* const arg_proto = proto->add_arguments(); - arg_proto->set_argument_index(tags->Add(it.first)); - arg_proto->set_integer_expression_index(it.second); - arg_proto->set_type(CpArgument::EXPRESSION); - } - - for (const auto& it : integer_variable_array_argument_) { - CpArgument* const arg_proto = proto->add_arguments(); - arg_proto->set_argument_index(tags->Add(it.first)); - for (int expr : it.second) { - arg_proto->add_integer_expression_array(expr); - } - arg_proto->set_type(CpArgument::EXPRESSION_ARRAY); - } - - for (const auto& it : interval_argument_) { - CpArgument* const arg_proto = proto->add_arguments(); - arg_proto->set_argument_index(tags->Add(it.first)); - arg_proto->set_interval_index(it.second); - arg_proto->set_type(CpArgument::INTERVAL); - } - - for (const auto& it : interval_array_argument_) { - CpArgument* const arg_proto = proto->add_arguments(); - arg_proto->set_argument_index(tags->Add(it.first)); - for (int arg : it.second) { - arg_proto->add_interval_array(arg); - } - arg_proto->set_type(CpArgument::INTERVAL_ARRAY); - } - - for (const auto& it : sequence_argument_) { - CpArgument* const arg_proto = proto->add_arguments(); - arg_proto->set_argument_index(tags->Add(it.first)); - arg_proto->set_sequence_index(it.second); - arg_proto->set_type(CpArgument::SEQUENCE); - } - - for (const auto& it : sequence_array_argument_) { - CpArgument* const arg_proto = proto->add_arguments(); - arg_proto->set_argument_index(tags->Add(it.first)); - for (int arg : it.second) { - arg_proto->add_sequence_array(arg); - } - arg_proto->set_type(CpArgument::SEQUENCE_ARRAY); - } - } - - const std::string& type_name() const { return type_name_; } - - void set_type_name(const std::string& type_name) { type_name_ = type_name; } - - void set_integer_argument(const std::string& arg_name, int64 value) { - integer_argument_[arg_name] = value; - } - - void set_integer_array_argument(const std::string& arg_name, - const std::vector& values) { - integer_array_argument_[arg_name] = values; - } - - void set_integer_matrix_argument(const std::string& arg_name, - const IntTupleSet& values) { - const int rows = values.NumTuples(); - const int columns = values.Arity(); - std::pair> matrix = - std::make_pair(columns, std::vector()); - integer_matrix_argument_[arg_name] = matrix; - std::vector* const vals = &integer_matrix_argument_[arg_name].second; - for (int i = 0; i < rows; ++i) { - for (int j = 0; j < columns; ++j) { - vals->push_back(values.Value(i, j)); - } - } - } - - void set_integer_expression_argument(const std::string& arg_name, int index) { - integer_expression_argument_[arg_name] = index; - } - - void set_integer_variable_array_argument(const std::string& arg_name, - const int* const indices, int size) { - for (int i = 0; i < size; ++i) { - integer_variable_array_argument_[arg_name].push_back(indices[i]); - } - } - - void set_interval_argument(const std::string& arg_name, int index) { - interval_argument_[arg_name] = index; - } - - void set_interval_array_argument(const std::string& arg_name, - const int* const indices, int size) { - for (int i = 0; i < size; ++i) { - interval_array_argument_[arg_name].push_back(indices[i]); - } - } - - void set_sequence_argument(const std::string& arg_name, int index) { - sequence_argument_[arg_name] = index; - } - - void set_sequence_array_argument(const std::string& arg_name, - const int* const indices, int size) { - for (int i = 0; i < size; ++i) { - sequence_array_argument_[arg_name].push_back(indices[i]); - } - } - - int64 FindIntegerArgumentWithDefault(const std::string& arg_name, int64 def) { - return gtl::FindWithDefault(integer_argument_, arg_name, def); - } - - int64 FindIntegerArgumentOrDie(const std::string& arg_name) { - return gtl::FindOrDie(integer_argument_, arg_name); - } - - int64 FindIntegerExpressionArgumentOrDie(const std::string& arg_name) { - return gtl::FindOrDie(integer_expression_argument_, arg_name); - } - - private: - std::string type_name_; - std::unordered_map integer_expression_argument_; - std::unordered_map integer_argument_; - std::unordered_map interval_argument_; - std::unordered_map sequence_argument_; - std::unordered_map> integer_array_argument_; - std::unordered_map>> - integer_matrix_argument_; - std::unordered_map> - integer_variable_array_argument_; - std::unordered_map> interval_array_argument_; - std::unordered_map> sequence_array_argument_; -}; - -// ----- Second Pass Visitor ----- - -static const int kModelVersion = 1; - -// The second pass visitor will visited sorted expressions, interval -// vars and expressions and export them to a CpModel protocol -// buffer. -class SecondPassVisitor : public ModelVisitor { - public: - SecondPassVisitor(const FirstPassVisitor& first_pass, - CpModel* const model_proto) - : expression_map_(first_pass.expression_map()), - interval_map_(first_pass.interval_map()), - sequence_map_(first_pass.sequence_map()), - delegate_map_(first_pass.delegate_map()), - expression_list_(first_pass.expression_list()), - constraint_list_(first_pass.constraint_list()), - interval_list_(first_pass.interval_list()), - sequence_list_(first_pass.sequence_list()), - model_proto_(model_proto) {} - - ~SecondPassVisitor() override {} - - std::string DebugString() const override { return "SecondPassVisitor"; } - - void BeginVisitModel(const std::string& model_name) override { - model_proto_->set_model(model_name); - model_proto_->set_version(kModelVersion); - PushArgumentHolder(); - for (const IntExpr* const expr : expression_list_) { - expr->Accept(this); - } - - for (const IntervalVar* const var : interval_list_) { - var->Accept(this); - } - - for (const SequenceVar* const seq : sequence_list_) { - seq->Accept(this); - } - } - - void EndVisitModel(const std::string& model_name) override { - for (ArgumentHolder* const arg : extensions_) { - WriteModelExtension(arg); - } - PopArgumentHolder(); - // Write tags. - for (int i = 0; i < tags_.size(); ++i) { - model_proto_->add_tags(tags_.Element(i)); - } - } - - void BeginVisitConstraint(const std::string& type_name, - const Constraint* const constraint) override { - PushArgumentHolder(); - } - - void EndVisitConstraint(const std::string& type_name, - const Constraint* const constraint) override { - // We ignore cast constraints, they will be regenerated automatically. - if (constraint->IsCastConstraint()) { - PopArgumentHolder(); - return; - } - - const int index = model_proto_->constraints_size(); - CpConstraint* const constraint_proto = model_proto_->add_constraints(); - ExportToProto(constraint, constraint_proto, type_name, index); - if (constraint->HasName()) { - constraint_proto->set_name(constraint->name()); - } - PopArgumentHolder(); - } - - void BeginVisitIntegerExpression(const std::string& type_name, - const IntExpr* const expression) override { - PushArgumentHolder(); - } - - void EndVisitIntegerExpression(const std::string& type_name, - const IntExpr* const expression) override { - const int index = model_proto_->expressions_size(); - CpIntegerExpression* const expression_proto = - model_proto_->add_expressions(); - ExportToProto(expression, expression_proto, type_name, index); - PopArgumentHolder(); - } - - void BeginVisitExtension(const std::string& type_name) override { - PushExtension(type_name); - } - - void EndVisitExtension(const std::string& type_name) override { - PopAndSaveExtension(); - } - - void VisitIntegerArgument(const std::string& arg_name, int64 value) override { - top()->set_integer_argument(arg_name, value); - } - - void VisitIntegerArrayArgument(const std::string& arg_name, - const std::vector& values) override { - top()->set_integer_array_argument(arg_name, values); - } - - void VisitIntegerMatrixArgument(const std::string& arg_name, - const IntTupleSet& values) override { - top()->set_integer_matrix_argument(arg_name, values); - } - - void VisitIntegerExpressionArgument(const std::string& arg_name, - IntExpr* const argument) override { - top()->set_integer_expression_argument(arg_name, - FindExpressionIndexOrDie(argument)); - } - - void VisitIntegerVariableArrayArgument( - const std::string& arg_name, - const std::vector& arguments) override { - std::vector indices; - for (int i = 0; i < arguments.size(); ++i) { - indices.push_back(FindExpressionIndexOrDie(arguments[i])); - } - top()->set_integer_variable_array_argument(arg_name, indices.data(), - indices.size()); - } - - void VisitIntervalArgument(const std::string& arg_name, - IntervalVar* argument) override { - top()->set_interval_argument(arg_name, FindIntervalIndexOrDie(argument)); - } - - void VisitIntervalArrayArgument( - const std::string& arg_name, - const std::vector& arguments) override { - std::vector indices; - for (int i = 0; i < arguments.size(); ++i) { - indices.push_back(FindIntervalIndexOrDie(arguments[i])); - } - top()->set_interval_array_argument(arg_name, indices.data(), - indices.size()); - } - - void VisitSequenceArgument(const std::string& arg_name, - SequenceVar* argument) override { - top()->set_sequence_argument(arg_name, FindSequenceIndexOrDie(argument)); - } - - void VisitSequenceArrayArgument( - const std::string& arg_name, - const std::vector& arguments) override { - std::vector indices; - for (int i = 0; i < arguments.size(); ++i) { - indices.push_back(FindSequenceIndexOrDie(arguments[i])); - } - top()->set_sequence_array_argument(arg_name, indices.data(), - indices.size()); - } - - void VisitIntegerVariable(const IntVar* const variable, - IntExpr* const delegate) override { - if (delegate != nullptr) { - const int index = model_proto_->expressions_size(); - CpIntegerExpression* const var_proto = model_proto_->add_expressions(); - var_proto->set_index(index); - var_proto->set_type_index(TagIndex(ModelVisitor::kIntegerVariable)); - CpArgument* const sub_proto = var_proto->add_arguments(); - sub_proto->set_argument_index( - TagIndex(ModelVisitor::kExpressionArgument)); - sub_proto->set_integer_expression_index( - FindExpressionIndexOrDie(delegate)); - sub_proto->set_type(CpArgument::EXPRESSION); - } else { - const int index = model_proto_->expressions_size(); - CpIntegerExpression* const var_proto = model_proto_->add_expressions(); - var_proto->set_index(index); - var_proto->set_type_index(TagIndex(ModelVisitor::kIntegerVariable)); - if (variable->HasName()) { - var_proto->set_name(variable->name()); - } - if (variable->Size() == static_cast(variable->Max()) - - static_cast(variable->Min()) + 1) { - // Contiguous - CpArgument* const min_proto = var_proto->add_arguments(); - min_proto->set_argument_index(TagIndex(ModelVisitor::kMinArgument)); - min_proto->set_integer_value(variable->Min()); - min_proto->set_type(CpArgument::INTEGER_VALUE); - CpArgument* const max_proto = var_proto->add_arguments(); - max_proto->set_argument_index(TagIndex(ModelVisitor::kMaxArgument)); - max_proto->set_integer_value(variable->Max()); - max_proto->set_type(CpArgument::INTEGER_VALUE); - } else { - // Non Contiguous - CpArgument* const values_proto = var_proto->add_arguments(); - values_proto->set_argument_index( - TagIndex(ModelVisitor::kValuesArgument)); - std::unique_ptr it(variable->MakeDomainIterator(false)); - for (const int64 value : InitAndGetValues(it.get())) { - values_proto->add_integer_array(value); - } - values_proto->set_type(CpArgument::INTEGER_ARRAY); - } - } - } - - void VisitIntegerVariable(const IntVar* const variable, - const std::string& operation, int64 value, - IntVar* const delegate) override { - const int index = model_proto_->expressions_size(); - CpIntegerExpression* const var_proto = model_proto_->add_expressions(); - var_proto->set_index(index); - var_proto->set_type_index(TagIndex(ModelVisitor::kIntegerVariable)); - CpArgument* const sub_proto = var_proto->add_arguments(); - sub_proto->set_argument_index(TagIndex(ModelVisitor::kVariableArgument)); - sub_proto->set_integer_expression_index(FindExpressionIndexOrDie(delegate)); - sub_proto->set_type(CpArgument::EXPRESSION); - CpArgument* const value_proto = var_proto->add_arguments(); - value_proto->set_argument_index(TagIndex(operation)); - value_proto->set_integer_value(value); - value_proto->set_type(CpArgument::INTEGER_VALUE); - } - - void VisitIntervalVariable(const IntervalVar* const variable, - const std::string& operation, int64 value, - IntervalVar* const delegate) override { - if (delegate != nullptr) { - const int index = model_proto_->intervals_size(); - CpIntervalVariable* const var_proto = model_proto_->add_intervals(); - var_proto->set_index(index); - var_proto->set_type_index(TagIndex(ModelVisitor::kIntervalVariable)); - CpArgument* const sub_proto = var_proto->add_arguments(); - sub_proto->set_argument_index(TagIndex(operation)); - sub_proto->set_interval_index(FindIntervalIndexOrDie(delegate)); - sub_proto->set_integer_value(value); - if (operation == ModelVisitor::kStartSyncOnStartOperation || - operation == ModelVisitor::kStartSyncOnEndOperation) { - CHECK_EQ(delegate->DurationMin(), delegate->DurationMax()); - sub_proto->add_integer_array(delegate->DurationMin()); - } - } else { - const int index = model_proto_->intervals_size(); - CpIntervalVariable* const var_proto = model_proto_->add_intervals(); - var_proto->set_index(index); - var_proto->set_type_index(TagIndex(ModelVisitor::kIntervalVariable)); - if (variable->HasName()) { - var_proto->set_name(variable->name()); - } - CpArgument* const start_min_proto = var_proto->add_arguments(); - start_min_proto->set_argument_index( - TagIndex(ModelVisitor::kStartMinArgument)); - start_min_proto->set_integer_value(variable->StartMin()); - CpArgument* const start_max_proto = var_proto->add_arguments(); - start_max_proto->set_argument_index( - TagIndex(ModelVisitor::kStartMaxArgument)); - start_max_proto->set_integer_value(variable->StartMax()); - CpArgument* const end_min_proto = var_proto->add_arguments(); - end_min_proto->set_argument_index( - TagIndex(ModelVisitor::kEndMinArgument)); - end_min_proto->set_integer_value(variable->EndMin()); - CpArgument* const end_max_proto = var_proto->add_arguments(); - end_max_proto->set_argument_index( - TagIndex(ModelVisitor::kEndMaxArgument)); - end_max_proto->set_integer_value(variable->EndMax()); - CpArgument* const duration_min_proto = var_proto->add_arguments(); - duration_min_proto->set_argument_index( - TagIndex(ModelVisitor::kDurationMinArgument)); - duration_min_proto->set_integer_value(variable->DurationMin()); - CpArgument* const duration_max_proto = var_proto->add_arguments(); - duration_max_proto->set_argument_index( - TagIndex(ModelVisitor::kDurationMaxArgument)); - duration_max_proto->set_integer_value(variable->DurationMax()); - CpArgument* const optional_proto = var_proto->add_arguments(); - optional_proto->set_argument_index( - TagIndex(ModelVisitor::kOptionalArgument)); - optional_proto->set_integer_value(!variable->MustBePerformed()); - } - } - - void VisitSequenceVariable(const SequenceVar* const sequence) override { - const int index = model_proto_->sequences_size(); - CpSequenceVariable* const var_proto = model_proto_->add_sequences(); - var_proto->set_index(index); - var_proto->set_type_index(TagIndex(ModelVisitor::kSequenceVariable)); - if (sequence->HasName()) { - var_proto->set_name(sequence->name()); - } - CpArgument* const sub_proto = var_proto->add_arguments(); - sub_proto->set_argument_index(TagIndex(ModelVisitor::kIntervalsArgument)); - for (int i = 0; i < sequence->size(); ++i) { - IntervalVar* const interval = sequence->Interval(i); - sub_proto->add_interval_array(FindIntervalIndexOrDie(interval)); - } - sub_proto->set_type(CpArgument::INTERVAL_ARRAY); - } - - int TagIndex(const std::string& tag) { return tags_.Add(tag); } - - private: - void WriteModelExtension(ArgumentHolder* const holder) { - CHECK(holder != nullptr); - if (holder->type_name().compare(kObjectiveExtension) == 0) { - WriteObjective(holder); - } else if (holder->type_name().compare(kSearchLimitExtension) == 0) { - WriteSearchLimit(holder); - } else if (holder->type_name().compare(kVariableGroupExtension) == 0) { - WriteVariableGroup(holder); - } else { - LOG(INFO) << "Unknown model extension :" << holder->type_name(); - } - } - - void WriteObjective(ArgumentHolder* const holder) { - CHECK(holder != nullptr); - const bool maximize = holder->FindIntegerArgumentOrDie(kMaximizeArgument); - const int64 step = holder->FindIntegerArgumentOrDie(kStepArgument); - const int objective_index = - holder->FindIntegerExpressionArgumentOrDie(kExpressionArgument); - CpObjective* const objective_proto = model_proto_->mutable_objective(); - objective_proto->set_maximize(maximize); - objective_proto->set_step(step); - objective_proto->set_objective_index(objective_index); - } - - void WriteSearchLimit(ArgumentHolder* const holder) { - CHECK(holder != nullptr); - SearchLimitParameters* const proto = model_proto_->mutable_search_limit(); - proto->set_time( - holder->FindIntegerArgumentWithDefault(kTimeLimitArgument, kint64max)); - proto->set_branches(holder->FindIntegerArgumentWithDefault( - kBranchesLimitArgument, kint64max)); - proto->set_failures(holder->FindIntegerArgumentWithDefault( - kFailuresLimitArgument, kint64max)); - proto->set_solutions(holder->FindIntegerArgumentWithDefault( - kSolutionLimitArgument, kint64max)); - proto->set_smart_time_check( - holder->FindIntegerArgumentWithDefault(kSmartTimeCheckArgument, false)); - proto->set_cumulative( - holder->FindIntegerArgumentWithDefault(kCumulativeArgument, false)); - } - - void WriteVariableGroup(ArgumentHolder* const holder) { - CpVariableGroup* const group_proto = model_proto_->add_variable_groups(); - holder->ExportToProto(&tags_, group_proto); - } - - template - void ExportToProto(const A* const argument, P* const proto, - const std::string& type_name, int index) { - CHECK(proto != nullptr); - CHECK(argument != nullptr); - proto->set_index(index); - proto->set_type_index(TagIndex(type_name)); - if (argument->HasName()) { - proto->set_name(argument->name()); - } - top()->ExportToProto(&tags_, proto); - for (ArgumentHolder* const arg : extensions_) { - CpExtension* const extension_proto = proto->add_extensions(); - extension_proto->set_type_index(TagIndex(arg->type_name())); - arg->ExportToProto(&tags_, extension_proto); - } - } - - void PushArgumentHolder() { holders_.push_back(new ArgumentHolder); } - - void PopArgumentHolder() { - CHECK(!holders_.empty()); - delete holders_.back(); - holders_.pop_back(); - gtl::STLDeleteElements(&extensions_); - extensions_.clear(); - } - - void PushExtension(const std::string& type_name) { - PushArgumentHolder(); - holders_.back()->set_type_name(type_name); - } - - void PopAndSaveExtension() { - CHECK(!holders_.empty()); - extensions_.push_back(holders_.back()); - holders_.pop_back(); - } - - ArgumentHolder* top() const { - CHECK(!holders_.empty()); - return holders_.back(); - } - - int FindExpressionIndexOrDie(IntExpr* const expression) const { - return gtl::FindOrDie(expression_map_, expression); - } - - int FindIntervalIndexOrDie(IntervalVar* const interval) const { - return gtl::FindOrDie(interval_map_, interval); - } - - int FindSequenceIndexOrDie(SequenceVar* const sequence) const { - return gtl::FindOrDie(sequence_map_, sequence); - } - - std::unordered_map expression_map_; - std::unordered_map interval_map_; - std::unordered_map sequence_map_; - std::unordered_map delegate_map_; - std::vector expression_list_; - std::vector constraint_list_; - std::vector interval_list_; - std::vector sequence_list_; - CpModel* const model_proto_; - - std::vector holders_; - std::vector extensions_; - VectorMap tags_; -}; - -// ---------- Model Protocol Reader ---------- - -#define VERIFY(expr) \ - if (!(expr)) return nullptr -#define VERIFY_BOOL(expr) \ - if (!(expr)) return false -#define VERIFY_EQ(e1, e2) \ - if ((e1) != (e2)) return nullptr - -// ----- kAbs ----- - -IntExpr* BuildAbs(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - return builder->solver()->MakeAbs(expr); -} - -// ----- kAbsEqual ----- - -Constraint* BuildAbsEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - return builder->solver()->MakeAbsEquality(expr->Var(), target->Var()); -} - -// ----- kAllDifferent ----- - -Constraint* BuildAllDifferent(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - int64 escape = 0; - if (builder->ScanArguments(ModelVisitor::kValueArgument, proto, &escape)) { - return builder->solver()->MakeAllDifferentExcept(vars, escape); - } else { - int64 range = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kRangeArgument, proto, &range)); - return builder->solver()->MakeAllDifferent(vars, range); - } -} - -// ----- kAllowedAssignments ----- - -Constraint* BuildAllowedAssignments(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - IntTupleSet tuples(vars.size()); - VERIFY(builder->ScanArguments(ModelVisitor::kTuplesArgument, proto, &tuples)); - return builder->solver()->MakeAllowedAssignments(vars, tuples); -} - -// ----- kBetween ----- - -Constraint* BuildBetween(CpModelLoader* const builder, - const CpConstraint& proto) { - int64 value_min = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kMinArgument, proto, &value_min)); - int64 value_max = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kMaxArgument, proto, &value_max)); - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - return builder->solver()->MakeBetweenCt(expr->Var(), value_min, value_max); -} - -// ----- kConditionalExpr ----- - -IntExpr* BuildConditionalExpr(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* condition = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kVariableArgument, proto, - &condition)); - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeConditionalExpression(condition->Var(), expr, - value); -} - -// ----- kCircuit ----- - -Constraint* BuildCircuit(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kNextsArgument, proto, &vars)); - int64 v; - VERIFY(builder->ScanArguments(ModelVisitor::kPartialArgument, proto, &v)); - if (v == 1) { - return builder->solver()->MakeSubCircuit(vars); - } else { - return builder->solver()->MakeCircuit(vars); - } -} - -// ----- kConvexPiecewise ----- - -IntExpr* BuildConvexPiecewise(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - int64 early_cost = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kEarlyCostArgument, proto, - &early_cost)); - int64 early_date = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kEarlyDateArgument, proto, - &early_date)); - int64 late_cost = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kLateCostArgument, proto, - &late_cost)); - int64 late_date = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kLateDateArgument, proto, - &late_date)); - return builder->solver()->MakeConvexPiecewiseExpr( - expr->Var(), early_cost, early_date, late_date, late_cost); -} - -// ----- kCountEqual ----- - -Constraint* BuildCountEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - int64 count = 0; - if (builder->ScanArguments(ModelVisitor::kCountArgument, proto, &value)) { - return builder->solver()->MakeCount(vars, value, count); - } else { - IntExpr* count_expr = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kCountArgument, proto, - &count_expr)); - return builder->solver()->MakeCount(vars, value, count_expr->Var()); - } -} - -// ----- kCover ----- - -Constraint* BuildCover(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY( - builder->ScanArguments(ModelVisitor::kIntervalsArgument, proto, &vars)); - IntervalVar* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - return builder->solver()->MakeCover(vars, target); -} - -// ----- kCumulative ----- - -Constraint* BuildCumulative(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY( - builder->ScanArguments(ModelVisitor::kIntervalsArgument, proto, &vars)); - std::vector demands; - VERIFY( - builder->ScanArguments(ModelVisitor::kDemandsArgument, proto, &demands)); - int64 capacity; - VERIFY(builder->ScanArguments(ModelVisitor::kCapacityArgument, proto, - &capacity)); - std::string name; - if (!proto.name().empty()) { - name = proto.name(); - } - return builder->solver()->MakeCumulative(vars, demands, capacity, name); -} - -// ----- kDeviation ----- - -Constraint* BuildDeviation(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeDeviation(vars, target->Var(), value); -} - -// ----- kDifference ----- - -IntExpr* BuildDifference(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeDifference(left, right); - } - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeDifference(value, expr); -} - -// ----- kDisjunctive ----- - -Constraint* BuildDisjunctive(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - if (builder->ScanArguments(ModelVisitor::kIntervalsArgument, proto, &vars)) { - return builder->solver()->MakeDisjunctiveConstraint(vars, proto.name()); - } else { - std::vector x; - std::vector dx; - std::vector y; - std::vector dy; - VERIFY(builder->ScanArguments(ModelVisitor::kPositionXArgument, proto, &x)); - VERIFY(builder->ScanArguments(ModelVisitor::kPositionYArgument, proto, &y)); - VERIFY(builder->ScanArguments(ModelVisitor::kSizeXArgument, proto, &dx)); - VERIFY(builder->ScanArguments(ModelVisitor::kSizeYArgument, proto, &dy)); - return builder->solver()->MakeNonOverlappingBoxesConstraint(x, y, dx, dy); - } -} - -// ----- kDistribute ----- - -Constraint* BuildDistribute(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - if (builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)) { - std::vector cards; - if (builder->ScanArguments(ModelVisitor::kCardsArgument, proto, &cards)) { - std::vector values; - if (builder->ScanArguments(ModelVisitor::kValuesArgument, proto, - &values)) { - return builder->solver()->MakeDistribute(vars, values, cards); - } else { - return builder->solver()->MakeDistribute(vars, cards); - } - } else { - int64 card_min = 0; - VERIFY( - builder->ScanArguments(ModelVisitor::kMinArgument, proto, &card_min)); - int64 card_max = 0; - VERIFY( - builder->ScanArguments(ModelVisitor::kMaxArgument, proto, &card_max)); - int64 card_size = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kSizeArgument, proto, - &card_size)); - return builder->solver()->MakeDistribute(vars, card_min, card_max, - card_size); - } - } else { - std::vector cards; - VERIFY(builder->ScanArguments(ModelVisitor::kCardsArgument, proto, &cards)); - return builder->solver()->MakeDistribute(vars, cards); - } -} - -// ----- kDivide ----- - -IntExpr* BuildDivide(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeDiv(left, right); - } - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeDiv(expr, value); -} - -// ----- kDurationExpr ----- - -IntExpr* BuildDurationExpr(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntervalVar* var = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kIntervalArgument, proto, &var)); - return var->DurationExpr(); -} - -// ----- kElement ----- - -IntExpr* BuildElement(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* index = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kIndexArgument, proto, &index)); - std::vector values; - IntExpr* index2 = nullptr; - if (builder->ScanArguments(ModelVisitor::kIndex2Argument, proto, &index2)) { - int64 index_min = 0; - VERIFY( - builder->ScanArguments(ModelVisitor::kMinArgument, proto, &index_min)); - int64 index_max = 0; - VERIFY( - builder->ScanArguments(ModelVisitor::kMaxArgument, proto, &index_max)); - const int extension_tag_index = - builder->TagIndex(ModelVisitor::kInt64ToInt64Extension); - ArrayWithOffset* const array = - builder->solver()->RevAlloc( - new ArrayWithOffset(index_min, index_max)); - for (int i = index_min; i <= index_max; ++i) { - array->SetValue(i, MakeFunctionFromProto( - builder, proto.extensions(i - index_min), - extension_tag_index)); - } - return builder->solver()->MakeElement( - [array](int64 i, int64 j) { return array->Evaluate(i)(j); }, - index->Var(), index2->Var()); - } - if (proto.extensions_size() > 0) { - VERIFY_EQ(1, proto.extensions_size()); - const int extension_tag_index = - builder->TagIndex(ModelVisitor::kInt64ToInt64Extension); - Solver::IndexEvaluator1 callback = MakeFunctionFromProto( - builder, proto.extensions(0), extension_tag_index); - return builder->solver()->MakeElement(callback, index->Var()); - } - if (builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)) { - return builder->solver()->MakeElement(values, index->Var()); - } - std::vector vars; - if (builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)) { - return builder->solver()->MakeElement(vars, index->Var()); - } - return nullptr; -} - -// ----- kElementEqual ----- - -Constraint* BuildElementEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* index = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kIndexArgument, proto, &index)); - std::vector values; - if (builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)) { - IntExpr* target = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - return builder->solver()->MakeElementEquality(values, index->Var(), - target->Var()); - } else { - std::vector vars; - if (builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)) { - IntExpr* target = nullptr; - if (builder->ScanArguments(ModelVisitor::kTargetArgument, proto, - &target)) { - return builder->solver()->MakeElementEquality(vars, index->Var(), - target->Var()); - } else { - int64 target_value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, - &target_value)); - return builder->solver()->MakeElementEquality(vars, index->Var(), - target_value); - } - } - } - return nullptr; -} - -// ----- kEndExpr ----- - -IntExpr* BuildEndExpr(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntervalVar* var = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kIntervalArgument, proto, &var)); - return var->EndExpr(); -} - -// ----- kEquality ----- - -Constraint* BuildEquality(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeEquality(left, right); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeEquality(expr, value); - } - IntervalVar* vleft = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &vleft)) { - IntervalVar* vright = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kRightArgument, proto, &vright)); - return builder->solver()->MakeEquality(vleft, vright); - } - return nullptr; -} - -// ----- kFalseConstraint ----- - -Constraint* BuildFalseConstraint(CpModelLoader* const builder, - const CpConstraint& proto) { - return builder->solver()->MakeFalseConstraint(); -} - -// ----- kGreater ----- - -Constraint* BuildGreater(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* left = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeGreater(left, right); -} - -// ----- kGreaterOrEqual ----- - -Constraint* BuildGreaterOrEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeGreaterOrEqual(left, right); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeGreaterOrEqual(expr, value); - } - return nullptr; -} - -// ----- kIndexOf ----- - -Constraint* BuildIndexOf(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* index = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kIndexArgument, proto, &index)); - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - int64 target_value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, - &target_value)); - return builder->solver()->MakeIndexOfConstraint(vars, index->Var(), - target_value); -} - -// ----- kIntegerVariable ----- - -IntExpr* BuildIntegerVariable(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* sub_expression = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, - &sub_expression)) { - IntVar* const result = sub_expression->Var(); - if (!proto.name().empty()) { - result->set_name(proto.name()); - } - return result; - } - IntExpr* sub_var = nullptr; - if (builder->ScanArguments(ModelVisitor::kVariableArgument, proto, - &sub_var)) { - int64 value; - IntExpr* result = nullptr; - if (builder->ScanArguments(ModelVisitor::kSumOperation, proto, &value)) { - result = builder->solver()->MakeSum(sub_var, value); - } else if (builder->ScanArguments(ModelVisitor::kDifferenceOperation, proto, - &value)) { - result = builder->solver()->MakeDifference(value, sub_var); - } else if (builder->ScanArguments(ModelVisitor::kProductOperation, proto, - &value)) { - result = builder->solver()->MakeProd(sub_var, value); - } - if (!proto.name().empty()) { - result->set_name(proto.name()); - } - return result; - } - int64 var_min = 0; - if (builder->ScanArguments(ModelVisitor::kMinArgument, proto, &var_min)) { - int64 var_max = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kMaxArgument, proto, &var_max)); - return builder->solver()->MakeIntVar(var_min, var_max, proto.name()); - } - std::vector values; - if (builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)) { - return builder->solver()->MakeIntVar(values, proto.name()); - } - return nullptr; -} - -// ----- kIntervalBinaryRelation ----- - -Constraint* BuildIntervalBinaryRelation(CpModelLoader* const builder, - const CpConstraint& proto) { - IntervalVar* left = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); - IntervalVar* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - int64 relation = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kRelationArgument, proto, - &relation)); - Solver::BinaryIntervalRelation rel = - static_cast(relation); - return builder->solver()->MakeIntervalVarRelation(left, rel, right); -} - -// ----- kIntervalDisjunction ----- - -Constraint* BuildIntervalDisjunction(CpModelLoader* const builder, - const CpConstraint& proto) { - IntervalVar* left = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); - IntervalVar* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - return builder->solver()->MakeTemporalDisjunction(left, right, target->Var()); -} - -// ----- kIntervalUnaryRelation ----- - -Constraint* BuildIntervalUnaryRelation(CpModelLoader* const builder, - const CpConstraint& proto) { - IntervalVar* interval = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kIntervalArgument, proto, - &interval)); - int64 date = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &date)); - int64 relation = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kRelationArgument, proto, - &relation)); - Solver::UnaryIntervalRelation rel = - static_cast(relation); - return builder->solver()->MakeIntervalVarRelation(interval, rel, date); -} - -// ----- kIntervalVariable ----- - -IntervalVar* BuildIntervalVariable(CpModelLoader* const builder, - const CpIntervalVariable& proto) { - Solver* const solver = builder->solver(); - int64 start_min = 0; - if (builder->ScanArguments(ModelVisitor::kStartMinArgument, proto, - &start_min)) { - int64 start_max = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kStartMaxArgument, proto, - &start_max)); - int64 end_min = 0; - VERIFY( - builder->ScanArguments(ModelVisitor::kEndMinArgument, proto, &end_min)); - int64 end_max = 0; - VERIFY( - builder->ScanArguments(ModelVisitor::kEndMaxArgument, proto, &end_max)); - int64 duration_min = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kDurationMinArgument, proto, - &duration_min)); - int64 duration_max = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kDurationMaxArgument, proto, - &duration_max)); - int64 optional = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kOptionalArgument, proto, - &optional)); - VERIFY_EQ(duration_max, duration_min); - VERIFY_EQ(end_max - duration_max, start_max); - VERIFY_EQ(end_min - duration_min, start_min); - const std::string name = proto.name(); - if (start_min == start_max) { - return solver->MakeFixedInterval(start_min, duration_min, name); - } else { - return solver->MakeFixedDurationIntervalVar(start_min, start_max, - duration_min, optional, name); - } - } else { - VERIFY_EQ(1, proto.arguments_size()); - const CpArgument& sub_proto = proto.arguments(0); - IntervalVar* const derived = - builder->IntervalVariable(sub_proto.interval_index()); - const int operation_index = sub_proto.argument_index(); - DCHECK_NE(-1, operation_index); - if (operation_index == builder->TagIndex(ModelVisitor::kMirrorOperation)) { - return solver->MakeMirrorInterval(derived); - } else if (operation_index == - builder->TagIndex(ModelVisitor::kRelaxedMaxOperation)) { - return solver->MakeIntervalRelaxedMax(derived); - } else if (operation_index == - builder->TagIndex(ModelVisitor::kRelaxedMinOperation)) { - return solver->MakeIntervalRelaxedMin(derived); - } else if (operation_index == - builder->TagIndex(ModelVisitor::kStartSyncOnStartOperation)) { - const int64 delay = sub_proto.integer_value(); - VERIFY_EQ(1, sub_proto.integer_array_size()); - const int64 duration = sub_proto.integer_array(0); - return solver->MakeFixedDurationStartSyncedOnStartIntervalVar( - derived, duration, delay); - } else if (operation_index == - builder->TagIndex(ModelVisitor::kStartSyncOnEndOperation)) { - const int64 delay = sub_proto.integer_value(); - VERIFY_EQ(1, sub_proto.integer_array_size()); - const int64 duration = sub_proto.integer_array(0); - return solver->MakeFixedDurationStartSyncedOnEndIntervalVar( - derived, duration, delay); - } - } - return nullptr; -} - -// ----- kInversePermutation ----- - -Constraint* BuildInversePermutation(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector left; - VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); - std::vector right; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeInversePermutationConstraint(left, right); -} - -// ----- kIsBetween ----- - -Constraint* BuildIsBetween(CpModelLoader* const builder, - const CpConstraint& proto) { - int64 value_min = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kMinArgument, proto, &value_min)); - int64 value_max = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kMaxArgument, proto, &value_max)); - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - return builder->solver()->MakeIsBetweenCt(expr->Var(), value_min, value_max, - target->Var()); -} - -// ----- kIsDifferent ----- - -Constraint* BuildIsDifferent(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeIsDifferentCt(left, right, target->Var()); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeIsDifferentCstCt(expr, value, target->Var()); - } - return nullptr; -} - -// ----- kIsEqual ----- - -Constraint* BuildIsEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeIsEqualCt(left, right, target->Var()); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeIsEqualCstCt(expr, value, target->Var()); - } - return nullptr; -} - -// ----- kIsGreater ----- - -Constraint* BuildIsGreater(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - IntExpr* left = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeIsGreaterCt(left, right, target->Var()); -} - -// ----- kIsGreaterOrEqual ----- - -Constraint* BuildIsGreaterOrEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeIsGreaterOrEqualCt(left, right, - target->Var()); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeIsGreaterOrEqualCstCt(expr, value, - target->Var()); - } - return nullptr; -} - -// ----- kIsLess ----- - -Constraint* BuildIsLess(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - IntExpr* left = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeIsLessCt(left, right, target->Var()); -} - -// ----- kIsLessOrEqual ----- - -Constraint* BuildIsLessOrEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeIsLessOrEqualCt(left, right, target->Var()); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeIsLessOrEqualCstCt(expr, value, - target->Var()); - } - return nullptr; -} - -// ----- kIsMember ----- - -Constraint* BuildIsMember(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector values; - VERIFY(builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)); - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - return builder->solver()->MakeIsMemberCt(expr->Var(), values, target->Var()); -} - -// ----- kLess ----- - -Constraint* BuildLess(CpModelLoader* const builder, const CpConstraint& proto) { - IntExpr* left = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeLess(left, right); -} - -// ----- kLessOrEqual ----- - -Constraint* BuildLessOrEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeLessOrEqual(left, right); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeLessOrEqual(expr, value); - } - return nullptr; -} - -// ----- kLexLess ----- - -Constraint* BuildLexLess(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector left; - VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); - std::vector right; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return value == 1 ? builder->solver()->MakeLexicalLess(left, right) - : builder->solver()->MakeLexicalLessOrEqual(left, right); -} - -// ----- kMapDomain ----- - -Constraint* BuildMapDomain(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - return builder->solver()->MakeMapDomain(target->Var(), vars); -} - -// ----- kMax ----- - -IntExpr* BuildMax(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeMax(left, right); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeMax(expr, value); - } - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - return builder->solver()->MakeMax(vars); -} - -// ----- kMaxEqual ----- - -Constraint* BuildMaxEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - return builder->solver()->MakeMaxEquality(vars, target->Var()); -} - -// ----- kMember ----- - -Constraint* BuildMember(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector values; - VERIFY(builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)); - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - return builder->solver()->MakeMemberCt(expr->Var(), values); -} - -// ----- kMin ----- - -IntExpr* BuildMin(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeMin(left, right); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeMin(expr, value); - } - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - return builder->solver()->MakeMin(vars); -} - -// ----- kMinEqual ----- - -Constraint* BuildMinEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - return builder->solver()->MakeMinEquality(vars, target->Var()); -} - -// ----- kNoCycle ----- - -Constraint* BuildNoCycle(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector nexts; - VERIFY(builder->ScanArguments(ModelVisitor::kNextsArgument, proto, &nexts)); - std::vector active; - VERIFY(builder->ScanArguments(ModelVisitor::kActiveArgument, proto, &active)); - int64 assume_paths = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kAssumePathsArgument, proto, - &assume_paths)); - Solver::IndexFilter1 sink_handler = nullptr; - if (proto.extensions_size() > 0) { - VERIFY_EQ(1, proto.extensions_size()); - const int tag_index = - builder->TagIndex(ModelVisitor::kInt64ToBoolExtension); - sink_handler = - MakeFunctionFromProto(builder, proto.extensions(0), tag_index); - } - return builder->solver()->MakeNoCycle(nexts, active, sink_handler, - assume_paths); -} - -// ----- kNonEqual ----- - -Constraint* BuildNonEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeNonEquality(left, right); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeNonEquality(expr, value); - } - return nullptr; -} - -// ----- kNotBetween ----- - -Constraint* BuildNotBetween(CpModelLoader* const builder, - const CpConstraint& proto) { - int64 value_min = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kMinArgument, proto, &value_min)); - int64 value_max = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kMaxArgument, proto, &value_max)); - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - return builder->solver()->MakeNotBetweenCt(expr->Var(), value_min, value_max); -} - -// ----- kNotMember ----- - -Constraint* BuildNotMember(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector starts; - VERIFY(builder->ScanArguments(ModelVisitor::kStartsArgument, proto, &starts)); - std::vector ends; - VERIFY(builder->ScanArguments(ModelVisitor::kEndsArgument, proto, &ends)); - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - return builder->solver()->MakeNotMemberCt(expr, starts, ends); -} - -// ----- kNullIntersect ----- - -Constraint* BuildNullIntersect(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector left; - VERIFY(builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)); - std::vector right; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - int64 escape = 0; - if (builder->ScanArguments(ModelVisitor::kValueArgument, proto, &escape)) { - return builder->solver()->MakeNullIntersectExcept(left, right, escape); - } else { - return builder->solver()->MakeNullIntersect(left, right); - } -} - -// ----- kOpposite ----- - -IntExpr* BuildOpposite(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - return builder->solver()->MakeOpposite(expr); -} - -// ----- kPack ----- - -bool AddUsageLessConstantDimension(Pack* const pack, - CpModelLoader* const builder, - const CpExtension& proto) { - std::vector weights; - VERIFY_BOOL(builder->ScanArguments(ModelVisitor::kCoefficientsArgument, proto, - &weights)); - std::vector upper; - VERIFY_BOOL( - builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &upper)); - pack->AddWeightedSumLessOrEqualConstantDimension(weights, upper); - return true; -} - -bool AddCountAssignedItemsDimension(Pack* const pack, - CpModelLoader* const builder, - const CpExtension& proto) { - IntExpr* target = nullptr; - VERIFY_BOOL( - builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - pack->AddCountAssignedItemsDimension(target->Var()); - return true; -} - -bool AddCountUsedBinDimension(Pack* const pack, CpModelLoader* const builder, - const CpExtension& proto) { - IntExpr* target = nullptr; - VERIFY_BOOL( - builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - pack->AddCountUsedBinDimension(target->Var()); - return true; -} - -bool AddUsageEqualVariableDimension(Pack* const pack, - CpModelLoader* const builder, - const CpExtension& proto) { - std::vector weights; - VERIFY_BOOL(builder->ScanArguments(ModelVisitor::kCoefficientsArgument, proto, - &weights)); - std::vector loads; - VERIFY_BOOL( - builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &loads)); - pack->AddWeightedSumEqualVarDimension(weights, loads); - return true; -} - -bool AddVariableUsageLessConstantDimension(Pack* const pack, - CpModelLoader* const builder, - const CpExtension& proto) { - std::vector uppers; - VERIFY_BOOL( - builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &uppers)); - std::vector usages; - VERIFY_BOOL( - builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &usages)); - pack->AddSumVariableWeightsLessOrEqualConstantDimension(usages, uppers); - return true; -} - -bool AddWeightedSumOfAssignedDimension(Pack* const pack, - CpModelLoader* const builder, - const CpExtension& proto) { - std::vector weights; - VERIFY_BOOL(builder->ScanArguments(ModelVisitor::kCoefficientsArgument, proto, - &weights)); - IntExpr* target = nullptr; - VERIFY_BOOL( - builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - pack->AddWeightedSumOfAssignedDimension(weights, target->Var()); - return true; -} - -#define IS_TYPE(index, builder, tag) \ - index == builder->TagIndex(ModelVisitor::tag) - -Constraint* BuildPack(CpModelLoader* const builder, const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - int64 bins = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kSizeArgument, proto, &bins)); - Pack* const pack = builder->solver()->MakePack(vars, bins); - // Add dimensions. They are stored as extensions in the proto. - for (int i = 0; i < proto.extensions_size(); ++i) { - const CpExtension& dimension_proto = proto.extensions(i); - const int type_index = dimension_proto.type_index(); - if (IS_TYPE(type_index, builder, kUsageLessConstantExtension)) { - VERIFY(AddUsageLessConstantDimension(pack, builder, dimension_proto)); - } else if (IS_TYPE(type_index, builder, kCountAssignedItemsExtension)) { - VERIFY(AddCountAssignedItemsDimension(pack, builder, dimension_proto)); - } else if (IS_TYPE(type_index, builder, kCountUsedBinsExtension)) { - VERIFY(AddCountUsedBinDimension(pack, builder, dimension_proto)); - } else if (IS_TYPE(type_index, builder, kUsageEqualVariableExtension)) { - VERIFY(AddUsageEqualVariableDimension(pack, builder, dimension_proto)); - } else if (IS_TYPE(type_index, builder, - kVariableUsageLessConstantExtension)) { - VERIFY(AddVariableUsageLessConstantDimension(pack, builder, - dimension_proto)); - } else if (IS_TYPE(type_index, builder, - kWeightedSumOfAssignedEqualVariableExtension)) { - VERIFY(AddWeightedSumOfAssignedDimension(pack, builder, dimension_proto)); - } else { - LOG(ERROR) << "Unrecognized extension " << dimension_proto.DebugString(); - return nullptr; - } - } - return pack; -} -#undef IS_TYPE - -// ----- kPathCumul ----- - -Constraint* BuildPathCumul(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector nexts; - VERIFY(builder->ScanArguments(ModelVisitor::kNextsArgument, proto, &nexts)); - std::vector active; - VERIFY(builder->ScanArguments(ModelVisitor::kActiveArgument, proto, &active)); - std::vector cumuls; - VERIFY(builder->ScanArguments(ModelVisitor::kCumulsArgument, proto, &cumuls)); - std::vector transits; - VERIFY(builder->ScanArguments(ModelVisitor::kTransitsArgument, proto, - &transits)); - return builder->solver()->MakePathCumul(nexts, active, cumuls, transits); -} - -// ----- kDelayedPathCumul ----- - -Constraint* BuildDelayedPathCumul(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector nexts; - VERIFY(builder->ScanArguments(ModelVisitor::kNextsArgument, proto, &nexts)); - std::vector active; - VERIFY(builder->ScanArguments(ModelVisitor::kActiveArgument, proto, &active)); - std::vector cumuls; - VERIFY(builder->ScanArguments(ModelVisitor::kCumulsArgument, proto, &cumuls)); - std::vector transits; - VERIFY(builder->ScanArguments(ModelVisitor::kTransitsArgument, proto, - &transits)); - return builder->solver()->MakeDelayedPathCumul(nexts, active, cumuls, - transits); -} - -// ----- kPerformedExpr ----- - -IntExpr* BuildPerformedExpr(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntervalVar* var = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kIntervalArgument, proto, &var)); - return var->PerformedExpr(); -} - -// ----- kPower ----- - -IntExpr* BuildPower(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakePower(expr, value); -} - -// ----- kProduct ----- - -IntExpr* BuildProduct(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeProd(left, right); - } - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeProd(expr, value); -} - -// ----- kScalProd ----- - -IntExpr* BuildScalProd(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - std::vector values; - VERIFY(builder->ScanArguments(ModelVisitor::kCoefficientsArgument, proto, - &values)); - return builder->solver()->MakeScalProd(vars, values); -} - -// ----- kScalProdEqual ----- - -Constraint* BuildScalProdEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - std::vector values; - VERIFY(builder->ScanArguments(ModelVisitor::kCoefficientsArgument, proto, - &values)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeScalProdEquality(vars, values, value); -} - -// ----- kScalProdGreaterOrEqual ----- - -Constraint* BuildScalProdGreaterOrEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - std::vector values; - VERIFY(builder->ScanArguments(ModelVisitor::kCoefficientsArgument, proto, - &values)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeScalProdGreaterOrEqual(vars, values, value); -} - -// ----- kScalProdLessOrEqual ----- - -Constraint* BuildScalProdLessOrEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - std::vector values; - VERIFY(builder->ScanArguments(ModelVisitor::kCoefficientsArgument, proto, - &values)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeScalProdLessOrEqual(vars, values, value); -} - -// ----- kSemiContinuous ----- - -IntExpr* BuildSemiContinuous(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - int64 fixed_charge = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kFixedChargeArgument, proto, - &fixed_charge)); - int64 step = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kStepArgument, proto, &step)); - return builder->solver()->MakeSemiContinuousExpr(expr, fixed_charge, step); -} - -// ----- kSortingConstraint ----- - -Constraint* BuildSortingConstraint(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - std::vector targets; - VERIFY( - builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &targets)); - return builder->solver()->MakeSortingConstraint(vars, targets); -} - -// ----- kSquare ----- - -IntExpr* BuildSquare(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* expr = nullptr; - VERIFY( - builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)); - return builder->solver()->MakeSquare(expr); -} - -// ----- kStartExpr ----- - -IntExpr* BuildStartExpr(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntervalVar* var = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kIntervalArgument, proto, &var)); - return var->StartExpr(); -} - -// ----- kSum ----- - -IntExpr* BuildSum(CpModelLoader* const builder, - const CpIntegerExpression& proto) { - IntExpr* left = nullptr; - if (builder->ScanArguments(ModelVisitor::kLeftArgument, proto, &left)) { - IntExpr* right = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kRightArgument, proto, &right)); - return builder->solver()->MakeSum(left, right); - } - IntExpr* expr = nullptr; - if (builder->ScanArguments(ModelVisitor::kExpressionArgument, proto, &expr)) { - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeSum(expr, value); - } - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - return builder->solver()->MakeSum(vars); -} - -// ----- kSumEqual ----- - -Constraint* BuildSumEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - int64 value = 0; - if (builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)) { - return builder->solver()->MakeSumEquality(vars, value); - } - IntExpr* target = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)); - return builder->solver()->MakeSumEquality(vars, target->Var()); -} - -// ----- kSumGreaterOrEqual ----- - -Constraint* BuildSumGreaterOrEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeSumGreaterOrEqual(vars, value); -} - -// ----- kSumLessOrEqual ----- - -Constraint* BuildSumLessOrEqual(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - int64 value = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kValueArgument, proto, &value)); - return builder->solver()->MakeSumLessOrEqual(vars, value); -} - -// ----- kTransition ----- - -Constraint* BuildTransition(CpModelLoader* const builder, - const CpConstraint& proto) { - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - IntTupleSet tuples(3); - VERIFY(builder->ScanArguments(ModelVisitor::kTuplesArgument, proto, &tuples)); - int64 initial_state = 0; - VERIFY(builder->ScanArguments(ModelVisitor::kInitialState, proto, - &initial_state)); - std::vector final_states; - VERIFY(builder->ScanArguments(ModelVisitor::kFinalStatesArgument, proto, - &final_states)); - - return builder->solver()->MakeTransitionConstraint( - vars, tuples, initial_state, final_states); -} - -// ----- kTrueConstraint ----- - -Constraint* BuildTrueConstraint(CpModelLoader* const builder, - const CpConstraint& proto) { - return builder->solver()->MakeTrueConstraint(); -} - -// ----- kVarValueWatcher ----- - -Constraint* BuildVarValueWatcher(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* expr = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kVariableArgument, proto, &expr)); - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - std::vector values; - VERIFY(builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)); - return SetIsEqual(expr->Var(), values, vars); -} - -// ----- kVarBoundWatcher ----- - -Constraint* BuildVarBoundWatcher(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* expr = nullptr; - VERIFY(builder->ScanArguments(ModelVisitor::kVariableArgument, proto, &expr)); - std::vector vars; - VERIFY(builder->ScanArguments(ModelVisitor::kVarsArgument, proto, &vars)); - std::vector values; - VERIFY(builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)); - return SetIsGreaterOrEqual(expr->Var(), values, vars); -} - -#undef VERIFY -#undef VERIFY_EQ -} // namespace - -// ----- CpModelLoader ----- - -bool CpModelLoader::BuildFromProto(const CpIntegerExpression& proto) { - const int index = proto.index(); - const int tag_index = proto.type_index(); - Solver::IntegerExpressionBuilder builder = - solver_->GetIntegerExpressionBuilder(tags_.Element(tag_index)); - if (!builder) { - LOG(WARNING) << "Tag " << tags_.Element(tag_index) << " was not found"; - return false; - } - IntExpr* const built = builder(this, proto); - if (!built) { - return false; - } - expressions_.resize( - std::max(static_cast(expressions_.size()), index + 1)); - expressions_[index] = built; - return true; -} - -Constraint* CpModelLoader::BuildFromProto(const CpConstraint& proto) { - const int tag_index = proto.type_index(); - Solver::ConstraintBuilder builder = - solver_->GetConstraintBuilder(tags_.Element(tag_index)); - if (!builder) { - LOG(WARNING) << "Tag " << tags_.Element(tag_index) << " was not found"; - return nullptr; - } - Constraint* const built = builder(this, proto); - return built; -} - -bool CpModelLoader::BuildFromProto(const CpIntervalVariable& proto) { - const int index = proto.index(); - const int tag_index = proto.type_index(); - Solver::IntervalVariableBuilder builder = - solver_->GetIntervalVariableBuilder(tags_.Element(tag_index)); - if (!builder) { - LOG(WARNING) << "Tag " << tags_.Element(tag_index) << " was not found"; - return false; - } - IntervalVar* const built = builder(this, proto); - if (!built) { - return false; - } - intervals_.resize(std::max(static_cast(intervals_.size()), index + 1)); - intervals_[index] = built; - return true; -} - -bool CpModelLoader::BuildFromProto(const CpSequenceVariable& proto) { - const int index = proto.index(); - const int tag_index = proto.type_index(); - Solver::SequenceVariableBuilder builder = - solver_->GetSequenceVariableBuilder(tags_.Element(tag_index)); - if (!builder) { - LOG(WARNING) << "Tag " << tags_.Element(tag_index) << " was not found"; - return false; - } - SequenceVar* const built = builder(this, proto); - if (!built) { - return false; - } - sequences_.resize(std::max(static_cast(sequences_.size()), index + 1)); - sequences_[index] = built; - return true; -} - -IntExpr* CpModelLoader::IntegerExpression(int index) const { - CHECK_GE(index, 0); - CHECK_LT(index, expressions_.size()); - CHECK(expressions_[index] != nullptr); - return expressions_[index]; -} - -IntervalVar* CpModelLoader::IntervalVariable(int index) const { - CHECK_GE(index, 0); - CHECK_LT(index, intervals_.size()); - CHECK(intervals_[index] != nullptr); - return intervals_[index]; -} - -bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, - int64* to_fill) { - if (arg_proto.argument_index() == type_index && - arg_proto.type() == CpArgument::INTEGER_VALUE) { - *to_fill = arg_proto.integer_value(); - return true; - } - return false; -} - -bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, - IntExpr** to_fill) { - if (arg_proto.argument_index() == type_index && - arg_proto.type() == CpArgument::EXPRESSION) { - const int expression_index = arg_proto.integer_expression_index(); - CHECK(expressions_[expression_index] != nullptr); - *to_fill = expressions_[expression_index]; - return true; - } - return false; -} - -bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, - std::vector* to_fill) { - if (arg_proto.argument_index() == type_index && - arg_proto.type() == CpArgument::INTEGER_ARRAY) { - const int values_size = arg_proto.integer_array_size(); - for (int j = 0; j < values_size; ++j) { - to_fill->push_back(arg_proto.integer_array(j)); - } - return true; - } - return false; -} - -bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, - IntTupleSet* to_fill) { - if (arg_proto.argument_index() == type_index && - arg_proto.has_integer_matrix()) { - to_fill->Clear(); - const CpIntegerMatrix& matrix = arg_proto.integer_matrix(); - const int rows = matrix.rows(); - const int columns = matrix.columns(); - int counter = 0; - for (int i = 0; i < rows; ++i) { - std::vector tuple; - for (int j = 0; j < columns; ++j) { - const int64 value = matrix.values(counter++); - tuple.push_back(value); - } - to_fill->Insert(tuple); - } - CHECK_EQ(matrix.values_size(), counter); - return true; - } - return false; -} - -bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, - std::vector* to_fill) { - if (arg_proto.argument_index() == type_index && - arg_proto.type() == CpArgument::EXPRESSION_ARRAY) { - const int vars_size = arg_proto.integer_expression_array_size(); - for (int j = 0; j < vars_size; ++j) { - const int expression_index = arg_proto.integer_expression_array(j); - CHECK(expressions_[expression_index] != nullptr); - to_fill->push_back(expressions_[expression_index]->Var()); - } - return true; - } - return false; -} - -bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, - IntervalVar** to_fill) { - if (arg_proto.argument_index() == type_index && - arg_proto.type() == CpArgument::INTERVAL) { - const int interval_index = arg_proto.interval_index(); - CHECK(intervals_[interval_index] != nullptr); - *to_fill = intervals_[interval_index]; - return true; - } - return false; -} - -bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, - std::vector* to_fill) { - if (arg_proto.argument_index() == type_index && - arg_proto.type() == CpArgument::INTERVAL_ARRAY) { - const int vars_size = arg_proto.interval_array_size(); - for (int j = 0; j < vars_size; ++j) { - const int interval_index = arg_proto.interval_array(j); - CHECK(intervals_[interval_index] != nullptr); - to_fill->push_back(intervals_[interval_index]); - } - return true; - } - return false; -} - -bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, - SequenceVar** to_fill) { - if (arg_proto.argument_index() == type_index && - arg_proto.type() == CpArgument::SEQUENCE) { - const int sequence_index = arg_proto.sequence_index(); - CHECK(sequences_[sequence_index] != nullptr); - *to_fill = sequences_[sequence_index]; - return true; - } - return false; -} - -bool CpModelLoader::ScanOneArgument(int type_index, const CpArgument& arg_proto, - std::vector* to_fill) { - if (arg_proto.argument_index() == type_index && - arg_proto.type() == CpArgument::SEQUENCE_ARRAY) { - const int vars_size = arg_proto.sequence_array_size(); - for (int j = 0; j < vars_size; ++j) { - const int sequence_index = arg_proto.sequence_array(j); - CHECK(sequences_[sequence_index] != nullptr); - to_fill->push_back(sequences_[sequence_index]); - } - return true; - } - return false; -} - -// ----- Solver API ----- - -CpModel Solver::ExportModelWithSearchMonitorsAndDecisionBuilder( - const std::vector& monitors, - DecisionBuilder* const db) const { - CpModel model_proto; - FirstPassVisitor first_pass; - Accept(&first_pass, monitors, db); - SecondPassVisitor second_pass(first_pass, &model_proto); - Accept(&second_pass, monitors, db); - return model_proto; -} - -CpModel Solver::ExportModelWithSearchMonitors( - const std::vector& monitors) const { - return ExportModelWithSearchMonitorsAndDecisionBuilder(monitors, nullptr); -} - -CpModel Solver::ExportModel() const { - std::vector monitors; - return ExportModelWithSearchMonitorsAndDecisionBuilder(monitors, nullptr); -} - -bool Solver::LoadModel(const CpModel& model_proto) { - return LoadModelWithSearchMonitors(model_proto, nullptr); -} - -bool Solver::LoadModelWithSearchMonitors( - const CpModel& model_proto, std::vector* monitors) { - if (model_proto.version() > kModelVersion) { - LOG(ERROR) << "Model protocol buffer version is greater than" - << " the one compiled in the reader (" << model_proto.version() - << " vs " << kModelVersion << ")"; - return false; - } - CHECK(model_loader_.get() == nullptr) << "You can only load a model once"; - model_loader_.reset(new CpModelLoader(this)); - for (int i = 0; i < model_proto.tags_size(); ++i) { - model_loader_->AddTag(model_proto.tags(i)); - } - for (int i = 0; i < model_proto.intervals_size(); ++i) { - if (!model_loader_->BuildFromProto(model_proto.intervals(i))) { - LOG(ERROR) << "Interval variable proto " - << model_proto.intervals(i).DebugString() - << " was not parsed correctly"; - return false; - } - } - for (int i = 0; i < model_proto.sequences_size(); ++i) { - if (!model_loader_->BuildFromProto(model_proto.sequences(i))) { - LOG(ERROR) << "Sequence variable proto " - << model_proto.sequences(i).DebugString() - << " was not parsed correctly"; - return false; - } - } - for (int i = 0; i < model_proto.expressions_size(); ++i) { - if (!model_loader_->BuildFromProto(model_proto.expressions(i))) { - LOG(ERROR) << "Integer expression proto " - << model_proto.expressions(i).DebugString() - << " was not parsed correctly"; - return false; - } - } - for (int i = 0; i < model_proto.constraints_size(); ++i) { - Constraint* const constraint = - model_loader_->BuildFromProto(model_proto.constraints(i)); - if (constraint == nullptr) { - LOG(ERROR) << "Constraint proto " - << model_proto.constraints(i).DebugString() - << " was not parsed correctly"; - return false; - } - AddConstraint(constraint); - } - if (monitors != nullptr) { - if (model_proto.has_search_limit()) { - monitors->push_back(MakeLimit(model_proto.search_limit())); - } - if (model_proto.has_objective()) { - const CpObjective& objective_proto = model_proto.objective(); - IntVar* const objective_var = - model_loader_->IntegerExpression(objective_proto.objective_index()) - ->Var(); - const bool maximize = objective_proto.maximize(); - const int64 step = objective_proto.step(); - OptimizeVar* const objective = - MakeOptimize(maximize, objective_var, step); - monitors->push_back(objective); - } - } - return true; -} - -bool Solver::UpgradeModel(CpModel* const proto) { - if (proto->version() == kModelVersion) { - LOG(INFO) << "Model already up to date with version " << kModelVersion; - } - return true; -} - -void Solver::RegisterBuilder(const std::string& tag, - ConstraintBuilder builder) { - gtl::InsertOrDie(&constraint_builders_, tag, builder); -} - -void Solver::RegisterBuilder(const std::string& tag, - IntegerExpressionBuilder builder) { - gtl::InsertOrDie(&expression_builders_, tag, builder); -} - -void Solver::RegisterBuilder(const std::string& tag, - IntervalVariableBuilder builder) { - gtl::InsertOrDie(&interval_builders_, tag, builder); -} - -void Solver::RegisterBuilder(const std::string& tag, - SequenceVariableBuilder builder) { - gtl::InsertOrDie(&sequence_builders_, tag, builder); -} - -Solver::ConstraintBuilder Solver::GetConstraintBuilder( - const std::string& tag) const { - return gtl::FindWithDefault(constraint_builders_, tag, nullptr); -} - -Solver::IntegerExpressionBuilder Solver::GetIntegerExpressionBuilder( - const std::string& tag) const { - return gtl::FindWithDefault(expression_builders_, tag, nullptr); -} - -Solver::IntervalVariableBuilder Solver::GetIntervalVariableBuilder( - const std::string& tag) const { - IntervalVariableBuilder builder = - gtl::FindWithDefault(interval_builders_, tag, nullptr); - return builder; -} - -Solver::SequenceVariableBuilder Solver::GetSequenceVariableBuilder( - const std::string& tag) const { - SequenceVariableBuilder builder = - gtl::FindWithDefault(sequence_builders_, tag, nullptr); - return builder; -} - -// ----- Manage builders ----- - -#define REGISTER(tag, func) RegisterBuilder(ModelVisitor::tag, func) - -void Solver::InitBuilders() { - // Explicit casting required by MSV compiler. - REGISTER(kAbs, IntegerExpressionBuilder(BuildAbs)); - REGISTER(kAbsEqual, ConstraintBuilder(BuildAbsEqual)); - REGISTER(kAllDifferent, ConstraintBuilder(BuildAllDifferent)); - REGISTER(kAllowedAssignments, ConstraintBuilder(BuildAllowedAssignments)); - REGISTER(kBetween, ConstraintBuilder(BuildBetween)); - REGISTER(kConditionalExpr, IntegerExpressionBuilder(BuildConditionalExpr)); - REGISTER(kCircuit, ConstraintBuilder(BuildCircuit)); - REGISTER(kConvexPiecewise, IntegerExpressionBuilder(BuildConvexPiecewise)); - REGISTER(kCountEqual, ConstraintBuilder(BuildCountEqual)); - REGISTER(kCover, ConstraintBuilder(BuildCover)); - REGISTER(kCumulative, ConstraintBuilder(BuildCumulative)); - REGISTER(kDeviation, ConstraintBuilder(BuildDeviation)); - REGISTER(kDifference, IntegerExpressionBuilder(BuildDifference)); - REGISTER(kDisjunctive, ConstraintBuilder(BuildDisjunctive)); - REGISTER(kDistribute, ConstraintBuilder(BuildDistribute)); - REGISTER(kDivide, IntegerExpressionBuilder(BuildDivide)); - REGISTER(kDurationExpr, IntegerExpressionBuilder(BuildDurationExpr)); - REGISTER(kElement, IntegerExpressionBuilder(BuildElement)); - REGISTER(kElementEqual, ConstraintBuilder(BuildElementEqual)); - REGISTER(kEndExpr, IntegerExpressionBuilder(BuildEndExpr)); - REGISTER(kEquality, ConstraintBuilder(BuildEquality)); - REGISTER(kFalseConstraint, ConstraintBuilder(BuildFalseConstraint)); - REGISTER(kGreater, ConstraintBuilder(BuildGreater)); - REGISTER(kGreaterOrEqual, ConstraintBuilder(BuildGreaterOrEqual)); - REGISTER(kIndexOf, ConstraintBuilder(BuildIndexOf)); - REGISTER(kIntegerVariable, IntegerExpressionBuilder(BuildIntegerVariable)); - REGISTER(kIntervalBinaryRelation, - ConstraintBuilder(BuildIntervalBinaryRelation)); - REGISTER(kIntervalDisjunction, ConstraintBuilder(BuildIntervalDisjunction)); - REGISTER(kIntervalUnaryRelation, - ConstraintBuilder(BuildIntervalUnaryRelation)); - REGISTER(kIntervalVariable, IntervalVariableBuilder(BuildIntervalVariable)); - REGISTER(kInversePermutation, ConstraintBuilder(BuildInversePermutation)); - REGISTER(kIsBetween, ConstraintBuilder(BuildIsBetween)); - REGISTER(kIsDifferent, ConstraintBuilder(BuildIsDifferent)); - REGISTER(kIsEqual, ConstraintBuilder(BuildIsEqual)); - REGISTER(kIsGreater, ConstraintBuilder(BuildIsGreater)); - REGISTER(kIsGreaterOrEqual, ConstraintBuilder(BuildIsGreaterOrEqual)); - REGISTER(kIsLess, ConstraintBuilder(BuildIsLess)); - REGISTER(kIsLessOrEqual, ConstraintBuilder(BuildIsLessOrEqual)); - REGISTER(kIsMember, ConstraintBuilder(BuildIsMember)); - REGISTER(kLess, ConstraintBuilder(BuildLess)); - REGISTER(kLessOrEqual, ConstraintBuilder(BuildLessOrEqual)); - REGISTER(kLexLess, ConstraintBuilder(BuildLexLess)); - REGISTER(kMapDomain, ConstraintBuilder(BuildMapDomain)); - REGISTER(kMax, IntegerExpressionBuilder(BuildMax)); - REGISTER(kMaxEqual, ConstraintBuilder(BuildMaxEqual)); - REGISTER(kMember, ConstraintBuilder(BuildMember)); - REGISTER(kMin, IntegerExpressionBuilder(BuildMin)); - REGISTER(kMinEqual, ConstraintBuilder(BuildMinEqual)); - REGISTER(kNoCycle, ConstraintBuilder(BuildNoCycle)); - REGISTER(kNonEqual, ConstraintBuilder(BuildNonEqual)); - REGISTER(kNotBetween, ConstraintBuilder(BuildNotBetween)); - REGISTER(kNotMember, ConstraintBuilder(BuildNotMember)); - REGISTER(kNullIntersect, ConstraintBuilder(BuildNullIntersect)); - REGISTER(kOpposite, IntegerExpressionBuilder(BuildOpposite)); - REGISTER(kPack, ConstraintBuilder(BuildPack)); - REGISTER(kPathCumul, ConstraintBuilder(BuildPathCumul)); - REGISTER(kDelayedPathCumul, ConstraintBuilder(BuildDelayedPathCumul)); - REGISTER(kPerformedExpr, IntegerExpressionBuilder(BuildPerformedExpr)); - REGISTER(kPower, IntegerExpressionBuilder(BuildPower)); - REGISTER(kProduct, IntegerExpressionBuilder(BuildProduct)); - REGISTER(kScalProd, IntegerExpressionBuilder(BuildScalProd)); - REGISTER(kScalProdEqual, ConstraintBuilder(BuildScalProdEqual)); - REGISTER(kScalProdGreaterOrEqual, - ConstraintBuilder(BuildScalProdGreaterOrEqual)); - REGISTER(kScalProdLessOrEqual, ConstraintBuilder(BuildScalProdLessOrEqual)); - REGISTER(kSemiContinuous, IntegerExpressionBuilder(BuildSemiContinuous)); - REGISTER(kSortingConstraint, ConstraintBuilder(BuildSortingConstraint)); - REGISTER(kSquare, IntegerExpressionBuilder(BuildSquare)); - REGISTER(kStartExpr, IntegerExpressionBuilder(BuildStartExpr)); - REGISTER(kSum, IntegerExpressionBuilder(BuildSum)); - REGISTER(kSumEqual, ConstraintBuilder(BuildSumEqual)); - REGISTER(kSumGreaterOrEqual, ConstraintBuilder(BuildSumGreaterOrEqual)); - REGISTER(kSumLessOrEqual, ConstraintBuilder(BuildSumLessOrEqual)); - REGISTER(kTransition, ConstraintBuilder(BuildTransition)); - REGISTER(kTrueConstraint, ConstraintBuilder(BuildTrueConstraint)); - REGISTER(kVarBoundWatcher, ConstraintBuilder(BuildVarBoundWatcher)); - REGISTER(kVarValueWatcher, ConstraintBuilder(BuildVarValueWatcher)); -} -#undef REGISTER - -void Solver::DeleteBuilders() { - expression_builders_.clear(); - constraint_builders_.clear(); - interval_builders_.clear(); - sequence_builders_.clear(); -} -} // namespace operations_research diff --git a/ortools/constraint_solver/java/constraint_solver.i b/ortools/constraint_solver/java/constraint_solver.i index 3aad0049ea3..2d828655117 100644 --- a/ortools/constraint_solver/java/constraint_solver.i +++ b/ortools/constraint_solver/java/constraint_solver.i @@ -350,7 +350,7 @@ class SolverToVoid { %rename (makeLess) operations_research::Solver::MakeLess; %rename (makeLessOrEqual) operations_research::Solver::MakeLessOrEqual; %rename (makeLimit) operations_research::Solver::MakeLimit; -%rename (makeLocalSearchObjectiveFilter) operations_research::Solver::MakeLocalSearchObjectiveFilter; +%rename (makeSumObjectiveFilter) operations_research::Solver::MakeSumObjectiveFilter; %rename (makeLocalSearchPhase) operations_research::Solver::MakeLocalSearchPhase; %rename (makeLocalSearchPhaseParameters) operations_research::Solver::MakeLocalSearchPhaseParameters; %rename (makeLubyRestart) operations_research::Solver::MakeLubyRestart; @@ -741,6 +741,10 @@ WRAP_STD_FUNCTION_JAVA( LongLongToLong, "com/google/ortools/constraintsolver/", int64, Long, int64, int64) +WRAP_STD_FUNCTION_JAVA( + IntToLong, + "com/google/ortools/constraintsolver/", + int64, Long, int) WRAP_STD_FUNCTION_JAVA( IntIntToLong, "com/google/ortools/constraintsolver/", diff --git a/ortools/constraint_solver/java/routing.i b/ortools/constraint_solver/java/routing.i index 946ed41eb7d..1fd2c988c18 100644 --- a/ortools/constraint_solver/java/routing.i +++ b/ortools/constraint_solver/java/routing.i @@ -1,4 +1,4 @@ -// Copyright 2010-2014 Google +// Copyright 2010-2018 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -14,6 +14,9 @@ // TODO(user): Refactor this file to adhere to the SWIG style guide. %include "ortools/constraint_solver/java/constraint_solver.i" +%include "ortools/constraint_solver/java/routing_types.i" +%include "ortools/constraint_solver/java/routing_index_manager.i" +%include "ortools/util/java/functions.i" // We need to forward-declare the proto here, so that PROTO_INPUT involving it // works correctly. The order matters very much: this declaration needs to be @@ -23,107 +26,14 @@ class RoutingModelParameters; class RoutingSearchParameters; } // namespace operations_research -// Include the file we want to wrap a first time. +// Include the files we want to wrap a first time. %{ +#include "ortools/constraint_solver/routing_types.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" +#include "ortools/constraint_solver/routing_parameters.h" #include "ortools/constraint_solver/routing.h" %} -// Convert RoutingModel::NodeIndex to (32-bit signed) integers. -%typemap(jni) operations_research::RoutingModel::NodeIndex "jint" -%typemap(jtype) operations_research::RoutingModel::NodeIndex "int" -%typemap(jstype) operations_research::RoutingModel::NodeIndex "int" -%typemap(javain) operations_research::RoutingModel::NodeIndex "$javainput" -%typemap(javaout) operations_research::RoutingModel::NodeIndex { - return $jnicall; -} -%typemap(in) operations_research::RoutingModel::NodeIndex { - $1 = operations_research::RoutingModel::NodeIndex($input); -} -%typemap(out) operations_research::RoutingModel::NodeIndex { - $result = (jlong)$1.value(); -} - -// Convert std::vector to/from int arrays. -VECTOR_AS_JAVA_ARRAY(operations_research::RoutingModel::NodeIndex, int, Int); - -// TODO(user): define a macro in util/java/vector.i for std::vector> and -// reuse it here. -%typemap(jni) const std::vector >& "jobjectArray" -%typemap(jtype) const std::vector >& "int[][]" -%typemap(jstype) const std::vector >& "int[][]" -%typemap(javain) const std::vector >& "$javainput" - -// Useful directors. -%feature("director") NodeEvaluator2; - -%{ -#include -#include "ortools/base/callback.h" -#include "ortools/base/integral_types.h" - -// When a director is created for a class with SWIG, the C++ part of the -// director keeps a JNI global reference to the Java part. This global reference -// only gets deleted in the destructor of the C++ part, but by default, this -// only happens when the Java part is processed by the GC (however, this never -// happens, because there is the JNI global reference...). -// -// To break the cycle, it is necessary to delete the C++ part manually. For the -// callback classes, this is done by deriving them from the respective C++ -// ResultCallback classes. When the java callback class is asked for a C++ -// callback class, it hands over its C++ part. It is expected, that whoever -// receives the C++ callback class, owns it and destroys it after they no longer -// need it. But by destroying it, they also break the reference cycle and the -// Java part may be processed by the GC. -// -// When created, instances of NodeEvaluator2 must thus be used in a context -// where someone takes ownership of the C++ part of the NodeEvaluator2 and -// deletes it when no longer needed. Otherwise, the object would remain on the -// heap forever. -class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 { - public: - NodeEvaluator2() : used_as_permanent_handler_(false) {} - virtual int64 run(int i, int j) = 0; - operations_research::RoutingModel::NodeEvaluator2* getPermanentCallback() { - CHECK(!used_as_permanent_handler_); - used_as_permanent_handler_ = true; - // The evaluator is wrapped to avoid having its ownership shared between - // jni/java and C++. C++ will take care of handling the wrapper while the - // actual evaluator will be handled by java. Refer to the typemap for - // NodeEvaluator2 below to see how this method is called. - return NewPermanentCallback(this, &NodeEvaluator2::Run); - } - virtual ~NodeEvaluator2() {} - - private: - virtual bool IsRepeatable() const { return true; } - virtual int64 Run(operations_research::RoutingModel::NodeIndex i, - operations_research::RoutingModel::NodeIndex j) { - return run(i.value(), j.value()); - } - bool used_as_permanent_handler_; -}; -%} - -class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 { - public: - NodeEvaluator2() : used_as_permanent_handler_(false) {} - virtual int64 run(int i, int j) = 0; - operations_research::RoutingModel::NodeEvaluator2* getPermanentCallback(); - virtual ~NodeEvaluator2() {} -}; - -// Typemaps for callbacks in java. -%typemap(jstype) operations_research::RoutingModel::NodeEvaluator2* "NodeEvaluator2"; -%typemap(javain) operations_research::RoutingModel::NodeEvaluator2* "$descriptor(ResultCallback2*).getCPtr($javainput.getPermanentCallback())"; - -namespace operations_research { -%define NODE_EVALUATOR_CAST(CType, ptr) -reinterpret_cast(ptr)->getPermanentCallback() -%enddef - -CONVERT_VECTOR_WITH_CAST(RoutingModel::NodeEvaluator2, NodeEvaluator2, NODE_EVALUATOR_CAST); -} // namespace operations_research - %typemap(in) const std::vector >& (std::vector > temp) { if ($input) { @@ -163,16 +73,17 @@ CONVERT_VECTOR_WITH_CAST(RoutingModel::NodeEvaluator2, NodeEvaluator2, NODE_EVAL } } +%ignore operations_research::RoutingModel::RegisterStateDependentTransitCallback; +%ignore operations_research::RoutingModel::StateDependentTransitCallback; %ignore operations_research::RoutingModel::MakeStateDependentTransit; %ignore operations_research::RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity; -%ignore operations_research::RoutingModel::RoutingModel( - int nodes, int vehicles, - const std::vector >& start_end); // RoutingModel methods. %rename (solve) Solve; %rename (solveWithParameters) SolveWithParameters; %rename (solveFromAssignmentWithParameters) SolveFromAssignmentWithParameters; +%rename (registerTransitCallback) RegisterTransitCallback; +%rename (registerUnaryTransitCallback) RegisterUnaryTransitCallback; %rename (setArcCostEvaluatorOfAllVehicles) SetArcCostEvaluatorOfAllVehicles; %rename (setArcCostEvaluatorOfVehicle) SetArcCostEvaluatorOfVehicle; %rename (addDimension) AddDimension; @@ -190,8 +101,6 @@ CONVERT_VECTOR_WITH_CAST(RoutingModel::NodeEvaluator2, NodeEvaluator2, NODE_EVAL %rename (applyLocks) ApplyLocks; %rename (writeAssignment) WriteAssignment; %rename (readAssignment) ReadAssignment; -%rename (nodeToIndex) NodeToIndex; -%rename (indexToNode) IndexToNode; %rename (start) Start; %rename (end) End; %rename (isStart) IsStart; @@ -212,8 +121,6 @@ CONVERT_VECTOR_WITH_CAST(RoutingModel::NodeEvaluator2, NodeEvaluator2, NODE_EVAL %rename (setFirstSolutionEvaluator) SetFirstSolutionEvaluator; %rename (routesToAssignment) RoutesToAssignment; %rename (closeModel) CloseModel; -%rename (defaultSearchParameters) DefaultSearchParameters; -%rename (defaultModelParameters) DefaultModelParameters; // RoutingDimension methods. %rename (cumulVar) CumulVar; @@ -228,15 +135,6 @@ CONVERT_VECTOR_WITH_CAST(RoutingModel::NodeEvaluator2, NodeEvaluator2, NODE_EVAL %rename (getCumulVarSoftUpperBound) GetCumulVarSoftUpperBound; %rename (getCumulVarSoftUpperBoundCoefficient) GetCumulVarSoftUpperBoundCoefficient; -// DEPRECATED METHODS. See ./routing.h for how to replace them. -%rename (setCost) SetCost; -%rename (setVehicleCost) SetVehicleCost; -%rename (setDimensionTransitCost) SetDimensionTransitCost; -%rename (getDimensionTransitCost) GetDimensionTransitCost; -%rename (setDimensionSpanCost) SetDimensionSpanCost; -%rename (getDimensionSpanCost) GetDimensionSpanCost; - - // Protobuf support PROTO_INPUT(operations_research::RoutingSearchParameters, com.google.ortools.constraintsolver.RoutingSearchParameters, @@ -249,17 +147,20 @@ PROTO2_RETURN(operations_research::RoutingSearchParameters, PROTO2_RETURN(operations_research::RoutingModelParameters, com.google.ortools.constraintsolver.RoutingModelParameters) +// Wrap routing_types.h, routing_parameters.h according to the SWIG styleguide. %ignoreall -%unignore RoutingNodeIndex; -%unignore RoutingCostClassIndex; -%unignore RoutingDimensionIndex; -%unignore RoutingDisjunctionIndex; -%unignore RoutingVehicleClassIndex; -%unignore RoutingNodeEvaluator2; -%unignore RoutingTransitEvaluator2; -%unignore RoutingNodePair; -%unignore RoutingNodePairs; +%unignore RoutingTransitCallback2; +%unignore RoutingIndexPair; +%unignore RoutingIndexPairs; + +// IMPORTANT(viger): These functions from routing_parameters.h are global, so in +// java they are in the main.java (import com.[...].constraintsolver.main). +%rename (defaultRoutingSearchParameters) DefaultRoutingSearchParameters; +%rename (defaultRoutingModelParameters) DefaultRoutingModelParameters; +%rename (findErrorInRoutingSearchParameters) FindErrorInRoutingSearchParameters; + %include "ortools/constraint_solver/routing_types.h" +%include "ortools/constraint_solver/routing_parameters.h" %unignoreall // TODO(user): Use ignoreall/unignoreall for this one. A lot of work. diff --git a/ortools/constraint_solver/java/routing_index_manager.i b/ortools/constraint_solver/java/routing_index_manager.i new file mode 100644 index 00000000000..e126da38274 --- /dev/null +++ b/ortools/constraint_solver/java/routing_index_manager.i @@ -0,0 +1,43 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Wrapper for RoutingIndexManager. + +%include "ortools/base/base.i" +%include "ortools/util/java/vector.i" + +%{ +#include "ortools/constraint_solver/routing_index_manager.h" +%} + +DEFINE_INDEX_TYPE_TYPEDEF(operations_research::RoutingNodeIndex, + operations_research::RoutingIndexManager::NodeIndex); + +%rename (indexToNode) IndexToNode; +%rename (nodeToIndex) NodeToIndex; +%rename (nodesToIndices) NodesToIndices; + +%ignoreall + +%unignore operations_research; +%unignore operations_research::RoutingIndexManager; +%unignore operations_research::RoutingIndexManager::IndexToNode(int64); +%unignore operations_research::RoutingIndexManager::NodeToIndex(NodeIndex); +%unignore operations_research::RoutingIndexManager::NodesToIndices(const std::vector&); +%unignore operations_research::RoutingIndexManager::RoutingIndexManager(int, int, NodeIndex); +%unignore operations_research::RoutingIndexManager::RoutingIndexManager(int, int, const std::vector&, const std::vector&); +%unignore operations_research::RoutingIndexManager::~RoutingIndexManager; + +%include "ortools/constraint_solver/routing_index_manager.h" + +%unignoreall diff --git a/ortools/constraint_solver/java/routing_types.i b/ortools/constraint_solver/java/routing_types.i new file mode 100644 index 00000000000..b5c1a828bab --- /dev/null +++ b/ortools/constraint_solver/java/routing_types.i @@ -0,0 +1,74 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Typemaps for Routing Index Types. This does not define any type wrappings, +// because these index types are are never exposed to the target language. +// Instead, indices are manipulated as native target language types (e.g. Java +// int). +// This file is to be %included when wrapped objects need to use these typemaps. + +%include "ortools/base/base.i" +%import "ortools/util/java/vector.i" + +%{ +#include "ortools/constraint_solver/routing_types.h" +%} + +// This macro defines typemaps for IndexT, std::vector and +// std::vector>. +%define DEFINE_INDEX_TYPE(IndexT) + +// Convert IndexT to (32-bit signed) integers. +%typemap(jni) IndexT "jint" +%typemap(jtype) IndexT "int" +%typemap(jstype) IndexT "int" +%typemap(javain) IndexT "$javainput" +%typemap(javaout) IndexT { + return $jnicall; +} +%typemap(in) IndexT { + $1 = IndexT($input); +} +%typemap(out) IndexT { + $result = (jlong)$1.value(); +} + +// Convert std::vector to/from int arrays. +VECTOR_AS_JAVA_ARRAY(IndexT, int, Int); + +// TODO(user): define a macro in util/java/vector.i for std::vector> and +// reuse it here. +%typemap(jni) const std::vector >& "jobjectArray" +%typemap(jtype) const std::vector >& "int[][]" +%typemap(jstype) const std::vector >& "int[][]" +%typemap(javain) const std::vector >& "$javainput" + +%enddef // DEFINE_INDEX_TYPE + +// This macro applies all typemaps for a given index type to a typedef. +// Normally we should not need that as SWIG is supposed to automatically apply +// all typemaps to typedef definitions (http://www.swig.org/Doc2.0/SWIGDocumentation.html#Typemaps_typedef_reductions), +// but this is not actually the case. +%define DEFINE_INDEX_TYPE_TYPEDEF(IndexT, NewIndexT) +%apply IndexT { NewIndexT }; +%apply std::vector { std::vector }; +%apply const std::vector& { std::vector& }; +%apply const std::vector >& { const std::vector >& }; +%enddef // DEFINE_INDEX_TYPE_TYPEDEF + +DEFINE_INDEX_TYPE(operations_research::RoutingNodeIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingCostClassIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingDimensionIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingDisjunctionIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingVehicleClassIndex); + diff --git a/ortools/constraint_solver/local_search.cc b/ortools/constraint_solver/local_search.cc index 601b52f3282..c1a2a7c3b99 100644 --- a/ortools/constraint_solver/local_search.cc +++ b/ortools/constraint_solver/local_search.cc @@ -18,21 +18,20 @@ #include #include #include -#include -#include #include #include -#include "ortools/base/callback.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/map_util.h" #include "ortools/base/random.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/graph/hamiltonian_path.h" @@ -336,6 +335,7 @@ class DecrementValue : public ChangeValue { PathOperator::PathOperator(const std::vector& next_vars, const std::vector& path_vars, int number_of_base_nodes, + bool skip_locally_optimal_paths, std::function start_empty_path_class) : IntVarLocalSearchOperator(next_vars), number_of_nexts_(next_vars.size()), @@ -346,13 +346,30 @@ PathOperator::PathOperator(const std::vector& next_vars, base_paths_(number_of_base_nodes), just_started_(false), first_start_(true), - start_empty_path_class_(std::move(start_empty_path_class)) { + start_empty_path_class_(std::move(start_empty_path_class)), + skip_locally_optimal_paths_(skip_locally_optimal_paths), + optimal_paths_enabled_(false) { + DCHECK_GT(number_of_base_nodes, 0); if (!ignore_path_vars_) { AddVars(path_vars); } + path_basis_.push_back(0); + for (int i = 1; i < base_nodes_.size(); ++i) { + if (!OnSamePathAsPreviousBase(i)) path_basis_.push_back(i); + } + if ((path_basis_.size() > 2) || + (!next_vars.empty() && !next_vars.back() + ->solver() + ->parameters() + .skip_locally_optimal_paths())) { + skip_locally_optimal_paths_ = false; + } } +void PathOperator::Reset() { optimal_paths_.clear(); } + void PathOperator::OnStart() { + optimal_paths_enabled_ = false; InitializeBaseNodes(); OnNodeInitialization(); } @@ -493,18 +510,70 @@ bool PathOperator::IncrementPosition() { return CheckEnds(); } // If all base nodes have been restarted, base nodes are moved to new paths. - for (int i = base_node_size - 1; i >= 0; --i) { - const int next_path_index = base_paths_[i] + 1; - if (next_path_index < number_of_paths) { - base_paths_[i] = next_path_index; - base_nodes_[i] = path_starts_[next_path_index]; - if (i == 0 || !OnSamePathAsPreviousBase(i)) { - return CheckEnds(); + // First we mark the current paths as locally optimal if they have been + // completely explored. + if (optimal_paths_enabled_ && skip_locally_optimal_paths_) { + if (path_basis_.size() > 1) { + for (int i = 1; i < path_basis_.size(); ++i) { + optimal_paths_[num_paths_ * + start_to_path_[StartNode(path_basis_[i - 1])] + + start_to_path_[StartNode(path_basis_[i])]] = true; + } + } else { + optimal_paths_[num_paths_ * start_to_path_[StartNode(path_basis_[0])] + + start_to_path_[StartNode(path_basis_[0])]] = true; + } + } + std::vector current_starts(base_node_size); + for (int i = 0; i < base_node_size; ++i) { + current_starts[i] = StartNode(i); + } + // Exploration of next paths can lead to locally optimal paths since we are + // exploring them from scratch. + optimal_paths_enabled_ = true; + while (true) { + for (int i = base_node_size - 1; i >= 0; --i) { + const int next_path_index = base_paths_[i] + 1; + if (next_path_index < number_of_paths) { + base_paths_[i] = next_path_index; + base_nodes_[i] = path_starts_[next_path_index]; + if (i == 0 || !OnSamePathAsPreviousBase(i)) { + break; + } + } else { + base_paths_[i] = 0; + base_nodes_[i] = path_starts_[0]; + } + } + if (!skip_locally_optimal_paths_) return CheckEnds(); + // If the new paths have already been completely explored, we can + // skip them from now on. + if (path_basis_.size() > 1) { + for (int j = 1; j < path_basis_.size(); ++j) { + if (!optimal_paths_[num_paths_ * start_to_path_[StartNode( + path_basis_[j - 1])] + + start_to_path_[StartNode(path_basis_[j])]]) { + return CheckEnds(); + } } } else { - base_paths_[i] = 0; - base_nodes_[i] = path_starts_[0]; + if (!optimal_paths_[num_paths_ * + start_to_path_[StartNode(path_basis_[0])] + + start_to_path_[StartNode(path_basis_[0])]]) { + return CheckEnds(); + } + } + // If we are back to paths we just iterated on or have reached the end + // of the neighborhood search space, we can stop. + if (!CheckEnds()) return false; + bool stop = true; + for (int i = 0; i < base_node_size; ++i) { + if (StartNode(i) != current_starts[i]) { + stop = false; + break; + } } + if (stop) return false; } } else { just_started_ = false; @@ -525,6 +594,36 @@ void PathOperator::InitializePathStarts() { } max_next = std::max(max_next, next); } + // Update locally optimal paths. + if (optimal_paths_.empty() && skip_locally_optimal_paths_) { + num_paths_ = 0; + start_to_path_.clear(); + start_to_path_.resize(number_of_nexts_, -1); + for (int i = 0; i < number_of_nexts_; ++i) { + if (!has_prevs[i]) { + start_to_path_[i] = num_paths_; + ++num_paths_; + } + } + optimal_paths_.resize(num_paths_ * num_paths_, false); + } + if (skip_locally_optimal_paths_) { + for (int i = 0; i < number_of_nexts_; ++i) { + if (!has_prevs[i]) { + int current = i; + while (!IsPathEnd(current)) { + if ((OldNext(current) != prev_values_[current])) { + for (int j = 0; j < num_paths_; ++j) { + optimal_paths_[num_paths_ * start_to_path_[i] + j] = false; + optimal_paths_[num_paths_ * j + start_to_path_[i]] = false; + } + break; + } + current = OldNext(current); + } + } + } + } // Create a list of path starts, dropping equivalent path starts of // currently empty paths. std::vector empty_found(number_of_nexts_, false); @@ -572,7 +671,7 @@ void PathOperator::InitializePathStarts() { // path starts (there could be fewer if a new path was made empty, or more // if nodes were added to a formerly empty path). int new_index = 0; - std::unordered_set found_bases; + absl::flat_hash_set found_bases; for (int i = 0; i < path_starts_.size(); ++i) { int index = new_index; // Note: old and new path starts are sorted by construction. @@ -691,7 +790,7 @@ PathWithPreviousNodesOperator::PathWithPreviousNodesOperator( const std::vector& vars, const std::vector& secondary_vars, int number_of_base_nodes, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, number_of_base_nodes, + : PathOperator(vars, secondary_vars, number_of_base_nodes, true, std::move(start_empty_path_class)) { int64 max_next = -1; for (const IntVar* const var : vars) { @@ -708,7 +807,7 @@ void PathWithPreviousNodesOperator::OnNodeInitialization() { // ----- 2Opt ----- -// Reverves a sub-chain of a path. It is called 2Opt because it breaks +// Reverses a sub-chain of a path. It is called 2Opt because it breaks // 2 arcs on the path; resulting paths are called 2-optimal. // Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5 // (where (1, 5) are first and last nodes of the path and can therefore not be @@ -721,7 +820,7 @@ class TwoOpt : public PathOperator { TwoOpt(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 2, + : PathOperator(vars, secondary_vars, 2, true, std::move(start_empty_path_class)), last_base_(-1), last_(-1) {} @@ -792,7 +891,7 @@ class Relocate : public PathOperator { const std::vector& secondary_vars, const std::string& name, std::function start_empty_path_class, int64 chain_length = 1LL, bool single_path = false) - : PathOperator(vars, secondary_vars, 2, + : PathOperator(vars, secondary_vars, 2, true, std::move(start_empty_path_class)), chain_length_(chain_length), single_path_(single_path), @@ -854,7 +953,7 @@ class Exchange : public PathOperator { Exchange(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 2, + : PathOperator(vars, secondary_vars, 2, true, std::move(start_empty_path_class)) {} ~Exchange() override {} bool MakeNeighbor() override; @@ -897,7 +996,7 @@ class Cross : public PathOperator { Cross(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 2, + : PathOperator(vars, secondary_vars, 2, true, std::move(start_empty_path_class)) {} ~Cross() override {} bool MakeNeighbor() override; @@ -937,9 +1036,11 @@ class BaseInactiveNodeToPathOperator : public PathOperator { const std::vector& vars, const std::vector& secondary_vars, int number_of_base_nodes, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, number_of_base_nodes, + : PathOperator(vars, secondary_vars, number_of_base_nodes, false, std::move(start_empty_path_class)), - inactive_node_(0) {} + inactive_node_(0) { + // TODO(user): Activate skipping optimal paths. + } ~BaseInactiveNodeToPathOperator() override {} protected: @@ -1078,7 +1179,7 @@ class MakeInactiveOperator : public PathOperator { MakeInactiveOperator(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 1, + : PathOperator(vars, secondary_vars, 1, true, std::move(start_empty_path_class)) {} ~MakeInactiveOperator() override {} bool MakeNeighbor() override { @@ -1106,7 +1207,7 @@ class RelocateAndMakeInactiveOperator : public PathOperator { const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 2, + : PathOperator(vars, secondary_vars, 2, true, std::move(start_empty_path_class)) {} ~RelocateAndMakeInactiveOperator() override {} bool MakeNeighbor() override { @@ -1138,7 +1239,7 @@ class MakeChainInactiveOperator : public PathOperator { MakeChainInactiveOperator(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class) - : PathOperator(vars, secondary_vars, 2, + : PathOperator(vars, secondary_vars, 2, true, std::move(start_empty_path_class)) {} ~MakeChainInactiveOperator() override {} bool MakeNeighbor() override { @@ -1270,7 +1371,7 @@ class TSPOpt : public PathOperator { TSPOpt::TSPOpt(const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 evaluator, int chain_length) - : PathOperator(vars, secondary_vars, 1, nullptr), + : PathOperator(vars, secondary_vars, 1, true, nullptr), hamiltonian_path_solver_(cost_), evaluator_(std::move(evaluator)), chain_length_(chain_length) {} @@ -1342,7 +1443,7 @@ class TSPLns : public PathOperator { TSPLns::TSPLns(const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 evaluator, int tsp_size) - : PathOperator(vars, secondary_vars, 1, nullptr), + : PathOperator(vars, secondary_vars, 1, true, nullptr), hamiltonian_path_solver_(cost_), evaluator_(std::move(evaluator)), tsp_size_(tsp_size), @@ -1354,10 +1455,11 @@ TSPLns::TSPLns(const std::vector& vars, } bool TSPLns::MakeOneNeighbor() { - while (true) { + while (Size() != 0) { if (PathOperator::MakeOneNeighbor()) { return true; } + Var(0)->solver()->TopPeriodicCheck(); } return false; } @@ -1376,7 +1478,7 @@ bool TSPLns::MakeNeighbor() { } // Randomly select break nodes (final nodes of a meta-node, after which // an arc is relaxed. - std::unordered_set breaks_set; + absl::flat_hash_set breaks_set; // Always add base node to break nodes (diversification) breaks_set.insert(base_node); while (breaks_set.size() < tsp_size_) { @@ -1542,7 +1644,7 @@ class LinKernighan : public PathOperator { Solver::IndexEvaluator3 const evaluator_; NearestNeighbors neighbors_; - std::unordered_set marked_; + absl::flat_hash_set marked_; const bool topt_; }; @@ -1553,7 +1655,7 @@ class LinKernighan : public PathOperator { LinKernighan::LinKernighan(const std::vector& vars, const std::vector& secondary_vars, const Solver::IndexEvaluator3& evaluator, bool topt) - : PathOperator(vars, secondary_vars, 1, nullptr), + : PathOperator(vars, secondary_vars, 1, true, nullptr), evaluator_(evaluator), neighbors_(evaluator, *this, kNeighbors), topt_(topt) {} @@ -1669,7 +1771,7 @@ class PathLns : public PathOperator { PathLns(const std::vector& vars, const std::vector& secondary_vars, int number_of_chunks, int chunk_size, bool unactive_fragments) - : PathOperator(vars, secondary_vars, number_of_chunks, nullptr), + : PathOperator(vars, secondary_vars, number_of_chunks, true, nullptr), number_of_chunks_(number_of_chunks), chunk_size_(chunk_size), unactive_fragments_(unactive_fragments) { @@ -1774,6 +1876,7 @@ class CompoundOperator : public LocalSearchOperator { CompoundOperator(std::vector operators, std::function evaluator); ~CompoundOperator() override {} + void Reset() override; void Start(const Assignment* assignment) override; bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; @@ -1823,6 +1926,12 @@ CompoundOperator::CompoundOperator(std::vector operators, std::iota(operator_indices_.begin(), operator_indices_.end(), 0); } +void CompoundOperator::Reset() { + for (LocalSearchOperator* const op : operators_) { + op->Reset(); + } +} + void CompoundOperator::Start(const Assignment* assignment) { start_assignment_ = assignment; started_.ClearAll(); @@ -1901,6 +2010,7 @@ class RandomCompoundOperator : public LocalSearchOperator { RandomCompoundOperator(std::vector operators, int32 seed); ~RandomCompoundOperator() override {} + void Reset() override; void Start(const Assignment* assignment) override; bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; @@ -1926,12 +2036,18 @@ RandomCompoundOperator::RandomCompoundOperator( std::vector operators, int32 seed) : rand_(seed), operators_(std::move(operators)) {} +void RandomCompoundOperator::Reset() { + for (LocalSearchOperator* const op : operators_) { + op->Reset(); + } +} + bool RandomCompoundOperator::MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) { const int size = operators_.size(); std::vector indices(size); std::iota(indices.begin(), indices.end(), 0); - std::random_shuffle(indices.begin(), indices.end(), rand_); + std::shuffle(indices.begin(), indices.end(), rand_); for (int index : indices) { if (operators_[index]->MakeNextNeighbor(delta, deltadelta)) { return true; @@ -2297,73 +2413,75 @@ void IntVarLocalSearchFilter::SynchronizeOnAssignment( } } -// ----- Objective filter ------ -// Assignment is accepted if it improves the best objective value found so far. -// 'Values' callback takes an index of a variable and its value and returns the -// contribution into the objective value. The type of objective function -// is determined by LocalSearchOperation enum. Conditions on neighbor -// acceptance are presented in LocalSearchFilterBound enum. Objective function -// can be represented by any variable. - +// ----- Sum Objective filter ------ +// Maintains the sum of costs of variables, where the subclass implements +// CostOfSynchronizedVariable() and FillCostOfBoundDeltaVariable() to compute +// the cost of a variable depending on its value. +// An assignment is accepted by this filter if the total cost is allowed +// depending on the relation defined by filter_enum: +// - Solver::LE -> total_cost <= min(objective.Max(), delta->ObjectiveMax()) +// - Solver::GE -> total_cost >= max(objective.Min(), delta->ObjectiveMin()) +// - Solver::EQ -> the conjunction of LE and GE. namespace { -template -class ObjectiveFilter : public IntVarLocalSearchFilter { +class SumObjectiveFilter : public IntVarLocalSearchFilter { public: - ObjectiveFilter(const std::vector& vars, - Solver::ObjectiveWatcher delta_objective_callback, - const IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum) + SumObjectiveFilter(const std::vector& vars, + Solver::ObjectiveWatcher delta_objective_callback, + const IntVar* const objective, + Solver::LocalSearchFilterBound filter_enum) : IntVarLocalSearchFilter(vars, std::move(delta_objective_callback)), primary_vars_size_(vars.size()), - cache_(new int64[vars.size()]), - delta_cache_(new int64[vars.size()]), + synchronized_costs_(new int64[vars.size()]), + delta_costs_(new int64[vars.size()]), objective_(objective), filter_enum_(filter_enum), - op_(), - old_value_(0), - old_delta_value_(0), + synchronized_sum_(kint64min), + delta_sum_(kint64min), incremental_(false) { - for (int i = 0; i < Size(); ++i) { - cache_[i] = 0; - delta_cache_[i] = 0; + for (int i = 0; i < vars.size(); ++i) { + synchronized_costs_[i] = 0; + delta_costs_[i] = 0; } - op_.Init(); - old_value_ = op_.value(); } - ~ObjectiveFilter() override { - delete[] cache_; - delete[] delta_cache_; + ~SumObjectiveFilter() override { + delete[] synchronized_costs_; + delete[] delta_costs_; } + // If delta->Objective() is not objective, then we take kint64max for + // delta->ObjectiveMax() and kint64min for delta->ObjectiveMin(). bool Accept(const Assignment* delta, const Assignment* deltadelta) override { if (delta == nullptr) { return false; } - int64 value = 0; - if (!deltadelta->Empty()) { - if (!incremental_) { - value = Evaluate(delta, old_value_, cache_, true); - } else { - value = Evaluate(deltadelta, old_delta_value_, delta_cache_, true); - } - incremental_ = true; - } else { + if (deltadelta->Empty()) { if (incremental_) { for (int i = 0; i < primary_vars_size_; ++i) { - delta_cache_[i] = cache_[i]; + delta_costs_[i] = synchronized_costs_[i]; } - old_delta_value_ = old_value_; + delta_sum_ = synchronized_sum_; } incremental_ = false; - value = Evaluate(delta, old_value_, cache_, false); + delta_sum_ = CapAdd(synchronized_sum_, + CostOfChanges(delta, synchronized_costs_, false)); + } else { + if (incremental_) { + delta_sum_ = + CapAdd(delta_sum_, CostOfChanges(deltadelta, delta_costs_, true)); + } else { + delta_sum_ = CapAdd(synchronized_sum_, + CostOfChanges(delta, synchronized_costs_, true)); + } + incremental_ = true; } - old_delta_value_ = value; + if (objective_ == nullptr) return true; + int64 var_min = objective_->Min(); int64 var_max = objective_->Max(); if (delta->Objective() == objective_) { var_min = std::max(var_min, delta->ObjectiveMin()); var_max = std::min(var_max, delta->ObjectiveMax()); } - value = CapAdd(value, injected_objective_value_); + const int64 value = CapAdd(delta_sum_, injected_objective_value_); PropagateObjectiveValue(value); switch (filter_enum_) { case Solver::LE: { @@ -2381,95 +2499,103 @@ class ObjectiveFilter : public IntVarLocalSearchFilter { } } } - virtual int64 SynchronizedElementValue(int64 index) = 0; - virtual bool EvaluateElementValue(const Assignment::IntContainer& container, - int index, int* container_index, - int64* obj_value) = 0; + // If the variable is synchronized, returns its associated cost, otherwise + // returns 0. + virtual int64 CostOfSynchronizedVariable(int64 index) = 0; + // If the variable is bound, fills new_cost with the cost associated to the + // variable's valuation in container, and returns true. Otherwise, fills + // new_cost with 0, and returns false. + virtual bool FillCostOfBoundDeltaVariable( + const Assignment::IntContainer& container, int index, + int* container_index, int64* new_cost) = 0; bool IsIncremental() const override { return true; } - std::string DebugString() const override { return "ObjectiveFilter"; } + std::string DebugString() const override { return "SumObjectiveFilter"; } + + int64 GetSynchronizedObjectiveValue() const override { + return synchronized_sum_; + } + int64 GetAcceptedObjectiveValue() const override { return delta_sum_; } protected: const int primary_vars_size_; - int64* const cache_; - int64* const delta_cache_; + int64* const synchronized_costs_; + int64* const delta_costs_; const IntVar* const objective_; Solver::LocalSearchFilterBound filter_enum_; - Operator op_; - int64 old_value_; - int64 old_delta_value_; + int64 synchronized_sum_; + int64 delta_sum_; bool incremental_; private: void OnSynchronize(const Assignment* delta) override { - op_.Init(); + synchronized_sum_ = 0; for (int i = 0; i < primary_vars_size_; ++i) { - const int64 obj_value = SynchronizedElementValue(i); - cache_[i] = obj_value; - delta_cache_[i] = obj_value; - op_.Update(obj_value); + const int64 cost = CostOfSynchronizedVariable(i); + synchronized_costs_[i] = cost; + delta_costs_[i] = cost; + synchronized_sum_ = CapAdd(synchronized_sum_, cost); } - old_value_ = op_.value(); - old_delta_value_ = old_value_; + delta_sum_ = synchronized_sum_; incremental_ = false; - PropagateObjectiveValue(CapAdd(op_.value(), injected_objective_value_)); + PropagateObjectiveValue( + CapAdd(synchronized_sum_, injected_objective_value_)); } - int64 Evaluate(const Assignment* delta, int64 current_value, - const int64* const out_values, bool cache_delta_values) { - if (current_value == kint64max) return current_value; - op_.set_value(current_value); - const Assignment::IntContainer& container = delta->IntVarContainer(); + int64 CostOfChanges(const Assignment* changes, const int64* const old_costs, + bool cache_delta_values) { + int64 total_cost = 0; + const Assignment::IntContainer& container = changes->IntVarContainer(); const int size = container.Size(); for (int i = 0; i < size; ++i) { const IntVarElement& new_element = container.Element(i); IntVar* const var = new_element.Var(); int64 index = -1; if (FindIndex(var, &index) && index < primary_vars_size_) { - op_.Remove(out_values[index]); - int64 obj_value = 0LL; - if (EvaluateElementValue(container, index, &i, &obj_value)) { - op_.Update(obj_value); - if (cache_delta_values) { - delta_cache_[index] = obj_value; - } + total_cost = CapSub(total_cost, old_costs[index]); + int64 new_cost = 0LL; + if (FillCostOfBoundDeltaVariable(container, index, &i, &new_cost)) { + total_cost = CapAdd(total_cost, new_cost); + } + if (cache_delta_values) { + delta_costs_[index] = new_cost; } } } - return op_.value(); + return total_cost; } }; -template -class BinaryObjectiveFilter : public ObjectiveFilter { +class BinaryObjectiveFilter : public SumObjectiveFilter { public: BinaryObjectiveFilter(const std::vector& vars, Solver::IndexEvaluator2 value_evaluator, Solver::ObjectiveWatcher delta_objective_callback, const IntVar* const objective, Solver::LocalSearchFilterBound filter_enum) - : ObjectiveFilter(vars, delta_objective_callback, objective, - filter_enum), + : SumObjectiveFilter(vars, std::move(delta_objective_callback), objective, + filter_enum), value_evaluator_(std::move(value_evaluator)) {} ~BinaryObjectiveFilter() override {} - int64 SynchronizedElementValue(int64 index) override { + int64 CostOfSynchronizedVariable(int64 index) override { return IntVarLocalSearchFilter::IsVarSynced(index) ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index)) : 0; } - bool EvaluateElementValue(const Assignment::IntContainer& container, - int index, int* container_index, - int64* obj_value) override { + bool FillCostOfBoundDeltaVariable(const Assignment::IntContainer& container, + int index, int* container_index, + int64* new_cost) override { const IntVarElement& element = container.Element(*container_index); if (element.Activated()) { - *obj_value = value_evaluator_(index, element.Value()); + *new_cost = value_evaluator_(index, element.Value()); return true; } else { const IntVar* var = element.Var(); if (var->Bound()) { - *obj_value = value_evaluator_(index, var->Min()); + *new_cost = value_evaluator_(index, var->Min()); return true; } } + *new_cost = 0; return false; } @@ -2477,8 +2603,7 @@ class BinaryObjectiveFilter : public ObjectiveFilter { Solver::IndexEvaluator2 value_evaluator_; }; -template -class TernaryObjectiveFilter : public ObjectiveFilter { +class TernaryObjectiveFilter : public SumObjectiveFilter { public: TernaryObjectiveFilter(const std::vector& vars, const std::vector& secondary_vars, @@ -2486,15 +2611,15 @@ class TernaryObjectiveFilter : public ObjectiveFilter { Solver::ObjectiveWatcher delta_objective_callback, const IntVar* const objective, Solver::LocalSearchFilterBound filter_enum) - : ObjectiveFilter(vars, delta_objective_callback, objective, - filter_enum), + : SumObjectiveFilter(vars, std::move(delta_objective_callback), objective, + filter_enum), secondary_vars_offset_(vars.size()), value_evaluator_(std::move(value_evaluator)) { IntVarLocalSearchFilter::AddVars(secondary_vars); CHECK_GE(IntVarLocalSearchFilter::Size(), 0); } ~TernaryObjectiveFilter() override {} - int64 SynchronizedElementValue(int64 index) override { + int64 CostOfSynchronizedVariable(int64 index) override { DCHECK_LT(index, secondary_vars_offset_); return IntVarLocalSearchFilter::IsVarSynced(index) ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index), @@ -2502,11 +2627,11 @@ class TernaryObjectiveFilter : public ObjectiveFilter { index + secondary_vars_offset_)) : 0; } - bool EvaluateElementValue(const Assignment::IntContainer& container, - int index, int* container_index, - int64* obj_value) override { + bool FillCostOfBoundDeltaVariable(const Assignment::IntContainer& container, + int index, int* container_index, + int64* new_cost) override { DCHECK_LT(index, secondary_vars_offset_); - *obj_value = 0LL; + *new_cost = 0LL; const IntVarElement& element = container.Element(*container_index); const IntVar* secondary_var = IntVarLocalSearchFilter::Var(index + secondary_vars_offset_); @@ -2515,21 +2640,22 @@ class TernaryObjectiveFilter : public ObjectiveFilter { int hint_index = *container_index + 1; if (hint_index < container.Size() && secondary_var == container.Element(hint_index).Var()) { - *obj_value = value_evaluator_(index, value, - container.Element(hint_index).Value()); + *new_cost = value_evaluator_(index, value, + container.Element(hint_index).Value()); *container_index = hint_index; } else { - *obj_value = value_evaluator_(index, value, - container.Element(secondary_var).Value()); + *new_cost = value_evaluator_(index, value, + container.Element(secondary_var).Value()); } return true; } else { const IntVar* var = element.Var(); if (var->Bound() && secondary_var->Bound()) { - *obj_value = value_evaluator_(index, var->Min(), secondary_var->Min()); + *new_cost = value_evaluator_(index, var->Min(), secondary_var->Min()); return true; } } + *new_cost = 0; return false; } @@ -2539,96 +2665,47 @@ class TernaryObjectiveFilter : public ObjectiveFilter { }; } // namespace -// ---- Local search filter factory ---- - -#define ReturnObjectiveFilter5(Filter, op_enum, arg0, arg1, arg2, arg3, arg4) \ - switch (op_enum) { \ - case Solver::SUM: { \ - return RevAlloc(new Filter(arg0, arg1, arg2, arg3, arg4)); \ - } \ - case Solver::PROD: { \ - return RevAlloc( \ - new Filter(arg0, arg1, arg2, arg3, arg4)); \ - } \ - case Solver::MAX: { \ - return RevAlloc(new Filter(arg0, arg1, arg2, arg3, arg4)); \ - } \ - case Solver::MIN: { \ - return RevAlloc(new Filter(arg0, arg1, arg2, arg3, arg4)); \ - } \ - default: \ - LOG(FATAL) << "Unknown operator " << op_enum; \ - } \ - return nullptr; - -#define ReturnObjectiveFilter6(Filter, op_enum, arg0, arg1, arg2, arg3, arg4, \ - arg5) \ - switch (op_enum) { \ - case Solver::SUM: { \ - return RevAlloc( \ - new Filter(arg0, arg1, arg2, arg3, arg4, arg5)); \ - } \ - case Solver::PROD: { \ - return RevAlloc( \ - new Filter(arg0, arg1, arg2, arg3, arg4, arg5)); \ - } \ - case Solver::MAX: { \ - return RevAlloc( \ - new Filter(arg0, arg1, arg2, arg3, arg4, arg5)); \ - } \ - case Solver::MIN: { \ - return RevAlloc( \ - new Filter(arg0, arg1, arg2, arg3, arg4, arg5)); \ - } \ - default: \ - LOG(FATAL) << "Unknown operator " << op_enum; \ - } \ - return nullptr; - -IntVarLocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( +IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter( const std::vector& vars, Solver::IndexEvaluator2 values, - IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum) { - ReturnObjectiveFilter5(BinaryObjectiveFilter, op_enum, vars, values, nullptr, - objective, filter_enum); + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum) { + return RevAlloc(new BinaryObjectiveFilter(vars, std::move(values), nullptr, + objective, filter_enum)); } -IntVarLocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( +IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter( const std::vector& vars, Solver::IndexEvaluator2 values, ObjectiveWatcher delta_objective_callback, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum) { - ReturnObjectiveFilter5(BinaryObjectiveFilter, op_enum, vars, values, - delta_objective_callback, objective, filter_enum); + Solver::LocalSearchFilterBound filter_enum) { + return RevAlloc(new BinaryObjectiveFilter(vars, std::move(values), + std::move(delta_objective_callback), + objective, filter_enum)); } -IntVarLocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( +IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 values, - IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum) { - ReturnObjectiveFilter6(TernaryObjectiveFilter, op_enum, vars, secondary_vars, - values, nullptr, objective, filter_enum); + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum) { + return RevAlloc(new TernaryObjectiveFilter(vars, secondary_vars, + std::move(values), nullptr, + objective, filter_enum)); } -IntVarLocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( +IntVarLocalSearchFilter* Solver::MakeSumObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, Solver::IndexEvaluator3 values, ObjectiveWatcher delta_objective_callback, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum) { - ReturnObjectiveFilter6(TernaryObjectiveFilter, op_enum, vars, secondary_vars, - values, delta_objective_callback, objective, - filter_enum); + Solver::LocalSearchFilterBound filter_enum) { + return RevAlloc(new TernaryObjectiveFilter( + vars, secondary_vars, std::move(values), + std::move(delta_objective_callback), objective, filter_enum)); } -#undef ReturnObjectiveFilter6 -#undef ReturnObjectiveFilter5 // ----- LocalSearchProfiler ----- class LocalSearchProfiler : public LocalSearchMonitor { public: explicit LocalSearchProfiler(Solver* solver) : LocalSearchMonitor(solver) {} + std::string DebugString() const override { return "LocalSearchProfiler"; } void RestartSearch() override { operator_stats_.clear(); filter_stats_.clear(); @@ -2645,53 +2722,48 @@ class LocalSearchProfiler : public LocalSearchMonitor { op_name_size = std::max(op_name_size, stat.first.length()); } std::string overview = "Local search operator statistics:\n"; - StringAppendF(&overview, - absl::StrCat("%", op_name_size, - "s | Neighbors | Filtered | " - "Accepted | Time (s)\n") - .c_str(), - ""); + absl::StrAppendFormat(&overview, + "%*s | Neighbors | Filtered | Accepted | Time (s)\n", + op_name_size, ""); OperatorStats total_stats; - const std::string row_format = - absl::StrCat("%", op_name_size, "s | %9d | %8d | %8d | %7.2g\n"); for (const auto& stat : operator_stats_) { - StringAppendF(&overview, row_format.c_str(), stat.first.c_str(), - stat.second.neighbors, stat.second.filtered_neighbors, - stat.second.accepted_neighbors, stat.second.seconds); + absl::StrAppendFormat( + &overview, "%*s | %9ld | %8ld | %8ld | %7.2g\n", op_name_size, + stat.first, stat.second.neighbors, stat.second.filtered_neighbors, + stat.second.accepted_neighbors, stat.second.seconds); total_stats.neighbors += stat.second.neighbors; total_stats.filtered_neighbors += stat.second.filtered_neighbors; total_stats.accepted_neighbors += stat.second.accepted_neighbors; total_stats.seconds += stat.second.seconds; } - StringAppendF(&overview, row_format.c_str(), "Total", total_stats.neighbors, - total_stats.filtered_neighbors, - total_stats.accepted_neighbors, total_stats.seconds); + absl::StrAppendFormat(&overview, "%*s | %9ld | %8ld | %8ld | %7.2g\n", + op_name_size, "Total", total_stats.neighbors, + total_stats.filtered_neighbors, + total_stats.accepted_neighbors, total_stats.seconds); op_name_size = 0; for (const auto& stat : filter_stats_) { op_name_size = std::max(op_name_size, stat.first.length()); } - StringAppendF( - &overview, - absl::StrCat("Local search filter statistics:\n%", op_name_size, - "s | Calls | Rejects | Time (s) " - "| Rejects/s\n") - .c_str(), - ""); + absl::StrAppendFormat(&overview, + "Local search filter statistics:\n%*s | Calls | " + " Rejects | Time (s) " + "| Rejects/s\n", + op_name_size, ""); FilterStats total_filter_stats; - const std::string filter_row_format = - absl::StrCat("%", op_name_size, "s | %9d | %9d | %7.2g | %7.2g\n"); for (const auto& stat : filter_stats_) { - StringAppendF(&overview, filter_row_format.c_str(), stat.first.c_str(), - stat.second.calls, stat.second.rejects, stat.second.seconds, - stat.second.rejects / stat.second.seconds); + absl::StrAppendFormat(&overview, "%*s | %9ld | %9ld | %7.2g | %7.2g\n", + op_name_size, stat.first, stat.second.calls, + stat.second.rejects, stat.second.seconds, + stat.second.rejects / stat.second.seconds); total_filter_stats.calls += stat.second.calls; total_filter_stats.rejects += stat.second.rejects; total_filter_stats.seconds += stat.second.seconds; } - StringAppendF(&overview, filter_row_format.c_str(), "Total", - total_filter_stats.calls, total_filter_stats.rejects, - total_filter_stats.seconds, - total_filter_stats.rejects / total_filter_stats.seconds); + absl::StrAppendFormat( + &overview, "%*s | %9ld | %9ld | %7.2g | %7.2g\n", op_name_size, + "Total", total_filter_stats.calls, total_filter_stats.rejects, + total_filter_stats.seconds, + total_filter_stats.rejects / total_filter_stats.seconds); return overview; } void BeginOperatorStart() override {} @@ -2747,15 +2819,15 @@ class LocalSearchProfiler : public LocalSearchMonitor { } struct OperatorStats { - int neighbors = 0; - int filtered_neighbors = 0; - int accepted_neighbors = 0; + int64 neighbors = 0; + int64 filtered_neighbors = 0; + int64 accepted_neighbors = 0; double seconds = 0; }; struct FilterStats { - int calls = 0; - int rejects = 0; + int64 calls = 0; + int64 rejects = 0; double seconds = 0; }; WallTimer timer_; @@ -2916,6 +2988,8 @@ Decision* FindOneNeighbor::Next(Solver* const solver) { solver->GetLocalSearchMonitor()->EndAcceptNeighbor(ls_operator_, accept); if (accept) { + solver->SetSearchContext(solver->ParentSearch(), + ls_operator_->DebugString()); solver->accepted_neighbors_ += 1; assignment_->Store(); neighbor_found_ = true; @@ -3290,6 +3364,10 @@ Decision* LocalSearch::Next(Solver* const solver) { const int state = decision->state(); switch (state) { case NestedSolveDecision::DECISION_FAILED: { + // A local optimum has been reached. The search will continue only if we + // accept up-hill moves (due to metaheuristics). In this case we need to + // reset neighborhood optimal routes. + ls_operator_->Reset(); if (!LocalOptimumReached(solver->ActiveSearch())) { nested_decision_index_ = -1; // Stop the search } @@ -3351,7 +3429,7 @@ class DefaultSolutionPool : public SolutionPool { ~DefaultSolutionPool() override {} void Initialize(Assignment* const assignment) override { - reference_assignment_.reset(new Assignment(assignment)); + reference_assignment_ = absl::make_unique(assignment); } void RegisterNewSolution(Assignment* const assignment) override { diff --git a/ortools/constraint_solver/model.proto b/ortools/constraint_solver/model.proto deleted file mode 100644 index d4920b50437..00000000000 --- a/ortools/constraint_solver/model.proto +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -syntax = "proto3"; -import "ortools/constraint_solver/search_limit.proto"; - -option java_package = "com.google.ortools.constraintsolver"; -option java_multiple_files = true; -option csharp_namespace = "Google.OrTools.ConstraintSolver"; - -package operations_research; - -message CpIntegerMatrix { - int32 rows = 1; - int32 columns = 2; - repeated int64 values = 3; -} - -// This message holds one argument of a constraint or expression. It -// is referenced by the argument_name. Only one field apart the name -// must be set. -message CpArgument { - int32 argument_index = 1; - int64 integer_value = 2; - repeated int64 integer_array = 3; - int32 integer_expression_index = 4; - repeated int32 integer_expression_array = 5; - int32 interval_index = 6; - repeated int32 interval_array = 7; - int32 sequence_index = 8; - repeated int32 sequence_array = 9; - CpIntegerMatrix integer_matrix = 10; - - enum Type { - UNDEFINED = 0; - INTEGER_VALUE = 1; - INTEGER_ARRAY = 2; - EXPRESSION = 3; - EXPRESSION_ARRAY = 4; - INTERVAL = 5; - INTERVAL_ARRAY = 6; - SEQUENCE = 7; - SEQUENCE_ARRAY = 8; - INTEGER_MATRIX = 9; - } - Type type = 11; -} - -message CpExtension { - int32 type_index = 1; - repeated CpArgument arguments = 2; -} - -message CpIntegerExpression { - int32 index = 1; - int32 type_index = 2; - string name = 3; - repeated CpArgument arguments = 4; - repeated CpExtension extensions = 5; -} - -message CpIntervalVariable { - int32 index = 1; - int32 type_index = 2; - string name = 3; - repeated CpArgument arguments = 4; -} - -message CpSequenceVariable { - int32 index = 1; - int32 type_index = 2; - string name = 3; - repeated CpArgument arguments = 4; -} - -message CpConstraint { - int32 index = 1; - int32 type_index = 2; - string name = 3; - repeated CpArgument arguments = 4; - repeated CpExtension extensions = 5; -} - -message CpObjective { - bool maximize = 1; - int64 step = 2; - int32 objective_index = 3; -} - -message CpVariableGroup { - repeated CpArgument arguments = 1; - string type = 2; -} - -message CpModel { - string model = 1; - int32 version = 2; - repeated string tags = 3; - repeated CpIntegerExpression expressions = 4; - repeated CpIntervalVariable intervals = 5; - repeated CpSequenceVariable sequences = 6; - repeated CpConstraint constraints = 7; - CpObjective objective = 8; - SearchLimitParameters search_limit = 9; - repeated CpVariableGroup variable_groups = 10; - string license_text = 11; -} diff --git a/ortools/constraint_solver/nogoods.cc b/ortools/constraint_solver/nogoods.cc deleted file mode 100644 index 25df990a340..00000000000 --- a/ortools/constraint_solver/nogoods.cc +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2010-2018 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include "ortools/base/integral_types.h" -#include "ortools/base/logging.h" -#include "ortools/base/macros.h" -#include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" -#include "ortools/constraint_solver/constraint_solver.h" -#include "ortools/util/string_array.h" - -namespace operations_research { - -// ----- Base Class ----- - -void NoGoodManager::EnterSearch() { Init(); } - -void NoGoodManager::BeginNextDecision(DecisionBuilder* const db) { Apply(); } - -bool NoGoodManager::AcceptSolution() { - Apply(); - return true; -} - -NoGood* NoGoodManager::MakeNoGood() { return new NoGood(); } - -// ----- Base class for NoGood terms ----- - -class NoGoodTerm { - public: - enum TermStatus { ALWAYS_TRUE, ALWAYS_FALSE, UNDECIDED }; - NoGoodTerm() {} - virtual ~NoGoodTerm() {} - - virtual TermStatus Evaluate() const = 0; - virtual void Refute() = 0; - virtual std::string DebugString() const = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(NoGoodTerm); -}; - -namespace { -// ----- IntegerVariableNoGoodTerm ----- - -// IntVar == int64 specialization. -class IntegerVariableNoGoodTerm : public NoGoodTerm { - public: - IntegerVariableNoGoodTerm(IntVar* const var, int64 value, bool assign) - : integer_variable_(var), value_(value), assign_(assign) { - CHECK(integer_variable_ != nullptr); - } - - TermStatus Evaluate() const override { - if (!integer_variable_->Contains(value_)) { - return assign_ ? ALWAYS_FALSE : ALWAYS_TRUE; - } else if (integer_variable_->Bound()) { - return assign_ ? ALWAYS_TRUE : ALWAYS_FALSE; - } else { - return UNDECIDED; - } - } - - void Refute() override { - if (assign_) { - integer_variable_->RemoveValue(value_); - } else { - integer_variable_->SetValue(value_); - } - } - - std::string DebugString() const override { - return StringPrintf("(%s %s %lld)", integer_variable_->name().c_str(), - assign_ ? "==" : "!=", value_); - } - - IntVar* integer_variable() const { return integer_variable_; } - int64 value() const { return value_; } - bool assign() const { return assign_; } - - private: - IntVar* const integer_variable_; - const int64 value_; - const bool assign_; - DISALLOW_COPY_AND_ASSIGN(IntegerVariableNoGoodTerm); -}; -} // namespace - -// ----- NoGood ----- - -NoGood::~NoGood() { gtl::STLDeleteElements(&terms_); } - -void NoGood::AddIntegerVariableEqualValueTerm(IntVar* const var, int64 value) { - terms_.push_back(new IntegerVariableNoGoodTerm(var, value, true)); -} - -void NoGood::AddIntegerVariableNotEqualValueTerm(IntVar* const var, - int64 value) { - terms_.push_back(new IntegerVariableNoGoodTerm(var, value, false)); -} - -bool NoGood::Apply(Solver* const solver) { - NoGoodTerm* first_undecided = nullptr; - for (int i = 0; i < terms_.size(); ++i) { - switch (terms_[i]->Evaluate()) { - case NoGoodTerm::ALWAYS_TRUE: { - break; - } - case NoGoodTerm::ALWAYS_FALSE: { - return false; - } - case NoGoodTerm::UNDECIDED: { - if (first_undecided == nullptr) { - first_undecided = terms_[i]; - } else { - // more than one undecided, we cannot deduce anything. - return true; - } - break; - } - } - } - if (first_undecided == nullptr && !terms_.empty()) { - VLOG(2) << "No Good " << DebugString() << " -> Fail"; - solver->Fail(); - } - if (first_undecided != nullptr) { - VLOG(2) << "No Good " << DebugString() << " -> Refute " - << first_undecided->DebugString(); - first_undecided->Refute(); - return false; - } - return false; -} - -std::string NoGood::DebugString() const { - return StringPrintf("(%s)", JoinDebugStringPtr(terms_, " && ").c_str()); -} - -namespace { -// ----- NoGoodManager ----- - -// This implementation is very naive. It will be kept in the future as -// a reference point. - -class NaiveNoGoodManager : public NoGoodManager { - public: - explicit NaiveNoGoodManager(Solver* const solver) : NoGoodManager(solver) {} - ~NaiveNoGoodManager() override { Clear(); } - - void Clear() override { gtl::STLDeleteElements(&nogoods_); } - - void Init() override {} - - void AddNoGood(NoGood* const nogood) override { nogoods_.push_back(nogood); } - - int NoGoodCount() const override { return nogoods_.size(); } - - void Apply() override { - Solver* const s = solver(); - for (int i = 0; i < nogoods_.size(); ++i) { - nogoods_[i]->Apply(s); - } - } - - std::string DebugString() const override { - return StringPrintf("NaiveNoGoodManager(%d)", NoGoodCount()); - } - - private: - std::vector nogoods_; -}; -} // namespace - -// ----- API ----- - -NoGoodManager* Solver::MakeNoGoodManager() { - return RevAlloc(new NaiveNoGoodManager(this)); -} - -} // namespace operations_research diff --git a/ortools/constraint_solver/pack.cc b/ortools/constraint_solver/pack.cc index 9eb8b0416a0..a6dc7abd721 100644 --- a/ortools/constraint_solver/pack.cc +++ b/ortools/constraint_solver/pack.cc @@ -20,10 +20,10 @@ #include #include +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -224,17 +224,16 @@ void Pack::InitialPropagate() { } for (int bin_index = 0; bin_index < bins_; ++bin_index) { if (need_context) { - solver()->GetPropagationMonitor()->PushContext(StringPrintf( - "Pack(bin %d, forced = [%s], undecided = [%s])", bin_index, - absl::StrJoin(forced_[bin_index], ", ").c_str(), - absl::StrJoin(data->undecided(bin_index), ", ").c_str())); + solver()->GetPropagationMonitor()->PushContext( + absl::StrFormat("Pack(bin %d, forced = [%s], undecided = [%s])", + bin_index, absl::StrJoin(forced_[bin_index], ", "), + absl::StrJoin(data->undecided(bin_index), ", "))); } for (int dim_index = 0; dim_index < dims_.size(); ++dim_index) { if (need_context) { - solver()->GetPropagationMonitor()->PushContext( - StringPrintf("InitialProgateDimension(%s)", - dims_[dim_index]->DebugString().c_str())); + solver()->GetPropagationMonitor()->PushContext(absl::StrFormat( + "InitialProgateDimension(%s)", dims_[dim_index]->DebugString())); } dims_[dim_index]->InitialPropagate(bin_index, forced_[bin_index], data->undecided(bin_index)); @@ -248,15 +247,14 @@ void Pack::InitialPropagate() { } if (need_context) { solver()->GetPropagationMonitor()->PushContext( - StringPrintf("Pack(assigned = [%s], unassigned = [%s])", - absl::StrJoin(data->assigned(), ", ").c_str(), - absl::StrJoin(data->unassigned(), ", ").c_str())); + absl::StrFormat("Pack(assigned = [%s], unassigned = [%s])", + absl::StrJoin(data->assigned(), ", "), + absl::StrJoin(data->unassigned(), ", "))); } for (int dim_index = 0; dim_index < dims_.size(); ++dim_index) { if (need_context) { - solver()->GetPropagationMonitor()->PushContext( - StringPrintf("InitialProgateDimension(%s)", - dims_[dim_index]->DebugString().c_str())); + solver()->GetPropagationMonitor()->PushContext(absl::StrFormat( + "InitialProgateDimension(%s)", dims_[dim_index]->DebugString())); } dims_[dim_index]->InitialPropagateUnassigned(data->assigned(), data->unassigned()); @@ -280,16 +278,16 @@ void Pack::Propagate() { for (int bin_index = 0; bin_index < bins_; ++bin_index) { if (!removed_[bin_index].empty() || !forced_[bin_index].empty()) { if (need_context) { - solver()->GetPropagationMonitor()->PushContext(StringPrintf( - "Pack(bin %d, forced = [%s], removed = [%s])", bin_index, - absl::StrJoin(forced_[bin_index], ", ").c_str(), - absl::StrJoin(removed_[bin_index], ", ").c_str())); + solver()->GetPropagationMonitor()->PushContext( + absl::StrFormat("Pack(bin %d, forced = [%s], removed = [%s])", + bin_index, absl::StrJoin(forced_[bin_index], ", "), + absl::StrJoin(removed_[bin_index], ", "))); } for (int dim_index = 0; dim_index < dims_.size(); ++dim_index) { if (need_context) { - solver()->GetPropagationMonitor()->PushContext(StringPrintf( - "ProgateDimension(%s)", dims_[dim_index]->DebugString().c_str())); + solver()->GetPropagationMonitor()->PushContext(absl::StrFormat( + "ProgateDimension(%s)", dims_[dim_index]->DebugString())); } dims_[dim_index]->Propagate(bin_index, forced_[bin_index], removed_[bin_index]); @@ -305,15 +303,15 @@ void Pack::Propagate() { if (!removed_[bins_].empty() || !forced_[bins_].empty()) { if (need_context) { solver()->GetPropagationMonitor()->PushContext( - StringPrintf("Pack(removed = [%s], forced = [%s])", - absl::StrJoin(removed_[bins_], ", ").c_str(), - absl::StrJoin(forced_[bins_], ", ").c_str())); + absl::StrFormat("Pack(removed = [%s], forced = [%s])", + absl::StrJoin(removed_[bins_], ", "), + absl::StrJoin(forced_[bins_], ", "))); } for (int dim_index = 0; dim_index < dims_.size(); ++dim_index) { if (need_context) { - solver()->GetPropagationMonitor()->PushContext(StringPrintf( - "ProgateDimension(%s)", dims_[dim_index]->DebugString().c_str())); + solver()->GetPropagationMonitor()->PushContext(absl::StrFormat( + "ProgateDimension(%s)", dims_[dim_index]->DebugString())); } dims_[dim_index]->PropagateUnassigned(removed_[bins_], forced_[bins_]); if (need_context) { @@ -347,8 +345,8 @@ void Pack::OneDomain(int var_index) { const int64 oldmax = var->OldMax(); const int64 vmin = var->Min(); const int64 vmax = var->Max(); - for (int64 value = std::max(oldmin, 0LL); value < std::min(vmin, bins_ + 1LL); - ++value) { + for (int64 value = std::max(oldmin, int64{0}); + value < std::min(vmin, bins_ + int64{1}); ++value) { if (unprocessed_->IsSet(value, var_index)) { unprocessed_->SetToZero(s, value, var_index); removed_[value].push_back(var_index); @@ -356,7 +354,7 @@ void Pack::OneDomain(int var_index) { } if (!bound) { for (const int64 value : InitAndGetValues(holes_[var_index])) { - if (value >= std::max(0LL, vmin) && + if (value >= std::max(int64{0}, vmin) && value <= std::min(static_cast(bins_), vmax)) { DCHECK(unprocessed_->IsSet(value, var_index)); unprocessed_->SetToZero(s, value, var_index); @@ -364,7 +362,7 @@ void Pack::OneDomain(int var_index) { } } } - for (int64 value = std::max(vmax + 1, 0LL); + for (int64 value = std::max(vmax + 1, int64{0}); value <= std::min(oldmax, static_cast(bins_)); ++value) { if (unprocessed_->IsSet(value, var_index)) { unprocessed_->SetToZero(s, value, var_index); @@ -387,7 +385,7 @@ std::string Pack::DebugString() const { for (int i = 0; i < dims_.size(); ++i) { result += dims_[i]->DebugString() + " "; } - StringAppendF(&result, "], bins = %d)", bins_); + absl::StrAppendFormat(&result, "], bins = %d)", bins_); return result; } diff --git a/ortools/constraint_solver/python/constraint_solver.i b/ortools/constraint_solver/python/constraint_solver.i index d52913bc564..73e5b1674ce 100644 --- a/ortools/constraint_solver/python/constraint_solver.i +++ b/ortools/constraint_solver/python/constraint_solver.i @@ -91,7 +91,7 @@ struct FailureProtect { %module(directors="1") operations_research // The %feature and %exception below let python exceptions that occur within // director method propagate to the user as they were originally. See -// http://www.i.org/Doc1.3/Python.html#Python_nn36 for example. +// http://www.swig.org/Doc1.3/Python.html#Python_nn36 for example. %feature("director:except") { if ($error != NULL) { throw Swig::DirectorMethodException(); @@ -351,17 +351,15 @@ PY_STRINGIFY_DEBUGSTRING(Decision); penalty_factor); } - LocalSearchFilter* LocalSearchObjectiveFilter( + LocalSearchFilter* SumObjectiveFilter( const std::vector& vars, Solver::IndexEvaluator2 values, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, - Solver::LocalSearchOperation op_enum) { - return $self->MakeLocalSearchObjectiveFilter(vars, - values, - objective, - filter_enum, - op_enum); + Solver::LocalSearchFilterBound filter_enum) { + return $self->MakeSumObjectiveFilter(vars, + values, + objective, + filter_enum); } } @@ -927,7 +925,6 @@ namespace operations_research { %rename (FailuresLimit) Solver::MakeFailuresLimit; %rename (SolutionsLimit) Solver::MakeSolutionsLimit; %rename (CustomLimit) Solver::MakeCustomLimit; -%rename (DefaultSearchLimitParameters) Solver::MakeDefaultSearchLimitParameters; // Solver: Search logs. %rename (SearchLog) Solver::MakeSearchLog; @@ -1051,12 +1048,6 @@ namespace operations_research { %unignore Solver::LE; %unignore Solver::EQ; -%unignore Solver::LocalSearchOperation; -%unignore Solver::SUM; -%unignore Solver::PROD; -%unignore Solver::MAX; -%unignore Solver::MIN; - } // namespace operations_research // ============= Unexposed C++ API : Solver class ============== @@ -1075,11 +1066,6 @@ namespace operations_research { // // - state() // -// - ExportModel() -// - LoadModel() -// - UpgradeModel() -// -// // - DebugString() // - VirtualMemorySize() // - SetClock() @@ -2019,10 +2005,10 @@ namespace operations_research { // - MakeNextNeighbor() %unignore IntVarLocalSearchOperator; %feature("director") IntVarLocalSearchOperator; -%feature("nodirector") IntVarLocalSearchOperator::Start; %unignore IntVarLocalSearchOperator::IntVarLocalSearchOperator; %unignore IntVarLocalSearchOperator::~IntVarLocalSearchOperator; %unignore IntVarLocalSearchOperator::Size; +%feature("nodirector") IntVarLocalSearchOperator::Start; %rename (OneNeighbor) IntVarLocalSearchOperator::MakeOneNeighbor; @@ -2250,3 +2236,4 @@ class PyConstraint(Constraint): } // %pythoncode + diff --git a/ortools/constraint_solver/python/routing.i b/ortools/constraint_solver/python/routing.i index f8d955655e0..7e4f328bf79 100644 --- a/ortools/constraint_solver/python/routing.i +++ b/ortools/constraint_solver/python/routing.i @@ -1,4 +1,4 @@ -// Copyright 2010-2014 Google +// Copyright 2010-2018 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -16,9 +16,8 @@ %include "ortools/base/base.i" %include "ortools/constraint_solver/python/constraint_solver.i" -// TODO(user): remove this when we no longer use callbacks in the routing. -#define FATAL_CALLBACK_EXCEPTION -%include "ortools/base/python/callbacks.i" +%include "ortools/constraint_solver/python/routing_types.i" +%include "ortools/constraint_solver/python/routing_index_manager.i" // We need to forward-declare the proto here, so that PROTO_INPUT involving it // works correctly. The order matters very much: this declaration needs to be @@ -28,81 +27,16 @@ class RoutingModelParameters; class RoutingSearchParameters; } // namespace operations_research -// Include the file we want to wrap a first time. +// Include the files we want to wrap a first time. %{ +#include "ortools/constraint_solver/routing_types.h" +#include "ortools/constraint_solver/routing_parameters.pb.h" +#include "ortools/constraint_solver/routing_parameters.h" #include "ortools/constraint_solver/routing.h" +#include "ortools/util/optional_boolean.pb.h" %} -// Convert RoutingModel::NodeIndex to (32-bit signed) integers. -%typemap(in) operations_research::RoutingModel::NodeIndex { - $1 = operations_research::RoutingModel::NodeIndex(PyInt_AsLong($input)); -} -%typemap(out) operations_research::RoutingModel::NodeIndex { - $result = PyInt_FromLong($1.value()); -} - -// Convert std::vector to/from int arrays. -%{ -template<> -bool PyObjAs(PyObject *py, operations_research::RoutingModel::NodeIndex* i) { - int temp; - if (!PyObjAs(py, &temp)) return false; - *i = operations_research::RoutingModel::NodeIndex(temp); - return true; -} -%} -PY_LIST_OUTPUT_TYPEMAP(operations_research::RoutingModel::NodeIndex, - PyInt_Check, PyInt_FromLong); -PY_LIST_LIST_INPUT_TYPEMAP(operations_research::RoutingModel::NodeIndex, - PyInt_Check); -// TODO(user): also support std::vector> <-> list of list. - -// Create input mapping for NodeEvaluator2 -%{ -static int64 PyCallback2NodeIndexNodeIndex( - PyObject* pyfunc, - operations_research::RoutingModel::NodeIndex i, - operations_research::RoutingModel::NodeIndex j) { - int64 result = 0; - // Cast to int needed, no int64 support - PyObject* arglist = Py_BuildValue("ll", - i.value(), - j.value()); - PyObject* pyresult = PyEval_CallObject(pyfunc, arglist); - Py_DECREF(arglist); - if (pyresult) { - result = PyInt_AsLong(pyresult); - } - Py_XDECREF(pyresult); - return result; -} -%} -%typemap(in) operations_research::RoutingModel::NodeEvaluator2* { - if (!PyCallable_Check($input)) { - PyErr_SetString(PyExc_TypeError, "Need a callable object!"); - SWIG_fail; - } - $1 = NewPermanentCallback(&PyCallback2NodeIndexNodeIndex, $input); -} -// Create conversion of vectors of NodeEvaluator2 -%{ -template<> -bool PyObjAs(PyObject* py_obj, - operations_research::RoutingModel::NodeEvaluator2** b) { - if (!PyCallable_Check(py_obj)) { - PyErr_SetString(PyExc_TypeError, "Need a callable object!"); - return false; - } - *b = NewPermanentCallback(&PyCallback2NodeIndexNodeIndex, py_obj); - return true; -} -%} -// Passing an empty parameter as converter is ok here since no API outputs -// a vector of NodeEvaluator2*. -PY_LIST_OUTPUT_TYPEMAP(operations_research::RoutingModel::NodeEvaluator2*, - PyCallable_Check, ); - %ignore operations_research::RoutingModel::AddMatrixDimension( std::vector > values, int64 capacity, @@ -118,6 +52,8 @@ PY_LIST_OUTPUT_TYPEMAP(operations_research::RoutingModel::NodeEvaluator2*, } } +%ignore operations_research::RoutingModel::RegisterStateDependentTransitCallback; +%ignore operations_research::RoutingModel::StateDependentTransitCallback; %ignore operations_research::RoutingModel::MakeStateDependentTransit; %ignore operations_research::RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity; @@ -128,55 +64,30 @@ PY_PROTO_TYPEMAP(ortools.constraint_solver.routing_parameters_pb2, RoutingSearchParameters, operations_research::RoutingSearchParameters) -%ignore operations_research::RoutingModel::WrapIndexEvaluator( - Solver::IndexEvaluator2* evaluator); - -%ignore operations_research::RoutingModel::RoutingModel( - int nodes, int vehicles, NodeIndex depot); - -%ignore operations_research::RoutingModel::RoutingModel( - int nodes, int vehicles, NodeIndex depot, - const RoutingModelParameters& parameters); - -%extend operations_research::RoutingModel { - RoutingModel(int nodes, int vehicles, int depot) { - operations_research::RoutingModel* model = - new operations_research::RoutingModel( - nodes, vehicles, - operations_research::RoutingModel::NodeIndex(depot)); - return model; - } - RoutingModel(int nodes, int vehicles, int depot, - const RoutingModelParameters& parameters) { - operations_research::RoutingModel* model = - new operations_research::RoutingModel( - nodes, vehicles, - operations_research::RoutingModel::NodeIndex(depot), parameters); - return model; - } -} - -%ignore operations_research::RoutingModel::RoutingModel( - int nodes, int vehicles, - const std::vector >& start_end); +// Wrap routing_types.h, routing_parameters.h according to the SWIG styleguide. +%ignoreall +%unignore RoutingTransitCallback1; +%unignore RoutingTransitCallback2; +%unignore RoutingIndexPair; +%unignore RoutingIndexPairs; -%ignore operations_research::RoutingModel::RoutingModel( - int nodes, int vehicles, - const std::vector >& start_end, - const RoutingModelParameters& parameters); +%unignore DefaultRoutingSearchParameters; +%unignore DefaultRoutingModelParameters; +%unignore FindErrorInRoutingSearchParameters; -%ignoreall -%unignore RoutingNodeIndex; -%unignore RoutingCostClassIndex; -%unignore RoutingDimensionIndex; -%unignore RoutingDisjunctionIndex; -%unignore RoutingVehicleClassIndex; -%unignore RoutingNodeEvaluator2; -%unignore RoutingTransitEvaluator2; -%unignore RoutingNodePair; -%unignore RoutingNodePairs; %include "ortools/constraint_solver/routing_types.h" +%include "ortools/constraint_solver/routing_parameters.h" %unignoreall +// %including a .proto.h is frowned upon (for good general reasons), so we +// have to duplicate the OptionalBoolean enum here to give it to python users. +namespace operations_research { +enum OptionalBoolean { + BOOL_UNSPECIFIED = 0, + BOOL_FALSE = 2, + BOOL_TRUE = 3, +}; +} // namespace operations_research + // TODO(user): Use ignoreall/unignoreall for this one. A lot of work. %include "ortools/constraint_solver/routing.h" diff --git a/ortools/constraint_solver/python/routing_index_manager.i b/ortools/constraint_solver/python/routing_index_manager.i new file mode 100644 index 00000000000..9847d66575f --- /dev/null +++ b/ortools/constraint_solver/python/routing_index_manager.i @@ -0,0 +1,42 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Wrapper for RoutingIndexManager. + +%include "ortools/base/base.i" +%include "ortools/constraint_solver/python/routing_types.i" +%import "ortools/util/python/vector.i" + +%{ +#include "ortools/constraint_solver/routing_index_manager.h" +%} + +DEFINE_INDEX_TYPE_TYPEDEF(operations_research::RoutingNodeIndex, + operations_research::RoutingIndexManager::NodeIndex); + +%ignoreall + +%unignore operations_research; +%unignore operations_research::RoutingIndexManager; +%unignore operations_research::RoutingIndexManager::IndexToNode; +%unignore operations_research::RoutingIndexManager::NodeToIndex; +%unignore operations_research::RoutingIndexManager::RoutingIndexManager( + int, int, NodeIndex); +%unignore operations_research::RoutingIndexManager::RoutingIndexManager( + int, int, const std::vector&, + const std::vector&); +%unignore operations_research::RoutingIndexManager::~RoutingIndexManager; + +%include "ortools/constraint_solver/routing_index_manager.h" + +%unignoreall diff --git a/ortools/constraint_solver/python/routing_types.i b/ortools/constraint_solver/python/routing_types.i new file mode 100644 index 00000000000..169a11dc0d5 --- /dev/null +++ b/ortools/constraint_solver/python/routing_types.i @@ -0,0 +1,70 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Typemaps for Routing Index Types. This does not define any type wrappings, +// because these index types are are never exposed to the target language. +// Instead, indices are manipulated as native target language types (e.g. python +// int). +// This file is to be %included when wrapped objects need to use these typemaps. + +%include "ortools/base/base.i" +%import "ortools/util/python/vector.i" + +%{ +#include "ortools/constraint_solver/routing_types.h" +%} + +// This macro defines typemaps for IndexT, std::vector and +// std::vector>. +%define DEFINE_INDEX_TYPE(IndexT) + +// Convert IndexT to (32-bit signed) integers. +%typemap(in) IndexT { + $1 = IndexT(PyInt_AsLong($input)); +} +%typemap(out) IndexT { + $result = PyInt_FromLong($1.value()); +} +%typemap(typecheck) IndexT = int; + +// Convert std::vector to/from int arrays. +%{ +template<> +bool PyObjAs(PyObject *py, IndexT* i) { + int temp; + if (!PyObjAs(py, &temp)) return false; + *i = IndexT(temp); + return true; +} +%} +PY_LIST_OUTPUT_TYPEMAP(IndexT, PyInt_Check, PyInt_FromLong); +PY_LIST_LIST_INPUT_TYPEMAP(IndexT, PyInt_Check); + +%enddef // DEFINE_INDEX_TYPE + +// This macro applies all typemaps for a given index type to a typedef. +// Normally we should not need that as SWIG is supposed to automatically apply +// all typemaps to typedef definitions (http://www.swig.org/Doc2.0/SWIGDocumentation.html#Typemaps_typedef_reductions), +// but this is not actually the case. +%define DEFINE_INDEX_TYPE_TYPEDEF(IndexT, NewIndexT) +%apply IndexT { NewIndexT }; +%apply std::vector { std::vector }; +%apply std::vector* { std::vector* }; +%apply const std::vector& { std::vector& }; +%enddef // DEFINE_INDEX_TYPE_TYPEDEF + +DEFINE_INDEX_TYPE(operations_research::RoutingNodeIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingCostClassIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingDimensionIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingDisjunctionIndex); +DEFINE_INDEX_TYPE(operations_research::RoutingVehicleClassIndex); diff --git a/ortools/constraint_solver/range_cst.cc b/ortools/constraint_solver/range_cst.cc index 68f3a53a042..f0ceed52047 100644 --- a/ortools/constraint_solver/range_cst.cc +++ b/ortools/constraint_solver/range_cst.cc @@ -17,8 +17,8 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -301,9 +301,8 @@ class IsEqualCt : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("IsEqualCt(%s, %s, %s)", left_->DebugString().c_str(), - right_->DebugString().c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("IsEqualCt(%s, %s, %s)", left_->DebugString(), + right_->DebugString(), target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -381,9 +380,8 @@ class IsDifferentCt : public CastConstraint { } std::string DebugString() const override { - return StringPrintf( - "IsDifferentCt(%s, %s, %s)", left_->DebugString().c_str(), - right_->DebugString().c_str(), target_var_->DebugString().c_str()); + return absl::StrFormat("IsDifferentCt(%s, %s, %s)", left_->DebugString(), + right_->DebugString(), target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -436,9 +434,8 @@ class IsLessOrEqualCt : public CastConstraint { } std::string DebugString() const override { - return StringPrintf( - "IsLessOrEqualCt(%s, %s, %s)", left_->DebugString().c_str(), - right_->DebugString().c_str(), target_var_->DebugString().c_str()); + return absl::StrFormat("IsLessOrEqualCt(%s, %s, %s)", left_->DebugString(), + right_->DebugString(), target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -490,9 +487,8 @@ class IsLessCt : public CastConstraint { } std::string DebugString() const override { - return StringPrintf("IsLessCt(%s, %s, %s)", left_->DebugString().c_str(), - right_->DebugString().c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("IsLessCt(%s, %s, %s)", left_->DebugString(), + right_->DebugString(), target_var_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -612,8 +608,8 @@ IntVar* Solver::MakeIsEqualVar(IntExpr* const v1, IntExpr* const v2) { if (name2.empty()) { name2 = v2->DebugString(); } - boolvar = MakeBoolVar( - StringPrintf("IsEqualVar(%s, %s)", name1.c_str(), name2.c_str())); + boolvar = + MakeBoolVar(absl::StrFormat("IsEqualVar(%s, %s)", name1, name2)); AddConstraint(MakeIsEqualCt(v1, v2, boolvar)); model_cache_->InsertExprExprExpression(boolvar, v1, v2, ModelCache::EXPR_EXPR_IS_EQUAL); @@ -676,8 +672,8 @@ IntVar* Solver::MakeIsDifferentVar(IntExpr* const v1, IntExpr* const v2) { if (name2.empty()) { name2 = v2->DebugString(); } - boolvar = MakeBoolVar( - StringPrintf("IsDifferentVar(%s, %s)", name1.c_str(), name2.c_str())); + boolvar = + MakeBoolVar(absl::StrFormat("IsDifferentVar(%s, %s)", name1, name2)); AddConstraint(MakeIsDifferentCt(v1, v2, boolvar)); } model_cache_->InsertExprExprExpression(boolvar, v1, v2, @@ -720,8 +716,8 @@ IntVar* Solver::MakeIsLessOrEqualVar(IntExpr* const left, if (name2.empty()) { name2 = right->DebugString(); } - IntVar* const boolvar = MakeBoolVar( - StringPrintf("IsLessOrEqual(%s, %s)", name1.c_str(), name2.c_str())); + IntVar* const boolvar = + MakeBoolVar(absl::StrFormat("IsLessOrEqual(%s, %s)", name1, name2)); AddConstraint(RevAlloc(new IsLessOrEqualCt(this, left, right, boolvar))); model_cache_->InsertExprExprExpression( @@ -763,8 +759,8 @@ IntVar* Solver::MakeIsLessVar(IntExpr* const left, IntExpr* const right) { if (name2.empty()) { name2 = right->DebugString(); } - IntVar* const boolvar = MakeBoolVar( - StringPrintf("IsLessOrEqual(%s, %s)", name1.c_str(), name2.c_str())); + IntVar* const boolvar = + MakeBoolVar(absl::StrFormat("IsLessOrEqual(%s, %s)", name1, name2)); AddConstraint(RevAlloc(new IsLessCt(this, left, right, boolvar))); model_cache_->InsertExprExprExpression(boolvar, left, right, diff --git a/ortools/constraint_solver/resource.cc b/ortools/constraint_solver/resource.cc index 44525895bb9..142cb05d76d 100644 --- a/ortools/constraint_solver/resource.cc +++ b/ortools/constraint_solver/resource.cc @@ -23,18 +23,19 @@ #include #include #include -#include #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/mathutil.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/bitset.h" @@ -114,8 +115,8 @@ struct CumulativeTask { void WhenAnything(Demon* const demon) { interval->WhenAnything(demon); } std::string DebugString() const { - return StringPrintf("Task{ %s, demand: %" GG_LL_FORMAT "d }", - interval->DebugString().c_str(), demand); + return absl::StrFormat("Task{ %s, demand: %" GG_LL_FORMAT "d }", + interval->DebugString(), demand); } IntervalVar* interval; @@ -143,9 +144,8 @@ struct VariableCumulativeTask { } std::string DebugString() const { - return StringPrintf("Task{ %s, demand: %s }", - interval->DebugString().c_str(), - demand->DebugString().c_str()); + return absl::StrFormat("Task{ %s, demand: %s }", interval->DebugString(), + demand->DebugString()); } IntervalVar* const interval; @@ -563,8 +563,8 @@ class EdgeFinderAndDetectablePrecedences { // use them must first sort them in the right order. // All of these vectors store the same set of objects. Therefore, at - // destruction time, gtl::STLDeleteElements should be called on only one of - // them. It does not matter which one. + // destruction time, STLDeleteElements should be called on only one of them. + // It does not matter which one. ThetaTree theta_tree_; std::vector by_end_min_; @@ -914,11 +914,10 @@ class RankedPropagator : public Constraint { } std::string DebugString() const override { - return StringPrintf( + return absl::StrFormat( "RankedPropagator([%s], nexts = [%s], intervals = [%s])", - partial_sequence_.DebugString().c_str(), - JoinDebugStringPtr(nexts_, ", ").c_str(), - JoinDebugStringPtr(intervals_, ", ").c_str()); + partial_sequence_.DebugString(), JoinDebugStringPtr(nexts_, ", "), + JoinDebugStringPtr(intervals_, ", ")); } void Accept(ModelVisitor* const visitor) const override { @@ -1066,8 +1065,8 @@ class FullDisjunctiveConstraint : public DisjunctiveConstraint { } std::string DebugString() const override { - return StringPrintf("FullDisjunctiveConstraint([%s], %i)", - JoinDebugStringPtr(intervals_, ", ").c_str(), strict_); + return absl::StrFormat("FullDisjunctiveConstraint([%s], %i)", + JoinDebugStringPtr(intervals_, ", "), strict_); } const std::vector& nexts() const override { return nexts_; } @@ -1138,7 +1137,7 @@ class FullDisjunctiveConstraint : public DisjunctiveConstraint { const int64 duration_min = var->DurationMin(); time_slacks_[i + 1] = s->MakeIntVar( duration_min, horizon, - StringPrintf("time_slacks(%" GG_LL_FORMAT "d)", i + 1)); + absl::StrFormat("time_slacks(%" GG_LL_FORMAT "d)", i + 1)); // TODO(user): Check SafeStartExpr(); time_cumuls_[i + 1] = var->SafeStartExpr(var->StartMin())->Var(); if (var->DurationMax() != duration_min) { @@ -1147,7 +1146,8 @@ class FullDisjunctiveConstraint : public DisjunctiveConstraint { } } else { time_slacks_[i + 1] = s->MakeIntVar( - 0, horizon, StringPrintf("time_slacks(%" GG_LL_FORMAT "d)", i + 1)); + 0, horizon, + absl::StrFormat("time_slacks(%" GG_LL_FORMAT "d)", i + 1)); time_cumuls_[i + 1] = s->MakeIntConst(horizon); } } @@ -1648,12 +1648,10 @@ class EdgeFinder : public Constraint { // Stack of updates to the new start min to do. std::vector> start_min_update_; - typedef std::unordered_map UpdateMap; - // update_map_[d][i] is an integer such that if a task // whose demand is d cannot end before by_end_max_[i], then it cannot start // before update_map_[d][i]. - UpdateMap update_map_; + absl::flat_hash_map update_map_; // Has one task a demand min == 0 Rev has_zero_demand_tasks_; @@ -2225,9 +2223,9 @@ class CumulativeConstraint : public Constraint { } std::string DebugString() const override { - return StringPrintf("CumulativeConstraint([%s], %s)", - JoinDebugString(tasks_, ", ").c_str(), - capacity_->DebugString().c_str()); + return absl::StrFormat("CumulativeConstraint([%s], %s)", + JoinDebugString(tasks_, ", "), + capacity_->DebugString()); } private: @@ -2414,9 +2412,9 @@ class VariableDemandCumulativeConstraint : public Constraint { } std::string DebugString() const override { - return StringPrintf("VariableDemandCumulativeConstraint([%s], %s)", - JoinDebugString(tasks_, ", ").c_str(), - capacity_->DebugString().c_str()); + return absl::StrFormat("VariableDemandCumulativeConstraint([%s], %s)", + JoinDebugString(tasks_, ", "), + capacity_->DebugString()); } private: diff --git a/ortools/constraint_solver/routing.cc b/ortools/constraint_solver/routing.cc index e8a143a6543..c042f057452 100644 --- a/ortools/constraint_solver/routing.cc +++ b/ortools/constraint_solver/routing.cc @@ -17,47 +17,47 @@ #include #include #include +#include #include #include -#include - +#include +#include +#include + +#include "absl/base/casts.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/time/time.h" +#include "google/protobuf/duration.pb.h" #include "google/protobuf/text_format.h" -#include "ortools/base/callback.h" -#include "ortools/base/casts.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" +#include "ortools/base/protoutil.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/thorough_hash.h" -#include "ortools/constraint_solver/model.pb.h" #include "ortools/constraint_solver/routing_enums.pb.h" #include "ortools/constraint_solver/routing_neighborhoods.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/glop/lp_solver.h" #include "ortools/graph/connectivity.h" #include "ortools/graph/linear_assignment.h" +#include "ortools/graph/min_cost_flow.h" +#include "ortools/lp_data/lp_data.h" +#include "ortools/lp_data/lp_types.h" +#include "ortools/util/optional_boolean.pb.h" #include "ortools/util/saturated_arithmetic.h" +#include "ortools/util/stats.h" namespace operations_research { class LocalSearchPhaseParameters; } // namespace operations_research -// TODO(user): Remove these flags after cleaning up the Savings heuristic. -DEFINE_double(savings_route_shape_parameter, 1.0, - "Coefficient of the added arc in the savings definition." - "Variation of this parameter may provide heuristic solutions " - "which are closer to the optimal solution than otherwise obtained" - " via the traditional algorithm where it is equal to 1."); -DEFINE_int64(savings_filter_neighbors, 0, - "Use filter which filters the pair of orders considered in " - "Savings first solution heuristic by limiting the number " - "of neighbors considered for each node."); -DEFINE_int64(savings_filter_radius, 0, - "Use filter which filters the pair of orders considered in " - "Savings first solution heuristic by limiting the distance " - "up to which a neighbor is considered for each node."); DEFINE_int64(sweep_sectors, 1, "The number of sectors the space is divided before it is sweeped " "by the ray."); @@ -69,8 +69,332 @@ DEFINE_int64(sweep_sectors, 1, namespace operations_research { +RouteDimensionCumulOptimizer::RouteDimensionCumulOptimizer( + const RoutingDimension* dimension) + : optimizer_core_(dimension) { + // Using one solver and linear program per vehicle in the hope that if + // routes don't change this will be faster. + const int vehicles = dimension->model()->vehicles(); + lp_solver_.resize(vehicles); + linear_program_.resize(vehicles); + // The following parameters give the fastest response time without impacting + // solutions found negatively. + glop::GlopParameters parameters; + parameters.set_use_dual_simplex(true); + parameters.set_use_preprocessing(false); + for (int vehicle = 0; vehicle < vehicles; ++vehicle) { + lp_solver_[vehicle] = absl::make_unique(); + lp_solver_[vehicle]->SetParameters(parameters); + linear_program_[vehicle] = absl::make_unique(); + } +} + +bool RouteDimensionCumulOptimizer::ComputeRouteCumulCost( + int vehicle, const std::function& next_accessor, + int64* optimal_cost) { + return optimizer_core_.OptimizeSingleRoute( + vehicle, next_accessor, linear_program_[vehicle].get(), + lp_solver_[vehicle].get(), nullptr, optimal_cost, nullptr); +} + +bool RouteDimensionCumulOptimizer::ComputeRouteCumulCostWithoutFixedTransits( + int vehicle, const std::function& next_accessor, + int64* optimal_cost_without_transits) { + int64 cost = 0; + int64 transit_cost = 0; + if (optimizer_core_.OptimizeSingleRoute( + vehicle, next_accessor, linear_program_[vehicle].get(), + lp_solver_[vehicle].get(), nullptr, &cost, &transit_cost)) { + if (optimal_cost_without_transits != nullptr) { + *optimal_cost_without_transits = CapSub(cost, transit_cost); + } + return true; + } + return false; +} + +bool RouteDimensionCumulOptimizer::ComputeRouteCumuls( + int vehicle, const std::function& next_accessor, + std::vector* optimal_cumuls) { + return optimizer_core_.OptimizeSingleRoute( + vehicle, next_accessor, linear_program_[vehicle].get(), + lp_solver_[vehicle].get(), optimal_cumuls, nullptr, nullptr); +} + +bool DimensionCumulOptimizerCore::OptimizeSingleRoute( + int vehicle, const std::function& next_accessor, + glop::LinearProgram* linear_program, glop::LPSolver* lp_solver, + std::vector* cumul_values, int64* cost, int64* transit_cost) { + linear_program->Clear(); + linear_program->SetMaximizationProblem(false); + + SetRouteCumulConstraints(vehicle, next_accessor, linear_program, + transit_cost); + return FinalizeAndSolve(linear_program, lp_solver, cumul_values, cost); +} + +void DimensionCumulOptimizerCore::SetRouteCumulConstraints( + int vehicle, const std::function& next_accessor, + glop::LinearProgram* linear_program, int64* route_transit_cost) { + current_route_cumul_variables_.clear(); + std::vector slacks; + int64 fixed_transit = 0; + int64 total_fixed_transit = 0; + + const std::function& transit_accessor = + dimension_->transit_evaluator(vehicle); + + RoutingModel* const model = dimension_->model(); + int current = model->Start(vehicle); + while (true) { + glop::ColIndex cumul = linear_program->CreateNewVariable(); + IntVar* const cp_cumul = dimension_->CumulVar(current); + // Handle potential precision errors due to infinite upper bound. + const double cumul_max = + (cp_cumul->Max() == kint64max) ? glop::kInfinity : cp_cumul->Max(); + linear_program->SetVariableBounds(cumul, cp_cumul->Min(), cumul_max); + if (dimension_->HasCumulVarSoftUpperBound(current)) { + // cumul - bound <= soft_ub_cost + const int64 bound = dimension_->GetCumulVarSoftUpperBound(current); + const int64 coef = + dimension_->GetCumulVarSoftUpperBoundCoefficient(current); + glop::ColIndex soft_ub_cost = linear_program->CreateNewVariable(); + glop::RowIndex ct = linear_program->CreateNewConstraint(); + linear_program->SetConstraintBounds(ct, -glop::kInfinity, bound); + linear_program->SetCoefficient(ct, cumul, 1); + linear_program->SetCoefficient(ct, soft_ub_cost, -1); + linear_program->SetObjectiveCoefficient(soft_ub_cost, coef); + } + if (dimension_->HasCumulVarSoftLowerBound(current)) { + // bound - cumul <= soft_lb_cost + const int64 bound = dimension_->GetCumulVarSoftLowerBound(current); + const int64 coef = + dimension_->GetCumulVarSoftLowerBoundCoefficient(current); + glop::ColIndex soft_lb_cost = linear_program->CreateNewVariable(); + glop::RowIndex ct = linear_program->CreateNewConstraint(); + linear_program->SetConstraintBounds(ct, bound, glop::kInfinity); + linear_program->SetCoefficient(ct, cumul, 1); + linear_program->SetCoefficient(ct, soft_lb_cost, 1); + linear_program->SetObjectiveCoefficient(soft_lb_cost, coef); + } + if (!slacks.empty()) { + // cumul = prev_cumul + prev_slack + transit + glop::RowIndex ct = linear_program->CreateNewConstraint(); + linear_program->SetConstraintBounds(ct, fixed_transit, fixed_transit); + linear_program->SetCoefficient(ct, cumul, 1); + linear_program->SetCoefficient(ct, current_route_cumul_variables_.back(), + -1); + linear_program->SetCoefficient(ct, slacks.back(), -1); + } + current_route_cumul_variables_.push_back(cumul); + if (!model->IsEnd(current)) { + IntVar* const cp_slack = dimension_->SlackVar(current); + glop::ColIndex slack = linear_program->CreateNewVariable(); + // Handle potential precision errors due to infinite upper bound. + const double slack_max = + (cp_slack->Max() == kint64max) ? glop::kInfinity : cp_slack->Max(); + linear_program->SetVariableBounds(slack, cp_slack->Min(), slack_max); + slacks.push_back(slack); + const int64 next_current = next_accessor(current); + fixed_transit = transit_accessor(current, next_current); + total_fixed_transit = CapAdd(total_fixed_transit, fixed_transit); + current = next_current; + } else { + break; + } + } + DCHECK_GE(current_route_cumul_variables_.size(), 2); + + const int64 span_bound = dimension_->GetSpanUpperBoundForVehicle(vehicle); + if (span_bound < kint64max) { + // end_cumul - start_cumul <= bound + glop::RowIndex ct = linear_program->CreateNewConstraint(); + linear_program->SetConstraintBounds(ct, -glop::kInfinity, span_bound); + linear_program->SetCoefficient(ct, current_route_cumul_variables_.back(), + 1); + linear_program->SetCoefficient(ct, current_route_cumul_variables_.front(), + -1); + } + const int64 span_cost = dimension_->GetSpanCostCoefficientForVehicle(vehicle); + if (span_cost > 0) { + linear_program->SetObjectiveCoefficient( + current_route_cumul_variables_.back(), span_cost); + linear_program->SetObjectiveCoefficient( + current_route_cumul_variables_.front(), -span_cost); + } + if (route_transit_cost != nullptr) { + if (span_cost > 0) { + *route_transit_cost = CapProd(total_fixed_transit, span_cost); + } else { + *route_transit_cost = 0; + } + } +} + +bool DimensionCumulOptimizerCore::FinalizeAndSolve( + glop::LinearProgram* linear_program, glop::LPSolver* lp_solver, + std::vector* cumul_values, int64* cost) { + linear_program->CleanUp(); + const glop::ProblemStatus status = lp_solver->Solve(*linear_program); + if (status != glop::ProblemStatus::OPTIMAL) { + if (cost != nullptr) { + *cost = kint64max; + } + return false; + } + if (cost != nullptr) { + *cost = std::round(lp_solver->GetObjectiveValue()); + } + if (cumul_values != nullptr) { + cumul_values->clear(); + cumul_values->reserve(current_route_cumul_variables_.size()); + for (auto cumul : current_route_cumul_variables_) { + cumul_values->push_back(std::round(lp_solver->variable_values()[cumul])); + } + } + return true; +} + +namespace { +// A decision builder which tries to assign values to variables as close as +// possible to target values first. +// TODO(user): Move to CP solver. +class SetValuesFromTargets : public DecisionBuilder { + public: + SetValuesFromTargets(std::vector variables, + std::vector targets) + : variables_(std::move(variables)), + targets_(std::move(targets)), + index_(0), + steps_(variables_.size(), 0) {} + Decision* Next(Solver* const solver) override { + int index = index_.Value(); + while (index < variables_.size() && variables_[index]->Bound()) { + ++index; + } + index_.SetValue(solver, index); + if (index >= variables_.size()) return nullptr; + if (!HasTargetsInBounds(index)) { + // Both bounds were hit, exiting search. + solver->Fail(); + } + const int step = steps_[index]; + steps_.SetValue(solver, index, GetNextStep(step)); + return solver->MakeAssignVariableValue(variables_[index], + CapAdd(targets_[index], step)); + } + + private: + int GetNextStep(int step) const { + return (step > 0) ? -step : CapSub(1, step); + } + bool HasTargetsInBounds(int index) const { + IntVar* const variable = variables_[index]; + const int64 target = targets_[index]; + const int64 step = steps_[index]; + const int64 value = CapAdd(target, step); + const int64 next_value = CapAdd(target, GetNextStep(step)); + return std::max(value, next_value) <= variable->Max() || + std::min(value, next_value) >= variable->Min(); + } + + const std::vector variables_; + const std::vector targets_; + Rev index_; + RevArray steps_; +}; + +} // namespace + +DecisionBuilder* MakeSetValuesFromTargets(Solver* solver, + std::vector variables, + std::vector targets) { + return solver->RevAlloc( + new SetValuesFromTargets(std::move(variables), std::move(targets))); +} + namespace { +class SetCumulsFromDimensionCosts : public DecisionBuilder { + public: + SetCumulsFromDimensionCosts(std::vector dimensions, + SearchMonitor* monitor) + : monitor_(monitor) { + optimizers_.reserve(dimensions.size()); + for (RoutingDimension* const dimension : dimensions) { + optimizers_.emplace_back(dimension); + } + } + Decision* Next(Solver* const solver) override { + for (RouteDimensionCumulOptimizer& optimizer : optimizers_) { + const RoutingDimension* const dimension = optimizer.dimension(); + RoutingModel* const model = dimension->model(); + std::function next = [model](int64 i) { + return model->NextVar(i)->Value(); + }; + for (int vehicle = 0; vehicle < model->vehicles(); ++vehicle) { + // TODO(user): Investigate if we should skip unused vehicles. + if (dimension->GetSpanCostCoefficientForVehicle(vehicle) <= 0) continue; +#ifndef NDEBUG + int node = model->Start(vehicle); + while (!model->IsEnd(node)) { + const int next = model->NextVar(node)->Value(); + CHECK_EQ(dimension->transit_evaluator(vehicle)(node, next), + dimension->FixedTransitVar(node)->Value()); + node = next; + } +#endif + std::vector cumul_values; + if (!optimizer.ComputeRouteCumuls(vehicle, next, &cumul_values)) { + solver->Fail(); + } + std::vector cumuls; + int current = model->Start(vehicle); + while (true) { + cumuls.push_back(dimension->CumulVar(current)); + if (!model->IsEnd(current)) { + current = model->NextVar(current)->Value(); + } else { + break; + } + } + if (!solver->SolveAndCommit( + MakeSetValuesFromTargets(solver, std::move(cumuls), + std::move(cumul_values)), + monitor_)) { + solver->Fail(); + } + } + } + return nullptr; + } + + private: + std::vector optimizers_; + SearchMonitor* const monitor_; +}; + +// Constraint which ensures that var != values. +class DifferentFromValues : public Constraint { + public: + DifferentFromValues(Solver* solver, IntVar* var, std::vector values) + : Constraint(solver), var_(var), values_(std::move(values)) {} + void Post() override {} + void InitialPropagate() override { var_->RemoveValues(values_); } + std::string DebugString() const override { return "DifferentFromValues"; } + void Accept(ModelVisitor* const visitor) const override { + visitor->BeginVisitConstraint(RoutingModelVisitor::kRemoveValues, this); + visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument, + {var_}); + visitor->VisitIntegerArrayArgument(ModelVisitor::kValuesArgument, values_); + visitor->EndVisitConstraint(RoutingModelVisitor::kRemoveValues, this); + } + + private: + IntVar* const var_; + const std::vector values_; +}; + // Set of "light" constraints, well-suited for use within Local Search. // These constraints are "checking" constraints, only triggered on WhenBound // events. The provide very little (or no) domain filtering. @@ -80,16 +404,19 @@ namespace { // var == values(index). // Doesn't perform bound reduction of the resulting variable until the index // variable is bound. -// Ownership of the 'values' callback is taken by the constraint. +// If deep_serialize returns false, the model visitor will not extract all +// possible values from the values function. template class LightFunctionElementConstraint : public Constraint { public: LightFunctionElementConstraint(Solver* const solver, IntVar* const var, - IntVar* const index, F values) + IntVar* const index, F values, + std::function deep_serialize) : Constraint(solver), var_(var), index_(index), - values_(std::move(values)) {} + values_(std::move(values)), + deep_serialize_(std::move(deep_serialize)) {} ~LightFunctionElementConstraint() override {} void Post() override { @@ -116,7 +443,10 @@ class LightFunctionElementConstraint : public Constraint { visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndexArgument, index_); // Warning: This will expand all values into a vector. - visitor->VisitInt64ToInt64Extension(values_, index_->Min(), index_->Max()); + if (deep_serialize_()) { + visitor->VisitInt64ToInt64Extension(values_, index_->Min(), + index_->Max()); + } visitor->EndVisitConstraint(RoutingModelVisitor::kLightElement, this); } @@ -126,38 +456,17 @@ class LightFunctionElementConstraint : public Constraint { IntVar* const var_; IntVar* const index_; F values_; + std::function deep_serialize_; }; template Constraint* MakeLightElement(Solver* const solver, IntVar* const var, - IntVar* const index, F values) { + IntVar* const index, F values, + std::function deep_serialize) { return solver->RevAlloc(new LightFunctionElementConstraint( - solver, var, index, std::move(values))); + solver, var, index, std::move(values), std::move(deep_serialize))); } -namespace { -Constraint* BuildLightElement(CpModelLoader* const builder, - const CpConstraint& proto) { - IntExpr* index = nullptr; - if (!builder->ScanArguments(ModelVisitor::kIndexArgument, proto, &index)) { - return nullptr; - } - IntExpr* target = nullptr; - if (!builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)) { - return nullptr; - } - if (proto.extensions_size() != 1) { - return nullptr; - } - const int extension_tag_index = - builder->TagIndex(ModelVisitor::kInt64ToInt64Extension); - Solver::IndexEvaluator1 callback = MakeFunctionFromProto( - builder, proto.extensions(0), extension_tag_index); - return MakeLightElement(builder->solver(), target->Var(), index->Var(), - std::move(callback)); -} -} // namespace - // Light two-dimension function-based element constraint ensuring: // var == values(index1, index2). // Doesn't perform bound reduction of the resulting variable until the index @@ -230,45 +539,6 @@ Constraint* MakeLightElement2(Solver* const solver, IntVar* const var, solver, var, index1, index2, std::move(values))); } -namespace { -Constraint* BuildLightElement2(CpModelLoader* const builder, - const CpConstraint& proto) { - Solver* const solver = builder->solver(); - IntExpr* index1 = nullptr; - if (!builder->ScanArguments(ModelVisitor::kIndexArgument, proto, &index1)) { - return nullptr; - } - IntExpr* index2 = nullptr; - if (!builder->ScanArguments(ModelVisitor::kIndex2Argument, proto, &index2)) { - return nullptr; - } - int64 index1_min = 0; - if (!builder->ScanArguments(ModelVisitor::kMinArgument, proto, &index1_min)) { - return nullptr; - } - int64 index1_max = 0; - if (!builder->ScanArguments(ModelVisitor::kMaxArgument, proto, &index1_max)) { - return nullptr; - } - const int extension_tag_index = - builder->TagIndex(ModelVisitor::kInt64ToInt64Extension); - ArrayWithOffset* const array = solver->RevAlloc( - new ArrayWithOffset(index1_min, index1_max)); - for (int i = index1_min; i <= index1_max; ++i) { - array->SetValue( - i, MakeFunctionFromProto( - builder, proto.extensions(i - index1_min), extension_tag_index)); - } - IntExpr* target = nullptr; - if (!builder->ScanArguments(ModelVisitor::kTargetArgument, proto, &target)) { - return nullptr; - } - return MakeLightElement2( - builder->solver(), target->Var(), index1->Var(), index2->Var(), - [array](int64 i, int64 j) { return array->Evaluate(i)(j); }); -} -} // namespace - // Shortcuts to spawn neighborhood operators from ./routing_neighborhoods.h. // TODO(user): Consider removing all these trivial wrappers and just inlining // the solver->RevAlloc(new ...Operator()) calls in the client code. @@ -277,7 +547,7 @@ LocalSearchOperator* MakeRelocateNeighbors( Solver* solver, const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - RoutingModel::TransitEvaluator2 arc_evaluator) { + RoutingModel::TransitCallback2 arc_evaluator) { return solver->RevAlloc(new MakeRelocateNeighborsOperator( vars, secondary_vars, std::move(start_empty_path_class), std::move(arc_evaluator))); @@ -287,7 +557,7 @@ LocalSearchOperator* MakePairActive( Solver* const solver, const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingModel::NodePairs& pairs) { + const RoutingModel::IndexPairs& pairs) { return solver->RevAlloc(new MakePairActiveOperator( vars, secondary_vars, std::move(start_empty_path_class), pairs)); } @@ -296,7 +566,7 @@ LocalSearchOperator* MakePairInactive( Solver* const solver, const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingModel::NodePairs& pairs) { + const RoutingModel::IndexPairs& pairs) { return solver->RevAlloc(new MakePairInactiveOperator( vars, secondary_vars, std::move(start_empty_path_class), pairs)); } @@ -305,17 +575,52 @@ LocalSearchOperator* MakePairRelocate( Solver* const solver, const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingModel::NodePairs& pairs) { + const RoutingModel::IndexPairs& pairs) { return solver->RevAlloc(new PairRelocateOperator( vars, secondary_vars, std::move(start_empty_path_class), pairs)); } -LocalSearchOperator* NodePairSwapActive( +LocalSearchOperator* MakeLightPairRelocate( + Solver* const solver, const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + const RoutingModel::IndexPairs& pairs) { + return solver->RevAlloc(new LightPairRelocateOperator( + vars, secondary_vars, std::move(start_empty_path_class), pairs)); +} + +LocalSearchOperator* MakePairExchange( + Solver* const solver, const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + const RoutingModel::IndexPairs& pairs) { + return solver->RevAlloc(new PairExchangeOperator( + vars, secondary_vars, std::move(start_empty_path_class), pairs)); +} + +LocalSearchOperator* MakePairExchangeRelocate( + Solver* const solver, const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + const RoutingModel::IndexPairs& pairs) { + return solver->RevAlloc(new PairExchangeRelocateOperator( + vars, secondary_vars, std::move(start_empty_path_class), pairs)); +} + +LocalSearchOperator* SwapIndexPair(Solver* const solver, + const std::vector& vars, + const std::vector& secondary_vars, + const RoutingModel::IndexPairs& pairs) { + return solver->RevAlloc( + new SwapIndexPairOperator(vars, secondary_vars, pairs)); +} + +LocalSearchOperator* IndexPairSwapActive( Solver* const solver, const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingModel::NodePairs& pairs) { - return solver->RevAlloc(new NodePairSwapActiveOperator( + const RoutingModel::IndexPairs& pairs) { + return solver->RevAlloc(new IndexPairSwapActiveOperator( vars, secondary_vars, std::move(start_empty_path_class), pairs)); } @@ -323,7 +628,7 @@ LocalSearchOperator* PairNodeSwapActive( Solver* const solver, const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingModel::NodePairs& pairs) { + const RoutingModel::IndexPairs& pairs) { return solver->ConcatenateOperators( {solver->RevAlloc(new PairNodeSwapActiveOperator( vars, secondary_vars, start_empty_path_class, pairs)), @@ -331,275 +636,93 @@ LocalSearchOperator* PairNodeSwapActive( vars, secondary_vars, std::move(start_empty_path_class), pairs))}); } -// Cached callbacks - -class RoutingCache : public RoutingModel::NodeEvaluator2 { - public: - // Creates a new cached callback based on 'callback'. The cache object does - // not take ownership of the callback; the user must ensure that the callback - // gets deleted when it or the cache is no longer used. - // - // When used in the RoutingModel class, the constructor should not be called - // directly, but through RoutingModel::NewCachedCallback that ensures that the - // base callback is deleted properly. - RoutingCache(RoutingModel::NodeEvaluator2* callback, int size) - : cached_(size), cache_(size), callback_(CHECK_NOTNULL(callback)) { - for (RoutingModel::NodeIndex i(0); i < RoutingModel::NodeIndex(size); ++i) { - cached_[i].resize(size, false); - cache_[i].resize(size, 0); - } - CHECK(callback->IsRepeatable()); - } - bool IsRepeatable() const override { return true; } - int64 Run(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) override { - // This method does lazy caching of results of callbacks: first - // checks if it has been run with these parameters before, and - // returns previous result if so, or runs underlaying callback and - // stores its result. - // Not MT-safe. - if (cached_[i][j]) { - return cache_[i][j]; - } else { - const int64 cached_value = callback_->Run(i, j); - cached_[i][j] = true; - cache_[i][j] = cached_value; - return cached_value; - } - } - - private: - gtl::ITIVector> - cached_; - gtl::ITIVector> - cache_; - RoutingModel::NodeEvaluator2* const callback_; -}; +// Evaluators +template +static int64 ReturnZero(A a, B b) { + return 0; +} -class StateDependentRoutingCache : public RoutingModel::VariableNodeEvaluator2 { - public: - // Creates a new cached callback based on 'callback'. The cache object does - // not take ownership of the callback; the user must ensure that the callback - // gets deleted when it or the cache is no longer used. - // - // When used in the RoutingModel class, the constructor should not be called - // directly, but through RoutingModel::NewCachedStateDependentCallback that - // ensures that the base callback is deleted properly. - StateDependentRoutingCache(RoutingModel::VariableNodeEvaluator2* callback, - int size) - : cache_(size), callback_(CHECK_NOTNULL(callback)) { - for (RoutingModel::NodeIndex i(0); i < RoutingModel::NodeIndex(size); ++i) { - cache_[i].resize(size, {nullptr, nullptr}); - } - CHECK(callback->IsRepeatable()); - } - ~StateDependentRoutingCache() override { - std::unordered_set value_functions_delete; - std::unordered_set index_functions_delete; - for (const auto& cache_line : cache_) { - for (const RoutingModel::StateDependentTransit& transit : cache_line) { - value_functions_delete.insert(transit.transit); - index_functions_delete.insert(transit.transit_plus_identity); +bool TransitCallbackPositive(const RoutingTransitCallback2& callback, + int size) { + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (callback(i, j) < 0) { + return false; } } - gtl::STLDeleteElements(&value_functions_delete); - gtl::STLDeleteElements(&index_functions_delete); - } - bool IsRepeatable() const override { return true; } - // This method returns cached results of the callback. - RoutingModel::StateDependentTransit Run(RoutingModel::NodeIndex i, - RoutingModel::NodeIndex j) override { - RoutingModel::StateDependentTransit& cache_cell = cache_[i][j]; - if (cache_cell.transit == nullptr) { - cache_cell = callback_->Run(i, j); - } - return cache_cell; } - - private: - gtl::ITIVector< - RoutingModel::NodeIndex, - gtl::ITIVector> - cache_; - RoutingModel::VariableNodeEvaluator2* const callback_; -}; - -// Evaluators - -class MatrixEvaluator : public BaseObject { - public: - explicit MatrixEvaluator(std::vector> values) - : values_(std::move(values)) {} - ~MatrixEvaluator() override {} - int64 Value(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const { - return values_[i.value()][j.value()]; - } - - private: - const std::vector> values_; -}; - -class VectorEvaluator : public BaseObject { - public: - explicit VectorEvaluator(std::vector values) - : values_(std::move(values)) {} - ~VectorEvaluator() override {} - int64 Value(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const { - return values_[i.value()]; - } - - private: - const std::vector values_; -}; - -template -class ConstantEvaluator : public BaseObject { - public: - explicit ConstantEvaluator(T value) : value_(value) {} - ~ConstantEvaluator() override {} - T Value(RoutingModel::NodeIndex i, RoutingModel::NodeIndex j) const { - return value_; - } - - static ResultCallback2* - MakeNodeEvaluatorCallback(T value, Solver* solver) { - const ConstantEvaluator* const constant_evaluator = - solver->RevAlloc(new ConstantEvaluator(value)); - return constant_evaluator->MakeNodeEvaluatorCallback(); - } - - ResultCallback2* - MakeNodeEvaluatorCallback() const { - return NewPermanentCallback(this, &ConstantEvaluator::Value); - } - - private: - const T value_; -}; + return true; +} } // namespace // ----- Routing model ----- static const int kUnassigned = -1; -static const int64 kNoPenalty = -1; - -const RoutingModel::NodeIndex RoutingModel::kFirstNode(0); -const RoutingModel::NodeIndex RoutingModel::kInvalidNodeIndex(-1); +const int64 RoutingModel::kNoPenalty = -1; const RoutingModel::DisjunctionIndex RoutingModel::kNoDisjunction(-1); const RoutingModel::DimensionIndex RoutingModel::kNoDimension(-1); -RoutingModel::RoutingModel(int nodes, int vehicles, NodeIndex depot) - : RoutingModel(nodes, vehicles, depot, DefaultModelParameters()) {} +RoutingModel::RoutingModel(const RoutingIndexManager& index_manager) + : RoutingModel(index_manager, DefaultRoutingModelParameters()) {} -RoutingModel::RoutingModel(int nodes, int vehicles, NodeIndex depot, +RoutingModel::RoutingModel(const RoutingIndexManager& index_manager, const RoutingModelParameters& parameters) - : RoutingModel(nodes, vehicles, std::vector(vehicles, depot), - std::vector(vehicles, depot), parameters) {} - -RoutingModel::RoutingModel( - int nodes, int vehicles, - const std::vector>& start_ends) - : RoutingModel(nodes, vehicles, start_ends, DefaultModelParameters()) {} - -RoutingModel::RoutingModel( - int nodes, int vehicles, - const std::vector>& start_ends, - const RoutingModelParameters& parameters) - : nodes_(nodes), - vehicles_(vehicles), - no_cycle_constraint_(nullptr), - cost_(nullptr), - transit_cost_of_vehicle_(vehicles, nullptr), - fixed_cost_of_vehicle_(vehicles), + : nodes_(index_manager.num_nodes()), + vehicles_(index_manager.num_vehicles()), + fixed_cost_of_vehicle_(vehicles_, 0), cost_class_index_of_vehicle_(vehicles_, CostClassIndex(-1)), + linear_cost_factor_of_vehicle_(vehicles_, 0), + quadratic_cost_factor_of_vehicle_(vehicles_, 0), + vehicle_amortized_cost_factors_set_(false), cost_classes_(), costs_are_homogeneous_across_vehicles_( parameters.reduce_vehicle_cost_model()), - cache_callbacks_(nodes <= parameters.max_callback_cache_size()), + cache_callbacks_(false), vehicle_class_index_of_vehicle_(vehicles_, VehicleClassIndex(-1)), - starts_(vehicles), - ends_(vehicles), - closed_(false), - status_(ROUTING_NOT_SOLVED), - collect_assignments_(nullptr), - solve_db_(nullptr), - improve_db_(nullptr), - restore_assignment_(nullptr), - assignment_(nullptr), - preassignment_(nullptr), - limit_(nullptr), - ls_limit_(nullptr), - lns_limit_(nullptr) { + vehicle_pickup_delivery_policy_(vehicles_, PickupAndDeliveryPolicy::ANY), + num_visit_types_(0), + starts_(vehicles_), + ends_(vehicles_), + manager_(index_manager) { + // Initialize vehicle costs to the zero evaluator. + vehicle_to_transit_cost_.assign( + vehicles_, RegisterTransitCallback(ReturnZero)); + // Active caching after initializing vehicle_to_transit_cost_ to avoid + // uselessly caching ReturnZero. + cache_callbacks_ = (nodes_ <= parameters.max_callback_cache_size()); + VLOG(1) << "Model parameters:\n" << parameters.DebugString(); ConstraintSolverParameters solver_parameters = parameters.has_solver_parameters() ? parameters.solver_parameters() : Solver::DefaultSolverParameters(); - solver_.reset(new Solver("Routing", solver_parameters)); - InitializeBuilders(solver_.get()); - CHECK_EQ(vehicles, start_ends.size()); - std::unordered_set> depot_set; - for (const std::pair start_end : start_ends) { - depot_set.insert(start_end.first); - depot_set.insert(start_end.second); - } - start_end_count_ = depot_set.size(); + solver_ = absl::make_unique("Routing", solver_parameters); + // TODO(user): Remove when removal of NodeIndex is complete. + start_end_count_ = index_manager.num_unique_depots(); Initialize(); - SetStartEnd(start_ends); -} -RoutingModel::RoutingModel(int nodes, int vehicles, - const std::vector& starts, - const std::vector& ends) - : RoutingModel(nodes, vehicles, starts, ends, DefaultModelParameters()) {} + const int64 size = Size(); + index_to_pickup_index_pairs_.resize(size); + index_to_delivery_index_pairs_.resize(size); -RoutingModel::RoutingModel(int nodes, int vehicles, - const std::vector& starts, - const std::vector& ends, - const RoutingModelParameters& parameters) - : nodes_(nodes), - vehicles_(vehicles), - no_cycle_constraint_(nullptr), - cost_(nullptr), - transit_cost_of_vehicle_(vehicles, nullptr), - fixed_cost_of_vehicle_(vehicles), - cost_class_index_of_vehicle_(vehicles_, CostClassIndex(-1)), - cost_classes_(), - costs_are_homogeneous_across_vehicles_( - parameters.reduce_vehicle_cost_model()), - cache_callbacks_(nodes <= parameters.max_callback_cache_size()), - vehicle_class_index_of_vehicle_(vehicles_, VehicleClassIndex(-1)), - starts_(vehicles), - ends_(vehicles), - closed_(false), - status_(ROUTING_NOT_SOLVED), - collect_assignments_(nullptr), - solve_db_(nullptr), - improve_db_(nullptr), - restore_assignment_(nullptr), - assignment_(nullptr), - preassignment_(nullptr), - limit_(nullptr), - ls_limit_(nullptr), - lns_limit_(nullptr) { - VLOG(1) << "Model parameters:\n" << parameters.DebugString(); - ConstraintSolverParameters solver_parameters = - parameters.has_solver_parameters() ? parameters.solver_parameters() - : Solver::DefaultSolverParameters(); - solver_.reset(new Solver("Routing", solver_parameters)); - InitializeBuilders(solver_.get()); - CHECK_EQ(vehicles, starts.size()); - CHECK_EQ(vehicles, ends.size()); - std::unordered_set> depot_set; - std::vector> start_ends(starts.size()); - for (int i = 0; i < starts.size(); ++i) { - depot_set.insert(starts[i]); - depot_set.insert(ends[i]); - start_ends[i] = std::make_pair(starts[i], ends[i]); - } - start_end_count_ = depot_set.size(); - Initialize(); - SetStartEnd(start_ends); + index_to_visit_type_.resize(index_manager.num_indices(), kUnassigned); + + index_to_vehicle_.resize(index_manager.num_indices(), kUnassigned); + for (int v = 0; v < index_manager.num_vehicles(); ++v) { + starts_[v] = index_manager.GetStartIndex(v); + index_to_vehicle_[starts_[v]] = v; + ends_[v] = index_manager.GetEndIndex(v); + index_to_vehicle_[ends_[v]] = v; + } + + const std::vector& index_to_node = + index_manager.GetIndexToNodeMap(); + index_to_equivalence_class_.resize(index_manager.num_indices()); + for (int i = 0; i < index_to_node.size(); ++i) { + index_to_equivalence_class_[i] = index_to_node[i].value(); + } + allowed_vehicles_.resize(Size() + vehicles_); } void RoutingModel::Initialize() { @@ -607,7 +730,7 @@ void RoutingModel::Initialize() { // Next variables solver_->MakeIntVarArray(size, 0, size + vehicles_ - 1, "Nexts", &nexts_); solver_->AddConstraint(solver_->MakeAllDifferent(nexts_, false)); - node_to_disjunctions_.resize(size + vehicles_); + index_to_disjunctions_.resize(size + vehicles_); // Vehicle variables. In case that node i is not active, vehicle_vars_[i] is // bound to -1. solver_->MakeIntVarArray(size + vehicles_, -1, vehicles_ - 1, "Vehicles", @@ -619,85 +742,81 @@ void RoutingModel::Initialize() { &is_bound_to_end_); // Cost cache cost_cache_.clear(); - cost_cache_.resize(size + vehicles_); - for (int i = 0; i < size + vehicles_; ++i) { - CostCacheElement& cache = cost_cache_[i]; - cache.index = kUnassigned; - cache.cost_class_index = CostClassIndex(-1); - cache.cost = 0; - } + cost_cache_.resize(size + vehicles_, {kUnassigned, CostClassIndex(-1), 0}); preassignment_ = solver_->MakeAssignment(); } -void RoutingModel::InitializeBuilders(Solver* solver) { - solver->RegisterBuilder(RoutingModelVisitor::kLightElement, - Solver::ConstraintBuilder(BuildLightElement)); - solver->RegisterBuilder(RoutingModelVisitor::kLightElement2, - Solver::ConstraintBuilder(BuildLightElement2)); -} - RoutingModel::~RoutingModel() { - gtl::STLDeleteElements(&owned_node_callbacks_); gtl::STLDeleteElements(&dimensions_); - gtl::STLDeleteElements(&owned_state_dependent_callbacks_); -} - -RoutingModelParameters RoutingModel::DefaultModelParameters() { - RoutingModelParameters parameters; - ConstraintSolverParameters* const solver_parameters = - parameters.mutable_solver_parameters(); - *solver_parameters = Solver::DefaultSolverParameters(); - solver_parameters->set_compress_trail( - ConstraintSolverParameters::COMPRESS_WITH_ZLIB); - parameters.set_reduce_vehicle_cost_model(true); - return parameters; -} - -RoutingSearchParameters RoutingModel::DefaultSearchParameters() { - static const char* const kSearchParameters = - "first_solution_strategy: AUTOMATIC " - "use_filtered_first_solution_strategy: true " - "savings_neighbors_ratio: 0 " - "savings_add_reverse_arcs: false " - "local_search_operators {" - " use_relocate: true" - " use_relocate_pair: true" - " use_relocate_neighbors: false" - " use_exchange: true" - " use_cross: true" - " use_cross_exchange: false" - " use_two_opt: true" - " use_or_opt: true" - " use_lin_kernighan: true" - " use_tsp_opt: false" - " use_make_active: true" - " use_relocate_and_make_active: false" // costly if true by default - " use_make_inactive: true" - " use_make_chain_inactive: false" - " use_swap_active: true" - " use_extended_swap_active: false" - " use_node_pair_swap_active: true" - " use_path_lns: false" - " use_full_path_lns: false" - " use_tsp_lns: false" - " use_inactive_lns: false" - "}" - "local_search_metaheuristic: AUTOMATIC " - "guided_local_search_lambda_coefficient: 0.1 " - "use_depth_first_search: false " - "optimization_step: 1 " - "solution_limit: 0x7FFFFFFFFFFFFFFF " // kint64max - "time_limit_ms: 0x7FFFFFFFFFFFFFFF " // kint64max - "lns_time_limit_ms: 100 " - "use_light_propagation: true " - "fingerprint_arc_cost_evaluators: true "; - RoutingSearchParameters parameters; - if (!google::protobuf::TextFormat::ParseFromString(kSearchParameters, - ¶meters)) { - LOG(ERROR) << "Unsupported default search parameters: " - << kSearchParameters; - } - return parameters; + + // State dependent transit callbacks. + absl::flat_hash_set value_functions_delete; + absl::flat_hash_set index_functions_delete; + for (const auto& cache_line : state_dependent_transit_evaluators_cache_) { + for (const auto& key_transit : *cache_line) { + value_functions_delete.insert(key_transit.second.transit); + index_functions_delete.insert(key_transit.second.transit_plus_identity); + } + } + gtl::STLDeleteElements(&value_functions_delete); + gtl::STLDeleteElements(&index_functions_delete); +} + +int RoutingModel::RegisterUnaryTransitCallback(TransitCallback1 callback) { + const int index = unary_transit_evaluators_.size(); + unary_transit_evaluators_.push_back(std::move(callback)); + return RegisterTransitCallback([this, index](int i, int j) { + return unary_transit_evaluators_[index](i); + }); +} + +int RoutingModel::RegisterTransitCallback(TransitCallback2 callback) { + if (cache_callbacks_) { + const int size = Size() + vehicles(); + std::vector cache(size * size, 0); + for (int i = 0; i < size; ++i) { + for (int j = 0; j < size; ++j) { + cache[i * size + j] = callback(i, j); + } + } + transit_evaluators_.push_back( + [cache, size](int64 i, int64 j) { return cache[i * size + j]; }); + } else { + transit_evaluators_.push_back(std::move(callback)); + } + if (transit_evaluators_.size() != unary_transit_evaluators_.size()) { + DCHECK_EQ(transit_evaluators_.size(), unary_transit_evaluators_.size() + 1); + unary_transit_evaluators_.push_back(nullptr); + } + if (transit_evaluators_.size() != is_transit_evaluator_positive_.size()) { + DCHECK_EQ(transit_evaluators_.size(), + is_transit_evaluator_positive_.size() + 1); + is_transit_evaluator_positive_.push_back(false); + } + return transit_evaluators_.size() - 1; +} + +int RoutingModel::RegisterPositiveTransitCallback(TransitCallback2 callback) { + is_transit_evaluator_positive_.push_back(true); + DCHECK(TransitCallbackPositive(callback, Size() + vehicles())); + return RegisterTransitCallback(std::move(callback)); +} + +int RoutingModel::RegisterStateDependentTransitCallback( + VariableIndexEvaluator2 callback) { + state_dependent_transit_evaluators_cache_.push_back( + absl::make_unique()); + StateDependentTransitCallbackCache* const cache = + state_dependent_transit_evaluators_cache_.back().get(); + state_dependent_transit_evaluators_.push_back( + [cache, callback](int64 i, int64 j) { + StateDependentTransit value; + if (gtl::FindCopy(*cache, CacheKey(i, j), &value)) return value; + value = callback(i, j); + cache->insert({CacheKey(i, j), value}); + return value; + }); + return state_dependent_transit_evaluators_.size() - 1; } void RoutingModel::AddNoCycleConstraintInternal() { @@ -707,83 +826,67 @@ void RoutingModel::AddNoCycleConstraintInternal() { } } -bool RoutingModel::AddDimension(NodeEvaluator2* evaluator, int64 slack_max, +bool RoutingModel::AddDimension(int evaluator_index, int64 slack_max, int64 capacity, bool fix_start_cumul_to_zero, - const std::string& dimension_name) { - const std::vector evaluators(vehicles_, evaluator); + const std::string& name) { + const std::vector evaluator_indices(vehicles_, evaluator_index); std::vector capacities(vehicles_, capacity); - return AddDimensionWithCapacityInternal( - evaluators, slack_max, std::move(capacities), fix_start_cumul_to_zero, - dimension_name); + return AddDimensionWithCapacityInternal(evaluator_indices, slack_max, + std::move(capacities), + fix_start_cumul_to_zero, name); } bool RoutingModel::AddDimensionWithVehicleTransits( - const std::vector& evaluators, int64 slack_max, - int64 capacity, bool fix_start_cumul_to_zero, - const std::string& dimension_name) { + const std::vector& evaluator_indices, int64 slack_max, int64 capacity, + bool fix_start_cumul_to_zero, const std::string& name) { std::vector capacities(vehicles_, capacity); - return AddDimensionWithCapacityInternal( - evaluators, slack_max, std::move(capacities), fix_start_cumul_to_zero, - dimension_name); + return AddDimensionWithCapacityInternal(evaluator_indices, slack_max, + std::move(capacities), + fix_start_cumul_to_zero, name); } bool RoutingModel::AddDimensionWithVehicleCapacity( - NodeEvaluator2* evaluator, int64 slack_max, - std::vector vehicle_capacities, bool fix_start_cumul_to_zero, - const std::string& dimension_name) { - const std::vector evaluators(vehicles_, evaluator); - return AddDimensionWithCapacityInternal( - evaluators, slack_max, std::move(vehicle_capacities), - fix_start_cumul_to_zero, dimension_name); + int evaluator_index, int64 slack_max, std::vector vehicle_capacities, + bool fix_start_cumul_to_zero, const std::string& name) { + const std::vector evaluator_indices(vehicles_, evaluator_index); + return AddDimensionWithCapacityInternal(evaluator_indices, slack_max, + std::move(vehicle_capacities), + fix_start_cumul_to_zero, name); } bool RoutingModel::AddDimensionWithVehicleTransitAndCapacity( - const std::vector& evaluators, int64 slack_max, + const std::vector& evaluator_indices, int64 slack_max, std::vector vehicle_capacities, bool fix_start_cumul_to_zero, - const std::string& dimension_name) { - return AddDimensionWithCapacityInternal( - evaluators, slack_max, std::move(vehicle_capacities), - fix_start_cumul_to_zero, dimension_name); + const std::string& name) { + return AddDimensionWithCapacityInternal(evaluator_indices, slack_max, + std::move(vehicle_capacities), + fix_start_cumul_to_zero, name); } bool RoutingModel::AddDimensionWithCapacityInternal( - const std::vector& evaluators, int64 slack_max, + const std::vector& evaluator_indices, int64 slack_max, std::vector vehicle_capacities, bool fix_start_cumul_to_zero, - const std::string& dimension_name) { + const std::string& name) { CHECK_EQ(vehicles_, vehicle_capacities.size()); return InitializeDimensionInternal( - evaluators, std::vector(), slack_max, - fix_start_cumul_to_zero, - new RoutingDimension(this, std::move(vehicle_capacities), dimension_name, - nullptr)); + evaluator_indices, std::vector(), slack_max, fix_start_cumul_to_zero, + new RoutingDimension(this, std::move(vehicle_capacities), name, nullptr)); } bool RoutingModel::InitializeDimensionInternal( - const std::vector& evaluators, - const std::vector& state_dependent_evaluators, - int64 slack_max, bool fix_start_cumul_to_zero, - RoutingDimension* dimension) { + const std::vector& evaluator_indices, + const std::vector& state_dependent_evaluator_indices, int64 slack_max, + bool fix_start_cumul_to_zero, RoutingDimension* dimension) { CHECK(dimension != nullptr); - CHECK_EQ(vehicles_, evaluators.size()); + CHECK_EQ(vehicles_, evaluator_indices.size()); CHECK((dimension->base_dimension_ == nullptr && - state_dependent_evaluators.empty()) || - vehicles_ == state_dependent_evaluators.size()); + state_dependent_evaluator_indices.empty()) || + vehicles_ == state_dependent_evaluator_indices.size()); if (!HasDimension(dimension->name())) { const DimensionIndex dimension_index(dimensions_.size()); dimension_name_to_index_[dimension->name()] = dimension_index; dimensions_.push_back(dimension); - std::vector cached_evaluators; - for (NodeEvaluator2* const evaluator : evaluators) { - CHECK(evaluator != nullptr); - cached_evaluators.push_back(NewCachedCallback(evaluator)); - } - std::vector cached_state_dependent_evaluators; - for (VariableNodeEvaluator2* const evaluator : state_dependent_evaluators) { - CHECK(evaluator != nullptr); - cached_state_dependent_evaluators.push_back( - NewCachedStateDependentCallback(evaluator)); - } - dimension->Initialize(cached_evaluators, cached_state_dependent_evaluators, + dimension->Initialize(evaluator_indices, state_dependent_evaluator_indices, slack_max); solver_->AddConstraint(solver_->MakeDelayedPathCumul( nexts_, active_, dimension->cumuls(), dimension->transits())); @@ -795,51 +898,68 @@ bool RoutingModel::InitializeDimensionInternal( } } return true; - } else { - delete dimension; - std::unordered_set evaluator_set(evaluators.begin(), - evaluators.end()); - gtl::STLDeleteElements(&evaluator_set); - std::unordered_set dependent_evaluator_set( - state_dependent_evaluators.begin(), state_dependent_evaluators.end()); - gtl::STLDeleteElements(&dependent_evaluator_set); - return false; } + delete dimension; + return false; } +namespace { +int RegisterCallback(RoutingTransitCallback2 callback, bool is_positive, + RoutingModel* model) { + if (is_positive) { + return model->RegisterPositiveTransitCallback(std::move(callback)); + } + return model->RegisterTransitCallback(std::move(callback)); +} +} // namespace + bool RoutingModel::AddConstantDimensionWithSlack( int64 value, int64 capacity, int64 slack_max, bool fix_start_cumul_to_zero, const std::string& dimension_name) { - return AddDimension( - ConstantEvaluator::MakeNodeEvaluatorCallback(value, solver_.get()), - slack_max, capacity, fix_start_cumul_to_zero, dimension_name); + return AddDimension(RegisterCallback([value](int64, int64) { return value; }, + /*is_positive=*/value >= 0, this), + slack_max, capacity, fix_start_cumul_to_zero, + dimension_name); } bool RoutingModel::AddVectorDimension(std::vector values, int64 capacity, bool fix_start_cumul_to_zero, const std::string& dimension_name) { - VectorEvaluator* const evaluator = - solver_->RevAlloc(new VectorEvaluator(std::move(values))); - return AddDimension(NewPermanentCallback(evaluator, &VectorEvaluator::Value), - 0, capacity, fix_start_cumul_to_zero, dimension_name); + return AddDimension( + RegisterCallback( + [this, values](int64 i, int64 j) { + return values[manager_.IndexToNode(i).value()]; + }, + /*is_positive=*/ + std::all_of(std::begin(values), std::end(values), + [](int64 transit) { return transit >= 0; }), + this), + 0, capacity, fix_start_cumul_to_zero, dimension_name); } bool RoutingModel::AddMatrixDimension(std::vector> values, int64 capacity, bool fix_start_cumul_to_zero, const std::string& dimension_name) { - MatrixEvaluator* const evaluator = - solver_->RevAlloc(new MatrixEvaluator(std::move(values))); - return AddDimension(NewPermanentCallback(evaluator, &MatrixEvaluator::Value), + bool all_transits_positive = true; + for (const std::vector& transit_values : values) { + all_transits_positive = + std::all_of(std::begin(transit_values), std::end(transit_values), + [](int64 transit) { return transit >= 0; }); + if (!all_transits_positive) { + break; + } + } + return AddDimension(RegisterCallback( + [this, values](int64 i, int64 j) { + return values[manager_.IndexToNode(i).value()] + [manager_.IndexToNode(j).value()]; + }, + all_transits_positive, this), 0, capacity, fix_start_cumul_to_zero, dimension_name); } namespace { -template -static int64 ReturnZero(A a, B b) { - return 0; -} - // RangeMakeElementExpr is an IntExpr that corresponds to a // RangeIntToIntFunction indexed by an IntVar. // Do not create this class dicretly, but rather use MakeRangeMakeElementExpr. @@ -847,7 +967,7 @@ class RangeMakeElementExpr : public BaseIntExpr { public: RangeMakeElementExpr(const RangeIntToIntFunction* callback, IntVar* index, Solver* s) - : BaseIntExpr(s), callback_(CHECK_NOTNULL(callback)), index_(index) { + : BaseIntExpr(s), callback_(ABSL_DIE_IF_NULL(callback)), index_(index) { CHECK(callback_ != nullptr); CHECK(index != nullptr); } @@ -916,9 +1036,29 @@ IntExpr* MakeRangeMakeElementExpr(const RangeIntToIntFunction* callback, } } // namespace +bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity( + const std::vector& dependent_transits, + const RoutingDimension* base_dimension, int64 slack_max, + std::vector vehicle_capacities, bool fix_start_cumul_to_zero, + const std::string& name) { + const std::vector pure_transits(vehicles_, /*zero_evaluator*/ 0); + return AddDimensionDependentDimensionWithVehicleCapacity( + pure_transits, dependent_transits, base_dimension, slack_max, + std::move(vehicle_capacities), fix_start_cumul_to_zero, name); +} + +bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity( + int transit, const RoutingDimension* dimension, int64 slack_max, + int64 vehicle_capacity, bool fix_start_cumul_to_zero, + const std::string& name) { + return AddDimensionDependentDimensionWithVehicleCapacity( + /*zero_evaluator*/ 0, transit, dimension, slack_max, vehicle_capacity, + fix_start_cumul_to_zero, name); +} + bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal( - const std::vector& pure_transits, - const std::vector& dependent_transits, + const std::vector& pure_transits, + const std::vector& dependent_transits, const RoutingDimension* base_dimension, int64 slack_max, std::vector vehicle_capacities, bool fix_start_cumul_to_zero, const std::string& name) { @@ -937,43 +1077,18 @@ bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal( } bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity( - const std::vector& dependent_evaluators, - const RoutingDimension* base_dimension, int64 slack_max, - std::vector vehicle_capacities, bool fix_start_cumul_to_zero, - const std::string& name) { - NodeEvaluator2* const zero_evaluator = - NewPermanentCallback(&ReturnZero); - const std::vector pure_transits(vehicles_, zero_evaluator); - return AddDimensionDependentDimensionWithVehicleCapacity( - pure_transits, dependent_evaluators, base_dimension, slack_max, - std::move(vehicle_capacities), fix_start_cumul_to_zero, name); -} - -bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity( - NodeEvaluator2* pure_transits, VariableNodeEvaluator2* dependent_transits, + int pure_transit, int dependent_transit, const RoutingDimension* base_dimension, int64 slack_max, int64 vehicle_capacity, bool fix_start_cumul_to_zero, const std::string& name) { - std::vector pure_evaluators(vehicles_, pure_transits); - std::vector transit_evaluators(vehicles_, - dependent_transits); + std::vector pure_transits(vehicles_, pure_transit); + std::vector dependent_transits(vehicles_, dependent_transit); std::vector vehicle_capacities(vehicles_, vehicle_capacity); return AddDimensionDependentDimensionWithVehicleCapacityInternal( - pure_evaluators, transit_evaluators, base_dimension, slack_max, + pure_transits, dependent_transits, base_dimension, slack_max, std::move(vehicle_capacities), fix_start_cumul_to_zero, name); } -bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity( - VariableNodeEvaluator2* transits, const RoutingDimension* dimension, - int64 slack_max, int64 vehicle_capacity, bool fix_start_cumul_to_zero, - const std::string& name) { - NodeEvaluator2* const zero_evaluator = - NewPermanentCallback(&ReturnZero); - return AddDimensionDependentDimensionWithVehicleCapacity( - zero_evaluator, transits, dimension, slack_max, vehicle_capacity, - fix_start_cumul_to_zero, name); -} - RoutingModel::StateDependentTransit RoutingModel::MakeStateDependentTransit( const std::function& f, int64 domain_start, int64 domain_end) { @@ -1016,28 +1131,18 @@ RoutingDimension* RoutingModel::GetMutableDimension( return nullptr; } -void RoutingModel::AddAllActive() { - for (IntVar* const active : active_) { - if (active->Max() != 0) { - active->SetValue(1); - } - } -} - -void RoutingModel::SetArcCostEvaluatorOfAllVehicles(NodeEvaluator2* evaluator) { +void RoutingModel::SetArcCostEvaluatorOfAllVehicles(int evaluator_index) { CHECK_LT(0, vehicles_); for (int i = 0; i < vehicles_; ++i) { - SetArcCostEvaluatorOfVehicle(evaluator, i); + SetArcCostEvaluatorOfVehicle(evaluator_index, i); } } -void RoutingModel::SetArcCostEvaluatorOfVehicle(NodeEvaluator2* evaluator, +void RoutingModel::SetArcCostEvaluatorOfVehicle(int evaluator_index, int vehicle) { - CHECK(evaluator != nullptr); CHECK_LT(vehicle, vehicles_); - CHECK(evaluator->IsRepeatable()); - transit_cost_of_vehicle_[vehicle] = evaluator; - owned_node_callbacks_.insert(evaluator); + CHECK_LT(evaluator_index, transit_evaluators_.size()); + vehicle_to_transit_cost_[vehicle] = evaluator_index; } void RoutingModel::SetFixedCostOfAllVehicles(int64 cost) { @@ -1057,6 +1162,27 @@ void RoutingModel::SetFixedCostOfVehicle(int64 cost, int vehicle) { fixed_cost_of_vehicle_[vehicle] = cost; } +void RoutingModel::SetAmortizedCostFactorsOfAllVehicles( + int64 linear_cost_factor, int64 quadratic_cost_factor) { + for (int v = 0; v < vehicles_; v++) { + SetAmortizedCostFactorsOfVehicle(linear_cost_factor, quadratic_cost_factor, + v); + } +} + +void RoutingModel::SetAmortizedCostFactorsOfVehicle(int64 linear_cost_factor, + int64 quadratic_cost_factor, + int vehicle) { + CHECK_LT(vehicle, vehicles_); + DCHECK_GE(linear_cost_factor, 0); + DCHECK_GE(quadratic_cost_factor, 0); + if (linear_cost_factor + quadratic_cost_factor > 0) { + vehicle_amortized_cost_factors_set_ = true; + } + linear_cost_factor_of_vehicle_[vehicle] = linear_cost_factor; + quadratic_cost_factor_of_vehicle_[vehicle] = quadratic_cost_factor; +} + namespace { // Some C++ versions used in the open-source export don't support comparison // functors for STL containers; so we need a comparator class instead. @@ -1079,115 +1205,27 @@ struct VehicleClassComparator { const RoutingModel::CostClassIndex RoutingModel::kCostClassIndexOfZeroCost = CostClassIndex(0); -uint64 RoutingModel::GetFingerprintOfEvaluator( - RoutingModel::NodeEvaluator2* evaluator, - bool fingerprint_arc_cost_evaluators) const { - if (!fingerprint_arc_cost_evaluators) { - // If we don't fingerprint the data returned by the evaluator, we can - // just return the address as fingerprint (ensures that evaluators with the - // same address are considered as equivalent). - return absl::bit_cast(evaluator); - } - uint64 evaluator_fprint = 0; - const int max_row_size = Size() + vehicles_; - std::unique_ptr row(new int64[max_row_size]); - for (int64 from = 0; from < Size(); ++from) { - if (IsEnd(from)) continue; - const bool from_start = IsStart(from); - int row_size = 0; - for (int64 to = 0; to < max_row_size; ++to) { - // (from, from), (end, start) and (start, end) arcs are never - // evaluated; some clients check this. - if (from != to && !IsStart(to) && (!from_start || !IsEnd(to))) { - row[row_size] = evaluator->Run(IndexToNode(from), IndexToNode(to)); - ++row_size; - } - } - const int row_num_bytes = row_size * sizeof(int64); - const uint64 fprint = - ThoroughHash(reinterpret_cast(row.get()), row_num_bytes); - // MixTwoUInt64 never returns 0. - evaluator_fprint = - evaluator_fprint != 0 ? MixTwoUInt64(evaluator_fprint, fprint) : fprint; - } - return evaluator_fprint; -} - void RoutingModel::ComputeCostClasses( const RoutingSearchParameters& parameters) { - // First, detect if all non-null transit cost evaluators are equal. - bool all_evaluators_equal = true; - // Find first non-null evaluator. - int evaluator_index = 0; - while (evaluator_index < transit_cost_of_vehicle_.size() && - transit_cost_of_vehicle_[evaluator_index] == nullptr) { - ++evaluator_index; - } - // Compare non-null evaluators. - if (evaluator_index < transit_cost_of_vehicle_.size()) { - const NodeEvaluator2* reference_evaluator = - transit_cost_of_vehicle_[evaluator_index]; - while (++evaluator_index < transit_cost_of_vehicle_.size()) { - NodeEvaluator2* const evaluator = - transit_cost_of_vehicle_[evaluator_index]; - if (evaluator != nullptr && evaluator != reference_evaluator) { - all_evaluators_equal = false; - break; - } - } - } - - // Then we create and reduce the cost classes. + // Create and reduce the cost classes. cost_classes_.reserve(vehicles_); cost_classes_.clear(); cost_class_index_of_vehicle_.assign(vehicles_, CostClassIndex(-1)); std::map cost_class_map; // Pre-insert the built-in cost class 'zero cost' with index 0. - const uint64 kNullEvaluatorFprint = 0; - NodeEvaluator2* const null_evaluator = nullptr; - std::unordered_map evaluator_to_fprint; - std::unordered_map fprint_to_cached_evaluator; - NodeEvaluator2* const zero_evaluator = - NewPermanentCallback(&ReturnZero); - owned_node_callbacks_.insert(zero_evaluator); - evaluator_to_fprint[null_evaluator] = kNullEvaluatorFprint; - fprint_to_cached_evaluator[kNullEvaluatorFprint] = zero_evaluator; - const CostClass zero_cost_class(zero_evaluator); + const CostClass zero_cost_class(0); cost_classes_.push_back(zero_cost_class); - DCHECK_EQ(zero_evaluator, - cost_classes_[kCostClassIndexOfZeroCost].arc_cost_evaluator); + DCHECK_EQ(cost_classes_[kCostClassIndexOfZeroCost].evaluator_index, 0); cost_class_map[zero_cost_class] = kCostClassIndexOfZeroCost; // Determine the canonicalized cost class for each vehicle, and insert it as - // a new cost class if it doesn't exist already. Building cached evaluators on - // the way. - const uint64 kAllEquivalentEvaluatorFprint = 1; - bool has_vehicle_with_zero_cost_class = false; - for (int vehicle = 0; vehicle < transit_cost_of_vehicle_.size(); ++vehicle) { - NodeEvaluator2* const uncached_evaluator = - transit_cost_of_vehicle_[vehicle]; - uint64 evaluator_fprint = kNullEvaluatorFprint; - // We try really hard not to evaluate the fingerprint of an evaluator, if - // we can avoid to: we detect duplicate evaluators, for example, and if - // there's only one evaluator callback used, we don't bother computing its - // fingerprint. - if (!gtl::FindCopy(evaluator_to_fprint, uncached_evaluator, - &evaluator_fprint)) { - evaluator_fprint = - all_evaluators_equal - ? kAllEquivalentEvaluatorFprint - : GetFingerprintOfEvaluator( - uncached_evaluator, - parameters.fingerprint_arc_cost_evaluators()); - evaluator_to_fprint[uncached_evaluator] = evaluator_fprint; - } - NodeEvaluator2** cached_evaluator = >l::LookupOrInsert( - &fprint_to_cached_evaluator, evaluator_fprint, nullptr); - if (*cached_evaluator == nullptr) { - *cached_evaluator = NewCachedCallback(uncached_evaluator); - } - CostClass cost_class(*cached_evaluator); + // a new cost class if it doesn't exist already. Building cached evaluators + // on the way. + has_vehicle_with_zero_cost_class_ = false; + for (int vehicle = 0; vehicle < vehicles(); ++vehicle) { + CostClass cost_class(vehicle_to_transit_cost_[vehicle]); + // Insert the dimension data in a canonical way. for (const RoutingDimension* const dimension : dimensions_) { const int64 coeff = dimension->vehicle_span_cost_coefficients()[vehicle]; @@ -1204,7 +1242,7 @@ void RoutingModel::ComputeCostClasses( const CostClassIndex cost_class_index = gtl::LookupOrInsert(&cost_class_map, cost_class, num_cost_classes); if (cost_class_index == kCostClassIndexOfZeroCost) { - has_vehicle_with_zero_cost_class = true; + has_vehicle_with_zero_cost_class_ = true; } else if (cost_class_index == num_cost_classes) { // New cost class. cost_classes_.push_back(cost_class); } @@ -1222,7 +1260,7 @@ void RoutingModel::ComputeCostClasses( // // Fixed costs are simply ignored for computing these cost classes. They are // attached to start nodes directly. - costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class + costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class_ ? GetCostClassesCount() == 1 : GetCostClassesCount() <= 2; } @@ -1235,11 +1273,11 @@ bool RoutingModel::VehicleClass::LessThan(const VehicleClass& a, if (a.fixed_cost != b.fixed_cost) { return a.fixed_cost < b.fixed_cost; } - if (a.start != b.start) { - return a.start < b.start; + if (a.start_equivalence_class != b.start_equivalence_class) { + return a.start_equivalence_class < b.start_equivalence_class; } - if (a.end != b.end) { - return a.end < b.end; + if (a.end_equivalence_class != b.end_equivalence_class) { + return a.end_equivalence_class < b.end_equivalence_class; } if (a.unvisitable_nodes_fprint != b.unvisitable_nodes_fprint) { return a.unvisitable_nodes_fprint < b.unvisitable_nodes_fprint; @@ -1271,12 +1309,14 @@ void RoutingModel::ComputeVehicleClasses() { const int nodes_unvisitability_num_bytes = (vehicle_vars_.size() + 7) / 8; std::unique_ptr nodes_unvisitability_bitmask( new char[nodes_unvisitability_num_bytes]); - for (int vehicle = 0; vehicle < transit_cost_of_vehicle_.size(); ++vehicle) { + for (int vehicle = 0; vehicle < vehicles(); ++vehicle) { VehicleClass vehicle_class; vehicle_class.cost_class_index = cost_class_index_of_vehicle_[vehicle]; vehicle_class.fixed_cost = fixed_cost_of_vehicle_[vehicle]; - vehicle_class.start = IndexToNode(Start(vehicle)); - vehicle_class.end = IndexToNode(End(vehicle)); + vehicle_class.start_equivalence_class = + index_to_equivalence_class_[Start(vehicle)]; + vehicle_class.end_equivalence_class = + index_to_equivalence_class_[End(vehicle)]; for (const RoutingDimension* const dimension : dimensions_) { IntVar* const start_cumul_var = dimension->cumuls()[Start(vehicle)]; vehicle_class.dimension_start_cumuls_min.push_back( @@ -1312,50 +1352,31 @@ void RoutingModel::ComputeVehicleClasses() { } } -void RoutingModel::AddDisjunction(const std::vector& nodes) { - AddDisjunctionInternal(nodes, kNoPenalty, 1); -} - -void RoutingModel::AddDisjunction(const std::vector& nodes, - int64 penalty) { - AddDisjunction(nodes, penalty, 1); -} - -void RoutingModel::AddDisjunction(const std::vector& nodes, - int64 penalty, int64 max_cardinality) { +RoutingModel::DisjunctionIndex RoutingModel::AddDisjunction( + const std::vector& indices, int64 penalty, int64 max_cardinality) { CHECK_GE(max_cardinality, 1); - AddDisjunctionInternal(nodes, penalty, max_cardinality); -} - -void RoutingModel::AddDisjunctionInternal(const std::vector& nodes, - int64 penalty, - int64 max_cardinality) { - const int size = disjunctions_.size(); - disjunctions_.resize(size + 1); - std::vector& disjunction_nodes = disjunctions_.back().nodes; - disjunction_nodes.resize(nodes.size()); - for (int i = 0; i < nodes.size(); ++i) { - CHECK_NE(kUnassigned, node_to_index_[nodes[i]]); - disjunction_nodes[i] = node_to_index_[nodes[i]]; + for (int i = 0; i < indices.size(); ++i) { + CHECK_NE(kUnassigned, indices[i]); } - disjunctions_.back().value.penalty = penalty; - disjunctions_.back().value.max_cardinality = max_cardinality; - for (const NodeIndex node : nodes) { - node_to_disjunctions_[node_to_index_[node]].push_back( - DisjunctionIndex(size)); + + const DisjunctionIndex disjunction_index(disjunctions_.size()); + disjunctions_.push_back({indices, {penalty, max_cardinality}}); + for (const int64 index : indices) { + index_to_disjunctions_[index].push_back(disjunction_index); } + return disjunction_index; } -std::vector> RoutingModel::GetPerfectBinaryDisjunctions() - const { - std::vector> var_index_pairs; +std::vector> +RoutingModel::GetPerfectBinaryDisjunctions() const { + std::vector> var_index_pairs; for (const Disjunction& disjunction : disjunctions_) { - const std::vector& var_indices = disjunction.nodes; + const std::vector& var_indices = disjunction.indices; if (var_indices.size() != 2) continue; - const int v0 = var_indices[0]; - const int v1 = var_indices[1]; - if (node_to_disjunctions_[v0].size() == 1 && - node_to_disjunctions_[v1].size() == 1) { + const int64 v0 = var_indices[0]; + const int64 v1 = var_indices[1]; + if (index_to_disjunctions_[v0].size() == 1 && + index_to_disjunctions_[v1].size() == 1) { // We output sorted pairs. var_index_pairs.push_back({std::min(v0, v1), std::max(v0, v1)}); } @@ -1368,7 +1389,7 @@ void RoutingModel::IgnoreDisjunctionsAlreadyForcedToZero() { CHECK(!closed_); for (Disjunction& disjunction : disjunctions_) { bool has_one_potentially_active_var = false; - for (const int var_index : disjunction.nodes) { + for (const int64 var_index : disjunction.indices) { if (ActiveVar(var_index)->Max() > 0) { has_one_potentially_active_var = true; break; @@ -1381,13 +1402,13 @@ void RoutingModel::IgnoreDisjunctionsAlreadyForcedToZero() { } IntVar* RoutingModel::CreateDisjunction(DisjunctionIndex disjunction) { - const std::vector& nodes = disjunctions_[disjunction].nodes; - const int nodes_size = nodes.size(); - std::vector disjunction_vars(nodes_size); - for (int i = 0; i < nodes_size; ++i) { - const int node = nodes[i]; - CHECK_LT(node, Size()); - disjunction_vars[i] = ActiveVar(node); + const std::vector& indices = disjunctions_[disjunction].indices; + const int indices_size = indices.size(); + std::vector disjunction_vars(indices_size); + for (int i = 0; i < indices_size; ++i) { + const int64 index = indices[i]; + CHECK_LT(index, Size()); + disjunction_vars[i] = ActiveVar(index); } const int64 max_cardinality = disjunctions_[disjunction].value.max_cardinality; @@ -1407,22 +1428,92 @@ IntVar* RoutingModel::CreateDisjunction(DisjunctionIndex disjunction) { } void RoutingModel::AddSoftSameVehicleConstraint( - const std::vector& nodes, int64 cost) { - if (!nodes.empty()) { + const std::vector& indices, int64 cost) { + if (!indices.empty()) { ValuedNodes same_vehicle_cost; - for (const NodeIndex node : nodes) { - same_vehicle_cost.nodes.push_back(node_to_index_[node]); + for (const int64 index : indices) { + same_vehicle_cost.indices.push_back(index); } same_vehicle_cost.value = cost; same_vehicle_costs_.push_back(same_vehicle_cost); } } -IntVar* RoutingModel::CreateSameVehicleCost(int index) { - const std::vector& nodes = same_vehicle_costs_[index].nodes; - CHECK(!nodes.empty()); +void RoutingModel::SetAllowedVehiclesForIndex(const std::vector& vehicles, + int64 index) { + auto& allowed_vehicles = allowed_vehicles_[index]; + allowed_vehicles.clear(); + for (int vehicle : vehicles) { + allowed_vehicles.insert(vehicle); + } +} + +void RoutingModel::AddPickupAndDelivery(int64 pickup, int64 delivery) { + AddPickupAndDeliverySetsInternal({pickup}, {delivery}); + pickup_delivery_disjunctions_.push_back({kNoDisjunction, kNoDisjunction}); +} + +void RoutingModel::AddPickupAndDeliverySets( + DisjunctionIndex pickup_disjunction, + DisjunctionIndex delivery_disjunction) { + AddPickupAndDeliverySetsInternal(GetDisjunctionIndices(pickup_disjunction), + GetDisjunctionIndices(delivery_disjunction)); + pickup_delivery_disjunctions_.push_back( + {pickup_disjunction, delivery_disjunction}); +} + +void RoutingModel::AddPickupAndDeliverySetsInternal( + const std::vector& pickups, const std::vector& deliveries) { + if (pickups.empty() || deliveries.empty()) { + return; + } + const int64 size = Size(); + const int pair_index = pickup_delivery_pairs_.size(); + for (int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) { + const int64 pickup = pickups[pickup_index]; + CHECK_LT(pickup, size); + index_to_pickup_index_pairs_[pickup].emplace_back(pair_index, pickup_index); + } + for (int delivery_index = 0; delivery_index < deliveries.size(); + delivery_index++) { + const int64 delivery = deliveries[delivery_index]; + CHECK_LT(delivery, size); + index_to_delivery_index_pairs_[delivery].emplace_back(pair_index, + delivery_index); + } + pickup_delivery_pairs_.push_back({pickups, deliveries}); +} + +const std::vector>& RoutingModel::GetPickupIndexPairs( + int64 node_index) const { + CHECK_LT(node_index, index_to_pickup_index_pairs_.size()); + return index_to_pickup_index_pairs_[node_index]; +} + +const std::vector>& RoutingModel::GetDeliveryIndexPairs( + int64 node_index) const { + CHECK_LT(node_index, index_to_delivery_index_pairs_.size()); + return index_to_delivery_index_pairs_[node_index]; +} + +int RoutingModel::GetNumOfSingletonNodes() const { + int count = 0; + for (int i = 0; i < Nexts().size(); ++i) { + // End nodes have no next variables. + if (!IsStart(i) && GetPickupIndexPairs(i).empty() && + GetDeliveryIndexPairs(i).empty()) { + ++count; + } + } + return count; +} + +IntVar* RoutingModel::CreateSameVehicleCost(int vehicle_index) { + const std::vector& indices = + same_vehicle_costs_[vehicle_index].indices; + CHECK(!indices.empty()); std::vector vehicle_counts; - solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, nodes.size() + 1, + solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1, &vehicle_counts); std::vector vehicle_values(vehicle_vars_.size() + 1); for (int i = 0; i < vehicle_vars_.size(); ++i) { @@ -1430,8 +1521,9 @@ IntVar* RoutingModel::CreateSameVehicleCost(int index) { } vehicle_values[vehicle_vars_.size()] = -1; std::vector vehicle_vars; - for (const int node : nodes) { - vehicle_vars.push_back(vehicle_vars_[node]); + vehicle_vars.reserve(indices.size()); + for (const int64 index : indices) { + vehicle_vars.push_back(vehicle_vars_[index]); } solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts)); std::vector vehicle_used; @@ -1442,7 +1534,7 @@ IntVar* RoutingModel::CreateSameVehicleCost(int index) { vehicle_used.push_back(solver_->MakeIntConst(-1)); return solver_ ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0), - same_vehicle_costs_[index].value) + same_vehicle_costs_[vehicle_index].value) ->Var(); } @@ -1452,71 +1544,6 @@ void RoutingModel::AddLocalSearchOperator(LocalSearchOperator* ls_operator) { int64 RoutingModel::GetDepot() const { return vehicles() > 0 ? Start(0) : -1; } -void RoutingModel::SetStartEnd( - const std::vector>& start_ends) { - CHECK_EQ(start_ends.size(), vehicles_); - const int size = Size(); - std::unordered_set> starts; - std::unordered_set> ends; - for (const std::pair start_end : start_ends) { - const NodeIndex start = start_end.first; - const NodeIndex end = start_end.second; - CHECK_GE(start, 0); - CHECK_GE(end, 0); - CHECK_LE(start, nodes_); - CHECK_LE(end, nodes_); - starts.insert(start); - ends.insert(end); - } - index_to_node_.resize(size + vehicles_); - node_to_index_.resize(nodes_, kUnassigned); - int index = 0; - for (NodeIndex i = kFirstNode; i < nodes_; ++i) { - if (starts.count(i) != 0 || ends.count(i) == 0) { - index_to_node_[index] = i; - node_to_index_[i] = index; - ++index; - } - } - std::unordered_set> node_set; - index_to_vehicle_.resize(size + vehicles_, kUnassigned); - for (int i = 0; i < vehicles_; ++i) { - const NodeIndex start = start_ends[i].first; - if (node_set.count(start) == 0) { - node_set.insert(start); - const int start_index = node_to_index_[start]; - starts_[i] = start_index; - CHECK_NE(kUnassigned, start_index); - index_to_vehicle_[start_index] = i; - } else { - starts_[i] = index; - index_to_node_[index] = start; - index_to_vehicle_[index] = i; - ++index; - } - } - for (int i = 0; i < vehicles_; ++i) { - NodeIndex end = start_ends[i].second; - index_to_node_[index] = end; - ends_[i] = index; - CHECK_LE(size, index); - index_to_vehicle_[index] = i; - ++index; - } - - // Logging model information. - VLOG(1) << "Number of nodes: " << nodes_; - VLOG(1) << "Number of vehicles: " << vehicles_; - for (int index = 0; index < index_to_node_.size(); ++index) { - VLOG(2) << "Variable index " << index << " -> Node index " - << index_to_node_[index]; - } - for (NodeIndex node = kFirstNode; node < node_to_index_.size(); ++node) { - VLOG(2) << "Node index " << node << " -> Variable index " - << node_to_index_[node]; - } -} - // TODO(user): Remove the need for the homogeneous version once the // vehicle var to cost class element constraint is fast enough. void RoutingModel::AppendHomogeneousArcCosts( @@ -1532,7 +1559,8 @@ void RoutingModel::AppendHomogeneousArcCosts( // the search in GLS in some cases (Solomon instances for instance). IntVar* const base_cost_var = solver_->MakeIntVar(0, kint64max); solver_->AddConstraint(MakeLightElement( - solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator)); + solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator, + [this]() { return enable_deep_serialization_; })); IntVar* const var = solver_->MakeProd(base_cost_var, active_[node_index])->Var(); cost_elements->push_back(var); @@ -1589,34 +1617,30 @@ int RoutingModel::GetVehicleStartClass(int64 start_index) const { return kUnassigned; } -bool RoutingModel::ValidateSearchParameters( - const RoutingSearchParameters& search_parameters) { - bool valid = true; +std::string RoutingModel::FindErrorInSearchParametersForModel( + const RoutingSearchParameters& search_parameters) const { const FirstSolutionStrategy::Value first_solution_strategy = search_parameters.first_solution_strategy(); if (GetFirstSolutionDecisionBuilder(search_parameters) == nullptr) { - LOG(ERROR) << "Undefined first solution strategy: " - << first_solution_strategy; - valid = false; + return absl::StrCat( + "Undefined first solution strategy: ", + FirstSolutionStrategy::Value_Name(first_solution_strategy), + " (int value: ", first_solution_strategy, ")"); } - if (first_solution_strategy == FirstSolutionStrategy::SWEEP && + if (search_parameters.first_solution_strategy() == + FirstSolutionStrategy::SWEEP && sweep_arranger() == nullptr) { - LOG(ERROR) << "Undefined sweep arranger for ROUTING_SWEEP strategy."; - valid = false; + return "Undefined sweep arranger for ROUTING_SWEEP strategy."; } - if (!valid) { - status_ = ROUTING_INVALID; - return false; - } - return true; + return ""; } void RoutingModel::QuietCloseModel() { - QuietCloseModelWithParameters(DefaultSearchParameters()); + QuietCloseModelWithParameters(DefaultRoutingSearchParameters()); } void RoutingModel::CloseModel() { - CloseModelWithParameters(DefaultSearchParameters()); + CloseModelWithParameters(DefaultRoutingSearchParameters()); } class RoutingModelInspector : public ModelVisitor { @@ -1639,7 +1663,7 @@ class RoutingModelInspector : public ModelVisitor { ~RoutingModelInspector() override {} void EndVisitModel(const std::string& solver_name) override { // Compact same vehicle component indices. - std::unordered_map component_indices; + absl::flat_hash_map component_indices; int component_index = 0; for (int node = 0; node < model_->Size(); ++node) { const int component = @@ -1745,12 +1769,12 @@ class RoutingModelInspector : public ModelVisitor { RoutingModel* const model_; ConnectedComponents same_vehicle_components_; - std::unordered_map> + absl::flat_hash_map> cumul_to_dim_indices_; - std::unordered_map vehicle_var_to_indices_; - std::unordered_map expr_inspectors_; - std::unordered_map array_inspectors_; - std::unordered_map constraint_inspectors_; + absl::flat_hash_map vehicle_var_to_indices_; + absl::flat_hash_map expr_inspectors_; + absl::flat_hash_map array_inspectors_; + absl::flat_hash_map constraint_inspectors_; const IntExpr* expr_ = nullptr; const IntExpr* left_ = nullptr; const IntExpr* right_ = nullptr; @@ -1760,6 +1784,12 @@ class RoutingModelInspector : public ModelVisitor { void RoutingModel::CloseModelWithParameters( const RoutingSearchParameters& parameters) { + std::string error = FindErrorInRoutingSearchParameters(parameters); + if (!error.empty()) { + status_ = ROUTING_INVALID; + LOG(ERROR) << "Invalid RoutingSearchParameters: " << error; + return; + } if (closed_) { LOG(WARNING) << "Model already closed"; return; @@ -1798,15 +1828,32 @@ void RoutingModel::CloseModelWithParameters( nexts_, active_, vehicle_vars_, zero_transit)); } - // Set all active unless there are disjunctions - if (disjunctions_.empty()) { - AddAllActive(); + // Nodes which are not in a disjunction are mandatory. + for (int i = 0; i < size; ++i) { + if (GetDisjunctionIndices(i).empty() && active_[i]->Max() != 0) { + active_[i]->SetValue(1); + } + } + + // Reduce domains of vehicle variables + for (int i = 0; i < allowed_vehicles_.size(); ++i) { + const auto& allowed_vehicles = allowed_vehicles_[i]; + if (!allowed_vehicles.empty()) { + std::vector vehicles; + vehicles.reserve(allowed_vehicles.size() + 1); + vehicles.push_back(-1); + for (int vehicle : allowed_vehicles) { + vehicles.push_back(vehicle); + } + solver_->AddConstraint(solver_->MakeMemberCt(VehicleVar(i), vehicles)); + } } - // Reduce domain of next varibles. + // Reduce domain of next variables. for (int i = 0; i < size; ++i) { // No variable can point back to a start. - nexts_[i]->RemoveValues(starts_); + solver_->AddConstraint(solver_->RevAlloc( + new DifferentFromValues(solver_.get(), nexts_[i], starts_))); // Extra constraint to state an active node can't point to itself. solver_->AddConstraint( solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i])); @@ -1819,13 +1866,38 @@ void RoutingModel::CloseModelWithParameters( solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i])); } + // Add constraints to avoid incompatible types from being on the same route. + if (!incompatible_types_per_type_index_.empty()) { + for (int i = 0; i < size; i++) { + const int first_type = index_to_visit_type_[i]; + for (int j = i + 1; j < size; j++) { + const int second_type = index_to_visit_type_[j]; + if (first_type < 0 || second_type < 0) { + continue; + } + if (GetTypeIncompatibilities(first_type).count(second_type) > 0) { + DCHECK_GT(GetTypeIncompatibilities(second_type).count(first_type), 0); + // Make vehicle vars different (if nodes are active). + IntVar* different_vehicles = + solver_->MakeIsDifferentVar(vehicle_vars_[i], vehicle_vars_[j]); + solver_->AddConstraint( + solver_->MakeLessOrEqual(active_[i], different_vehicles)); + } + } + } + } + // Associate first and "logical" last nodes for (int i = 0; i < vehicles_; ++i) { + std::vector forbidden_ends; + forbidden_ends.reserve(vehicles_ - 1); for (int j = 0; j < vehicles_; ++j) { if (i != j) { - nexts_[starts_[i]]->RemoveValue(ends_[j]); + forbidden_ends.push_back(ends_[j]); } } + solver_->AddConstraint(solver_->RevAlloc(new DifferentFromValues( + solver_.get(), nexts_[starts_[i]], std::move(forbidden_ends)))); } // Constraining is_bound_to_end_ variables. @@ -1836,14 +1908,35 @@ void RoutingModel::CloseModelWithParameters( std::vector cost_elements; // Arc and dimension costs. if (vehicles_ > 0) { - if (GetNonZeroCostClassesCount() > 0) { - for (int node_index = 0; node_index < size; ++node_index) { - if (CostsAreHomogeneousAcrossVehicles()) { - AppendHomogeneousArcCosts(parameters, node_index, &cost_elements); - } else { - AppendArcCosts(parameters, node_index, &cost_elements); - } + for (int node_index = 0; node_index < size; ++node_index) { + if (CostsAreHomogeneousAcrossVehicles()) { + AppendHomogeneousArcCosts(parameters, node_index, &cost_elements); + } else { + AppendArcCosts(parameters, node_index, &cost_elements); + } + } + if (vehicle_amortized_cost_factors_set_) { + std::vector route_lengths; + solver_->MakeIntVarArray(vehicles_, 0, size, &route_lengths); + solver_->AddConstraint( + solver_->MakeDistribute(vehicle_vars_, route_lengths)); + std::vector vehicle_used; + for (int i = 0; i < vehicles_; i++) { + // The start/end of the vehicle are always on the route. + vehicle_used.push_back( + solver_->MakeIsGreaterCstVar(route_lengths[i], 2)); + IntVar* const var = + solver_ + ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare( + solver_->MakeSum(route_lengths[i], -2))), + quadratic_cost_factor_of_vehicle_[i]) + ->Var(); + cost_elements.push_back(var); } + IntVar* const vehicle_usage_cost = + solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_) + ->Var(); + cost_elements.push_back(vehicle_usage_cost); } } // Dimension span costs @@ -1874,20 +1967,41 @@ void RoutingModel::CloseModelWithParameters( // Precedences std::vector> precedences; for (const auto& pair : pickup_delivery_pairs_) { - precedences.push_back({pair.first[0], pair.second[0]}); + DCHECK(!pair.first.empty() && !pair.second.empty()); + for (int pickup : pair.first) { + for (int delivery : pair.second) { + precedences.emplace_back(pickup, delivery); + } + } } - solver_->AddConstraint( - solver_->MakePathPrecedenceConstraint(nexts_, precedences)); + std::vector lifo_vehicles; + std::vector fifo_vehicles; + for (int i = 0; i < vehicles_; ++i) { + switch (vehicle_pickup_delivery_policy_[i]) { + case PickupAndDeliveryPolicy::ANY: + break; + case PickupAndDeliveryPolicy::LIFO: + lifo_vehicles.push_back(Start(i)); + break; + case PickupAndDeliveryPolicy::FIFO: + fifo_vehicles.push_back(Start(i)); + break; + } + } + solver_->AddConstraint(solver_->MakePathPrecedenceConstraint( + nexts_, precedences, lifo_vehicles, fifo_vehicles)); // Detect constraints + enable_deep_serialization_ = false; std::unique_ptr inspector( new RoutingModelInspector(this)); solver_->Accept(inspector.get()); + enable_deep_serialization_ = true; // Dimension precedences, discovered by model inspection (which must be // performed before adding path transit precedences. for (const RoutingDimension* const dimension : dimensions_) { - const ::util::ReverseArcListGraph& graph = + const ReverseArcListGraph& graph = dimension->GetPrecedenceGraph(); std::vector> precedences; for (const auto tail : graph.AllNodes()) { @@ -1904,9 +2018,12 @@ void RoutingModel::CloseModelWithParameters( // Keep this out of SetupSearch as this contains static search objects. // This will allow calling SetupSearch multiple times with different search // parameters. - CreateNeighborhoodOperators(); + CreateNeighborhoodOperators(parameters); CreateFirstSolutionDecisionBuilders(parameters); - if (!ValidateSearchParameters(parameters)) { + error = FindErrorInSearchParametersForModel(parameters); + if (!error.empty()) { + status_ = ROUTING_INVALID; + LOG(ERROR) << "Invalid RoutingSearchParameters for this model: " << error; return; } SetupSearch(parameters); @@ -1935,36 +2052,6 @@ struct LinkSort { } } LinkComparator; -struct VehicleClass { - VehicleClass(RoutingModel::NodeIndex start_node, int64 start_index, - RoutingModel::NodeIndex end_node, int64 end_index, - RoutingModel::CostClassIndex cost_class_index) - : start_node(start_node), - end_node(end_node), - cost_class_index(cost_class_index), - start_depot(start_index), - end_depot(end_index), - vehicle_class_index(-1) {} - - RoutingModel::NodeIndex start_node; - RoutingModel::NodeIndex end_node; - RoutingModel::CostClassIndex cost_class_index; - int64 start_depot; - int64 end_depot; - int64 vehicle_class_index; - - // Comparators. - static bool Equals(const VehicleClass& a, const VehicleClass& b) { - return (a.start_node == b.start_node && a.end_node == b.end_node && - a.cost_class_index == b.cost_class_index); - } - static bool LessThan(const VehicleClass& a, const VehicleClass& b) { - if (a.start_node != b.start_node) return a.start_node < b.start_node; - if (a.end_node != b.end_node) return a.end_node < b.end_node; - return a.cost_class_index < b.cost_class_index; - } -}; - // The RouteConstructor creates the routes of a VRP instance subject to its // constraints by iterating on a list of arcs appearing in descending order // of priority. @@ -1973,21 +2060,19 @@ struct VehicleClass { class RouteConstructor { public: RouteConstructor(Assignment* const assignment, RoutingModel* const model, - bool check_assignment, int64 nodes_number, - const std::vector& links_list, - const std::vector& vehicle_classes) + bool check_assignment, int64 num_indices, + const std::vector& links_list) : assignment_(assignment), model_(model), check_assignment_(check_assignment), solver_(model_->solver()), - nodes_number_(nodes_number), + num_indices_(num_indices), links_list_(links_list), - vehicle_classes_(vehicle_classes), nexts_(model_->Nexts()), - in_route_(nodes_number_, -1), + in_route_(num_indices_, -1), final_routes_(), - node_to_chain_index_(nodes_number, -1), - node_to_vehicle_class_index_(nodes_number, -1) { + index_to_chain_index_(num_indices, -1), + index_to_vehicle_class_index_(num_indices, -1) { { const std::vector dimension_names = model_->GetAllDimensionNames(); @@ -1998,7 +2083,7 @@ class RouteConstructor { } cumuls_.resize(dimensions_.size()); for (std::vector& cumuls : cumuls_) { - cumuls.resize(nodes_number_); + cumuls.resize(num_indices_); } new_possible_cumuls_.resize(dimensions_.size()); } @@ -2008,52 +2093,52 @@ class RouteConstructor { void Construct() { model_->solver()->TopPeriodicCheck(); // Initial State: Each order is served by its own vehicle. - for (int node = 0; node < nodes_number_; ++node) { - if (!model_->IsStart(node) && !model_->IsEnd(node)) { - std::vector route(1, node); + for (int index = 0; index < num_indices_; ++index) { + if (!model_->IsStart(index) && !model_->IsEnd(index)) { + std::vector route(1, index); routes_.push_back(route); - in_route_[node] = routes_.size() - 1; + in_route_[index] = routes_.size() - 1; } } for (const Link& link : links_list_) { model_->solver()->TopPeriodicCheck(); - const int node1 = link.link.first; - const int node2 = link.link.second; + const int index1 = link.link.first; + const int index2 = link.link.second; const int vehicle_class = link.vehicle_class; const int64 start_depot = link.start_depot; const int64 end_depot = link.end_depot; - // Initialisation of cumuls_ if the nodes are encountered for first time - if (node_to_vehicle_class_index_[node1] < 0) { + // Initialisation of cumuls_ if the indices are encountered for first time + if (index_to_vehicle_class_index_[index1] < 0) { for (int dimension_index = 0; dimension_index < dimensions_.size(); ++dimension_index) { - cumuls_[dimension_index][node1] = + cumuls_[dimension_index][index1] = std::max(dimensions_[dimension_index]->GetTransitValue( - start_depot, node1, 0), - dimensions_[dimension_index]->CumulVar(node1)->Min()); + start_depot, index1, 0), + dimensions_[dimension_index]->CumulVar(index1)->Min()); } } - if (node_to_vehicle_class_index_[node2] < 0) { + if (index_to_vehicle_class_index_[index2] < 0) { for (int dimension_index = 0; dimension_index < dimensions_.size(); ++dimension_index) { - cumuls_[dimension_index][node2] = + cumuls_[dimension_index][index2] = std::max(dimensions_[dimension_index]->GetTransitValue( - start_depot, node2, 0), - dimensions_[dimension_index]->CumulVar(node2)->Min()); + start_depot, index2, 0), + dimensions_[dimension_index]->CumulVar(index2)->Min()); } } - const int route_index1 = in_route_[node1]; - const int route_index2 = in_route_[node2]; + const int route_index1 = in_route_[index1]; + const int route_index2 = in_route_[index2]; const bool merge = route_index1 >= 0 && route_index2 >= 0 && - FeasibleMerge(routes_[route_index1], routes_[route_index2], node1, - node2, route_index1, route_index2, vehicle_class, + FeasibleMerge(routes_[route_index1], routes_[route_index2], index1, + index2, route_index1, route_index2, vehicle_class, start_depot, end_depot); if (Merge(merge, route_index1, route_index2)) { - node_to_vehicle_class_index_[node1] = vehicle_class; - node_to_vehicle_class_index_[node2] = vehicle_class; + index_to_vehicle_class_index_[index1] = vehicle_class; + index_to_vehicle_class_index_[index2] = vehicle_class; } } @@ -2136,7 +2221,7 @@ class RouteConstructor { struct RouteSort { bool operator()(const std::vector& route1, - const std::vector& route2) { + const std::vector& route2) const { return (route1.size() < route2.size()); } } RouteComparator; @@ -2206,7 +2291,7 @@ class RouteConstructor { const int tail2 = route2.back(); const RoutingDimension& dimension = *dimensions_[dimension_index]; int non_depot_node = -1; - for (int node = 0; node < nodes_number_; ++node) { + for (int node = 0; node < num_indices_; ++node) { if (!model_->IsStart(node) && !model_->IsEnd(node)) { non_depot_node = node; break; @@ -2260,14 +2345,14 @@ class RouteConstructor { } // Vehicle Class Check - if (!((node_to_vehicle_class_index_[node1] == -1 && - node_to_vehicle_class_index_[node2] == -1) || - (node_to_vehicle_class_index_[node1] == vehicle_class && - node_to_vehicle_class_index_[node2] == -1) || - (node_to_vehicle_class_index_[node1] == -1 && - node_to_vehicle_class_index_[node2] == vehicle_class) || - (node_to_vehicle_class_index_[node1] == vehicle_class && - node_to_vehicle_class_index_[node2] == vehicle_class))) { + if (!((index_to_vehicle_class_index_[node1] == -1 && + index_to_vehicle_class_index_[node2] == -1) || + (index_to_vehicle_class_index_[node1] == vehicle_class && + index_to_vehicle_class_index_[node2] == -1) || + (index_to_vehicle_class_index_[node1] == -1 && + index_to_vehicle_class_index_[node2] == vehicle_class) || + (index_to_vehicle_class_index_[node1] == vehicle_class && + index_to_vehicle_class_index_[node2] == vehicle_class))) { return false; } @@ -2288,6 +2373,9 @@ class RouteConstructor { bool CheckTempAssignment(Assignment* const temp_assignment, int new_chain_index, int old_chain_index, int head1, int tail1, int head2, int tail2) { + // TODO(user): If the chain index is greater than the number of vehicles, + // use another vehicle instead. + if (new_chain_index >= model_->vehicles()) return false; const int start = head1; temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index))); temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)), @@ -2319,8 +2407,8 @@ class RouteConstructor { const int tail1 = route1.back(); const int head2 = route2.front(); const int tail2 = route2.back(); - const int chain_index1 = node_to_chain_index_[head1]; - const int chain_index2 = node_to_chain_index_[head2]; + const int chain_index1 = index_to_chain_index_[head1]; + const int chain_index2 = index_to_chain_index_[head2]; if (chain_index1 < 0 && chain_index2 < 0) { const int chain_index = chains_.size(); if (check_assignment_) { @@ -2334,8 +2422,8 @@ class RouteConstructor { chain.head = head1; chain.tail = tail2; chain.nodes = 2; - node_to_chain_index_[head1] = chain_index; - node_to_chain_index_[tail2] = chain_index; + index_to_chain_index_[head1] = chain_index; + index_to_chain_index_[tail2] = chain_index; chains_.push_back(chain); } } else if (chain_index1 >= 0 && chain_index2 < 0) { @@ -2347,7 +2435,7 @@ class RouteConstructor { head1, tail1, head2, tail2); } if (feasible) { - node_to_chain_index_[tail2] = chain_index1; + index_to_chain_index_[tail2] = chain_index1; chains_[chain_index1].head = head1; chains_[chain_index1].tail = tail2; ++chains_[chain_index1].nodes; @@ -2361,7 +2449,7 @@ class RouteConstructor { head1, tail1, head2, tail2); } if (feasible) { - node_to_chain_index_[head1] = chain_index2; + index_to_chain_index_[head1] = chain_index2; chains_[chain_index2].head = head1; chains_[chain_index2].tail = tail2; ++chains_[chain_index2].nodes; @@ -2375,7 +2463,7 @@ class RouteConstructor { head1, tail1, head2, tail2); } if (feasible) { - node_to_chain_index_[tail2] = chain_index1; + index_to_chain_index_[tail2] = chain_index1; chains_[chain_index1].head = head1; chains_[chain_index1].tail = tail2; chains_[chain_index1].nodes += chains_[chain_index2].nodes; @@ -2416,231 +2504,90 @@ class RouteConstructor { RoutingModel* const model_; const bool check_assignment_; Solver* const solver_; - const int64 nodes_number_; + const int64 num_indices_; const std::vector links_list_; - const std::vector vehicle_classes_; std::vector nexts_; std::vector dimensions_; // Not owned. std::vector> cumuls_; - std::vector> new_possible_cumuls_; + std::vector> new_possible_cumuls_; std::vector> routes_; std::vector in_route_; - std::unordered_set deleted_routes_; + absl::flat_hash_set deleted_routes_; std::vector> final_routes_; std::vector chains_; - std::unordered_set deleted_chains_; + absl::flat_hash_set deleted_chains_; std::vector final_chains_; - std::vector node_to_chain_index_; - std::vector node_to_vehicle_class_index_; -}; - -void GetVehicleClasses(const RoutingModel& model, - std::vector* vehicle_classes) { - vehicle_classes->clear(); - vehicle_classes->reserve(model.vehicles()); - for (int vehicle = 0; vehicle < model.vehicles(); ++vehicle) { - const int64 start_index = model.Start(vehicle); - const int64 end_index = model.End(vehicle); - vehicle_classes->push_back( - VehicleClass(model.IndexToNode(start_index), start_index, - model.IndexToNode(end_index), end_index, - model.GetCostClassIndexOfVehicle(vehicle))); - } - std::sort(vehicle_classes->begin(), vehicle_classes->end(), - &VehicleClass::LessThan); - vehicle_classes->erase( - std::unique(vehicle_classes->begin(), vehicle_classes->end(), - &VehicleClass::Equals), - vehicle_classes->end()); - // Populate the vehicle_class_index. - for (int i = 0; i < vehicle_classes->size(); ++i) { - (*vehicle_classes)[i].vehicle_class_index = i; - } -} - -// Desicion Builder building a first solution based on Savings (Clarke & Wright) -// heuristic for Vehicle Routing Problem. -class SavingsBuilder : public DecisionBuilder { - public: - SavingsBuilder(RoutingModel* const model, double savings_neighbors_ratio, - bool check_assignment) - : model_(model), - savings_neighbors_ratio_(savings_neighbors_ratio > 0 - ? std::min(savings_neighbors_ratio, 1.0) - : 1), - check_assignment_(check_assignment) {} - ~SavingsBuilder() override {} - - Decision* Next(Solver* const solver) override { - // Setup the model of the instance for the Savings Algorithm - ModelSetup(); - - // Create the Savings List - CreateSavingsList(); - - // Build the assignment routes for the model - Assignment* const assignment = solver->MakeAssignment(); - route_constructor_.reset( - new RouteConstructor(assignment, model_, check_assignment_, - nodes_number_, savings_list_, vehicle_classes_)); - // This call might cause backtracking if the search limit is reached. - route_constructor_->Construct(); - route_constructor_.reset(nullptr); - // This call might cause backtracking if the solution is not feasible. - assignment->Restore(); - - return nullptr; - } - - private: - void ModelSetup() { - nodes_number_ = model_->Size() + model_->vehicles(); - neighbors_.resize(nodes_number_); - route_shape_parameter_ = FLAGS_savings_route_shape_parameter; - - int64 savings_filter_neighbors = - std::max(1.0, model_->nodes() * savings_neighbors_ratio_); - int64 savings_filter_radius = FLAGS_savings_filter_radius; - if (!savings_filter_radius) { - savings_filter_radius = -1; - } - - // For each node consider as neighbors the nearest nodes. - { - for (int node = 0; node < nodes_number_; ++node) { - model_->solver()->TopPeriodicCheck(); - neighbors_[node].reserve(nodes_number_); - for (int neighbor = 0; neighbor < nodes_number_; ++neighbor) { - if (model_->HasIndex(model_->IndexToNode(neighbor))) { - neighbors_[node].push_back(neighbor); - } - } - } - } - - // Setting Up Costs - for (int node = 0; node < nodes_number_; ++node) { - model_->solver()->TopPeriodicCheck(); - std::vector costs_from_node(nodes_number_); - for (const int neighbor : neighbors_[node]) { - // TODO(user): Take vehicle class into account here. - const int64 cost = model_->GetHomogeneousCost(node, neighbor); - costs_from_node[neighbor] = cost; - } - costs_.push_back(costs_from_node); - } - - // Find the different vehicle classes - GetVehicleClasses(*model_, &vehicle_classes_); - } - - void CreateSavingsList() { - for (const VehicleClass& vehicle_class : vehicle_classes_) { - int64 start_depot = vehicle_class.start_depot; - int64 end_depot = vehicle_class.end_depot; - int vehicle_class_index = vehicle_class.vehicle_class_index; - for (int node = 0; node < nodes_number_; ++node) { - model_->solver()->TopPeriodicCheck(); - for (const int neighbor : neighbors_[node]) { - if (node != start_depot && node != end_depot && - neighbor != start_depot && neighbor != end_depot && - node != neighbor) { - const double saving = - costs_[node][start_depot] + costs_[end_depot][neighbor] - - route_shape_parameter_ * costs_[node][neighbor]; - Link link(std::make_pair(node, neighbor), saving, - vehicle_class_index, start_depot, end_depot); - savings_list_.push_back(link); - } - } - } - std::stable_sort(savings_list_.begin(), savings_list_.end(), - LinkComparator); - } - } - - RoutingModel* const model_; - const double savings_neighbors_ratio_; - std::unique_ptr route_constructor_; - const bool check_assignment_; - std::vector dimensions_; - int64 nodes_number_; - std::vector> costs_; - std::vector> neighbors_; - std::vector savings_list_; - double route_shape_parameter_; - std::vector vehicle_classes_; + std::vector index_to_chain_index_; + std::vector index_to_vehicle_class_index_; }; #ifndef SWIG -struct SweepNode { - SweepNode(const RoutingModel::NodeIndex node, const double angle, - const double distance) - : node(node), angle(angle), distance(distance) {} - ~SweepNode() {} +struct SweepIndex { + SweepIndex(const int64 index, const double angle, const double distance) + : index(index), angle(angle), distance(distance) {} + ~SweepIndex() {} - RoutingModel::NodeIndex node; + int64 index; double angle; double distance; }; -struct SweepNodeSortAngle { - bool operator()(const SweepNode& node1, const SweepNode& node2) const { +struct SweepIndexSortAngle { + bool operator()(const SweepIndex& node1, const SweepIndex& node2) const { return (node1.angle < node2.angle); } -} SweepNodeAngleComparator; +} SweepIndexAngleComparator; -struct SweepNodeSortDistance { - bool operator()(const SweepNode& node1, const SweepNode& node2) const { +struct SweepIndexSortDistance { + bool operator()(const SweepIndex& node1, const SweepIndex& node2) const { return (node1.distance < node2.distance); } -} SweepNodeDistanceComparator; +} SweepIndexDistanceComparator; -SweepArranger::SweepArranger( - const gtl::ITIVector>& points) +SweepArranger::SweepArranger(const std::vector>& points) : coordinates_(2 * points.size(), 0), sectors_(1) { - for (RoutingModel::NodeIndex i(0); i < points.size(); ++i) { + for (int64 i = 0; i < points.size(); ++i) { coordinates_[2 * i] = points[i].first; coordinates_[2 * i + 1] = points[i].second; } } -// Splits the space of the nodes into sectors and sorts the nodes of each +// Splits the space of the indices into sectors and sorts the indices of each // sector with ascending angle from the depot. -void SweepArranger::ArrangeNodes(std::vector* nodes) { +void SweepArranger::ArrangeIndices(std::vector* indices) { const double pi_rad = 3.14159265; // Suppose that the center is at x0, y0. - const int x0 = coordinates_[RoutingModel::NodeIndex(0)]; - const int y0 = coordinates_[RoutingModel::NodeIndex(1)]; - - std::vector sweep_nodes; - for (RoutingModel::NodeIndex node(0); - node < static_cast(coordinates_.size()) / 2; ++node) { - const int x = coordinates_[2 * node]; - const int y = coordinates_[2 * node + 1]; + const int x0 = coordinates_[0]; + const int y0 = coordinates_[1]; + + std::vector sweep_indices; + for (int64 index = 0; index < static_cast(coordinates_.size()) / 2; + ++index) { + const int x = coordinates_[2 * index]; + const int y = coordinates_[2 * index + 1]; const double x_delta = x - x0; const double y_delta = y - y0; double square_distance = x_delta * x_delta + y_delta * y_delta; double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta); angle = angle >= 0 ? angle : 2 * pi_rad + angle; - SweepNode sweep_node(node, angle, square_distance); - sweep_nodes.push_back(sweep_node); + SweepIndex sweep_index(index, angle, square_distance); + sweep_indices.push_back(sweep_index); } - std::sort(sweep_nodes.begin(), sweep_nodes.end(), - SweepNodeDistanceComparator); + std::sort(sweep_indices.begin(), sweep_indices.end(), + SweepIndexDistanceComparator); - const int size = static_cast(sweep_nodes.size()) / sectors_; + const int size = static_cast(sweep_indices.size()) / sectors_; for (int sector = 0; sector < sectors_; ++sector) { - std::vector cluster; - std::vector::iterator begin = - sweep_nodes.begin() + sector * size; - std::vector::iterator end = - sector == sectors_ - 1 ? sweep_nodes.end() - : sweep_nodes.begin() + (sector + 1) * size; - std::sort(begin, end, SweepNodeAngleComparator); + std::vector cluster; + std::vector::iterator begin = + sweep_indices.begin() + sector * size; + std::vector::iterator end = + sector == sectors_ - 1 ? sweep_indices.end() + : sweep_indices.begin() + (sector + 1) * size; + std::sort(begin, end, SweepIndexAngleComparator); } - for (const SweepNode& sweep_node : sweep_nodes) { - nodes->push_back(sweep_node.node); + for (const SweepIndex& sweep_index : sweep_indices) { + indices->push_back(sweep_index.index); } } @@ -2659,9 +2606,8 @@ class SweepBuilder : public DecisionBuilder { // Build the assignment routes for the model Assignment* const assignment = solver->MakeAssignment(); - route_constructor_.reset( - new RouteConstructor(assignment, model_, check_assignment_, - nodes_number_, links_, vehicle_classes_)); + route_constructor_ = absl::make_unique( + assignment, model_, check_assignment_, num_indices_, links_); // This call might cause backtracking if the search limit is reached. route_constructor_->Construct(); route_constructor_.reset(nullptr); @@ -2674,21 +2620,19 @@ class SweepBuilder : public DecisionBuilder { private: void ModelSetup() { const int depot = model_->GetDepot(); - nodes_number_ = model_->nodes(); - if (FLAGS_sweep_sectors > 0 && FLAGS_sweep_sectors < nodes_number_) { + num_indices_ = model_->Size() + model_->vehicles(); + if (FLAGS_sweep_sectors > 0 && FLAGS_sweep_sectors < num_indices_) { model_->sweep_arranger()->SetSectors(FLAGS_sweep_sectors); } - std::vector nodes; - model_->sweep_arranger()->ArrangeNodes(&nodes); - for (int i = 0; i < nodes.size() - 1; ++i) { - const RoutingModel::NodeIndex first = nodes[i]; - const RoutingModel::NodeIndex second = nodes[i + 1]; - if (model_->HasIndex(first) && model_->HasIndex(second)) { - const int64 first_index = model_->NodeToIndex(first); - const int64 second_index = model_->NodeToIndex(second); - if (first_index != depot && second_index != depot) { - Link link(std::make_pair(first_index, second_index), 0, 0, depot, - depot); + std::vector indices; + model_->sweep_arranger()->ArrangeIndices(&indices); + for (int i = 0; i < indices.size() - 1; ++i) { + const int64 first = indices[i]; + const int64 second = indices[i + 1]; + if ((model_->IsStart(first) || !model_->IsEnd(first)) && + (model_->IsStart(second) || !model_->IsEnd(second))) { + if (first != depot && second != depot) { + Link link(std::make_pair(first, second), 0, 0, depot, depot); links_.push_back(link); } } @@ -2698,138 +2642,12 @@ class SweepBuilder : public DecisionBuilder { RoutingModel* const model_; std::unique_ptr route_constructor_; const bool check_assignment_; - int64 nodes_number_; + int64 num_indices_; std::vector links_; - std::vector vehicle_classes_; }; #endif -// Decision builder building a solution with a single path without propagating. -// Is very fast but has a very high probability of failing if the problem -// contains other constraints than path-related constraints. -// Based on an addition heuristics extending a path from its start node with -// the cheapest arc according to an evaluator. -// TODO(user): Deprecate this in favor of -// CheapestAdditionFilteredDecisionBuilder if performance is similar. -class FastOnePathBuilder : public DecisionBuilder { - public: - // Takes ownership of evaluator. - FastOnePathBuilder(RoutingModel* const model, - ResultCallback2* evaluator) - : model_(model), evaluator_(evaluator) { - CHECK(evaluator_->IsRepeatable()); - } - ~FastOnePathBuilder() override {} - Decision* Next(Solver* const solver) override { - int64 index = -1; - if (!FindPathStart(&index)) { - return nullptr; - } - IntVar* const* nexts = model_->Nexts().data(); - // Need to allocate in a reversible way so that if restoring the assignment - // fails, the assignment gets de-allocated. - Assignment* const assignment = solver->MakeAssignment(); - Assignment::IntContainer* const container = - assignment->MutableIntVarContainer(); - added_.assign(model_->Size(), false); - int64 next = FindCheapestValue(index); - while (next >= 0) { - added_[index] = true; - container->FastAdd(nexts[index])->SetValue(next); - index = next; - model_->ForEachNodeInDisjunctionWithMaxCardinalityFromIndex( - index, 1, [this, index, container, nexts](int alternate) { - if (index != alternate) { - added_[alternate] = true; - container->FastAdd(nexts[alternate])->SetValue(alternate); - } - }); - next = FindCheapestValue(index); - } - // Make unassigned nexts loop to themselves. - // TODO(user): Make finalization more robust, might have some next - // variables non-instantiated. - for (int index = 0; index < model_->Size(); ++index) { - if (!added_[index]) { - added_[index] = true; - IntVar* const next = nexts[index]; - IntVarElement* const element = container->FastAdd(next); - if (next->Contains(index)) { - element->SetValue(index); - } - } - } - assignment->Restore(); - return nullptr; - } - - private: - bool FindPathStart(int64* index) const { - IntVar* const* nexts = model_->Nexts().data(); - const int size = model_->Size(); - // Try to extend an existing path - for (int i = size - 1; i >= 0; --i) { - if (nexts[i]->Bound()) { - const int next = nexts[i]->Value(); - if (next < size && !nexts[next]->Bound()) { - *index = next; - return true; - } - } - } - // Pick path start - for (int i = size - 1; i >= 0; --i) { - if (!nexts[i]->Bound()) { - bool has_possible_prev = false; - for (int j = 0; j < size; ++j) { - if (nexts[j]->Contains(i)) { - has_possible_prev = true; - break; - } - } - if (!has_possible_prev) { - *index = i; - return true; - } - } - } - // Pick first unbound - for (int i = 0; i < size; ++i) { - if (!nexts[i]->Bound()) { - *index = i; - return true; - } - } - return false; - } - - int64 FindCheapestValue(int index) const { - IntVar* const* nexts = model_->Nexts().data(); - const int size = model_->Size(); - int64 best_evaluation = kint64max; - int64 best_value = -1; - if (index < size) { - IntVar* const next = nexts[index]; - std::unique_ptr it(next->MakeDomainIterator(false)); - for (const int64 value : InitAndGetValues(it.get())) { - if (value != index && (value >= size || !added_[value])) { - const int64 evaluation = evaluator_->Run(index, value); - if (evaluation <= best_evaluation) { - best_evaluation = evaluation; - best_value = value; - } - } - } - } - return best_value; - } - - RoutingModel* const model_; - // added_[node] is true if node had been added to the solution. - std::vector added_; - std::unique_ptr> evaluator_; -}; - +namespace { // Decision builder to build a solution with all nodes inactive. It does no // branching and may fail if some nodes cannot be made inactive. @@ -2854,6 +2672,7 @@ class AllUnperformed : public DecisionBuilder { private: RoutingModel* const model_; }; +} // namespace void RoutingModel::AddSearchMonitor(SearchMonitor* const monitor) { monitors_.push_back(monitor); @@ -2881,40 +2700,155 @@ void RoutingModel::AddAtSolutionCallback(std::function callback) { const Assignment* RoutingModel::Solve(const Assignment* assignment) { return SolveFromAssignmentWithParameters(assignment, - DefaultSearchParameters()); + DefaultRoutingSearchParameters()); } const Assignment* RoutingModel::SolveWithParameters( - const RoutingSearchParameters& parameters) { - return SolveFromAssignmentWithParameters(nullptr, parameters); + const RoutingSearchParameters& parameters, + std::vector* solutions) { + return SolveFromAssignmentWithParameters(nullptr, parameters, solutions); +} + +namespace { +// The CP solver engine uses int64 time limits in milliseconds, with kint64max +// for "no limit". This converter helps several times. +int64 GetTimeLimitMs(const RoutingSearchParameters& parameters) { + if (!parameters.has_time_limit()) return kint64max; + return absl::ToInt64Milliseconds( + util_time::DecodeGoogleApiProto(parameters.time_limit()).ValueOrDie()); +} + +// Ditto, for the LNS time limit. +int64 GetLnsTimeLimitMs(const RoutingSearchParameters& parameters) { + if (!parameters.has_lns_time_limit()) return kint64max; + return absl::ToInt64Milliseconds( + util_time::DecodeGoogleApiProto(parameters.lns_time_limit()) + .ValueOrDie()); +} + +} // namespace + +namespace { +void MakeAllUnperformed(const RoutingModel* model, Assignment* assignment) { + assignment->Clear(); + for (int i = 0; i < model->Nexts().size(); ++i) { + if (!model->IsStart(i)) { + assignment->Add(model->NextVar(i))->SetValue(i); + } + } + for (int vehicle = 0; vehicle < model->vehicles(); ++vehicle) { + assignment->Add(model->NextVar(model->Start(vehicle))) + ->SetValue(model->End(vehicle)); + } +} +} // namespace + +bool RoutingModel::AppendAssignmentIfFeasible( + const Assignment& assignment, + std::vector>* assignments) { + tmp_assignment_->CopyIntersection(&assignment); + solver_->Solve(restore_tmp_assignment_, collect_one_assignment_, + GetOrCreateLimit()); + if (collect_one_assignment_->solution_count() == 1) { + assignments->push_back(absl::make_unique(solver_.get())); + assignments->back()->Copy(collect_one_assignment_->solution(0)); + return true; + } + return false; +} + +void RoutingModel::LogSolution(const std::string& description, + int64 solution_cost, int64 start_time_ms) { + const std::string memory_str = MemoryUsage(); + LOG(INFO) << description << " (" << solution_cost + << ", time = " << (solver_->wall_time() - start_time_ms) + << " ms, memory used = " << memory_str << ")"; } const Assignment* RoutingModel::SolveFromAssignmentWithParameters( - const Assignment* assignment, const RoutingSearchParameters& parameters) { + const Assignment* assignment, const RoutingSearchParameters& parameters, + std::vector* solutions) { QuietCloseModelWithParameters(parameters); VLOG(1) << "Search parameters:\n" << parameters.DebugString(); + if (solutions != nullptr) solutions->clear(); if (status_ == ROUTING_INVALID) { return nullptr; } - solver_->UpdateLimits(parameters.time_limit_ms(), kint64max, kint64max, + solver_->UpdateLimits(GetTimeLimitMs(parameters), kint64max, kint64max, parameters.solution_limit(), limit_); - solver_->UpdateLimits(parameters.time_limit_ms(), kint64max, kint64max, 1, + solver_->UpdateLimits(GetTimeLimitMs(parameters), kint64max, kint64max, 1, ls_limit_); - solver_->UpdateLimits(parameters.lns_time_limit_ms(), kint64max, kint64max, + solver_->UpdateLimits(GetLnsTimeLimitMs(parameters), kint64max, kint64max, kint64max, lns_limit_); const int64 start_time_ms = solver_->wall_time(); + + std::vector> solution_pool; if (nullptr == assignment) { - solver_->Solve(solve_db_, monitors_); + bool solution_found = false; + Assignment matching(solver_.get()); + if (IsMatchingModel() && SolveMatchingModel(&matching) && + AppendAssignmentIfFeasible(matching, &solution_pool)) { + if (parameters.log_search()) { + LogSolution("Min-Cost Flow Solution", + solution_pool.back()->ObjectiveValue(), start_time_ms); + } + solution_found = true; + } + if (!solution_found) { + // Build trivial solutions to which we can come back too in case the + // solver does not manage to build something better. + Assignment unperformed(solver_.get()); + MakeAllUnperformed(this, &unperformed); + if (AppendAssignmentIfFeasible(unperformed, &solution_pool) && + parameters.log_search()) { + LogSolution("All Unperformed Solution", + solution_pool.back()->ObjectiveValue(), start_time_ms); + } + const int64 elapsed_time_ms = solver_->wall_time() - start_time_ms; + const int64 time_left_ms = + GetTimeLimitMs(parameters) != kint64max + ? GetTimeLimitMs(parameters) - elapsed_time_ms + : kint64max; + if (time_left_ms >= 0) { + solver_->UpdateLimits(time_left_ms, kint64max, kint64max, + parameters.solution_limit(), limit_); + solver_->UpdateLimits(time_left_ms, kint64max, kint64max, 1, ls_limit_); + solver_->Solve(solve_db_, monitors_); + } + } } else { - assignment_->Copy(assignment); + assignment_->CopyIntersection(assignment); solver_->Solve(improve_db_, monitors_); } const int64 elapsed_time_ms = solver_->wall_time() - start_time_ms; - if (collect_assignments_->solution_count() == 1) { + const int solution_count = collect_assignments_->solution_count(); + if (solution_count >= 1 || !solution_pool.empty()) { status_ = ROUTING_SUCCESS; - return collect_assignments_->solution(0); + if (solutions != nullptr) { + for (int i = 0; i < solution_count; ++i) { + solutions->push_back( + solver_->MakeAssignment(collect_assignments_->solution(i))); + } + for (const auto& solution : solution_pool) { + if (solutions->empty() || + solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) { + solutions->push_back(solver_->MakeAssignment(solution.get())); + } + } + return solutions->back(); + } + Assignment* best_assignment = + solution_count >= 1 ? collect_assignments_->solution(solution_count - 1) + : nullptr; + for (const auto& solution : solution_pool) { + if (best_assignment == nullptr || + solution->ObjectiveValue() < best_assignment->ObjectiveValue()) { + best_assignment = solution.get(); + } + } + return solver_->MakeAssignment(best_assignment); } else { - if (elapsed_time_ms >= parameters.time_limit_ms()) { + if (elapsed_time_ms >= GetTimeLimitMs(parameters)) { status_ = ROUTING_FAIL_TIMEOUT; } else { status_ = ROUTING_FAIL; @@ -2923,6 +2857,34 @@ const Assignment* RoutingModel::SolveFromAssignmentWithParameters( } } +void RoutingModel::SetAssignmentFromOtherModelAssignment( + Assignment* target_assignment, const RoutingModel* source_model, + const Assignment* source_assignment) { + const int size = Size(); + DCHECK_EQ(size, source_model->Size()); + CHECK_EQ(target_assignment->solver(), solver_.get()); + + if (CostsAreHomogeneousAcrossVehicles()) { + SetAssignmentFromAssignment(target_assignment, Nexts(), source_assignment, + source_model->Nexts()); + } else { + std::vector source_vars(size + size + vehicles_); + std::vector target_vars(size + size + vehicles_); + for (int index = 0; index < size; index++) { + source_vars[index] = source_model->NextVar(index); + target_vars[index] = NextVar(index); + } + for (int index = 0; index < size + vehicles_; index++) { + source_vars[size + index] = source_model->VehicleVar(index); + target_vars[size + index] = VehicleVar(index); + } + SetAssignmentFromAssignment(target_assignment, target_vars, + source_assignment, source_vars); + } + + target_assignment->AddObjective(cost_); +} + // Computing a lower bound to the cost of a vehicle routing problem solving a // a linear assignment problem (minimum-cost perfect bipartite matching). // A bipartite graph is created with left nodes representing the nodes of the @@ -2958,7 +2920,7 @@ int64 RoutingModel::ComputeLowerBound() { nexts_[tail]->MakeDomainIterator(false)); for (const int64 head : InitAndGetValues(iterator.get())) { // Given there are no disjunction constraints, a node cannot point to - // itself. Doing this explicitely given that outside the search, + // itself. Doing this explicitly given that outside the search, // propagation hasn't removed this value from next variables yet. if (head == tail) { continue; @@ -3116,8 +3078,10 @@ Assignment* RoutingModel::CompactAssignmentInternal( has_more_vehicles_with_route = true; const int swap_vehicle_start = Start(swap_vehicle); const int swap_vehicle_end = End(swap_vehicle); - if (IndexToNode(vehicle_start) != IndexToNode(swap_vehicle_start) || - IndexToNode(vehicle_end) != IndexToNode(swap_vehicle_end)) { + if (manager_.IndexToNode(vehicle_start) != + manager_.IndexToNode(swap_vehicle_start) || + manager_.IndexToNode(vehicle_end) != + manager_.IndexToNode(swap_vehicle_end)) { continue; } @@ -3156,17 +3120,17 @@ Assignment* RoutingModel::CompactAssignmentInternal( } int RoutingModel::FindNextActive(int index, - const std::vector& nodes) const { + const std::vector& indices) const { ++index; CHECK_LE(0, index); - const int size = nodes.size(); - while (index < size && ActiveVar(nodes[index])->Max() == 0) { + const int size = indices.size(); + while (index < size && ActiveVar(indices[index])->Max() == 0) { ++index; } return index; } -IntVar* RoutingModel::ApplyLocks(const std::vector& locks) { +IntVar* RoutingModel::ApplyLocks(const std::vector& locks) { // TODO(user): Replace calls to this method with calls to // ApplyLocksToAllVehicles and remove this method? CHECK_EQ(vehicles_, 1); @@ -3188,7 +3152,7 @@ IntVar* RoutingModel::ApplyLocks(const std::vector& locks) { } bool RoutingModel::ApplyLocksToAllVehicles( - const std::vector>& locks, bool close_routes) { + const std::vector>& locks, bool close_routes) { preassignment_->Clear(); return RoutesToAssignment(locks, true, close_routes, preassignment_); } @@ -3211,7 +3175,7 @@ int64 RoutingModel::GetNumberOfRejectsInFirstSolution( bool RoutingModel::WriteAssignment(const std::string& file_name) const { if (collect_assignments_->solution_count() == 1 && assignment_ != nullptr) { - assignment_->Copy(collect_assignments_->solution(0)); + assignment_->CopyIntersection(collect_assignments_->solution(0)); return assignment_->Save(file_name); } else { return false; @@ -3230,7 +3194,7 @@ Assignment* RoutingModel::ReadAssignment(const std::string& file_name) { Assignment* RoutingModel::RestoreAssignment(const Assignment& solution) { QuietCloseModel(); CHECK(assignment_ != nullptr); - assignment_->Copy(&solution); + assignment_->CopyIntersection(&solution); return DoRestoreAssignment(); } @@ -3250,9 +3214,8 @@ Assignment* RoutingModel::DoRestoreAssignment() { } bool RoutingModel::RoutesToAssignment( - const std::vector>& routes, - bool ignore_inactive_nodes, bool close_routes, - Assignment* const assignment) const { + const std::vector>& routes, bool ignore_inactive_indices, + bool close_routes, Assignment* const assignment) const { CHECK(assignment != nullptr); if (!closed_) { LOG(ERROR) << "The model is not closed yet"; @@ -3266,12 +3229,12 @@ bool RoutingModel::RoutesToAssignment( return false; } - std::unordered_set visited_indices; + absl::flat_hash_set visited_indices; // Set value to NextVars based on the routes. for (int vehicle = 0; vehicle < num_routes; ++vehicle) { - const std::vector& route = routes[vehicle]; + const std::vector& route = routes[vehicle]; int from_index = Start(vehicle); - std::pair::iterator, bool> insert_result = + std::pair::iterator, bool> insert_result = visited_indices.insert(from_index); if (!insert_result.second) { LOG(ERROR) << "Index " << from_index << " (start node for vehicle " @@ -3279,39 +3242,32 @@ bool RoutingModel::RoutesToAssignment( return false; } - for (const NodeIndex to_node : route) { - if (to_node < 0 || to_node >= nodes()) { - LOG(ERROR) << "Invalid node index: " << to_node; - return false; - } - const int to_index = NodeToIndex(to_node); + for (const int64 to_index : route) { if (to_index < 0 || to_index >= Size()) { - LOG(ERROR) << "Invalid index: " << to_index << " from node " << to_node; + LOG(ERROR) << "Invalid index: " << to_index; return false; } IntVar* const active_var = ActiveVar(to_index); if (active_var->Max() == 0) { - if (ignore_inactive_nodes) { + if (ignore_inactive_indices) { continue; } else { - LOG(ERROR) << "Index " << to_index << " (node " << to_node - << ") is not active"; + LOG(ERROR) << "Index " << to_index << " is not active"; return false; } } insert_result = visited_indices.insert(to_index); if (!insert_result.second) { - LOG(ERROR) << "Index " << to_index << " (node " << to_node - << ") is used multiple times"; + LOG(ERROR) << "Index " << to_index << " is used multiple times"; return false; } const IntVar* const vehicle_var = VehicleVar(to_index); if (!vehicle_var->Contains(vehicle)) { LOG(ERROR) << "Vehicle " << vehicle << " is not allowed at index " - << to_index << " (node " << to_node << ")"; + << to_index; return false; } @@ -3338,7 +3294,7 @@ bool RoutingModel::RoutesToAssignment( const int start_index = Start(vehicle); // Even if close_routes is false, we still need to add the start index to // visited_indices so that deactivating other nodes works correctly. - std::pair::iterator, bool> insert_result = + std::pair::iterator, bool> insert_result = visited_indices.insert(start_index); if (!insert_result.second) { LOG(ERROR) << "Index " << start_index << " is used multiple times"; @@ -3370,10 +3326,10 @@ bool RoutingModel::RoutesToAssignment( } Assignment* RoutingModel::ReadAssignmentFromRoutes( - const std::vector>& routes, - bool ignore_inactive_nodes) { + const std::vector>& routes, + bool ignore_inactive_indices) { QuietCloseModel(); - if (!RoutesToAssignment(routes, ignore_inactive_nodes, true, assignment_)) { + if (!RoutesToAssignment(routes, ignore_inactive_indices, true, assignment_)) { return nullptr; } // DoRestoreAssignment() might still fail when checking constraints (most @@ -3384,86 +3340,67 @@ Assignment* RoutingModel::ReadAssignmentFromRoutes( void RoutingModel::AssignmentToRoutes( const Assignment& assignment, - std::vector>* const routes) const { + std::vector>* const routes) const { CHECK(closed_); CHECK(routes != nullptr); const int model_size = Size(); routes->resize(vehicles_); for (int vehicle = 0; vehicle < vehicles_; ++vehicle) { - std::vector* const vehicle_route = &routes->at(vehicle); + std::vector* const vehicle_route = &routes->at(vehicle); vehicle_route->clear(); - int num_visited_nodes = 0; + int num_visited_indices = 0; const int first_index = Start(vehicle); const IntVar* const first_var = NextVar(first_index); CHECK(assignment.Contains(first_var)); CHECK(assignment.Bound(first_var)); int current_index = assignment.Value(first_var); while (!IsEnd(current_index)) { - vehicle_route->push_back(IndexToNode(current_index)); + vehicle_route->push_back(current_index); const IntVar* const next_var = NextVar(current_index); CHECK(assignment.Contains(next_var)); CHECK(assignment.Bound(next_var)); current_index = assignment.Value(next_var); - ++num_visited_nodes; - CHECK_LE(num_visited_nodes, model_size) + ++num_visited_indices; + CHECK_LE(num_visited_indices, model_size) << "The assignment contains a cycle"; } } } -RoutingModel::NodeIndex RoutingModel::IndexToNode(int64 index) const { - DCHECK_LT(index, index_to_node_.size()); - return index_to_node_[index]; -} - -int64 RoutingModel::NodeToIndex(NodeIndex node) const { - DCHECK_LT(node, node_to_index_.size()); - DCHECK_NE(node_to_index_[node], kUnassigned) - << "RoutingModel::NodeToIndex should not be used for Start or End nodes"; - return node_to_index_[node]; -} - -bool RoutingModel::HasIndex(NodeIndex node) const { - return node < node_to_index_.size() && node_to_index_[node] != kUnassigned; -} - int64 RoutingModel::GetArcCostForClassInternal( - int64 i, int64 j, CostClassIndex cost_class_index) { + int64 from_index, int64 to_index, CostClassIndex cost_class_index) { DCHECK(closed_); DCHECK_GE(cost_class_index, 0); DCHECK_LT(cost_class_index, cost_classes_.size()); - CostCacheElement* const cache = &cost_cache_[i]; + CostCacheElement* const cache = &cost_cache_[from_index]; // See the comment in CostCacheElement in the .h for the int64->int cast. - if (cache->index == static_cast(j) && + if (cache->index == static_cast(to_index) && cache->cost_class_index == cost_class_index) { return cache->cost; } - const NodeIndex node_i = IndexToNode(i); - const NodeIndex node_j = IndexToNode(j); int64 cost = 0; const CostClass& cost_class = cost_classes_[cost_class_index]; - if (!IsStart(i)) { - // TODO(user): fix overflows. - cost = cost_class.arc_cost_evaluator->Run(node_i, node_j) + - GetDimensionTransitCostSum(i, j, cost_class); - } else if (!IsEnd(j)) { + const auto& evaluator = transit_evaluators_[cost_class.evaluator_index]; + if (!IsStart(from_index)) { + cost = CapAdd(evaluator(from_index, to_index), + GetDimensionTransitCostSum(from_index, to_index, cost_class)); + } else if (!IsEnd(to_index)) { // Apply route fixed cost on first non-first/last node, in other words on // the arc from the first node to its next node if it's not the last node. - cost = cost_class.arc_cost_evaluator->Run(node_i, node_j) + - GetDimensionTransitCostSum(i, j, cost_class) + - fixed_cost_of_vehicle_[index_to_vehicle_[i]]; + cost = CapAdd( + evaluator(from_index, to_index), + CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class), + fixed_cost_of_vehicle_[index_to_vehicle_[from_index]])); } else { // If there's only the first and last nodes on the route, it is considered // as an empty route thus the cost of 0. cost = 0; } - cache->index = static_cast(j); - cache->cost_class_index = cost_class_index; - cache->cost = cost; + *cache = {static_cast(to_index), cost_class_index, cost}; return cost; } @@ -3481,11 +3418,6 @@ bool RoutingModel::IsVehicleUsed(const Assignment& assignment, return !IsEnd(assignment.Value(start_var)); } -const std::vector& RoutingModel::CumulVars( - const std::string& dimension_name) const { - return GetDimensionOrDie(dimension_name).cumuls(); -} - int64 RoutingModel::Next(const Assignment& assignment, int64 index) const { CHECK_EQ(solver_.get(), assignment.solver()); IntVar* const next_var = NextVar(index); @@ -3494,9 +3426,10 @@ int64 RoutingModel::Next(const Assignment& assignment, int64 index) const { return assignment.Value(next_var); } -int64 RoutingModel::GetArcCostForVehicle(int64 i, int64 j, int64 vehicle) { - if (i != j && vehicle >= 0) { - return GetArcCostForClassInternal(i, j, +int64 RoutingModel::GetArcCostForVehicle(int64 from_index, int64 to_index, + int64 vehicle) { + if (from_index != to_index && vehicle >= 0) { + return GetArcCostForClassInternal(from_index, to_index, GetCostClassIndexOfVehicle(vehicle)); } else { return 0; @@ -3504,15 +3437,18 @@ int64 RoutingModel::GetArcCostForVehicle(int64 i, int64 j, int64 vehicle) { } int64 RoutingModel::GetArcCostForClass( - int64 i, int64 j, int64 /*CostClassIndex*/ cost_class_index) { - if (i != j) { - return GetArcCostForClassInternal(i, j, CostClassIndex(cost_class_index)); + int64 from_index, int64 to_index, + int64 /*CostClassIndex*/ cost_class_index) { + if (from_index != to_index) { + return GetArcCostForClassInternal(from_index, to_index, + CostClassIndex(cost_class_index)); } else { return 0; } } -int64 RoutingModel::GetArcCostForFirstSolution(int64 i, int64 j) { +int64 RoutingModel::GetArcCostForFirstSolution(int64 from_index, + int64 to_index) { // Return high cost if connecting to an end (or bound-to-end) node; // this is used in the cost-based first solution strategies to avoid closing // routes too soon. @@ -3524,9 +3460,9 @@ int64 RoutingModel::GetArcCostForFirstSolution(int64 i, int64 j) { nexts_, active_, is_bound_to_end_, zero_transit)); is_bound_to_end_ct_added_.Switch(solver_.get()); } - if (is_bound_to_end_[j]->Min() == 1) return kint64max; + if (is_bound_to_end_[to_index]->Min() == 1) return kint64max; // TODO(user): Take vehicle into account. - return GetHomogeneousCost(i, j); + return GetHomogeneousCost(from_index, to_index); } int64 RoutingModel::GetDimensionTransitCostSum( @@ -3535,9 +3471,11 @@ int64 RoutingModel::GetDimensionTransitCostSum( for (const auto& evaluator_and_coefficient : cost_class.dimension_transit_evaluator_class_and_cost_coefficient) { DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0); - cost += evaluator_and_coefficient.cost_coefficient * - evaluator_and_coefficient.dimension->GetTransitValueFromClass( - i, j, evaluator_and_coefficient.transit_evaluator_class); + cost = CapAdd( + cost, + CapProd(evaluator_and_coefficient.cost_coefficient, + evaluator_and_coefficient.dimension->GetTransitValueFromClass( + i, j, evaluator_and_coefficient.transit_evaluator_class))); } return cost; } @@ -3601,7 +3539,7 @@ bool RoutingModel::ArcIsMoreConstrainedThanArc(int64 from, int64 to1, // and reconsider the need for a "primary" dimension. if (!GetPrimaryConstrainedDimension().empty()) { const std::vector& cumul_vars = - CumulVars(GetPrimaryConstrainedDimension()); + GetDimensionOrDie(GetPrimaryConstrainedDimension()).cumuls(); IntVar* const dim1 = cumul_vars[to1]; IntVar* const dim2 = cumul_vars[to2]; // Prefer the destination that has a lower upper bound for the constrained @@ -3635,6 +3573,38 @@ bool RoutingModel::ArcIsMoreConstrainedThanArc(int64 from, int64 to1, return to1 < to2; } +void RoutingModel::SetVisitType(int64 index, int type) { + CHECK_LT(index, index_to_visit_type_.size()); + index_to_visit_type_[index] = type; + num_visit_types_ = std::max(num_visit_types_, type + 1); +} + +int RoutingModel::GetVisitType(int64 index) const { + CHECK_LT(index, index_to_visit_type_.size()); + return index_to_visit_type_[index]; +} + +void RoutingModel::AddTypeIncompatibility(int type1, int type2) { + // Resizing incompatible_types_per_type_index_ if necessary (first + // incompatibility or new type). + num_visit_types_ = std::max(num_visit_types_, std::max(type1, type2) + 1); + if (incompatible_types_per_type_index_.size() < num_visit_types_) { + incompatible_types_per_type_index_.resize(num_visit_types_); + } + incompatible_types_per_type_index_[type1].insert(type2); + incompatible_types_per_type_index_[type2].insert(type1); +} + +const absl::flat_hash_set& RoutingModel::GetTypeIncompatibilities( + int type) const { + CHECK_GE(type, 0); + if (type >= incompatible_types_per_type_index_.size()) { + // No incompatibilities added for this type. + return empty_incompatibilities_; + } + return incompatible_types_per_type_index_[type]; +} + int64 RoutingModel::UnperformedPenalty(int64 var_index) const { return UnperformedPenaltyOrValue(0, var_index); } @@ -3643,15 +3613,13 @@ int64 RoutingModel::UnperformedPenaltyOrValue(int64 default_value, int64 var_index) const { if (active_[var_index]->Min() == 1) return kint64max; // Forced active. const std::vector& disjunction_indices = - GetDisjunctionIndicesFromVariableIndex(var_index); + GetDisjunctionIndices(var_index); if (disjunction_indices.size() != 1) return default_value; const DisjunctionIndex disjunction_index = disjunction_indices[0]; - if (disjunctions_[disjunction_index].nodes.size() != 1) return default_value; - DCHECK_EQ(var_index, disjunctions_[disjunction_index].nodes[0]); - // The disjunction penalty can't be kNoPenalty, otherwise we would have - // caught it earlier (the node would be forced active). - DCHECK_GE(disjunctions_[disjunction_index].value.penalty, 0); - return disjunctions_[disjunction_index].value.penalty; + // The disjunction penalty can be kNoPenalty iff there is more than one node + // in the disjunction; otherwise we would have caught it earlier (the node + // would be forced active). + return std::max(int64{0}, disjunctions_[disjunction_index].value.penalty); } std::string RoutingModel::DebugOutputAssignment( @@ -3666,8 +3634,8 @@ std::string RoutingModel::DebugOutputAssignment( } } std::string output; - std::unordered_set dimension_names; - if (dimension_to_print == "") { + absl::flat_hash_set dimension_names; + if (dimension_to_print.empty()) { const std::vector all_dimension_names = GetAllDimensionNames(); dimension_names.insert(all_dimension_names.begin(), all_dimension_names.end()); @@ -3682,27 +3650,28 @@ std::string RoutingModel::DebugOutputAssignment( } if (empty_vehicle_range_start != vehicle) { if (empty_vehicle_range_start == vehicle - 1) { - StringAppendF(&output, "Vehicle %d: empty", empty_vehicle_range_start); + absl::StrAppendFormat(&output, "Vehicle %d: empty", + empty_vehicle_range_start); } else { - StringAppendF(&output, "Vehicles %d-%d: empty", - empty_vehicle_range_start, vehicle - 1); + absl::StrAppendFormat(&output, "Vehicles %d-%d: empty", + empty_vehicle_range_start, vehicle - 1); } output.append("\n"); } if (vehicle < vehicles()) { - StringAppendF(&output, "Vehicle %d:", vehicle); + absl::StrAppendFormat(&output, "Vehicle %d:", vehicle); int64 index = Start(vehicle); for (;;) { const IntVar* vehicle_var = VehicleVar(index); - StringAppendF(&output, - "%" GG_LL_FORMAT "d Vehicle(%" GG_LL_FORMAT "d) ", index, - solution_assignment.Value(vehicle_var)); + absl::StrAppendFormat(&output, + "%" GG_LL_FORMAT "d Vehicle(%" GG_LL_FORMAT "d) ", + index, solution_assignment.Value(vehicle_var)); for (const RoutingDimension* const dimension : dimensions_) { if (gtl::ContainsKey(dimension_names, dimension->name())) { const IntVar* const var = dimension->CumulVar(index); - StringAppendF( + absl::StrAppendFormat( &output, "%s(%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d) ", - dimension->name().c_str(), solution_assignment.Min(var), + dimension->name(), solution_assignment.Min(var), solution_assignment.Max(var)); } } @@ -3717,7 +3686,7 @@ std::string RoutingModel::DebugOutputAssignment( for (int i = 0; i < Size(); ++i) { if (!IsEnd(i) && !IsStart(i) && solution_assignment.Value(NextVar(i)) == i) { - StringAppendF(&output, "%d ", i); + absl::StrAppendFormat(&output, "%d ", i); } } output.append("\n"); @@ -3736,6 +3705,14 @@ Assignment* RoutingModel::GetOrCreateAssignment() { return assignment_; } +Assignment* RoutingModel::GetOrCreateTmpAssignment() { + if (tmp_assignment_ == nullptr) { + tmp_assignment_ = solver_->MakeAssignment(); + tmp_assignment_->Add(nexts_); + } + return tmp_assignment_; +} + SearchLimit* RoutingModel::GetOrCreateLimit() { if (limit_ == nullptr) { limit_ = @@ -3813,22 +3790,23 @@ LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() { #define CP_ROUTING_ADD_CALLBACK_OPERATOR(operator_type, cp_operator_type) \ if (CostsAreHomogeneousAcrossVehicles()) { \ - local_search_operators_[operator_type] = \ - solver_->MakeOperator(nexts_, \ - [this](int64 i, int64 j, int64 k) { \ - return GetArcCostForVehicle(i, j, k); \ - }, \ - Solver::cp_operator_type); \ + local_search_operators_[operator_type] = solver_->MakeOperator( \ + nexts_, \ + [this](int64 i, int64 j, int64 k) { \ + return GetArcCostForVehicle(i, j, k); \ + }, \ + Solver::cp_operator_type); \ } else { \ - local_search_operators_[operator_type] = \ - solver_->MakeOperator(nexts_, vehicle_vars_, \ - [this](int64 i, int64 j, int64 k) { \ - return GetArcCostForVehicle(i, j, k); \ - }, \ - Solver::cp_operator_type); \ + local_search_operators_[operator_type] = solver_->MakeOperator( \ + nexts_, vehicle_vars_, \ + [this](int64 i, int64 j, int64 k) { \ + return GetArcCostForVehicle(i, j, k); \ + }, \ + Solver::cp_operator_type); \ } -void RoutingModel::CreateNeighborhoodOperators() { +void RoutingModel::CreateNeighborhoodOperators( + const RoutingSearchParameters& parameters) { local_search_operators_.clear(); local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER, nullptr); CP_ROUTING_ADD_OPERATOR2(RELOCATE, Relocate); @@ -3837,20 +3815,47 @@ void RoutingModel::CreateNeighborhoodOperators() { solver_.get(), nexts_, CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, vehicle_start_class_callback_, pickup_delivery_pairs_); + local_search_operators_[LIGHT_RELOCATE_PAIR] = MakeLightPairRelocate( + solver_.get(), nexts_, + CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, + vehicle_start_class_callback_, pickup_delivery_pairs_); + local_search_operators_[EXCHANGE_PAIR] = MakePairExchange( + solver_.get(), nexts_, + CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, + vehicle_start_class_callback_, pickup_delivery_pairs_); + local_search_operators_[EXCHANGE_RELOCATE_PAIR] = MakePairExchangeRelocate( + solver_.get(), nexts_, + CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, + vehicle_start_class_callback_, pickup_delivery_pairs_); local_search_operators_[RELOCATE_NEIGHBORS] = MakeRelocateNeighbors( solver_.get(), nexts_, CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, vehicle_start_class_callback_, [this](int64 from, int64 to) { return GetHomogeneousCost(from, to); }); local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators( - {NodePairSwapActive( + {IndexPairSwapActive( solver_.get(), nexts_, CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, vehicle_start_class_callback_, pickup_delivery_pairs_), + SwapIndexPair( + solver_.get(), nexts_, + CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, + pickup_delivery_pairs_), PairNodeSwapActive( solver_.get(), nexts_, CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, vehicle_start_class_callback_, pickup_delivery_pairs_)}); + const auto arc_cost_for_path_start = + [this](int64 before_node, int64 after_node, int64 start_index) { + return GetArcCostForVehicle(before_node, after_node, + index_to_vehicle_[start_index]); + }; + local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] = + solver_->RevAlloc(new RelocateExpensiveChain( + nexts_, CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_, + vehicle_start_class_callback_, + parameters.relocate_expensive_chain_num_arcs_to_consider(), + arc_cost_for_path_start)); CP_ROUTING_ADD_OPERATOR2(EXCHANGE, Exchange); CP_ROUTING_ADD_OPERATOR2(CROSS, Cross); CP_ROUTING_ADD_OPERATOR2(TWO_OPT, TwoOpt); @@ -3876,7 +3881,8 @@ void RoutingModel::CreateNeighborhoodOperators() { #undef CP_ROUTING_ADD_OPERATOR #define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \ - if (search_parameters.local_search_operators().use_##operator_method()) { \ + if (search_parameters.local_search_operators().use_##operator_method() == \ + BOOL_TRUE) { \ operators.push_back(local_search_operators_[operator_type]); \ } @@ -3885,28 +3891,45 @@ LocalSearchOperator* RoutingModel::GetNeighborhoodOperators( std::vector operators = extra_operators_; if (!pickup_delivery_pairs_.empty()) { CP_ROUTING_PUSH_OPERATOR(RELOCATE_PAIR, relocate_pair, operators); + // Only add the light version of relocate pair if the normal version has not + // already been added as it covers a subset of its neighborhood. + if (search_parameters.local_search_operators().use_relocate_pair() == + BOOL_FALSE) { + CP_ROUTING_PUSH_OPERATOR(LIGHT_RELOCATE_PAIR, light_relocate_pair, + operators); + } + CP_ROUTING_PUSH_OPERATOR(EXCHANGE_PAIR, exchange_pair, operators); CP_ROUTING_PUSH_OPERATOR(NODE_PAIR_SWAP, node_pair_swap_active, operators); } if (vehicles_ > 1) { - CP_ROUTING_PUSH_OPERATOR(RELOCATE, relocate, operators); + if (GetNumOfSingletonNodes() > 0) { + // If there are only pairs in the model the only case where Relocate will + // work is for intra-route moves, already covered by OrOpt. + // We are not disabling Exchange and Cross because there are no + // intra-route equivalents. + CP_ROUTING_PUSH_OPERATOR(RELOCATE, relocate, operators); + } CP_ROUTING_PUSH_OPERATOR(EXCHANGE, exchange, operators); CP_ROUTING_PUSH_OPERATOR(CROSS, cross, operators); } if (!pickup_delivery_pairs_.empty() || - search_parameters.local_search_operators().use_relocate_neighbors()) { + search_parameters.local_search_operators().use_relocate_neighbors() == + BOOL_TRUE) { operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]); } const LocalSearchMetaheuristic::Value local_search_metaheuristic = search_parameters.local_search_metaheuristic(); if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH && local_search_metaheuristic != - LocalSearchMetaheuristic::OBJECTIVE_TABU_SEARCH && + LocalSearchMetaheuristic::GENERIC_TABU_SEARCH && local_search_metaheuristic != LocalSearchMetaheuristic::SIMULATED_ANNEALING) { CP_ROUTING_PUSH_OPERATOR(LIN_KERNIGHAN, lin_kernighan, operators); } CP_ROUTING_PUSH_OPERATOR(TWO_OPT, two_opt, operators); CP_ROUTING_PUSH_OPERATOR(OR_OPT, or_opt, operators); + CP_ROUTING_PUSH_OPERATOR(RELOCATE_EXPENSIVE_CHAIN, relocate_expensive_chain, + operators); if (!disjunctions_.empty()) { CP_ROUTING_PUSH_OPERATOR(MAKE_INACTIVE, make_inactive, operators); CP_ROUTING_PUSH_OPERATOR(MAKE_CHAIN_INACTIVE, make_chain_inactive, @@ -3928,14 +3951,14 @@ LocalSearchOperator* RoutingModel::GetNeighborhoodOperators( // loop. if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH && local_search_metaheuristic != - LocalSearchMetaheuristic::OBJECTIVE_TABU_SEARCH && + LocalSearchMetaheuristic::GENERIC_TABU_SEARCH && local_search_metaheuristic != LocalSearchMetaheuristic::SIMULATED_ANNEALING) { CP_ROUTING_PUSH_OPERATOR(TSP_OPT, tsp_opt, operators); } if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH && local_search_metaheuristic != - LocalSearchMetaheuristic::OBJECTIVE_TABU_SEARCH && + LocalSearchMetaheuristic::GENERIC_TABU_SEARCH && local_search_metaheuristic != LocalSearchMetaheuristic::SIMULATED_ANNEALING) { CP_ROUTING_PUSH_OPERATOR(TSP_LNS, tsp_lns, operators); @@ -3957,18 +3980,27 @@ RoutingModel::GetOrCreateLocalSearchFilters() { // function: // - NodeDisjunctionFilter: takes disjunction penalty costs into account, // - PathCumulFilter: takes dimension span costs into account, - // - LocalSearchObjectiveFilter: takes dimension "arc" costs into account. + // - ObjectiveFilter: + // - VehicleAmortizedCostFilter, which considers the part of the cost + // related to amortized linear and quadratic vehicle cost factors. + // - LocalSearchObjectiveFilter, which takes dimension "arc" costs into + // account. // To be able to filter cost values properly, a filter needs to be aware of // cost bounds computed by other filters before it (for the same delta). // Communication of cost between filters is done through callbacks, - // LocalSearchObjectiveFilter sending total arc costs to - // NodeDisjunctionFilter, itself sending this cost + total penalty cost to - // PathCumulFilters (if you have several of these, they send updated costs to - // each other too). Callbacks are called on OnSynchronize to send the cost - // of the current solution and on Accept to send the cost of solution deltas. + // VehicleAmortizedCostFilter sending the total "vehicle cost" to + // LocalSearchObjectiveFilter, itself sending this vehicle cost + total arc + // costs to NodeDisjunctionFilter, in turn sending this cost + total penalty + // cost to PathCumulFilters (if you have several of these, they send updated + // costs to each other too). + // Note that since the VehicleAmortizedCostFilter is the only filter with a + // possible negative contribution to the objective, it has to be the first + // filter called so it propagates this value to all other filters. + // Callbacks are called on OnSynchronize to send the cost of the current + // solution and on Accept to send the cost of solution deltas. if (filters_.empty()) { - std::vector path_cumul_filters; - RoutingLocalSearchFilter* path_cumul_filter = nullptr; + std::vector path_cumul_filters; + IntVarLocalSearchFilter* path_cumul_filter = nullptr; for (const RoutingDimension* dimension : dimensions_) { Solver::ObjectiveWatcher objective_callback = nullptr; if (path_cumul_filter != nullptr) { @@ -3983,7 +4015,7 @@ RoutingModel::GetOrCreateLocalSearchFilters() { // Due to the way cost injection is setup, path filters have to be // called in reverse order. std::reverse(path_cumul_filters.begin(), path_cumul_filters.end()); - RoutingLocalSearchFilter* node_disjunction_filter = nullptr; + IntVarLocalSearchFilter* node_disjunction_filter = nullptr; if (!disjunctions_.empty()) { Solver::ObjectiveWatcher objective_callback = nullptr; if (path_cumul_filter != nullptr) { @@ -4004,33 +4036,55 @@ RoutingModel::GetOrCreateLocalSearchFilters() { return path_cumul_filter->InjectObjectiveValue(value); }; } + + IntVarLocalSearchFilter* local_search_objective_filter = nullptr; if (CostsAreHomogeneousAcrossVehicles()) { - LocalSearchFilter* filter = solver_->MakeLocalSearchObjectiveFilter( + local_search_objective_filter = solver_->MakeSumObjectiveFilter( nexts_, [this](int64 i, int64 j) { return GetHomogeneousCost(i, j); }, - objective_callback, cost_, Solver::LE, Solver::SUM); - filters_.push_back(filter); + objective_callback, cost_, Solver::LE); } else { - LocalSearchFilter* filter = solver_->MakeLocalSearchObjectiveFilter( + local_search_objective_filter = solver_->MakeSumObjectiveFilter( nexts_, vehicle_vars_, [this](int64 i, int64 j, int64 k) { return GetArcCostForVehicle(i, j, k); }, - objective_callback, cost_, Solver::LE, Solver::SUM); - filters_.push_back(filter); + objective_callback, cost_, Solver::LE); + } + + if (vehicle_amortized_cost_factors_set_) { + objective_callback = [local_search_objective_filter](int64 value) { + return local_search_objective_filter->InjectObjectiveValue(value); + }; + filters_.push_back( + MakeVehicleAmortizedCostFilter(*this, objective_callback)); } + + // Must be added after VehicleAmortizedCostFilter. + filters_.push_back(local_search_objective_filter); + filters_.push_back(solver_->MakeVariableDomainFilter()); if (node_disjunction_filter != nullptr) { // Must be added after ObjectiveFilter. filters_.push_back(node_disjunction_filter); } if (!pickup_delivery_pairs_.empty()) { - filters_.push_back( - MakeNodePrecedenceFilter(*this, pickup_delivery_pairs_)); + filters_.push_back(MakePickupDeliveryFilter( + *this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_)); + } + if (!incompatible_types_per_type_index_.empty()) { + filters_.push_back(MakeTypeIncompatibilityFilter(*this)); } filters_.push_back(MakeVehicleVarFilter(*this)); // Must be added after NodeDisjunctionFilter and ObjectiveFilter. filters_.insert(filters_.end(), path_cumul_filters.begin(), path_cumul_filters.end()); + for (const RoutingDimension* dimension : dimensions_) { + if (!dimension->vehicle_break_intervals_.empty()) { + IntVarLocalSearchFilter* breaks_filter = + MakeVehicleBreaksFilter(*this, *dimension); + filters_.push_back(breaks_filter); + } + } filters_.insert(filters_.end(), extra_filters_.begin(), extra_filters_.end()); } @@ -4048,20 +4102,82 @@ RoutingModel::GetOrCreateFeasibilityFilters() { } feasibility_filters_.push_back(solver_->MakeVariableDomainFilter()); if (!pickup_delivery_pairs_.empty()) { - feasibility_filters_.push_back( - MakeNodePrecedenceFilter(*this, pickup_delivery_pairs_)); + feasibility_filters_.push_back(MakePickupDeliveryFilter( + *this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_)); + } + if (!incompatible_types_per_type_index_.empty()) { + feasibility_filters_.push_back(MakeTypeIncompatibilityFilter(*this)); } feasibility_filters_.push_back(MakeVehicleVarFilter(*this)); + for (const RoutingDimension* dimension : dimensions_) { + if (!dimension->vehicle_break_intervals_.empty()) { + IntVarLocalSearchFilter* breaks_filter = + MakeVehicleBreaksFilter(*this, *dimension); + feasibility_filters_.push_back(breaks_filter); + } + } feasibility_filters_.insert(feasibility_filters_.end(), extra_filters_.begin(), extra_filters_.end()); } return feasibility_filters_; } +std::vector RoutingModel::GetDimensionsWithSoftAndSpanCosts() + const { + std::vector dimensions; + for (RoutingDimension* dimension : dimensions_) { + bool has_span_cost = false; + for (int vehicle = 0; vehicle < vehicles(); ++vehicle) { + if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) { + has_span_cost = true; + break; + } + } + if (has_span_cost) { + for (int i = 0; i < dimension->cumuls().size(); ++i) { + if (dimension->HasCumulVarSoftUpperBound(i) || + dimension->HasCumulVarSoftLowerBound(i)) { + dimensions.push_back(dimension); + break; + } + } + } + } + return dimensions; +} + +std::vector RoutingModel::GetDimensionsWithSoftOrSpanCosts() + const { + std::vector dimensions; + for (RoutingDimension* dimension : dimensions_) { + bool has_soft_or_span_cost = false; + for (int vehicle = 0; vehicle < vehicles(); ++vehicle) { + if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) { + has_soft_or_span_cost = true; + break; + } + } + if (!has_soft_or_span_cost) { + for (int i = 0; i < dimension->cumuls().size(); ++i) { + if (dimension->HasCumulVarSoftUpperBound(i) || + dimension->HasCumulVarSoftLowerBound(i)) { + has_soft_or_span_cost = true; + break; + } + } + } + if (has_soft_or_span_cost) dimensions.push_back(dimension); + } + return dimensions; +} + DecisionBuilder* RoutingModel::CreateSolutionFinalizer() { std::vector decision_builders; decision_builders.push_back(solver_->MakePhase( nexts_, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE)); + decision_builders.push_back(solver_->RevAlloc(new SetCumulsFromDimensionCosts( + GetDimensionsWithSoftAndSpanCosts(), + GetOrCreateLargeNeighborhoodSearchLimit()))); for (IntVar* const variable : variables_minimized_by_finalizer_) { decision_builders.push_back(solver_->MakePhase( variable, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE)); @@ -4098,23 +4214,14 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( // Path-based cheapest addition heuristic. first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] = solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, eval); - if (vehicles() == 1 && pickup_delivery_pairs_.empty()) { - DecisionBuilder* fast_one_path_builder = - solver_->RevAlloc(new FastOnePathBuilder( - this, NewPermanentCallback( - this, &RoutingModel::GetArcCostForFirstSolution))); - first_solution_decision_builders_ - [FirstSolutionStrategy::PATH_CHEAPEST_ARC] = - solver_->Try(fast_one_path_builder, - first_solution_decision_builders_ - [FirstSolutionStrategy::PATH_CHEAPEST_ARC]); - } else if (search_parameters.use_filtered_first_solution_strategy()) { + if (!search_parameters.use_unfiltered_first_solution_strategy()) { first_solution_filtered_decision_builders_ [FirstSolutionStrategy::PATH_CHEAPEST_ARC] = solver_->RevAlloc( new EvaluatorCheapestAdditionFilteredDecisionBuilder( this, - NewPermanentCallback(this, - &RoutingModel::GetArcCostForFirstSolution), + [this](int64 i, int64 j) { + return GetArcCostForFirstSolution(i, j); + }, GetOrCreateFeasibilityFilters())); first_solution_decision_builders_ [FirstSolutionStrategy::PATH_CHEAPEST_ARC] = @@ -4131,7 +4238,7 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( first_solution_decision_builders_ [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, comp); - if (search_parameters.use_filtered_first_solution_strategy()) { + if (!search_parameters.use_unfiltered_first_solution_strategy()) { first_solution_filtered_decision_builders_ [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->RevAlloc( new ComparatorCheapestAdditionFilteredDecisionBuilder( @@ -4157,15 +4264,13 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( solver_->RevAlloc(new AllUnperformed(this)); // Best insertion heuristic. SearchLimit* const ls_limit = solver_->MakeLimit( - search_parameters.time_limit_ms(), kint64max, kint64max, kint64max, true); + GetTimeLimitMs(search_parameters), kint64max, kint64max, kint64max, true); DecisionBuilder* const finalize = solver_->MakeSolveOnce( finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit()); LocalSearchPhaseParameters* const insertion_parameters = solver_->MakeLocalSearchPhaseParameters(CreateInsertionOperator(), finalize, ls_limit, GetOrCreateLocalSearchFilters()); - std::vector monitors; - monitors.push_back(GetOrCreateLimit()); std::vector decision_vars = nexts_; if (!CostsAreHomogeneousAcrossVehicles()) { decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(), @@ -4176,8 +4281,8 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( solver_->MakeLocalSearchPhase( decision_vars, solver_->RevAlloc(new AllUnperformed(this)), insertion_parameters), - GetOrCreateAssignment(), false, search_parameters.optimization_step(), - monitors); + GetOrCreateAssignment(), false, + search_parameters.optimization_step()); first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] = solver_->Compose(first_solution_decision_builders_ [FirstSolutionStrategy::BEST_INSERTION], @@ -4187,22 +4292,47 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( [FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION] = solver_->RevAlloc(new GlobalCheapestInsertionFilteredDecisionBuilder( this, - NewPermanentCallback(this, &RoutingModel::GetArcCostForVehicle), - NewPermanentCallback(this, - &RoutingModel::UnperformedPenaltyOrValue, 0), - GetOrCreateFeasibilityFilters())); + [this](int64 i, int64 j, int64 vehicle) { + return GetArcCostForVehicle(i, j, vehicle); + }, + [this](int64 i) { return UnperformedPenaltyOrValue(0, i); }, + GetOrCreateFeasibilityFilters(), /* is_sequential */ false, + search_parameters.cheapest_insertion_farthest_seeds_ratio(), + search_parameters.cheapest_insertion_neighbors_ratio())); first_solution_decision_builders_ [FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION] = solver_->Try(first_solution_filtered_decision_builders_ [FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION], first_solution_decision_builders_ [FirstSolutionStrategy::BEST_INSERTION]); + + // Sequential global cheapest insertion + first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION] = + solver_->RevAlloc(new GlobalCheapestInsertionFilteredDecisionBuilder( + this, + [this](int64 i, int64 j, int64 vehicle) { + return GetArcCostForVehicle(i, j, vehicle); + }, + [this](int64 i) { return UnperformedPenaltyOrValue(0, i); }, + GetOrCreateFeasibilityFilters(), /* is_sequential */ true, + search_parameters.cheapest_insertion_farthest_seeds_ratio(), + search_parameters.cheapest_insertion_neighbors_ratio())); + first_solution_decision_builders_ + [FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION] = solver_->Try( + first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION], + first_solution_decision_builders_ + [FirstSolutionStrategy::BEST_INSERTION]); + // Local cheapest insertion first_solution_filtered_decision_builders_ [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->RevAlloc(new LocalCheapestInsertionFilteredDecisionBuilder( this, - NewPermanentCallback(this, &RoutingModel::GetArcCostForVehicle), + [this](int64 i, int64 j, int64 vehicle) { + return GetArcCostForVehicle(i, j, vehicle); + }, GetOrCreateFeasibilityFilters())); first_solution_decision_builders_ [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = @@ -4213,27 +4343,49 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( // Savings const double savings_neighbors_ratio = search_parameters.savings_neighbors_ratio(); - if (search_parameters.use_filtered_first_solution_strategy()) { - first_solution_filtered_decision_builders_[FirstSolutionStrategy::SAVINGS] = - solver_->RevAlloc(new SavingsFilteredDecisionBuilder( - this, savings_neighbors_ratio, + std::vector filters; + if (!search_parameters.use_unfiltered_first_solution_strategy()) { + filters = GetOrCreateFeasibilityFilters(); + } + + if (search_parameters.savings_parallel_routes()) { + IntVarFilteredDecisionBuilder* savings_db = + solver_->RevAlloc(new ParallelSavingsFilteredDecisionBuilder( + this, &manager_, savings_neighbors_ratio, search_parameters.savings_add_reverse_arcs(), - GetOrCreateFeasibilityFilters())); + search_parameters.savings_arc_coefficient(), filters)); + if (!search_parameters.use_unfiltered_first_solution_strategy()) { + first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::SAVINGS] = savings_db; + } + + filters.push_back(MakeCPFeasibilityFilter(this)); first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] = - solver_->Try(first_solution_filtered_decision_builders_ - [FirstSolutionStrategy::SAVINGS], - solver_->RevAlloc(new SavingsBuilder( - this, savings_neighbors_ratio, true))); + solver_->Try( + savings_db, + solver_->RevAlloc(new ParallelSavingsFilteredDecisionBuilder( + this, &manager_, savings_neighbors_ratio, + search_parameters.savings_add_reverse_arcs(), + search_parameters.savings_arc_coefficient(), filters))); } else { - first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] = - solver_->RevAlloc( - new SavingsBuilder(this, savings_neighbors_ratio, true)); - DecisionBuilder* savings_builder = solver_->RevAlloc( - new SavingsBuilder(this, savings_neighbors_ratio, false)); + IntVarFilteredDecisionBuilder* savings_db = + solver_->RevAlloc(new SequentialSavingsFilteredDecisionBuilder( + this, &manager_, savings_neighbors_ratio, + search_parameters.savings_add_reverse_arcs(), + search_parameters.savings_arc_coefficient(), filters)); + if (!search_parameters.use_unfiltered_first_solution_strategy()) { + first_solution_filtered_decision_builders_ + [FirstSolutionStrategy::SAVINGS] = savings_db; + } + + filters.push_back(MakeCPFeasibilityFilter(this)); first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] = solver_->Try( - savings_builder, - first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS]); + savings_db, + solver_->RevAlloc(new SequentialSavingsFilteredDecisionBuilder( + this, &manager_, savings_neighbors_ratio, + search_parameters.savings_add_reverse_arcs(), + search_parameters.savings_arc_coefficient(), filters))); } // Sweep first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] = @@ -4259,6 +4411,8 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( first_solution_decision_builders_ [FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION]; } + first_solution_decision_builders_[FirstSolutionStrategy::UNSET] = + first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC]; } DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder( @@ -4286,7 +4440,9 @@ LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters( GetNeighborhoodOperators(search_parameters), solver_->MakeSolveOnce(CreateSolutionFinalizer(), GetOrCreateLargeNeighborhoodSearchLimit()), - GetOrCreateLocalSearchLimit(), GetOrCreateLocalSearchFilters()); + GetOrCreateLocalSearchLimit(), + {solver_->RevAlloc(new LocalSearchFilterManager( + GetOrCreateLocalSearchFilters(), CostVar()))}); } DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder( @@ -4314,7 +4470,10 @@ DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder( void RoutingModel::SetupDecisionBuilders( const RoutingSearchParameters& search_parameters) { if (search_parameters.use_depth_first_search()) { - solve_db_ = GetFirstSolutionDecisionBuilder(search_parameters); + solve_db_ = solver_->Compose( + GetFirstSolutionDecisionBuilder(search_parameters), + solver_->MakeSolveOnce(CreateSolutionFinalizer(), + GetOrCreateLargeNeighborhoodSearchLimit())); } else { solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters); } @@ -4330,6 +4489,10 @@ void RoutingModel::SetupDecisionBuilders( restore_assignment_ = solver_->Compose(solver_->MakeRestoreAssignment(GetOrCreateAssignment()), CreateSolutionFinalizer()); + restore_tmp_assignment_ = solver_->Compose( + restore_preassignment, + solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()), + CreateSolutionFinalizer()); } void RoutingModel::SetupMetaheuristics( @@ -4337,6 +4500,10 @@ void RoutingModel::SetupMetaheuristics( SearchMonitor* optimize; const LocalSearchMetaheuristic::Value metaheuristic = search_parameters.local_search_metaheuristic(); + // Some metaheuristics will effectively never terminate; warn + // user if they fail to set a time limit. + bool limit_too_long = !search_parameters.has_time_limit() && + search_parameters.solution_limit() == kint64max; switch (metaheuristic) { case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH: if (CostsAreHomogeneousAcrossVehicles()) { @@ -4364,14 +4531,35 @@ void RoutingModel::SetupMetaheuristics( search_parameters.optimization_step(), nexts_, 10, 10, .8); break; + case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: { + std::vector tabu_vars; + if (tabu_var_callback_) { + tabu_vars = tabu_var_callback_(this); + } else { + tabu_vars.push_back(cost_); + } + optimize = solver_->MakeGenericTabuSearch( + false, cost_, search_parameters.optimization_step(), tabu_vars, 100); + break; + } default: + limit_too_long = false; optimize = solver_->MakeMinimize(cost_, search_parameters.optimization_step()); } + if (limit_too_long) { + LOG(WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic) + << " specified without sane timeout: solve may run forever."; + } monitors_.push_back(optimize); } -void RoutingModel::SetupAssignmentCollector() { +void RoutingModel::SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback) { + tabu_var_callback_ = std::move(tabu_var_callback); +} + +void RoutingModel::SetupAssignmentCollector( + const RoutingSearchParameters& search_parameters) { Assignment* full_assignment = solver_->MakeAssignment(); for (const RoutingDimension* const dimension : dimensions_) { full_assignment->Add(dimension->cumuls()); @@ -4387,8 +4575,11 @@ void RoutingModel::SetupAssignmentCollector() { full_assignment->Add(vehicle_vars_); full_assignment->AddObjective(cost_); - collect_assignments_ = - solver_->MakeBestValueSolutionCollector(full_assignment, false); + collect_assignments_ = solver_->MakeNBestValueSolutionCollector( + full_assignment, search_parameters.number_of_solutions_to_collect(), + false); + collect_one_assignment_ = + solver_->MakeFirstSolutionCollector(full_assignment); monitors_.push_back(collect_assignments_); } @@ -4404,13 +4595,13 @@ void RoutingModel::SetupSearchMonitors( const RoutingSearchParameters& search_parameters) { monitors_.push_back(GetOrCreateLimit()); SetupMetaheuristics(search_parameters); - SetupAssignmentCollector(); + SetupAssignmentCollector(search_parameters); SetupTrace(search_parameters); } bool RoutingModel::UsesLightPropagation( const RoutingSearchParameters& search_parameters) const { - return search_parameters.use_light_propagation() && + return !search_parameters.use_full_propagation() && !search_parameters.use_depth_first_search() && search_parameters.first_solution_strategy() != FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE; @@ -4440,190 +4631,9 @@ void RoutingModel::AddIntervalToAssignment(IntervalVar* const interval) { extra_intervals_.push_back(interval); } -RoutingModel::NodeEvaluator2* RoutingModel::NewCachedCallback( - NodeEvaluator2* callback) { - const int size = node_to_index_.size(); - if (cache_callbacks_) { - NodeEvaluator2* cached_evaluator = nullptr; - if (!gtl::FindCopy(cached_node_callbacks_, callback, &cached_evaluator)) { - cached_evaluator = new RoutingCache(callback, size); - cached_node_callbacks_[callback] = cached_evaluator; - // Make sure that both the cache and the base callback get deleted - // properly. - owned_node_callbacks_.insert(callback); - owned_node_callbacks_.insert(cached_evaluator); - } - return cached_evaluator; - } else { - owned_node_callbacks_.insert(callback); - return callback; - } -} - -// NewCachedStateDependentCallback returns a new evaluator creating at most one -// RangeIntToIntFunction per pair of nodes. The evaluator manages the cached -// functions, while the routing model takes owenership of both the old and the -// new callbacks. -RoutingModel::VariableNodeEvaluator2* -RoutingModel::NewCachedStateDependentCallback( - VariableNodeEvaluator2* callback) { - const int size = node_to_index_.size(); - VariableNodeEvaluator2* cached_evaluator = nullptr; - if (!gtl::FindCopy(cached_state_dependent_callbacks_, callback, - &cached_evaluator)) { - cached_evaluator = new StateDependentRoutingCache(callback, size); - cached_state_dependent_callbacks_[callback] = cached_evaluator; - owned_state_dependent_callbacks_.insert(callback); - owned_state_dependent_callbacks_.insert(cached_evaluator); - } - return cached_evaluator; -} - -// BEGIN(DEPRECATED) -// Deprecated RoutingModel methods. See the .h. -// DON'T REMOVE RASHLY! These methods might still be used by old open-source -// users, even if they are unused at Google. -void RoutingModel::SetCost(NodeEvaluator2* e) { - SetArcCostEvaluatorOfAllVehicles(e); -} -void RoutingModel::SetVehicleCost(int v, NodeEvaluator2* e) { - return SetArcCostEvaluatorOfVehicle(e, v); -} -int64 RoutingModel::GetRouteFixedCost() const { - return GetFixedCostOfVehicle(0); -} -void RoutingModel::SetRouteFixedCost(int64 c) { SetFixedCostOfAllVehicles(c); } -int64 RoutingModel::GetVehicleFixedCost(int v) const { - return GetFixedCostOfVehicle(v); -} -void RoutingModel::SetVehicleFixedCost(int v, int64 c) { - SetFixedCostOfVehicle(c, v); -} -bool RoutingModel::homogeneous_costs() const { - return CostsAreHomogeneousAcrossVehicles(); -} -int RoutingModel::GetVehicleCostCount() const { - return GetNonZeroCostClassesCount(); -} -int64 RoutingModel::GetCost(int64 i, int64 j, int64 v) { - return GetArcCostForVehicle(i, j, v); -} -int64 RoutingModel::GetVehicleClassCost(int64 i, int64 j, int64 c) { - return GetArcCostForClass(i, j, c); -} -void RoutingModel::SetDimensionTransitCost(const std::string& name, - int64 coeff) { - GetMutableDimension(name)->SetSpanCostCoefficientForAllVehicles(coeff); -} -int64 RoutingModel::GetDimensionTransitCost(const std::string& name) const { - return HasDimension(name) - ? GetDimensionOrDie(name).vehicle_span_cost_coefficients()[0] - : 0; -} -void RoutingModel::SetDimensionSpanCost(const std::string& name, int64 coeff) { - GetMutableDimension(name)->SetGlobalSpanCostCoefficient(coeff); -} -int64 RoutingModel::GetDimensionSpanCost(const std::string& name) const { - return HasDimension(name) - ? GetDimensionOrDie(name).global_span_cost_coefficient() - : 0; -} -int64 RoutingModel::GetTransitValue(const std::string& dimension_name, - int64 from_index, int64 to_index, - int64 vehicle) const { - DimensionIndex dimension_index(-1); - if (gtl::FindCopy(dimension_name_to_index_, dimension_name, - &dimension_index)) { - return dimensions_[dimension_index]->GetTransitValue(from_index, to_index, - vehicle); - } else { - return 0; - } -} -void RoutingModel::SetCumulVarSoftUpperBound(NodeIndex node, - const std::string& name, int64 ub, - int64 coeff) { - GetMutableDimension(name)->SetCumulVarSoftUpperBound(node, ub, coeff); -} -bool RoutingModel::HasCumulVarSoftUpperBound(NodeIndex node, - const std::string& name) const { - return HasDimension(name) && - GetDimensionOrDie(name).HasCumulVarSoftUpperBound(node); -} -int64 RoutingModel::GetCumulVarSoftUpperBound(NodeIndex node, - const std::string& name) const { - return HasDimension(name) - ? GetDimensionOrDie(name).GetCumulVarSoftUpperBound(node) - : kint64max; -} -int64 RoutingModel::GetCumulVarSoftUpperBoundCoefficient( - NodeIndex node, const std::string& name) const { - return HasDimension(name) - ? GetDimensionOrDie(name).GetCumulVarSoftUpperBoundCoefficient( - node) - : 0; -} -void RoutingModel::SetStartCumulVarSoftUpperBound(int vehicle, - const std::string& name, - int64 ub, int64 coeff) { - GetMutableDimension(name)->SetStartCumulVarSoftUpperBound(vehicle, ub, coeff); -} -bool RoutingModel::HasStartCumulVarSoftUpperBound( - int vehicle, const std::string& name) const { - return HasDimension(name) && - GetDimensionOrDie(name).HasStartCumulVarSoftUpperBound(vehicle); -} -int64 RoutingModel::GetStartCumulVarSoftUpperBound( - int vehicle, const std::string& name) const { - return HasDimension(name) - ? GetDimensionOrDie(name).GetStartCumulVarSoftUpperBound(vehicle) - : kint64max; -} -int64 RoutingModel::GetStartCumulVarSoftUpperBoundCoefficient( - int vehicle, const std::string& name) const { - return HasDimension(name) - ? GetDimensionOrDie(name) - .GetStartCumulVarSoftUpperBoundCoefficient(vehicle) - : 0; -} -void RoutingModel::SetEndCumulVarSoftUpperBound(int vehicle, - const std::string& name, - int64 ub, int64 coeff) { - GetMutableDimension(name)->SetEndCumulVarSoftUpperBound(vehicle, ub, coeff); -} -bool RoutingModel::HasEndCumulVarSoftUpperBound(int vehicle, - const std::string& name) const { - return HasDimension(name) && - GetDimensionOrDie(name).HasEndCumulVarSoftUpperBound(vehicle); -} -int64 RoutingModel::GetEndCumulVarSoftUpperBound( - int vehicle, const std::string& name) const { - return HasDimension(name) - ? GetDimensionOrDie(name).GetEndCumulVarSoftUpperBound(vehicle) - : kint64max; -} -int64 RoutingModel::GetEndCumulVarSoftUpperBoundCoefficient( - int vehicle, const std::string& name) const { - return HasDimension(name) - ? GetDimensionOrDie(name).GetEndCumulVarSoftUpperBoundCoefficient( - vehicle) - : 0; -} -IntVar* RoutingModel::CumulVar(int64 index, const std::string& name) const { - return HasDimension(name) ? GetDimensionOrDie(name).CumulVar(index) : nullptr; -} -IntVar* RoutingModel::TransitVar(int64 index, const std::string& name) const { - return HasDimension(name) ? GetDimensionOrDie(name).TransitVar(index) - : nullptr; -} -IntVar* RoutingModel::SlackVar(int64 index, const std::string& name) const { - return HasDimension(name) ? GetDimensionOrDie(name).SlackVar(index) : nullptr; -} - -// END(DEPRECATED) - const char RoutingModelVisitor::kLightElement[] = "LightElement"; const char RoutingModelVisitor::kLightElement2[] = "LightElement2"; +const char RoutingModelVisitor::kRemoveValues[] = "RemoveValues"; RoutingDimension::RoutingDimension(RoutingModel* model, std::vector vehicle_capacities, @@ -4645,20 +4655,15 @@ RoutingDimension::RoutingDimension(RoutingModel* model, : RoutingDimension(model, std::move(vehicle_capacities), name, this) {} RoutingDimension::~RoutingDimension() { - for (auto& cumul_var_piecewise_linear_cost : - cumul_var_piecewise_linear_cost_) { - delete cumul_var_piecewise_linear_cost.cost; - } cumul_var_piecewise_linear_cost_.clear(); } void RoutingDimension::Initialize( - const std::vector& transit_evaluators, - const std::vector& - state_dependent_node_evaluators, + const std::vector& transit_evaluators, + const std::vector& state_dependent_transit_evaluators, int64 slack_max) { InitializeCumuls(); - InitializeTransits(transit_evaluators, state_dependent_node_evaluators, + InitializeTransits(transit_evaluators, state_dependent_transit_evaluators, slack_max); } @@ -4763,87 +4768,45 @@ void RoutingDimension::InitializeCumuls() { } namespace { -template -auto WrappedEvaluator(RoutingModel* model, NodeEvaluator* evaluator, int64 from, - int64 to) - -> decltype(evaluator->Run(model->IndexToNode(to), - model->IndexToNode(to))) { - CHECK(evaluator != nullptr); - return evaluator->Run(model->IndexToNode(from), model->IndexToNode(to)); -} - template int64 IthElementOrValue(const std::vector& v, int64 index) { return index >= 0 ? v[index] : value; } -template -void ComputeTransitClasses(const std::vector& node_evaluators, - RoutingModel* const model, - std::vector* class_evaluators, +void ComputeTransitClasses(const std::vector& evaluator_indices, + std::vector* class_evaluators, std::vector* vehicle_to_class) { - CHECK(model != nullptr); CHECK(class_evaluators != nullptr); CHECK(vehicle_to_class != nullptr); class_evaluators->clear(); - vehicle_to_class->resize(node_evaluators.size(), -1); - std::unordered_map evaluator_to_class; - for (int i = 0; i < node_evaluators.size(); ++i) { - NodeEvaluator* const evaluator = node_evaluators[i]; + vehicle_to_class->resize(evaluator_indices.size(), -1); + absl::flat_hash_map evaluator_to_class; + for (int i = 0; i < evaluator_indices.size(); ++i) { + const int evaluator_index = evaluator_indices[i]; int evaluator_class = -1; - if (!gtl::FindCopy(evaluator_to_class, evaluator, &evaluator_class)) { + if (!gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) { evaluator_class = class_evaluators->size(); - evaluator_to_class[evaluator] = evaluator_class; - class_evaluators->emplace_back([model, evaluator](int64 from, int64 to) { - DCHECK(evaluator != nullptr); - return evaluator->Run(model->IndexToNode(from), model->IndexToNode(to)); - }); + evaluator_to_class[evaluator_index] = evaluator_class; + class_evaluators->push_back(evaluator_index); } (*vehicle_to_class)[i] = evaluator_class; } } } // namespace -void RoutingDimension::InitializeTransits( - const std::vector& node_evaluators, - const std::vector& - state_dependent_node_evaluators, - int64 slack_max) { - CHECK_EQ(model_->vehicles(), node_evaluators.size()); +void RoutingDimension::InitializeTransitVariables(int64 slack_max) { + CHECK(!class_evaluators_.empty()); CHECK(base_dimension_ == nullptr || - model_->vehicles() == state_dependent_node_evaluators.size()); - for (int64 index = 0; index < model_->vehicles(); ++index) { - CHECK(node_evaluators[index] != nullptr); - CHECK(node_evaluators[index]->IsRepeatable()); - CHECK(base_dimension_ == nullptr || - state_dependent_node_evaluators[index] != nullptr); - CHECK(base_dimension_ == nullptr || - state_dependent_node_evaluators[index]->IsRepeatable()); - } + !state_dependent_class_evaluators_.empty()); + Solver* const solver = model_->solver(); const int size = model_->Size(); - transits_.resize(size, nullptr); - fixed_transits_.resize(size, nullptr); - slacks_.resize(size, nullptr); - dependent_transits_.resize(size, nullptr); - ComputeTransitClasses(node_evaluators, model_, &class_evaluators_, - &vehicle_to_class_); - if (base_dimension_ != nullptr) { - ComputeTransitClasses(state_dependent_node_evaluators, model_, - &state_dependent_class_evaluators_, - &state_dependent_vehicle_to_class_); - } - const Solver::IndexEvaluator1 dependent_vehicle_class_function = [this](int index) { return (0 <= index && index < state_dependent_vehicle_to_class_.size()) ? state_dependent_vehicle_to_class_[index] : state_dependent_class_evaluators_.size(); }; - - CHECK(!class_evaluators_.empty()); - CHECK(base_dimension_ == nullptr || - !state_dependent_class_evaluators_.empty()); const std::string slack_name = name_ + " slack"; const std::string transit_name = name_ + " fixed transit"; @@ -4856,7 +4819,10 @@ void RoutingDimension::InitializeTransits( for (int64 j = 0; j < cumuls_.size(); ++j) { transition_variables[j] = MakeRangeMakeElementExpr( - state_dependent_class_evaluators_[0](i, j).transit, + model_ + ->StateDependentTransitCallback( + state_dependent_class_evaluators_[0])(i, j) + .transit, base_dimension_->CumulVar(i), solver) ->Var(); } @@ -4872,12 +4838,14 @@ void RoutingDimension::InitializeTransits( std::vector transit_for_vehicle; transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() + 1); - for (const auto& evaluator : state_dependent_class_evaluators_) { + for (int evaluator : state_dependent_class_evaluators_) { std::vector transition_variables(cumuls_.size(), nullptr); for (int64 j = 0; j < cumuls_.size(); ++j) { transition_variables[j] = - MakeRangeMakeElementExpr(evaluator(i, j).transit, - base_dimension_->CumulVar(i), solver) + MakeRangeMakeElementExpr( + model_->StateDependentTransitCallback(evaluator)(i, j) + .transit, + base_dimension_->CumulVar(i), solver) ->Var(); } transit_for_vehicle.push_back( @@ -4909,113 +4877,446 @@ void RoutingDimension::InitializeTransits( } } -namespace { - -// Break constraint ensures break intervals fit on the route of a vehicle. -// It posts a disjunction constraint on break intervals + intervals -// corresponding to route nodes. For each node, |break_intervals|+1 intervals -// are created representing the fixed transit after the node; the transit -// can therefore be interrupted at most |break_intervals|+1 times. The -// constraint ensures that the sum of the duration of the "node" intervals -// is at least the value of the fixed transit after the node. -class BreakConstraint : public Constraint { - public: - BreakConstraint(const RoutingDimension* dimension, int vehicle, - std::vector break_intervals) - : Constraint(dimension->model()->solver()), - dimension_(dimension), - vehicle_(vehicle), - break_intervals_(std::move(break_intervals)), - status_(solver()->MakeBoolVar(absl::StrCat("status", vehicle))) {} - void Post() override { - RoutingModel* const model = dimension_->model(); - solver()->AddConstraint( - solver()->MakePathConnected(model->Nexts(), {model->Start(vehicle_)}, - {model->End(vehicle_)}, {status_})); - status_->WhenBound(MakeDelayedConstraintDemon0( - solver(), this, &BreakConstraint::PathClosed, "PathClosed")); - } - void InitialPropagate() override { - if (status_->Bound()) { - PathClosed(); - } +void RoutingDimension::InitializeTransits( + const std::vector& transit_evaluators, + const std::vector& state_dependent_transit_evaluators, + int64 slack_max) { + CHECK_EQ(model_->vehicles(), transit_evaluators.size()); + CHECK(base_dimension_ == nullptr || + model_->vehicles() == state_dependent_transit_evaluators.size()); + const int size = model_->Size(); + transits_.resize(size, nullptr); + fixed_transits_.resize(size, nullptr); + slacks_.resize(size, nullptr); + dependent_transits_.resize(size, nullptr); + ComputeTransitClasses(transit_evaluators, &class_evaluators_, + &vehicle_to_class_); + if (base_dimension_ != nullptr) { + ComputeTransitClasses(state_dependent_transit_evaluators, + &state_dependent_class_evaluators_, + &state_dependent_vehicle_to_class_); } - private: - void PathClosed() { - // TODO(user): Make sure that 0 duration intervals are pushed at the end - // of the list. - if (status_->Max() == 0) { - for (IntervalVar* const break_interval : break_intervals_) { - break_interval->SetPerformed(false); - } - } else { - RoutingModel* const model = dimension_->model(); - int64 current = model->Start(vehicle_); - std::vector vehicle_intervals = break_intervals_; - while (!model->IsEnd(current)) { - const int next = model->NextVar(current)->Min(); - std::vector transit_intervals; - IntervalVar* last = nullptr; - for (int i = 0; i <= break_intervals_.size(); ++i) { - IntervalVar* const interval = solver()->MakeIntervalVar( - dimension_->CumulVar(current)->Min(), - dimension_->CumulVar(next)->Max(), 0, - dimension_->FixedTransitVar(current)->Value(), 0, kint64max, - false, absl::StrCat(current, "-", i)); - transit_intervals.push_back(interval); - vehicle_intervals.push_back(interval); - // Order transit intervals to cut symmetries. - if (last != nullptr) { - solver()->AddConstraint(solver()->MakeIntervalVarRelation( - interval, Solver::STARTS_AFTER_END, last)); - last = interval; + InitializeTransitVariables(slack_max); +} + +GlobalVehicleBreaksConstraint::GlobalVehicleBreaksConstraint( + const RoutingDimension* dimension) + : Constraint(dimension->model()->solver()), + model_(dimension->model()), + dimension_(dimension) { + vehicle_demons_.resize(model_->vehicles()); +} + +void GlobalVehicleBreaksConstraint::Post() { + for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) { + if (!dimension_->VehicleHasBreakIntervals(vehicle)) continue; + vehicle_demons_[vehicle] = MakeDelayedConstraintDemon1( + solver(), this, &GlobalVehicleBreaksConstraint::PropagateVehicle, + "PropagateVehicle", vehicle); + for (IntervalVar* interval : + dimension_->GetBreakIntervalsOfVehicle(vehicle)) { + interval->WhenAnything(vehicle_demons_[vehicle]); + } + } + const int num_nodes = model_->Nexts().size(); + for (int node = 0; node < num_nodes; node++) { + Demon* dimension_demon = MakeConstraintDemon1( + solver(), this, &GlobalVehicleBreaksConstraint::PropagateNode, + "PropagateNode", node); + model_->NextVar(node)->WhenBound(dimension_demon); + model_->VehicleVar(node)->WhenBound(dimension_demon); + dimension_->CumulVar(node)->WhenDomain(dimension_demon); + dimension_->SlackVar(node)->WhenDomain(dimension_demon); + } +} + +void GlobalVehicleBreaksConstraint::InitialPropagate() { + for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) { + if (dimension_->VehicleHasBreakIntervals(vehicle)) { + PropagateVehicle(vehicle); + } + } +} + +// This dispatches node events to the right vehicle propagator. +// It also filters out a part of uninteresting events, on which the vehicle +// propagator will not find anything new. +void GlobalVehicleBreaksConstraint::PropagateNode(int node) { + if (!model_->VehicleVar(node)->Bound()) return; + const int vehicle = model_->VehicleVar(node)->Min(); + if (vehicle < 0 || vehicle_demons_[vehicle] == nullptr) return; + EnqueueDelayedDemon(vehicle_demons_[vehicle]); +} + +// Naive filtering algorithm: for every arc on the path from start of vehicle, +// for every break B, either do pairwise disjunctive reasoning between +// [CumulVar(node), CumulVar(node) + node_transits(node)) and B +// or [CumulVar(node), CumulVar(Next(node))) and B, +// depending on whether B can fit inside the arc's slack or not. +// The pairwise disjunctive reasoning rule for two performed intervals is: +// StartMax(A) < EndMin(B) => End(A) < StartMax(B) and +// StartMax(A) < EndMin(B) => EndMin(A) < Start(B). +void GlobalVehicleBreaksConstraint::PropagateVehicle(int vehicle) { + const std::vector& break_intervals = + dimension_->GetBreakIntervalsOfVehicle(vehicle); + const std::vector& node_visit_transit = + dimension_->GetNodeVisitTransitsOfVehicle(vehicle); + // External loop: browse all arcs from start of vehicle. + int64 current = model_->Start(vehicle); + while (!model_->IsEnd(current)) { + if (!model_->NextVar(current)->Bound()) break; + const int64 next = model_->NextVar(current)->Min(); + const int64 current_start_max = dimension_->CumulVar(current)->Max(); + const int64 current_end_min = + dimension_->CumulVar(current)->Min() + node_visit_transit[current]; + const int64 next_start_min = dimension_->CumulVar(next)->Min(); + const int64 current_slack_max = dimension_->SlackVar(current)->Max(); + + int64 total_break_inside_arc = 0; + for (IntervalVar* interval : break_intervals) { + if (!interval->MayBePerformed()) continue; + + const bool interval_is_performed = interval->MustBePerformed(); + const int64 interval_start_max = interval->StartMax(); + const int64 interval_end_min = interval->EndMin(); + const int64 interval_duration_min = interval->DurationMin(); + + // When interval cannot fit inside current arc, + // do disjunctive reasoning on full arc. + if (interval_duration_min > current_slack_max) { + if (current_start_max < interval_end_min) { + interval->SetStartMin(next_start_min); + if (interval_is_performed) { + dimension_->CumulVar(next)->SetMax(interval_start_max); } } - std::vector durations(transit_intervals.size()); - for (int i = 0; i < transit_intervals.size(); ++i) { - durations[i] = transit_intervals[i]->DurationExpr()->Var(); - if (i == 0) { - solver()->AddConstraint( - solver()->MakeEquality(transit_intervals[i]->StartExpr(), - dimension_->CumulVar(current))); - } else { - solver()->AddConstraint( - solver()->MakeGreaterOrEqual(transit_intervals[i]->StartExpr(), - dimension_->CumulVar(current))); - } - if (i == break_intervals_.size()) { - solver()->AddConstraint(solver()->MakeEquality( - dimension_->CumulVar(next), transit_intervals[i]->EndExpr())); - } else { - solver()->AddConstraint(solver()->MakeGreaterOrEqual( - dimension_->CumulVar(next), transit_intervals[i]->EndExpr())); + if (interval_start_max < next_start_min) { + interval->SetEndMax(current_start_max); + if (interval_is_performed) { + dimension_->CumulVar(current)->SetMin(interval_end_min); } } - solver()->AddConstraint(solver()->MakeGreaterOrEqual( - solver()->MakeSum(durations), - dimension_->FixedTransitVar(current)->Value())); - current = next; + continue; + } + + // Interval could fit inside arc: do disjunctive reasoning between + // interval and service task only. + if (current_start_max < interval_end_min) { + interval->SetStartMin(current_end_min); + if (interval_is_performed) { + dimension_->CumulVar(current)->SetMax(interval_start_max); + } + } + if (interval_start_max < current_end_min) { + interval->SetEndMax(current_start_max); + if (interval_is_performed) { + dimension_->CumulVar(current)->SetMin(interval_end_min); + } + } + + const int64 actual_transit = dimension_->FixedTransitVar(current)->Min(); + if (interval_start_max < next_start_min) { // interval not after. + if (interval_is_performed) { + const int64 bound_interval_before = interval_end_min + actual_transit; + const int64 bound_interval_inside = + dimension_->CumulVar(current)->Min() + actual_transit + + interval_duration_min; + dimension_->CumulVar(next)->SetMin( + std::min(bound_interval_before, bound_interval_inside)); + } + } + + if (interval_end_min > current_start_max) { // interval not before + if (interval_is_performed) { + const int64 bound_interval_after = + interval_start_max - actual_transit; + const int64 bound_interval_inside = + dimension_->CumulVar(next)->Max() - actual_transit - + interval_duration_min; + dimension_->CumulVar(current)->SetMax( + std::max(bound_interval_after, bound_interval_inside)); + } + } + + // If interval must be inside the arc, the slack must contain it. + if (current_start_max < interval_end_min && + interval_start_max < next_start_min) { + if (interval_is_performed) { + total_break_inside_arc += interval_duration_min; + } } - solver()->AddConstraint(solver()->MakeStrictDisjunctiveConstraint( - vehicle_intervals, absl::StrCat("Vehicle breaks ", vehicle_))); } + dimension_->SlackVar(current)->SetMin(total_break_inside_arc); + current = next; } - const RoutingDimension* const dimension_; - const int vehicle_; - const std::vector break_intervals_; - IntVar* const status_; -}; + // Energy-based reasoning. + // Fill internal vectors with route and breaks information. + tasks_.start_min.clear(); + tasks_.duration_min.clear(); + tasks_.end_max.clear(); + tasks_.is_preemptible.clear(); + tasks_.forbidden_intervals.clear(); + task_translators_.clear(); + // Translate route to tasks: visits are nonpreemptible, transits are. + current = model_->Start(vehicle); + while (true) { + // Tasks from visits. + const bool node_is_last = current == model_->End(vehicle); + const int64 visit_duration = + node_is_last ? 0LL : node_visit_transit[current]; + tasks_.start_min.push_back(dimension_->CumulVar(current)->Min()); + tasks_.duration_min.push_back(visit_duration); + tasks_.end_max.push_back( + CapAdd(dimension_->CumulVar(current)->Max(), visit_duration)); + tasks_.is_preemptible.push_back(false); + task_translators_.emplace_back(dimension_->CumulVar(current), + visit_duration); + if (node_is_last) break; + + // Tasks from transits. + const int next = model_->NextVar(current)->Bound() + ? model_->NextVar(current)->Min() + : model_->End(vehicle); + tasks_.start_min.push_back(tasks_.start_min.back() + visit_duration); + tasks_.duration_min.push_back( + std::max(visit_duration, dimension_->FixedTransitVar(current)->Min()) - + visit_duration); + tasks_.end_max.push_back(dimension_->CumulVar(next)->Max()); + tasks_.is_preemptible.push_back(true); + task_translators_.emplace_back(); // Dummy translator, prunes nothing. + + current = next; + } + tasks_.num_chain_tasks = tasks_.start_min.size(); + // Tasks from breaks. + for (IntervalVar* interval : break_intervals) { + if (!interval->MustBePerformed()) continue; + tasks_.start_min.push_back(interval->StartMin()); + tasks_.duration_min.push_back(interval->DurationMin()); + tasks_.end_max.push_back(interval->EndMax()); + tasks_.is_preemptible.push_back(false); + task_translators_.emplace_back(interval); + } + if (!disjunctive_propagator_.Propagate(&tasks_)) solver()->Fail(); + + // Push new bounds to variables. + const int num_tasks = tasks_.start_min.size(); + for (int task = 0; task < num_tasks; ++task) { + task_translators_[task].SetStartMin(tasks_.start_min[task]); + task_translators_[task].SetEndMax(tasks_.end_max[task]); + } +} + +bool DisjunctivePropagator::Propagate(Tasks* tasks) { + DCHECK_LE(tasks->num_chain_tasks, tasks->start_min.size()); + DCHECK_EQ(tasks->start_min.size(), tasks->duration_min.size()); + DCHECK_EQ(tasks->start_min.size(), tasks->end_max.size()); + DCHECK_EQ(tasks->start_min.size(), tasks->is_preemptible.size()); + // Do forward deductions, then backward deductions. + // Interleave Precedences() that is O(n). + return Precedences(tasks) && EdgeFinding(tasks) && Precedences(tasks) && + DetectablePrecedencesWithChain(tasks) && ForbiddenIntervals(tasks) && + Precedences(tasks) && MirrorTasks(tasks) && EdgeFinding(tasks) && + Precedences(tasks) && DetectablePrecedencesWithChain(tasks) && + MirrorTasks(tasks); +} + +bool DisjunctivePropagator::Precedences(Tasks* tasks) { + const int num_chain_tasks = tasks->num_chain_tasks; + if (num_chain_tasks == 0) return true; + // Propagate forwards. + int64 time = tasks->start_min[0]; + for (int task = 0; task < num_chain_tasks; ++task) { + time = std::max(tasks->start_min[task], time); + tasks->start_min[task] = time; + time = CapAdd(time, tasks->duration_min[task]); + if (tasks->end_max[task] < time) return false; + } + // Propagate backwards. + time = tasks->end_max[num_chain_tasks - 1]; + for (int task = num_chain_tasks - 1; task >= 0; --task) { + time = std::min(tasks->end_max[task], time); + tasks->end_max[task] = time; + time = CapSub(time, tasks->duration_min[task]); + if (time < tasks->start_min[task]) return false; + } + return true; +} -Constraint* MakeBreakConstraint(const RoutingDimension* dimension, int vehicle, - std::vector break_intervals) { - Solver* const solver = dimension->model()->solver(); - return solver->RevAlloc( - new BreakConstraint(dimension, vehicle, std::move(break_intervals))); +bool DisjunctivePropagator::MirrorTasks(Tasks* tasks) { + // For all tasks, start_min := -end_max and end_max := -start_min. + const int num_tasks = tasks->start_min.size(); + for (int task = 0; task < num_tasks; ++task) { + const int64 t = -tasks->start_min[task]; + tasks->start_min[task] = -tasks->end_max[task]; + tasks->end_max[task] = t; + } + // In the mirror problem, tasks linked by precedences are in reversed order. + const int num_chain_tasks = tasks->num_chain_tasks; + if (num_chain_tasks > 0) { + std::reverse(tasks->start_min.begin(), + tasks->start_min.begin() + num_chain_tasks); + std::reverse(tasks->duration_min.begin(), + tasks->duration_min.begin() + num_chain_tasks); + std::reverse(tasks->end_max.begin(), + tasks->end_max.begin() + num_chain_tasks); + std::reverse(tasks->is_preemptible.begin(), + tasks->is_preemptible.begin() + num_chain_tasks); + } + return true; } -} // namespace +bool DisjunctivePropagator::EdgeFinding(Tasks* tasks) { + const int num_tasks = tasks->start_min.size(); + // Prepare start_min events for tree. + tasks_by_start_min_.resize(num_tasks); + std::iota(tasks_by_start_min_.begin(), tasks_by_start_min_.end(), 0); + std::sort( + tasks_by_start_min_.begin(), tasks_by_start_min_.end(), + [&](int i, int j) { return tasks->start_min[i] < tasks->start_min[j]; }); + event_of_task_.resize(num_tasks); + for (int event = 0; event < num_tasks; ++event) { + event_of_task_[tasks_by_start_min_[event]] = event; + } + // Tasks will be browsed according to end_max order. + tasks_by_end_max_.resize(num_tasks); + std::iota(tasks_by_end_max_.begin(), tasks_by_end_max_.end(), 0); + std::sort( + tasks_by_end_max_.begin(), tasks_by_end_max_.end(), + [&](int i, int j) { return tasks->end_max[i] < tasks->end_max[j]; }); + + // Generic overload checking: insert tasks by end_max, + // fail if envelope > end_max. + theta_lambda_tree_.Reset(num_tasks); + for (const int task : tasks_by_end_max_) { + theta_lambda_tree_.AddOrUpdateEvent( + event_of_task_[task], tasks->start_min[task], tasks->duration_min[task], + tasks->duration_min[task]); + if (theta_lambda_tree_.GetEnvelope() > tasks->end_max[task]) { + return false; + } + } + + // Generic edge finding: from full set of tasks, at each end_max event in + // decreasing order, check lambda feasibility, then move end_max task from + // theta to lambda. + for (int i = num_tasks - 1; i >= 0; --i) { + const int task = tasks_by_end_max_[i]; + const int64 envelope = theta_lambda_tree_.GetEnvelope(); + // If a nonpreemptible optional would overload end_max, push to envelope. + while (theta_lambda_tree_.GetOptionalEnvelope() > tasks->end_max[task]) { + int critical_event; // Dummy value. + int optional_event; + int64 available_energy; // Dummy value. + theta_lambda_tree_.GetEventsWithOptionalEnvelopeGreaterThan( + tasks->end_max[task], &critical_event, &optional_event, + &available_energy); + const int optional_task = tasks_by_start_min_[optional_event]; + tasks->start_min[optional_task] = + std::max(tasks->start_min[optional_task], envelope); + theta_lambda_tree_.RemoveEvent(optional_event); + } + if (!tasks->is_preemptible[task]) { + theta_lambda_tree_.AddOrUpdateOptionalEvent(event_of_task_[task], + tasks->start_min[task], + tasks->duration_min[task]); + } else { + theta_lambda_tree_.RemoveEvent(event_of_task_[task]); + } + } + return true; +} + +bool DisjunctivePropagator::DetectablePrecedencesWithChain(Tasks* tasks) { + const int num_tasks = tasks->start_min.size(); + // Prepare start_min events for tree. + tasks_by_start_min_.resize(num_tasks); + std::iota(tasks_by_start_min_.begin(), tasks_by_start_min_.end(), 0); + std::sort( + tasks_by_start_min_.begin(), tasks_by_start_min_.end(), + [&](int i, int j) { return tasks->start_min[i] < tasks->start_min[j]; }); + event_of_task_.resize(num_tasks); + for (int event = 0; event < num_tasks; ++event) { + event_of_task_[tasks_by_start_min_[event]] = event; + } + theta_lambda_tree_.Reset(num_tasks); + + // Sort nonchain tasks by start max = end_max - duration_min. + const int num_chain_tasks = tasks->num_chain_tasks; + nonchain_tasks_by_start_max_.resize(num_tasks - num_chain_tasks); + std::iota(nonchain_tasks_by_start_max_.begin(), + nonchain_tasks_by_start_max_.end(), num_chain_tasks); + std::sort(nonchain_tasks_by_start_max_.begin(), + nonchain_tasks_by_start_max_.end(), [&tasks](int i, int j) { + return tasks->end_max[i] - tasks->duration_min[i] < + tasks->end_max[j] - tasks->duration_min[j]; + }); + + // Detectable precedences, specialized for routes: for every task on route, + // put all tasks before it in the tree, then push with envelope. + int index_nonchain = 0; + for (int i = 0; i < num_chain_tasks; ++i) { + if (!tasks->is_preemptible[i]) { + // Add all nonchain tasks detected before i. + while (index_nonchain < nonchain_tasks_by_start_max_.size()) { + const int task = nonchain_tasks_by_start_max_[index_nonchain]; + if (tasks->end_max[task] - tasks->duration_min[task] >= + tasks->start_min[i] + tasks->duration_min[i]) + break; + theta_lambda_tree_.AddOrUpdateEvent( + event_of_task_[task], tasks->start_min[task], + tasks->duration_min[task], tasks->duration_min[task]); + index_nonchain++; + } + } + // All chain and nonchain tasks before i are now in the tree, push i. + const int64 new_start_min = theta_lambda_tree_.GetEnvelope(); + // Add i to the tree before updating it. + theta_lambda_tree_.AddOrUpdateEvent(event_of_task_[i], tasks->start_min[i], + tasks->duration_min[i], + tasks->duration_min[i]); + tasks->start_min[i] = std::max(tasks->start_min[i], new_start_min); + } + return true; +} + +bool DisjunctivePropagator::ForbiddenIntervals(Tasks* tasks) { + if (tasks->forbidden_intervals.empty()) return true; + const int num_tasks = tasks->start_min.size(); + for (int task = 0; task < num_tasks; ++task) { + if (tasks->duration_min[task] == 0) continue; + if (tasks->forbidden_intervals[task] == nullptr) continue; + // If start_min forbidden, push to next feasible value. + { + const auto& interval = + tasks->forbidden_intervals[task]->FirstIntervalGreaterOrEqual( + tasks->start_min[task]); + if (interval == tasks->forbidden_intervals[task]->end()) continue; + if (interval->start <= tasks->start_min[task]) { + tasks->start_min[task] = CapAdd(interval->end, 1); + } + } + // If end_max forbidden, push to next feasible value. + { + const int64 start_max = + CapSub(tasks->end_max[task], tasks->duration_min[task]); + const auto& interval = + tasks->forbidden_intervals[task]->LastIntervalLessOrEqual(start_max); + if (interval == tasks->forbidden_intervals[task]->end()) continue; + if (interval->end >= start_max) { + tasks->end_max[task] = + CapAdd(interval->start, tasks->duration_min[task] - 1); + } + } + if (CapAdd(tasks->start_min[task], tasks->duration_min[task]) > + tasks->end_max[task]) { + return false; + } + } + return true; +} void RoutingDimension::CloseModel(bool use_light_propagation) { Solver* const solver = model_->solver(); @@ -5026,8 +5327,9 @@ void RoutingDimension::CloseModel(bool use_light_propagation) { IntVar* const vehicle_var = model_->VehicleVar(i); IntVar* const capacity_var = capacity_vars_[i]; if (use_light_propagation) { - solver->AddConstraint( - MakeLightElement(solver, capacity_var, vehicle_var, capacity_lambda)); + solver->AddConstraint(MakeLightElement( + solver, capacity_var, vehicle_var, capacity_lambda, + [this]() { return model_->enable_deep_serialization_; })); } else { solver->AddConstraint(solver->MakeEquality( capacity_var, @@ -5042,15 +5344,23 @@ void RoutingDimension::CloseModel(bool use_light_propagation) { IntVar* const fixed_transit = fixed_transits_[i]; const auto transit_vehicle_evaluator = [this, i](int64 to, int64 eval_index) { - return eval_index >= 0 - ? class_evaluators_[vehicle_to_class_[eval_index]](i, to) - : 0LL; + return eval_index >= 0 ? transit_evaluator(eval_index)(i, to) : 0LL; }; if (use_light_propagation) { if (class_evaluators_.size() == 1) { - solver->AddConstraint(MakeLightElement( - solver, fixed_transit, next_var, - [this, i](int64 to) { return class_evaluators_[0](i, to); })); + const int class_evaluator_index = class_evaluators_[0]; + const auto& unary_callback = + model_->UnaryTransitCallbackOrNull(class_evaluator_index); + if (unary_callback == nullptr) { + solver->AddConstraint(MakeLightElement( + solver, fixed_transit, next_var, + [this, i](int64 to) { + return model_->TransitCallback(class_evaluators_[0])(i, to); + }, + [this]() { return model_->enable_deep_serialization_; })); + } else { + fixed_transit->SetValue(unary_callback(i)); + } } else { solver->AddConstraint(MakeLightElement2(solver, fixed_transit, next_var, model_->VehicleVar(i), @@ -5058,13 +5368,22 @@ void RoutingDimension::CloseModel(bool use_light_propagation) { } } else { if (class_evaluators_.size() == 1) { - solver->AddConstraint(solver->MakeEquality( - fixed_transit, - solver - ->MakeElement( - [this, i](int64 to) { return class_evaluators_[0](i, to); }, - model_->NextVar(i)) - ->Var())); + const int class_evaluator_index = class_evaluators_[0]; + const auto& unary_callback = + model_->UnaryTransitCallbackOrNull(class_evaluator_index); + if (unary_callback == nullptr) { + solver->AddConstraint(solver->MakeEquality( + fixed_transit, solver + ->MakeElement( + [this, i](int64 to) { + return model_->TransitCallback( + class_evaluators_[0])(i, to); + }, + model_->NextVar(i)) + ->Var())); + } else { + fixed_transit->SetValue(unary_callback(i)); + } } else { IntVar* const vehicle_class_var = solver->MakeElement(vehicle_class_function, model_->VehicleVar(i)) @@ -5077,12 +5396,17 @@ void RoutingDimension::CloseModel(bool use_light_propagation) { } } } + if (!vehicle_break_intervals_.empty()) { + GlobalVehicleBreaksConstraint* constraint = + model()->solver()->RevAlloc(new GlobalVehicleBreaksConstraint(this)); + solver->AddConstraint(constraint); + } } int64 RoutingDimension::GetTransitValue(int64 from_index, int64 to_index, int64 vehicle) const { - DCHECK(class_evaluators_[vehicle_to_class_[vehicle]] != nullptr); - return class_evaluators_[vehicle_to_class_[vehicle]](from_index, to_index); + DCHECK(transit_evaluator(vehicle) != nullptr); + return transit_evaluator(vehicle)(from_index, to_index); } void RoutingDimension::SetSpanUpperBoundForVehicle(int64 upper_bound, @@ -5117,28 +5441,6 @@ void RoutingDimension::SetGlobalSpanCostCoefficient(int64 coefficient) { } void RoutingDimension::SetCumulVarPiecewiseLinearCost( - RoutingModel::NodeIndex node, const PiecewiseLinearFunction& cost) { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - SetCumulVarPiecewiseLinearCostFromIndex(index, cost); - return; - } - } - VLOG(2) << "Cannot set piecewise linear cost on start or end nodes"; -} - -void RoutingDimension::SetStartCumulVarPiecewiseLinearCost( - int vehicle, const PiecewiseLinearFunction& cost) { - SetCumulVarPiecewiseLinearCostFromIndex(model_->Start(vehicle), cost); -} - -void RoutingDimension::SetEndCumulVarPiecewiseLinearCost( - int vehicle, const PiecewiseLinearFunction& cost) { - SetCumulVarPiecewiseLinearCostFromIndex(model_->End(vehicle), cost); -} - -void RoutingDimension::SetCumulVarPiecewiseLinearCostFromIndex( int64 index, const PiecewiseLinearFunction& cost) { if (!cost.IsNonDecreasing()) { LOG(WARNING) << "Only non-decreasing cost functions are supported."; @@ -5154,62 +5456,19 @@ void RoutingDimension::SetCumulVarPiecewiseLinearCostFromIndex( PiecewiseLinearCost& piecewise_linear_cost = cumul_var_piecewise_linear_cost_[index]; piecewise_linear_cost.var = cumuls_[index]; - piecewise_linear_cost.cost = new PiecewiseLinearFunction(cost); -} - -bool RoutingDimension::HasCumulVarPiecewiseLinearCost( - RoutingModel::NodeIndex node) const { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - return HasCumulVarPiecewiseLinearCostFromIndex(index); - } - } - VLOG(2) << "Cannot get piecewise linear cost on start or end nodes"; - return false; -} - -bool RoutingDimension::HasStartCumulVarPiecewiseLinearCost(int vehicle) const { - return HasCumulVarPiecewiseLinearCostFromIndex(model_->Start(vehicle)); -} - -bool RoutingDimension::HasEndCumulVarPiecewiseLinearCost(int vehicle) const { - return HasCumulVarPiecewiseLinearCostFromIndex(model_->End(vehicle)); + piecewise_linear_cost.cost = absl::make_unique(cost); } -bool RoutingDimension::HasCumulVarPiecewiseLinearCostFromIndex( - int64 index) const { +bool RoutingDimension::HasCumulVarPiecewiseLinearCost(int64 index) const { return (index < cumul_var_piecewise_linear_cost_.size() && cumul_var_piecewise_linear_cost_[index].var != nullptr); } const PiecewiseLinearFunction* RoutingDimension::GetCumulVarPiecewiseLinearCost( - RoutingModel::NodeIndex node) const { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - return GetCumulVarPiecewiseLinearCostFromIndex(index); - } - } - VLOG(2) << "Cannot get piecewise linear cost on start or end nodes"; - return nullptr; -} - -const PiecewiseLinearFunction* -RoutingDimension::GetStartCumulVarPiecewiseLinearCost(int vehicle) const { - return GetCumulVarPiecewiseLinearCostFromIndex(model_->Start(vehicle)); -} - -const PiecewiseLinearFunction* -RoutingDimension::GetEndCumulVarPiecewiseLinearCost(int vehicle) const { - return GetCumulVarPiecewiseLinearCostFromIndex(model_->End(vehicle)); -} - -const PiecewiseLinearFunction* -RoutingDimension::GetCumulVarPiecewiseLinearCostFromIndex(int64 index) const { + int64 index) const { if (index < cumul_var_piecewise_linear_cost_.size() && cumul_var_piecewise_linear_cost_[index].var != nullptr) { - return cumul_var_piecewise_linear_cost_[index].cost; + return cumul_var_piecewise_linear_cost_[index].cost.get(); } return nullptr; } @@ -5239,113 +5498,21 @@ void RoutingDimension::SetupCumulVarPiecewiseLinearCosts( } } -void RoutingDimension::SetCumulVarSoftUpperBound(RoutingModel::NodeIndex node, - int64 upper_bound, +void RoutingDimension::SetCumulVarSoftUpperBound(int64 index, int64 upper_bound, int64 coefficient) { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - SetCumulVarSoftUpperBoundFromIndex(index, upper_bound, coefficient); - return; - } - } - VLOG(2) << "Cannot set soft upper bound on start or end nodes"; -} - -bool RoutingDimension::HasCumulVarSoftUpperBound( - RoutingModel::NodeIndex node) const { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - return HasCumulVarSoftUpperBoundFromIndex(index); - } - } - VLOG(2) << "Cannot get soft upper bound on start or end nodes"; - return false; -} - -int64 RoutingDimension::GetCumulVarSoftUpperBound( - RoutingModel::NodeIndex node) const { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - return GetCumulVarSoftUpperBoundFromIndex(index); - } - } - VLOG(2) << "Cannot get soft upper bound on start or end nodes"; - return kint64max; -} - -int64 RoutingDimension::GetCumulVarSoftUpperBoundCoefficient( - RoutingModel::NodeIndex node) const { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - return GetCumulVarSoftUpperBoundCoefficientFromIndex(index); - } - } - VLOG(2) << "Cannot get soft upper bound on start or end nodes"; - return 0; -} - -void RoutingDimension::SetStartCumulVarSoftUpperBound(int vehicle, - int64 upper_bound, - int64 coefficient) { - SetCumulVarSoftUpperBoundFromIndex(model_->Start(vehicle), upper_bound, - coefficient); -} - -bool RoutingDimension::HasStartCumulVarSoftUpperBound(int vehicle) const { - return HasCumulVarSoftUpperBoundFromIndex(model_->Start(vehicle)); -} - -int64 RoutingDimension::GetStartCumulVarSoftUpperBound(int vehicle) const { - return GetCumulVarSoftUpperBoundFromIndex(model_->Start(vehicle)); -} - -int64 RoutingDimension::GetStartCumulVarSoftUpperBoundCoefficient( - int vehicle) const { - return GetCumulVarSoftUpperBoundCoefficientFromIndex(model_->Start(vehicle)); -} - -void RoutingDimension::SetEndCumulVarSoftUpperBound(int vehicle, - int64 upper_bound, - int64 coefficient) { - SetCumulVarSoftUpperBoundFromIndex(model_->End(vehicle), upper_bound, - coefficient); -} - -bool RoutingDimension::HasEndCumulVarSoftUpperBound(int vehicle) const { - return HasCumulVarSoftUpperBoundFromIndex(model_->End(vehicle)); -} - -int64 RoutingDimension::GetEndCumulVarSoftUpperBound(int vehicle) const { - return GetCumulVarSoftUpperBoundFromIndex(model_->End(vehicle)); -} - -int64 RoutingDimension::GetEndCumulVarSoftUpperBoundCoefficient( - int vehicle) const { - return GetCumulVarSoftUpperBoundCoefficientFromIndex(model_->End(vehicle)); -} - -void RoutingDimension::SetCumulVarSoftUpperBoundFromIndex(int64 index, - int64 upper_bound, - int64 coefficient) { if (index >= cumul_var_soft_upper_bound_.size()) { - cumul_var_soft_upper_bound_.resize(index + 1); + cumul_var_soft_upper_bound_.resize(index + 1, {nullptr, 0, 0}); } - SoftBound* const soft_upper_bound = &cumul_var_soft_upper_bound_[index]; - soft_upper_bound->var = cumuls_[index]; - soft_upper_bound->bound = upper_bound; - soft_upper_bound->coefficient = coefficient; + cumul_var_soft_upper_bound_[index] = {cumuls_[index], upper_bound, + coefficient}; } -bool RoutingDimension::HasCumulVarSoftUpperBoundFromIndex(int64 index) const { +bool RoutingDimension::HasCumulVarSoftUpperBound(int64 index) const { return (index < cumul_var_soft_upper_bound_.size() && cumul_var_soft_upper_bound_[index].var != nullptr); } -int64 RoutingDimension::GetCumulVarSoftUpperBoundFromIndex(int64 index) const { +int64 RoutingDimension::GetCumulVarSoftUpperBound(int64 index) const { if (index < cumul_var_soft_upper_bound_.size() && cumul_var_soft_upper_bound_[index].var != nullptr) { return cumul_var_soft_upper_bound_[index].bound; @@ -5353,7 +5520,7 @@ int64 RoutingDimension::GetCumulVarSoftUpperBoundFromIndex(int64 index) const { return cumuls_[index]->Max(); } -int64 RoutingDimension::GetCumulVarSoftUpperBoundCoefficientFromIndex( +int64 RoutingDimension::GetCumulVarSoftUpperBoundCoefficient( int64 index) const { if (index < cumul_var_soft_upper_bound_.size() && cumul_var_soft_upper_bound_[index].var != nullptr) { @@ -5387,112 +5554,21 @@ void RoutingDimension::SetupCumulVarSoftUpperBoundCosts( } } -void RoutingDimension::SetCumulVarSoftLowerBound(RoutingModel::NodeIndex node, - int64 lower_bound, +void RoutingDimension::SetCumulVarSoftLowerBound(int64 index, int64 lower_bound, int64 coefficient) { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - SetCumulVarSoftLowerBoundFromIndex(index, lower_bound, coefficient); - return; - } - } - VLOG(2) << "Cannot set soft lower bound on start or end nodes"; -} - -bool RoutingDimension::HasCumulVarSoftLowerBound( - RoutingModel::NodeIndex node) const { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - return HasCumulVarSoftLowerBoundFromIndex(index); - } - } - VLOG(2) << "Cannot get soft lower bound on start or end nodes"; - return false; -} - -int64 RoutingDimension::GetCumulVarSoftLowerBound( - RoutingModel::NodeIndex node) const { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - return GetCumulVarSoftLowerBoundFromIndex(index); - } - } - VLOG(2) << "Cannot get soft lower bound on start or end nodes"; - return 0; -} -int64 RoutingDimension::GetCumulVarSoftLowerBoundCoefficient( - RoutingModel::NodeIndex node) const { - if (model_->HasIndex(node)) { - const int64 index = model_->NodeToIndex(node); - if (!model_->IsStart(index) && !model_->IsEnd(index)) { - return GetCumulVarSoftLowerBoundCoefficientFromIndex(index); - } - } - VLOG(2) << "Cannot get soft lower bound on start or end nodes"; - return 0; -} - -void RoutingDimension::SetStartCumulVarSoftLowerBound(int vehicle, - int64 lower_bound, - int64 coefficient) { - SetCumulVarSoftLowerBoundFromIndex(model_->Start(vehicle), lower_bound, - coefficient); -} - -bool RoutingDimension::HasStartCumulVarSoftLowerBound(int vehicle) const { - return HasCumulVarSoftLowerBoundFromIndex(model_->Start(vehicle)); -} - -int64 RoutingDimension::GetStartCumulVarSoftLowerBound(int vehicle) const { - return GetCumulVarSoftLowerBoundFromIndex(model_->Start(vehicle)); -} - -int64 RoutingDimension::GetStartCumulVarSoftLowerBoundCoefficient( - int vehicle) const { - return GetCumulVarSoftLowerBoundCoefficientFromIndex(model_->Start(vehicle)); -} - -void RoutingDimension::SetEndCumulVarSoftLowerBound(int vehicle, - int64 lower_bound, - int64 coefficient) { - SetCumulVarSoftLowerBoundFromIndex(model_->End(vehicle), lower_bound, - coefficient); -} - -bool RoutingDimension::HasEndCumulVarSoftLowerBound(int vehicle) const { - return HasCumulVarSoftLowerBoundFromIndex(model_->End(vehicle)); -} - -int64 RoutingDimension::GetEndCumulVarSoftLowerBound(int vehicle) const { - return GetCumulVarSoftLowerBoundFromIndex(model_->End(vehicle)); -} - -int64 RoutingDimension::GetEndCumulVarSoftLowerBoundCoefficient( - int vehicle) const { - return GetCumulVarSoftLowerBoundCoefficientFromIndex(model_->End(vehicle)); -} - -void RoutingDimension::SetCumulVarSoftLowerBoundFromIndex(int64 index, - int64 lower_bound, - int64 coefficient) { if (index >= cumul_var_soft_lower_bound_.size()) { - cumul_var_soft_lower_bound_.resize(index + 1); + cumul_var_soft_lower_bound_.resize(index + 1, {nullptr, 0, 0}); } - SoftBound* const soft_lower_bound = &cumul_var_soft_lower_bound_[index]; - soft_lower_bound->var = cumuls_[index]; - soft_lower_bound->bound = lower_bound; - soft_lower_bound->coefficient = coefficient; + cumul_var_soft_lower_bound_[index] = {cumuls_[index], lower_bound, + coefficient}; } -bool RoutingDimension::HasCumulVarSoftLowerBoundFromIndex(int64 index) const { +bool RoutingDimension::HasCumulVarSoftLowerBound(int64 index) const { return (index < cumul_var_soft_lower_bound_.size() && cumul_var_soft_lower_bound_[index].var != nullptr); } -int64 RoutingDimension::GetCumulVarSoftLowerBoundFromIndex(int64 index) const { +int64 RoutingDimension::GetCumulVarSoftLowerBound(int64 index) const { if (index < cumul_var_soft_lower_bound_.size() && cumul_var_soft_lower_bound_[index].var != nullptr) { return cumul_var_soft_lower_bound_[index].bound; @@ -5500,7 +5576,7 @@ int64 RoutingDimension::GetCumulVarSoftLowerBoundFromIndex(int64 index) const { return cumuls_[index]->Min(); } -int64 RoutingDimension::GetCumulVarSoftLowerBoundCoefficientFromIndex( +int64 RoutingDimension::GetCumulVarSoftLowerBoundCoefficient( int64 index) const { if (index < cumul_var_soft_lower_bound_.size() && cumul_var_soft_lower_bound_[index].var != nullptr) { @@ -5549,25 +5625,83 @@ void RoutingDimension::SetupGlobalSpanCost( } IntVar* const min_start_cumul = solver->MakeMin(start_cumuls)->Var(); model_->AddVariableMaximizedByFinalizer(min_start_cumul); + IntVar* const end_range = + solver->MakeDifference(max_end_cumul, min_start_cumul)->Var(); + end_range->SetMin(0); cost_elements->push_back( - solver - ->MakeProd(solver->MakeDifference(max_end_cumul, min_start_cumul), - global_span_cost_coefficient_) - ->Var()); + solver->MakeProd(end_range, global_span_cost_coefficient_)->Var()); } } void RoutingDimension::SetBreakIntervalsOfVehicle( - std::vector breaks, int vehicle) { - if (!breaks.empty()) { - for (IntervalVar* const interval : breaks) { - model_->AddIntervalToAssignment(interval); - model_->AddVariableMinimizedByFinalizer( - interval->SafeStartExpr(0)->Var()); - } - model_->solver()->AddConstraint( - MakeBreakConstraint(this, vehicle, std::move(breaks))); + std::vector breaks, int vehicle, + std::vector node_visit_transits) { + for (IntervalVar* const interval : breaks) { + model_->AddIntervalToAssignment(interval); + model_->AddVariableMinimizedByFinalizer(interval->SafeStartExpr(0)->Var()); + } + DCHECK_LE(0, vehicle); + DCHECK_LT(vehicle, model_->vehicles()); + if (vehicle_node_visit_transits_.empty()) { + vehicle_node_visit_transits_.resize(model_->vehicles()); + vehicle_break_intervals_.resize(model_->vehicles()); + } + DCHECK_EQ(0, vehicle_node_visit_transits_[vehicle].size()); + vehicle_node_visit_transits_[vehicle] = std::move(node_visit_transits); + vehicle_break_intervals_[vehicle] = std::move(breaks); +} + +bool RoutingDimension::VehicleHasBreakIntervals(int vehicle) const { + DCHECK_LE(0, vehicle); + return (vehicle < vehicle_break_intervals_.size()) && + !vehicle_break_intervals_[vehicle].empty(); +} + +const std::vector& RoutingDimension::GetBreakIntervalsOfVehicle( + int vehicle) const { + DCHECK_LE(0, vehicle); + DCHECK_LT(vehicle, vehicle_break_intervals_.size()); + return vehicle_break_intervals_[vehicle]; +} + +const std::vector& RoutingDimension::GetNodeVisitTransitsOfVehicle( + int vehicle) const { + DCHECK_LE(0, vehicle); + DCHECK_LT(vehicle, vehicle_node_visit_transits_.size()); + return vehicle_node_visit_transits_[vehicle]; +} + +void RoutingDimension::SetPickupToDeliveryLimitFunctionForPair( + PickupToDeliveryLimitFunction limit_function, int pair_index) { + CHECK_GE(pair_index, 0); + if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) { + pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1); + } + pickup_to_delivery_limits_per_pair_index_[pair_index] = + std::move(limit_function); +} + +bool RoutingDimension::HasPickupToDeliveryLimits() const { + return !pickup_to_delivery_limits_per_pair_index_.empty(); +} + +int64 RoutingDimension::GetPickupToDeliveryLimitForPair(int pair_index, + int pickup, + int delivery) const { + DCHECK_GE(pair_index, 0); + + if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) { + return kint64max; + } + const PickupToDeliveryLimitFunction& pickup_to_delivery_limit_function = + pickup_to_delivery_limits_per_pair_index_[pair_index]; + if (!pickup_to_delivery_limit_function) { + // No limit function set for this pair. + return kint64max; } + DCHECK_GE(pickup, 0); + DCHECK_GE(delivery, 0); + return pickup_to_delivery_limit_function(pickup, delivery); } void RoutingDimension::SetupSlackAndDependentTransitCosts( diff --git a/ortools/constraint_solver/routing.h b/ortools/constraint_solver/routing.h index af4ddf0d14b..93da86b7b29 100644 --- a/ortools/constraint_solver/routing.h +++ b/ortools/constraint_solver/routing.h @@ -115,26 +115,24 @@ // Here is a simple example solving a traveling salesman problem given a cost // function callback (returns the cost of a route segment): // -// - Define a custom distance/cost function from a node to another; in this -// example just returns the sum of the node indices (note the conversion from -// the strongly-typed indices to integers): +// - Define a custom distance/cost function from an index to another; in this +// example just returns the sum of the indices: // -// int64 MyDistance(RoutingModel::NodeIndex from, -// RoutingModel::NodeIndex to) { -// return (from + to).value(); +// int64 MyDistance(int64 from, int64 to) { +// return from + to; // } // // - Create a routing model for a given problem size (int number of nodes) and // number of routes (here, 1): // -// RoutingModel routing(...number of nodes..., 1); +// RoutingIndexManager manager(...number of nodes..., 1); +// RoutingModel routing(manager); // -// - Set the cost function by passing a permanent callback to the distance -// accessor here. The callback has the following signature: -// ResultCallback2. +// - Set the cost function by registering an std::function +// in the model and passing its index as the vehicle cost. // -// routing.SetArcCostEvaluatorOfAllVehicles( -// NewPermanentCallback(MyDistance)); +// const int cost = routing.RegisterTransitCallback(MyDistance); +// routing.SetArcCostEvaluatorOfAllVehicles(cost); // // - Find a solution using Solve(), returns a solution if any (owned by // routing): @@ -159,16 +157,18 @@ #define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_H_ #include +#include #include +#include #include -#include -#include #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/hash/hash.h" #include "ortools/base/adjustable_priority_queue-inl.h" #include "ortools/base/adjustable_priority_queue.h" -#include "ortools/base/callback.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/int_type_indexed_vector.h" @@ -176,9 +176,14 @@ #include "ortools/base/macros.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" +#include "ortools/constraint_solver/routing_index_manager.h" #include "ortools/constraint_solver/routing_parameters.pb.h" #include "ortools/constraint_solver/routing_types.h" +#include "ortools/glop/lp_solver.h" #include "ortools/graph/graph.h" +#include "ortools/lp_data/lp_data.h" +#include "ortools/lp_data/lp_types.h" +#include "ortools/sat/theta_tree.h" #include "ortools/util/range_query_function.h" #include "ortools/util/sorted_interval_list.h" @@ -188,9 +193,10 @@ class IntVarFilteredDecisionBuilder; class LocalSearchOperator; class RoutingDimension; #ifndef SWIG +using util::ReverseArcListGraph; class SweepArranger; #endif -struct SweepNode; +struct SweepIndex; class RoutingModel { public: @@ -208,18 +214,28 @@ class RoutingModel { ROUTING_INVALID }; - typedef RoutingNodeIndex NodeIndex; +#ifndef SWIG + // Types of precedence policy applied to pickup and delivery pairs. + enum class PickupAndDeliveryPolicy { + // Any precedence is accepted. + ANY, + // Deliveries must be performed in reverse order of pickups. + LIFO, + // Deliveries must be performed in the same order as pickups. + FIFO + }; +#endif // SWIG typedef RoutingCostClassIndex CostClassIndex; typedef RoutingDimensionIndex DimensionIndex; typedef RoutingDisjunctionIndex DisjunctionIndex; typedef RoutingVehicleClassIndex VehicleClassIndex; - typedef RoutingNodeEvaluator2 NodeEvaluator2; - typedef RoutingTransitEvaluator2 TransitEvaluator2; + typedef RoutingTransitCallback1 TransitCallback1; + typedef RoutingTransitCallback2 TransitCallback2; // TODO(user): Remove all SWIG guards by adding the @ignore in .i. #if !defined(SWIG) - typedef RoutingNodePair NodePair; - typedef RoutingNodePairs NodePairs; + typedef RoutingIndexPair IndexPair; + typedef RoutingIndexPairs IndexPairs; #endif // SWIG #if !defined(SWIG) @@ -239,17 +255,14 @@ class RoutingModel { RangeIntToIntFunction* transit; // f(x) RangeMinMaxIndexFunction* transit_plus_identity; // g(x) = f(x) + x }; - typedef ResultCallback2 - VariableNodeEvaluator2; typedef std::function VariableIndexEvaluator2; #endif // SWIG #if !defined(SWIG) struct CostClass { - // arc_cost_evaluator->Run(from, to) is the transit cost of arc - // from->to. This may never be nullptr. - NodeEvaluator2* arc_cost_evaluator; + // Index of the arc cost evaluator, registered in the RoutingModel class. + int evaluator_index = 0; // SUBTLE: // The vehicle's fixed cost is skipped on purpose here, because we @@ -285,15 +298,13 @@ class RoutingModel { std::vector dimension_transit_evaluator_class_and_cost_coefficient; - explicit CostClass(NodeEvaluator2* arc_cost_evaluator) - : arc_cost_evaluator(arc_cost_evaluator) { - CHECK(arc_cost_evaluator != nullptr); - } + explicit CostClass(int evaluator_index) + : evaluator_index(evaluator_index) {} // Comparator for STL containers and algorithms. static bool LessThan(const CostClass& a, const CostClass& b) { - if (a.arc_cost_evaluator != b.arc_cost_evaluator) { - return a.arc_cost_evaluator < b.arc_cost_evaluator; + if (a.evaluator_index != b.evaluator_index) { + return a.evaluator_index < b.evaluator_index; } return a.dimension_transit_evaluator_class_and_cost_coefficient < b.dimension_transit_evaluator_class_and_cost_coefficient; @@ -305,13 +316,14 @@ class RoutingModel { CostClassIndex cost_class_index; // Contrarily to CostClass, here we need strict equivalence. int64 fixed_cost; - // Vehicle start and end nodes. Currently if two vehicles have different - // start/end nodes which are "physically" located at the same place, these - // two vehicles will be considered as non-equivalent. + // Vehicle start and end equivalence classes. Currently if two vehicles have + // different start/end nodes which are "physically" located at the same + // place, these two vehicles will be considered as non-equivalent unless the + // two indices are in the same class. // TODO(user): Find equivalent start/end nodes wrt dimensions and // callbacks. - RoutingModel::NodeIndex start; - RoutingModel::NodeIndex end; + int start_equivalence_class; + int end_equivalence_class; // Bounds of cumul variables at start and end vehicle nodes. // dimension_{start,end}_cumuls_{min,max}[d] is the bound for dimension d. gtl::ITIVector dimension_start_cumuls_min; @@ -330,10 +342,8 @@ class RoutingModel { }; #endif // defined(SWIG) - // Constants with an index of the first node (to be used in for loops for - // iteration), and a special index to signalize an invalid/unused value. - static const NodeIndex kFirstNode; - static const NodeIndex kInvalidNodeIndex; + // Constant used to express a hard constraint instead of a soft penalty. + static const int64 kNoPenalty; // Constant used to express the "no disjunction" index, returned when a node // does not appear in any disjunction. @@ -343,34 +353,32 @@ class RoutingModel { // dimension name does not correspond to an actual dimension. static const DimensionIndex kNoDimension; - // In the following constructors, the versions which do not take - // RoutingModelParameters are equivalent to passing DefaultModelParameters. - // A depot is the start and end node of the route of a vehicle. - // Constructor taking a single depot node which is used as the start and - // end node for all vehicles. - RoutingModel(int nodes, int vehicles, NodeIndex depot); - RoutingModel(int nodes, int vehicles, NodeIndex depot, - const RoutingModelParameters& parameters); - // Constructor taking a vector of (start node, end node) pairs for each - // vehicle route. Used to model multiple depots. - RoutingModel(int nodes, int vehicles, - const std::vector>& start_end); - RoutingModel(int nodes, int vehicles, - const std::vector>& start_end, - const RoutingModelParameters& parameters); - // Constructor taking vectors of start nodes and end nodes for each - // vehicle route. Used to model multiple depots. - // TODO(user): added to simplify SWIG wrapping. Remove when swigging - // std::vector > is ok. - RoutingModel(int nodes, int vehicles, const std::vector& starts, - const std::vector& ends); - RoutingModel(int nodes, int vehicles, const std::vector& starts, - const std::vector& ends, + // Constructor taking an index manager. The version which does not take + // RoutingModelParameters is equivalent to passing + // DefaultRoutingModelParameters(). + explicit RoutingModel(const RoutingIndexManager& index_manager); + RoutingModel(const RoutingIndexManager& index_manager, const RoutingModelParameters& parameters); ~RoutingModel(); - static RoutingModelParameters DefaultModelParameters(); - static RoutingSearchParameters DefaultSearchParameters(); + // Registers 'callback' and returns its index. + int RegisterUnaryTransitCallback(TransitCallback1 callback); + int RegisterTransitCallback(TransitCallback2 callback); + int RegisterPositiveTransitCallback(TransitCallback2 callback); + int RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback); + const TransitCallback2& TransitCallback(int callback_index) const { + CHECK_LT(callback_index, transit_evaluators_.size()); + return transit_evaluators_[callback_index]; + } + const TransitCallback1& UnaryTransitCallbackOrNull(int callback_index) const { + CHECK_LT(callback_index, unary_transit_evaluators_.size()); + return unary_transit_evaluators_[callback_index]; + } + const VariableIndexEvaluator2& StateDependentTransitCallback( + int callback_index) const { + CHECK_LT(callback_index, state_dependent_transit_evaluators_.size()); + return state_dependent_transit_evaluators_[callback_index]; + } // Model creation @@ -394,19 +402,17 @@ class RoutingModel { // Returns false if a dimension with the same name has already been created // (and doesn't create the new dimension). // Takes ownership of the callback 'evaluator'. - bool AddDimension(NodeEvaluator2* evaluator, int64 slack_max, int64 capacity, + bool AddDimension(int evaluator_index, int64 slack_max, int64 capacity, bool fix_start_cumul_to_zero, const std::string& name); bool AddDimensionWithVehicleTransits( - const std::vector& evaluators, int64 slack_max, + const std::vector& evaluator_indices, int64 slack_max, int64 capacity, bool fix_start_cumul_to_zero, const std::string& name); - // Takes ownership of both 'evaluator' and 'vehicle_capacity' callbacks. - bool AddDimensionWithVehicleCapacity(NodeEvaluator2* evaluator, - int64 slack_max, + bool AddDimensionWithVehicleCapacity(int evaluator_index, int64 slack_max, std::vector vehicle_capacities, bool fix_start_cumul_to_zero, const std::string& name); bool AddDimensionWithVehicleTransitAndCapacity( - const std::vector& evaluators, int64 slack_max, + const std::vector& evaluator_indices, int64 slack_max, std::vector vehicle_capacities, bool fix_start_cumul_to_zero, const std::string& name); // Creates a dimension where the transit variable is constrained to be @@ -445,17 +451,15 @@ class RoutingModel { bool AddMatrixDimension(std::vector> values, int64 capacity, bool fix_start_cumul_to_zero, const std::string& name); -#if !defined(SWIG) // Creates a dimension with transits depending on the cumuls of another // dimension. 'pure_transits' are the per-vehicle fixed transits as above. - // 'dependent_transits' is a vector containing for each vehicle a function - // taking two nodes and returning a function taking a dimension cumul value - // and returning a transit value. 'base_dimension' indicates the dimension - // from which the cumul variable is taken. If 'base_dimension' is nullptr, - // then the newly created dimension is self-based. + // 'dependent_transits' is a vector containing for each vehicle an index to a + // registered state dependent transit callback. 'base_dimension' indicates the + // dimension from which the cumul variable is taken. If 'base_dimension' is + // nullptr, then the newly created dimension is self-based. bool AddDimensionDependentDimensionWithVehicleCapacity( - const std::vector& pure_transits, - const std::vector& dependent_transits, + const std::vector& pure_transits, + const std::vector& dependent_transits, const RoutingDimension* base_dimension, int64 slack_max, std::vector vehicle_capacities, bool fix_start_cumul_to_zero, const std::string& name) { @@ -465,28 +469,36 @@ class RoutingModel { } // As above, but pure_transits are taken to be zero evaluators. bool AddDimensionDependentDimensionWithVehicleCapacity( - const std::vector& transits, - const RoutingDimension* base_dimension, int64 slack_max, - std::vector vehicle_capacities, bool fix_start_cumul_to_zero, - const std::string& name); + const std::vector& transits, const RoutingDimension* base_dimension, + int64 slack_max, std::vector vehicle_capacities, + bool fix_start_cumul_to_zero, const std::string& name); // Homogeneous versions of the functions above. bool AddDimensionDependentDimensionWithVehicleCapacity( - VariableNodeEvaluator2* transits, const RoutingDimension* base_dimension, - int64 slack_max, int64 vehicle_capacity, bool fix_start_cumul_to_zero, + int transit, const RoutingDimension* base_dimension, int64 slack_max, + int64 vehicle_capacity, bool fix_start_cumul_to_zero, const std::string& name); bool AddDimensionDependentDimensionWithVehicleCapacity( - NodeEvaluator2* pure_transits, VariableNodeEvaluator2* dependent_transits, + int pure_transit, int dependent_transit, const RoutingDimension* base_dimension, int64 slack_max, int64 vehicle_capacity, bool fix_start_cumul_to_zero, const std::string& name); + // Creates a cached StateDependentTransit from an std::function. static RoutingModel::StateDependentTransit MakeStateDependentTransit( const std::function& f, int64 domain_start, int64 domain_end); -#endif // SWIG + // Outputs the names of all dimensions added to the routing engine. // TODO(user): rename. std::vector<::std::string> GetAllDimensionNames() const; + // Returns all dimensions of the model. + const std::vector& GetDimensions() const { + return dimensions_.get(); + } + // Returns dimensions with soft and vehicle span costs. + std::vector GetDimensionsWithSoftAndSpanCosts() const; + // Returns dimensions with soft or vehicle span costs. + std::vector GetDimensionsWithSoftOrSpanCosts() const; // Returns true if a dimension exists for a given dimension name. bool HasDimension(const std::string& dimension_name) const; // Returns a dimension from its name. Dies if the dimension does not exist. @@ -509,48 +521,39 @@ class RoutingModel { const std::string& GetPrimaryConstrainedDimension() const { return primary_constrained_dimension_; } - // Constrains all nodes to be active (to belong to a route). - void AddAllActive(); - // Adds a disjunction constraint on the nodes: exactly one of the nodes is - // active. Start and end nodes of any vehicle cannot be part of a disjunction. - void AddDisjunction(const std::vector& nodes); - // Adds a penalized disjunction constraint on the nodes: at most one of the - // nodes is active; if none are active a penalty cost is applied (this cost - // is added to the global cost function). + // Adds a disjunction constraint on the indices: exactly 'max_cardinality' of + // the indices are active. Start and end indices of any vehicle cannot be part + // of a disjunction. + // If a penalty is given, at most 'max_cardinality' of the indices can be + // active, and if less are active, 'penalty' is payed per inactive index. // This is equivalent to adding the constraint: - // p + Sum(i)active[i] == 1, where p is a boolean variable - // and the following cost to the cost function: - // p * penalty. - // "penalty" must be positive to make the disjunction optional; a negative - // penalty will force one node of the disjunction to be performed, and - // therefore p == 0. - // Note: passing a vector with a single node will model an optional node + // p + Sum(i)active[i] == max_cardinality + // where p is an integer variable, and the following cost to the cost + // function: + // p * penalty. + // 'penalty' must be positive to make the disjunction optional; a negative + // penalty will force 'max_cardinality' indices of the disjunction to be + // performed, and therefore p == 0. + // Note: passing a vector with a single index will model an optional index // with a penalty cost if it is not visited. - void AddDisjunction(const std::vector& nodes, int64 penalty); - // Same as above except that instead of allowing at most one of the nodes to - // be active, the maximum number of active nodes is max_cardinality. - void AddDisjunction(const std::vector& nodes, int64 penalty, - int64 max_cardinality); - // Returns the indices of the disjunctions to which a node belongs. - const std::vector& GetDisjunctionIndicesFromNode( - NodeIndex node) const { - return GetDisjunctionIndicesFromVariableIndex(NodeToIndex(node)); - } - const std::vector& GetDisjunctionIndicesFromVariableIndex( + DisjunctionIndex AddDisjunction(const std::vector& indices, + int64 penalty = kNoPenalty, + int64 max_cardinality = 1); + // Returns the indices of the disjunctions to which an index belongs. + const std::vector& GetDisjunctionIndices( int64 index) const { - return node_to_disjunctions_[index]; + return index_to_disjunctions_[index]; } - // Calls f for each variable index of nodes in the same disjunctions as the + // Calls f for each variable index of indices in the same disjunctions as the // node corresponding to the variable index 'index'; only disjunctions of // cardinality 'cardinality' are considered. template void ForEachNodeInDisjunctionWithMaxCardinalityFromIndex( int64 index, int64 max_cardinality, F f) const { - for (const DisjunctionIndex disjunction : - GetDisjunctionIndicesFromVariableIndex(index)) { + for (const DisjunctionIndex disjunction : GetDisjunctionIndices(index)) { if (disjunctions_[disjunction].value.max_cardinality == max_cardinality) { - for (const int node : disjunctions_[disjunction].nodes) { - f(node); + for (const int64 d_index : disjunctions_[disjunction].indices) { + f(d_index); } } } @@ -558,8 +561,9 @@ class RoutingModel { #if !defined(SWIGPYTHON) // Returns the variable indices of the nodes in the disjunction of index // 'index'. - const std::vector& GetDisjunctionIndices(DisjunctionIndex index) const { - return disjunctions_[index].nodes; + const std::vector& GetDisjunctionIndices( + DisjunctionIndex index) const { + return disjunctions_[index].indices; } #endif // !defined(SWIGPYTHON) // Returns the penalty of the node disjunction of index 'index'. @@ -577,7 +581,7 @@ class RoutingModel { // indices: a disjunction is "perfect" when its variables do not appear in // any other disjunction. Each pair is sorted (lowest variable index first), // and the output vector is also sorted (lowest pairs first). - std::vector> GetPerfectBinaryDisjunctions() const; + std::vector> GetPerfectBinaryDisjunctions() const; // SPECIAL: Makes the solver ignore all the disjunctions whose active // variables are all trivially zero (i.e. Max() == 0), by setting their // max_cardinality to 0. @@ -585,35 +589,91 @@ class RoutingModel { // operators, in the context of arc-based routing. void IgnoreDisjunctionsAlreadyForcedToZero(); - // Adds a soft contraint to force a set of nodes to be on the same vehicle. - // If all nodes are not on the same vehicle, each extra vehicle used adds - // 'cost' to the cost function. - void AddSoftSameVehicleConstraint(const std::vector& nodes, + // Adds a soft contraint to force a set of variable indices to be on the same + // vehicle. If all nodes are not on the same vehicle, each extra vehicle used + // adds 'cost' to the cost function. + void AddSoftSameVehicleConstraint(const std::vector& indices, int64 cost); - // Notifies that node1 and node2 form a pair of nodes which should belong + // Sets the vehicles which can visit a given node. If the node is in a + // disjunction, this will not prevent it from being unperformed. + // Specifying an empty vector of vehicles has no effect (all vehicles + // will be allowed to visit the node). + void SetAllowedVehiclesForIndex(const std::vector& vehicles, + int64 index); + + // Returns true if a vehicle is allowed to visit a given node. + bool IsVehicleAllowedForIndex(int vehicle, int64 index) { + return allowed_vehicles_[index].empty() || + allowed_vehicles_[index].find(vehicle) != + allowed_vehicles_[index].end(); + } + + // Notifies that index1 and index2 form a pair of nodes which should belong // to the same route. This methods helps the search find better solutions, // especially in the local search phase. // It should be called each time you have an equality constraint linking // the vehicle variables of two node (including for instance pickup and // delivery problems): // Solver* const solver = routing.solver(); + // int64 index1 = manager.NodeToIndex(node1); + // int64 index2 = manager.NodeToIndex(node2); // solver->AddConstraint(solver->MakeEquality( - // routing.VehicleVar(routing.NodeToIndex(node1)), - // routing.VehicleVar(routing.NodeToIndex(node2)))); - // solver->AddPickupAndDelivery(node1, node2); + // routing.VehicleVar(index1), + // routing.VehicleVar(index2))); + // routing.AddPickupAndDelivery(index1, index2); // // TODO(user): Remove this when model introspection detects linked nodes. - void AddPickupAndDelivery(NodeIndex node1, NodeIndex node2) { - pickup_delivery_pairs_.push_back( - {{NodeToIndex(node1)}, {NodeToIndex(node2)}}); - } + void AddPickupAndDelivery(int64 pickup, int64 delivery); + // Same as AddPickupAndDelivery but notifying that the performed node from + // the disjunction of index 'pickup_disjunction' is on the same route as the + // performed node from the disjunction of index 'delivery_disjunction'. + void AddPickupAndDeliverySets(DisjunctionIndex pickup_disjunction, + DisjunctionIndex delivery_disjunction); + // clang-format off + // Returns pairs for which the node is a pickup; the first element of each + // pair is the index in the pickup and delivery pairs list in which the pickup + // appears, the second element is its index in the pickups list. + const std::vector >& + GetPickupIndexPairs(int64 node_index) const; + // Same as above for deliveries. + const std::vector >& + GetDeliveryIndexPairs(int64 node_index) const; + // clang-format on + #ifndef SWIG // Returns pickup and delivery pairs currently in the model. - const NodePairs& GetPickupAndDeliveryPairs() const { + const IndexPairs& GetPickupAndDeliveryPairs() const { return pickup_delivery_pairs_; } -#endif + const std::vector>& + GetPickupAndDeliveryDisjunctions() const { + return pickup_delivery_disjunctions_; + } + // Returns the number of non-start/end nodes which do not appear in a + // pickup/delivery pair. + int GetNumOfSingletonNodes() const; + void SetPickupAndDeliveryPolicyOfVehicle(PickupAndDeliveryPolicy policy, + int vehicle) { + vehicle_pickup_delivery_policy_[vehicle] = policy; + } + PickupAndDeliveryPolicy GetPickupAndDeliveryPolicyOfVehicle( + int vehicle) const { + return vehicle_pickup_delivery_policy_[vehicle]; + } +#endif // SWIG + // Set the node visit types and incompatibilities between the types. + // Two nodes with incompatible types cannot be visited by the same vehicle. + // TODO(user): Forbid incompatible types from being on the same route at + // the same time (instead of at any time). + // The visit type of a node must be positive. + // TODO(user): Support multiple visit types per node? + void SetVisitType(int64 index, int type); + int GetVisitType(int64 index) const; + void AddTypeIncompatibility(int type1, int type2); + // Returns visit types incompatible to a given type. + const absl::flat_hash_set& GetTypeIncompatibilities(int type) const; + int GetNumberOfVisitTypes() const { return num_visit_types_; } // Get the "unperformed" penalty of a node. This is only well defined if the // node is only part of a single Disjunction involving only itself, and that // disjunction has a penalty. In all other cases, including forced active @@ -631,11 +691,9 @@ class RoutingModel { // Sets the cost function of the model such that the cost of a segment of a // route between node 'from' and 'to' is evaluator(from, to), whatever the // route or vehicle performing the route. - // Takes ownership of the callback 'evaluator'. - void SetArcCostEvaluatorOfAllVehicles(NodeEvaluator2* evaluator); + void SetArcCostEvaluatorOfAllVehicles(int evaluator_index); // Sets the cost function for a given vehicle route. - // Takes ownership of the callback 'evaluator'. - void SetArcCostEvaluatorOfVehicle(NodeEvaluator2* evaluator, int vehicle); + void SetArcCostEvaluatorOfVehicle(int evaluator_index, int vehicle); // Sets the fixed cost of all vehicle routes. It is equivalent to calling // SetFixedCostOfVehicle on all vehicle routes. void SetFixedCostOfAllVehicles(int64 cost); @@ -646,10 +704,37 @@ class RoutingModel { // the first and last nodes. int64 GetFixedCostOfVehicle(int vehicle) const; + // The following methods set the linear and quadratic cost factors of vehicles + // (must be positive values). The default value of these parameters is zero + // for all vehicles. + // When set, the cost_ of the model will contain terms aiming at reducing the + // number of vehicles used in the model, by adding the following to the + // objective for every vehicle v: + // INDICATOR(v used in the model) * + // [linear_cost_factor_of_vehicle_[v] + // - quadratic_cost_factor_of_vehicle_[v]*(square of length of route v)] + // i.e. for every used vehicle, we add the linear factor as fixed cost, and + // subtract the square of the route length multiplied by the quadratic factor. + // This second term aims at making the routes as dense as possible. + // + // Sets the linear and quadratic cost factor of all vehicles. + void SetAmortizedCostFactorsOfAllVehicles(int64 linear_cost_factor, + int64 quadratic_cost_factor); + // Sets the linear and quadratic cost factor of the given vehicle. + void SetAmortizedCostFactorsOfVehicle(int64 linear_cost_factor, + int64 quadratic_cost_factor, + int vehicle); + + const std::vector& GetAmortizedLinearCostFactorOfVehicles() const { + return linear_cost_factor_of_vehicle_; + } + const std::vector& GetAmortizedQuadraticCostFactorOfVehicles() const { + return quadratic_cost_factor_of_vehicle_; + } + // Search -// Gets/sets the evaluator used when the first solution heuristic is set to -// ROUTING_EVALUATOR_STRATEGY (variant of ROUTING_PATH_CHEAPEST_ARC using -// 'evaluator' to sort node segments). +// Gets/sets the evaluator used during the search. Only relevant when +// RoutingSearchParameters.first_solution_strategy = EVALUATOR_STRATEGY. #ifndef SWIG const Solver::IndexEvaluator2& first_solution_evaluator() const { return first_solution_evaluator_; @@ -681,7 +766,7 @@ class RoutingModel { // available. Note that CloseModel() is automatically called by Solve() and // other methods that produce solution. // This is equivalent to calling - // CloseModelWithParameters(DefaultSearchParameters()). + // CloseModelWithParameters(DefaultRoutingSearchParameters()). void CloseModel(); // Same as above taking search parameters (as of 10/2015 some the parameters // have to be set when closing the model). @@ -689,16 +774,32 @@ class RoutingModel { const RoutingSearchParameters& search_parameters); // Solves the current routing model; closes the current model. // This is equivalent to calling - // SolveWithParameters(DefaultSearchParameters()) + // SolveWithParameters(DefaultRoutingSearchParameters()) // or - // SolveFromAssignmentWithParameters(assignment, DefaultSearchParameters()). + // SolveFromAssignmentWithParameters(assignment, + // DefaultRoutingSearchParameters()). const Assignment* Solve(const Assignment* assignment = nullptr); - // Solves the current routing model with the given parameters. + // Solves the current routing model with the given parameters. If 'solutions' + // is specified, it will contain the k best solutions found during the search + // (from worst to best, including the one returned by this method), where k + // corresponds to the 'number_of_solutions_to_collect' in 'search_parameters'. + // Note that the Assignment returned by the method and the ones in solutions + // are owned by the underlying solver and should not be deleted. const Assignment* SolveWithParameters( - const RoutingSearchParameters& search_parameters); + const RoutingSearchParameters& search_parameters, + std::vector* solutions = nullptr); const Assignment* SolveFromAssignmentWithParameters( const Assignment* assignment, - const RoutingSearchParameters& search_parameters); + const RoutingSearchParameters& search_parameters, + std::vector* solutions = nullptr); + // Given a "source_model" and its "source_assignment", resets + // "target_assignment" with the IntVar variables (nexts_, and vehicle_vars_ + // if costs aren't homogeneous across vehicles) of "this" model, with the + // values set according to those in "other_assignment". + // The objective_element of target_assignment is set to this->cost_. + void SetAssignmentFromOtherModelAssignment( + Assignment* target_assignment, const RoutingModel* source_model, + const Assignment* source_assignment); // Computes a lower bound to the routing problem solving a linear assignment // problem. The routing model must be closed before calling this method. // Note that problems with node disjunction constraints (including optional @@ -715,7 +816,7 @@ class RoutingModel { // Returns the next variable at the end of the locked chain; this variable is // not locked. An assignment containing the locks can be obtained by calling // PreAssignment(). - IntVar* ApplyLocks(const std::vector& locks); + IntVar* ApplyLocks(const std::vector& locks); // Applies lock chains to all vehicles to the next search, such that locks[p] // is the lock chain for route p. Returns false if the locks do not contain // valid routes; expects that the routes do not contain the depots, @@ -724,7 +825,7 @@ class RoutingModel { // vehicle and deactivates other nodes. // An assignment containing the locks can be obtained by calling // PreAssignment(). - bool ApplyLocksToAllVehicles(const std::vector>& locks, + bool ApplyLocksToAllVehicles(const std::vector>& locks, bool close_routes); // Returns an assignment used to fix some of the variables of the problem. // In practice, this assignment locks partial routes of the problem. This @@ -743,41 +844,38 @@ class RoutingModel { // Restores an assignment as a solution in the routing model and returns the // new solution. Returns nullptr if the assignment is not valid. Assignment* RestoreAssignment(const Assignment& solution); - // Restores the routes as the current solution. Returns nullptr if - // the solution cannot be restored (routes do not contain a valid - // solution). Note that calling this method will run the solver to - // assign values to the dimension variables; this may take - // considerable amount of time, especially when using dimensions - // with slack. + // Restores the routes as the current solution. Returns nullptr if the + // solution cannot be restored (routes do not contain a valid solution). Note + // that calling this method will run the solver to assign values to the + // dimension variables; this may take considerable amount of time, especially + // when using dimensions with slack. Assignment* ReadAssignmentFromRoutes( - const std::vector>& routes, - bool ignore_inactive_nodes); + const std::vector>& routes, + bool ignore_inactive_indices); // Fills an assignment from a specification of the routes of the - // vehicles. The routes are specified as lists of nodes that appear + // vehicles. The routes are specified as lists of variable indices that appear // on the routes of the vehicles. The indices of the outer vector in - // 'routes' correspond to vehicles IDs, the inner vector contain the - // nodes on the routes for the given vehicle. The inner vectors must - // not contain the start and end nodes, as these are determined by - // the routing model. Sets the value of NextVars in the assignment, - // adding the variables to the assignment if necessary. The method - // does not touch other variables in the assignment. The method can - // only be called after the model is closed. With - // ignore_inactive_nodes set to false, this method will fail (return - // nullptr) in case some of the route contain nodes that are - // deactivated in the model; when set to true, these nodes will be + // 'routes' correspond to vehicles IDs, the inner vector contains the + // variable indices on the routes for the given vehicle. The inner vectors + // must not contain the start and end indices, as these are determined by the + // routing model. Sets the value of NextVars in the assignment, adding the + // variables to the assignment if necessary. The method does not touch other + // variables in the assignment. The method can only be called after the model + // is closed. With ignore_inactive_indices set to false, this method will + // fail (return nullptr) in case some of the route contain indices that are + // deactivated in the model; when set to true, these indices will be // skipped. Returns true if routes were successfully // loaded. However, such assignment still might not be a valid // solution to the routing problem due to more complex constraints; // it is advisible to call solver()->CheckSolution() afterwards. - bool RoutesToAssignment(const std::vector>& routes, - bool ignore_inactive_nodes, bool close_routes, + bool RoutesToAssignment(const std::vector>& routes, + bool ignore_inactive_indices, bool close_routes, Assignment* const assignment) const; // Converts the solution in the given assignment to routes for all vehicles. // Expects that assignment contains a valid solution (i.e. routes for all - // vehicles end with an end node for that vehicle). - void AssignmentToRoutes( - const Assignment& assignment, - std::vector>* const routes) const; + // vehicles end with an end index for that vehicle). + void AssignmentToRoutes(const Assignment& assignment, + std::vector>* const routes) const; // Returns a compacted version of the given assignment, in which all vehicles // with id lower or equal to some N have non-empty routes, and all vehicles // with id greater than N have empty routes. Does not take ownership of the @@ -886,6 +984,15 @@ class RoutingModel { DCHECK(closed_); return cost_class_index_of_vehicle_[vehicle]; } + // Returns true iff the model contains a vehicle with the given + // cost_class_index. + bool HasVehicleWithCostClassIndex(CostClassIndex cost_class_index) const { + DCHECK(closed_); + if (cost_class_index == kCostClassIndexOfZeroCost) { + return has_vehicle_with_zero_cost_class_; + } + return cost_class_index < cost_classes_.size(); + } // Returns the number of different cost classes in the model. int GetCostClassesCount() const { return cost_classes_.size(); } // Ditto, minus the 'always zero', built-in cost class. @@ -934,6 +1041,9 @@ class RoutingModel { // constraints and/or modify search algoithms. Solver* solver() const { return solver_.get(); } + // Returns true if the search limit has been crossed. + bool CheckLimit() { return limit_->Check(); } + // Sizes and indices // Returns the number of nodes in the model. int nodes() const { return nodes_; } @@ -941,17 +1051,6 @@ class RoutingModel { int vehicles() const { return vehicles_; } // Returns the number of next variables in the model. int64 Size() const { return nodes_ + vehicles_ - start_end_count_; } - // Returns the node index from an index value resulting from a next variable. - NodeIndex IndexToNode(int64 index) const; - // Returns the variable index from a node value. - // Should not be used for nodes at the start / end of a route, - // because of node multiplicity. These cases return -1, which is - // considered a failure case. Clients who need start and end - // variable indices should use RoutingModel::Start and RoutingModel::End. - int64 NodeToIndex(NodeIndex node) const; - // Returns true if the node can be safely converted to variable index. All - // nodes that are not end of a route are safe. - bool HasIndex(NodeIndex node) const; // Returns statistics on first solution search, number of decisions sent to // filters, number of decisions rejected by filters. @@ -960,57 +1059,17 @@ class RoutingModel { int64 GetNumberOfRejectsInFirstSolution( const RoutingSearchParameters& search_parameters) const; - // Internal only: initializes the builders used to build a solver model from - // CpModels. - static void InitializeBuilders(Solver* solver); - - // DEPRECATED METHODS. - // Don't use these methods; instead use their proposed replacement (in - // comments). - // TODO(user): only keep those for the open-source export. - void SetCost(NodeEvaluator2* e); // SetArcCostEvaluatorOfAllVehicles() - // SetArcCostEvaluatorOfVehicle() - void SetVehicleCost(int v, NodeEvaluator2* e); - int64 GetRouteFixedCost() const; // GetFixedCostOfVehicle() - void SetRouteFixedCost(int64 c); // SetFixedCostOfAllVehicles() - int64 GetVehicleFixedCost(int v) const; // GetFixedCostOfVehicle() - void SetVehicleFixedCost(int v, int64 c); // SetFixedCostOfVehicle() - bool homogeneous_costs() const; // CostsAreHomogeneousAcrossVehicles() - int GetVehicleCostCount() const; // GetNonZeroCostClassesCount() - int64 GetCost(int64 i, int64 j, int64 v); // GetArcCostForVehicle() - int64 GetVehicleClassCost(int64 i, int64 j, int64 c); // GetArcCostForClass() - - // All the methods below are replaced by public methods of the - // RoutingDimension class. See that class. - void SetDimensionTransitCost(const std::string& d, int64 c); - int64 GetDimensionTransitCost(const std::string& d) const; - void SetDimensionSpanCost(const std::string& d, int64 c); - int64 GetDimensionSpanCost(const std::string& d) const; - int64 GetTransitValue(const std::string& d, int64 from, int64 to, - int64 vehicle) const; + // Returns true if a vehicle/node matching problem is detected. + bool IsMatchingModel() const; + #ifndef SWIG - const std::vector& CumulVars( - const std::string& dimension_name) const; -#endif - // All the methods below are replaced by public methods with the same name - // on the RoutingDimension class. See those. - IntVar* CumulVar(int64 index, const std::string& dimension_name) const; - IntVar* TransitVar(int64 index, const std::string& dimension_name) const; - IntVar* SlackVar(int64 index, const std::string& dimension_name) const; - void SetCumulVarSoftUpperBound(NodeIndex, const std::string&, int64, int64); - bool HasCumulVarSoftUpperBound(NodeIndex, const std::string&) const; - int64 GetCumulVarSoftUpperBound(NodeIndex, const std::string&) const; - int64 GetCumulVarSoftUpperBoundCoefficient(NodeIndex, - const std::string&) const; - void SetStartCumulVarSoftUpperBound(int, const std::string&, int64, int64); - bool HasStartCumulVarSoftUpperBound(int, const std::string&) const; - int64 GetStartCumulVarSoftUpperBound(int, const std::string&) const; - int64 GetStartCumulVarSoftUpperBoundCoefficient(int, - const std::string&) const; - void SetEndCumulVarSoftUpperBound(int, const std::string&, int64, int64); - bool HasEndCumulVarSoftUpperBound(int, const std::string&) const; - int64 GetEndCumulVarSoftUpperBound(int, const std::string&) const; - int64 GetEndCumulVarSoftUpperBoundCoefficient(int, const std::string&) const; + // Sets the callback returning the variable to use for the Tabu Search + // metaheuristic. + using GetTabuVarsCallback = + std::function(RoutingModel*)>; + + void SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback); +#endif // SWIG // The next few members are in the public section only for testing purposes. // TODO(user): Find a way to test and restrict the access at the same time. @@ -1057,12 +1116,15 @@ class RoutingModel { enum RoutingLocalSearchOperator { RELOCATE = 0, RELOCATE_PAIR, + LIGHT_RELOCATE_PAIR, RELOCATE_NEIGHBORS, EXCHANGE, + EXCHANGE_PAIR, CROSS, CROSS_EXCHANGE, TWO_OPT, OR_OPT, + RELOCATE_EXPENSIVE_CHAIN, LIN_KERNIGHAN, TSP_OPT, MAKE_ACTIVE, @@ -1077,14 +1139,16 @@ class RoutingModel { FULL_PATH_LNS, TSP_LNS, INACTIVE_LNS, + EXCHANGE_RELOCATE_PAIR, LOCAL_SEARCH_OPERATOR_COUNTER }; - // Structure storing a value for a set of nodes. Is used to store data for - // node disjunctions (nodes, max_cardinality and penalty when unperformed). + // Structure storing a value for a set of variable indices. Is used to store + // data for index disjunctions (variable indices, max_cardinality and penalty + // when unperformed). template struct ValuedNodes { - std::vector nodes; + std::vector indices; T value; }; struct DisjunctionValues { @@ -1107,29 +1171,23 @@ class RoutingModel { // Internal methods. void Initialize(); - void SetStartEnd( - const std::vector>& start_end); - void AddDisjunctionInternal(const std::vector& nodes, - int64 penalty, int64 max_cardinality); void AddNoCycleConstraintInternal(); bool AddDimensionWithCapacityInternal( - const std::vector& evaluators, int64 slack_max, + const std::vector& evaluator_indices, int64 slack_max, std::vector vehicle_capacities, bool fix_start_cumul_to_zero, - const std::string& dimension_name); + const std::string& name); bool AddDimensionDependentDimensionWithVehicleCapacityInternal( - const std::vector& pure_transits, - const std::vector& dependent_transits, + const std::vector& pure_transits, + const std::vector& dependent_transits, const RoutingDimension* base_dimension, int64 slack_max, std::vector vehicle_capacities, bool fix_start_cumul_to_zero, const std::string& name); bool InitializeDimensionInternal( - const std::vector& evaluators, - const std::vector& state_dependent_evaluators, + const std::vector& evaluator_indices, + const std::vector& state_dependent_evaluator_indices, int64 slack_max, bool fix_start_cumul_to_zero, RoutingDimension* dimension); DimensionIndex GetDimensionIndex(const std::string& dimension_name) const; - uint64 GetFingerprintOfEvaluator(NodeEvaluator2* evaluator, - bool fingerprint_arc_cost_evaluators) const; void ComputeCostClasses(const RoutingSearchParameters& parameters); void ComputeVehicleClasses(); int64 GetArcCostForClassInternal(int64 from_index, int64 to_index, @@ -1151,11 +1209,15 @@ class RoutingModel { const CostClass& cost_class) const; // Returns nullptr if no penalty cost, otherwise returns penalty variable. IntVar* CreateDisjunction(DisjunctionIndex disjunction); + // Sets up pickup and delivery sets. + void AddPickupAndDeliverySetsInternal(const std::vector& pickups, + const std::vector& deliveries); // Returns the cost variable related to the soft same vehicle constraint of - // index 'index'. - IntVar* CreateSameVehicleCost(int index); - // Returns the first active node in nodes starting from index + 1. - int FindNextActive(int index, const std::vector& nodes) const; + // index 'vehicle_index'. + IntVar* CreateSameVehicleCost(int vehicle_index); + // Returns the first active variable index in 'indices' starting from index + // + 1. + int FindNextActive(int index, const std::vector& indices) const; // Checks that all nodes on the route starting at start_index (using the // solution stored in assignment) can be visited by the given vehicle. @@ -1171,9 +1233,6 @@ class RoutingModel { bool ReplaceUnusedVehicle(int unused_vehicle, int active_vehicle, Assignment* compact_assignment) const; - NodeEvaluator2* NewCachedCallback(NodeEvaluator2* callback); - VariableNodeEvaluator2* NewCachedStateDependentCallback( - VariableNodeEvaluator2* callback); void QuietCloseModel(); void QuietCloseModelWithParameters( const RoutingSearchParameters& parameters) { @@ -1181,25 +1240,39 @@ class RoutingModel { CloseModelWithParameters(parameters); } } + + // Solve matching problem with min-cost flow and store result in assignment. + bool SolveMatchingModel(Assignment* assignment); +#ifndef SWIG + // Append an assignment to a vector of assignments if it is feasible. + bool AppendAssignmentIfFeasible( + const Assignment& assignment, + std::vector>* assignments); +#endif + // Log a solution. + void LogSolution(const std::string& description, int64 solution_cost, + int64 start_time_ms); // See CompactAssignment. Checks the final solution if // check_compact_assignement is true. Assignment* CompactAssignmentInternal(const Assignment& assignment, bool check_compact_assignment) const; - // Check that current search parameters are valid. If not, the status of the - // solver is set to ROUTING_INVALID. - bool ValidateSearchParameters( - const RoutingSearchParameters& search_parameters); + // Checks that the current search parameters are valid for the current model's + // specific settings. This assumes that FindErrorInSearchParameters() from + // ./routing_flags.h caught no error. + std::string FindErrorInSearchParametersForModel( + const RoutingSearchParameters& search_parameters) const; // Sets up search objects, such as decision builders and monitors. void SetupSearch(const RoutingSearchParameters& search_parameters); // Set of auxiliary methods used to setup the search. // TODO(user): Document each auxiliary method. Assignment* GetOrCreateAssignment(); + Assignment* GetOrCreateTmpAssignment(); SearchLimit* GetOrCreateLimit(); SearchLimit* GetOrCreateLocalSearchLimit(); SearchLimit* GetOrCreateLargeNeighborhoodSearchLimit(); LocalSearchOperator* CreateInsertionOperator(); LocalSearchOperator* CreateMakeInactiveOperator(); - void CreateNeighborhoodOperators(); + void CreateNeighborhoodOperators(const RoutingSearchParameters& parameters); LocalSearchOperator* GetNeighborhoodOperators( const RoutingSearchParameters& search_parameters) const; const std::vector& GetOrCreateLocalSearchFilters(); @@ -1217,13 +1290,14 @@ class RoutingModel { const RoutingSearchParameters& search_parameters); void SetupDecisionBuilders(const RoutingSearchParameters& search_parameters); void SetupMetaheuristics(const RoutingSearchParameters& search_parameters); - void SetupAssignmentCollector(); + void SetupAssignmentCollector( + const RoutingSearchParameters& search_parameters); void SetupTrace(const RoutingSearchParameters& search_parameters); void SetupSearchMonitors(const RoutingSearchParameters& search_parameters); bool UsesLightPropagation( const RoutingSearchParameters& search_parameters) const; + GetTabuVarsCallback tabu_var_callback_; - int64 GetArcCostForCostClassInternal(int64 i, int64 j, int64 cost_class); int GetVehicleStartClass(int64 start) const; void InitSameVehicleGroups(int number_of_groups) { @@ -1239,7 +1313,7 @@ class RoutingModel { std::unique_ptr solver_; int nodes_; int vehicles_; - Constraint* no_cycle_constraint_; + Constraint* no_cycle_constraint_ = nullptr; // Decision variables: indexed by int64 var index. std::vector nexts_; std::vector vehicle_vars_; @@ -1251,14 +1325,18 @@ class RoutingModel { std::vector is_bound_to_end_; RevSwitch is_bound_to_end_ct_added_; // Dimensions - std::unordered_map dimension_name_to_index_; + absl::flat_hash_map dimension_name_to_index_; gtl::ITIVector dimensions_; std::string primary_constrained_dimension_; // Costs - IntVar* cost_; - std::vector transit_cost_of_vehicle_; + IntVar* cost_ = nullptr; + std::vector vehicle_to_transit_cost_; std::vector fixed_cost_of_vehicle_; std::vector cost_class_index_of_vehicle_; + bool has_vehicle_with_zero_cost_class_; + std::vector linear_cost_factor_of_vehicle_; + std::vector quadratic_cost_factor_of_vehicle_; + bool vehicle_amortized_cost_factors_set_; #ifndef SWIG gtl::ITIVector cost_classes_; #endif // SWIG @@ -1270,32 +1348,59 @@ class RoutingModel { gtl::ITIVector vehicle_classes_; #endif // SWIG std::function vehicle_start_class_callback_; - // Cached callbacks - std::unordered_map - cached_node_callbacks_; - std::unordered_map - cached_state_dependent_callbacks_; // Disjunctions gtl::ITIVector disjunctions_; - std::vector> node_to_disjunctions_; + std::vector> index_to_disjunctions_; // Same vehicle costs std::vector> same_vehicle_costs_; + // Allowed vehicles +#ifndef SWIG + std::vector> allowed_vehicles_; +#endif // SWIG // Pickup and delivery - NodePairs pickup_delivery_pairs_; + IndexPairs pickup_delivery_pairs_; + std::vector> + pickup_delivery_disjunctions_; + // clang-format off + // If node_index is a pickup, index_to_pickup_index_pairs_[node_index] is the + // vector of pairs {pair_index, pickup_index} such that + // (pickup_delivery_pairs_[pair_index].first)[pickup_index] == node_index + std::vector > > index_to_pickup_index_pairs_; + // Same as above for deliveries. + std::vector > > + index_to_delivery_index_pairs_; + // clang-format on +#ifndef SWIG + std::vector vehicle_pickup_delivery_policy_; +#endif // SWIG // Same vehicle group to which a node belongs. std::vector same_vehicle_group_; // Same vehicle node groups. std::vector> same_vehicle_groups_; - // Index management - std::vector index_to_node_; - gtl::ITIVector node_to_index_; + // Node visit types + // Variable index to visit type index. + std::vector index_to_visit_type_; + // clang-format off + std::vector > incompatible_types_per_type_index_; + // clang-format on + // Empty set used in GetTypeIncompatibilities() when the given type has no + // incompatibilities. + const absl::flat_hash_set empty_incompatibilities_; + int num_visit_types_; + // Two indices are equivalent if they correspond to the same node (as given to + // the constructors taking a RoutingIndexManager). + std::vector index_to_equivalence_class_; std::vector index_to_vehicle_; std::vector starts_; std::vector ends_; + // TODO(user): b/62478706 Once the port is done, this shouldn't be needed + // anymore. + RoutingIndexManager manager_; int start_end_count_; // Model status - bool closed_; - Status status_; + bool closed_ = false; + Status status_ = ROUTING_NOT_SOLVED; + bool enable_deep_serialization_ = true; // Search data std::vector first_solution_decision_builders_; @@ -1304,12 +1409,15 @@ class RoutingModel { Solver::IndexEvaluator2 first_solution_evaluator_; std::vector local_search_operators_; std::vector monitors_; - SolutionCollector* collect_assignments_; - DecisionBuilder* solve_db_; - DecisionBuilder* improve_db_; - DecisionBuilder* restore_assignment_; - Assignment* assignment_; - Assignment* preassignment_; + SolutionCollector* collect_assignments_ = nullptr; + SolutionCollector* collect_one_assignment_ = nullptr; + DecisionBuilder* solve_db_ = nullptr; + DecisionBuilder* improve_db_ = nullptr; + DecisionBuilder* restore_assignment_ = nullptr; + DecisionBuilder* restore_tmp_assignment_ = nullptr; + Assignment* assignment_ = nullptr; + Assignment* preassignment_ = nullptr; + Assignment* tmp_assignment_ = nullptr; std::vector extra_vars_; std::vector extra_intervals_; std::vector extra_operators_; @@ -1322,14 +1430,31 @@ class RoutingModel { std::unique_ptr sweep_arranger_; #endif - SearchLimit* limit_; - SearchLimit* ls_limit_; - SearchLimit* lns_limit_; - - // Callbacks to be deleted - std::unordered_set owned_node_callbacks_; - std::unordered_set - owned_state_dependent_callbacks_; + SearchLimit* limit_ = nullptr; + SearchLimit* ls_limit_ = nullptr; + SearchLimit* lns_limit_ = nullptr; + + typedef std::pair CacheKey; + typedef absl::flat_hash_map TransitCallbackCache; + typedef absl::flat_hash_map + StateDependentTransitCallbackCache; + + std::vector unary_transit_evaluators_; + std::vector transit_evaluators_; + // The following vector stores a boolean per transit_evaluator_, indicating + // whether the transits are all positive. + // is_transit_evaluator_positive_ will be set to true only when registering a + // callback via RegisterPositiveTransitCallback(), and to false otherwise. + // The actual positivity of the transit values will only be checked in debug + // mode, when calling RegisterPositiveTransitCallback(). + // Therefore, RegisterPositiveTransitCallback() should only be called when the + // transits are known to be positive, as the positivity of a callback will + // allow some improvements in the solver, but will entail in errors if the + // transits are falsely assumed positive. + std::vector is_transit_evaluator_positive_; + std::vector state_dependent_transit_evaluators_; + std::vector> + state_dependent_transit_evaluators_cache_; friend class RoutingDimension; friend class RoutingModelInspector; @@ -1343,6 +1468,125 @@ class RoutingModelVisitor : public BaseObject { // Constraint types. static const char kLightElement[]; static const char kLightElement2[]; + static const char kRemoveValues[]; +}; + +#if !defined(SWIG) +// This class acts like a CP propagator: it takes a set of tasks given by +// their start/duration/end features, and reduces the range of possible values. +class DisjunctivePropagator { + public: + // A structure to hold tasks described by their features. + // The first num_chain_tasks are considered linked by a chain of precedences, + // i.e. if i < j < num_chain_tasks, then end(i) <= start(j). + // This occurs frequently in routing, and can be leveraged by + // some variants of classic propagators. + struct Tasks { + int num_chain_tasks = 0; + std::vector start_min; + std::vector duration_min; + std::vector end_max; + std::vector is_preemptible; + std::vector forbidden_intervals; + }; + + // Computes new bounds for all tasks, returns false if infeasible. + // This does not compute a fixed point, so recalling it may filter more. + bool Propagate(Tasks* tasks); + + // Propagates the deductions from the chain of precedences, if there is one. + bool Precedences(Tasks* tasks); + // Transforms the problem with a time symmetry centered in 0. Returns true for + // convenience. + bool MirrorTasks(Tasks* tasks); + // Does edge-finding deductions on all tasks. + bool EdgeFinding(Tasks* tasks); + // Does detectable precedences deductions on tasks in the chain precedence, + // taking the time windows of nonchain tasks into account. + bool DetectablePrecedencesWithChain(Tasks* tasks); + // Tasks might have holes in their domain, this enforces such holes. + bool ForbiddenIntervals(Tasks* tasks); + + private: + // The main algorithm uses Vilim's theta tree data structure. + // See Petr Vilim's PhD thesis "Global Constraints in Scheduling". + sat::ThetaLambdaTree theta_lambda_tree_; + // Mappings between events and tasks. + std::vector tasks_by_start_min_; + std::vector tasks_by_end_max_; + std::vector event_of_task_; + std::vector nonchain_tasks_by_start_max_; +}; +#endif // !defined(SWIG) + +// GlobalVehicleBreaksConstraint ensures breaks constraints are enforced on +// all vehicles in the dimension passed to its constructor. +// It is intended to be used for dimensions representing time. +// A break constraint ensures break intervals fit on the route of a vehicle. +// For a given vehicle, it forces break intervals to be disjoint from visit +// intervals, where visit intervals start at CumulVar(node) and last for +// node_visit_transit[node]. Moreover, it ensures that there is enough time +// between two consecutive nodes of a route to do transit and vehicle breaks, +// i.e. if Next(nodeA) = nodeB, CumulVar(nodeA) = tA and CumulVar(nodeB) = tB, +// then SlackVar(nodeA) >= sum_{breaks \subseteq [tA, tB)} duration(break). +// TODO(user): This does not enforce vehicle breaks to be nonoverlapping, +// and supposes travel/service times to be feasible (e.g. with a PathCumul). +// This is probably the desired behaviour, because vehicle breaks will most +// likely be constrained with precedence relations that are stronger than +// a resource constraint. +class GlobalVehicleBreaksConstraint : public Constraint { + public: + explicit GlobalVehicleBreaksConstraint(const RoutingDimension* dimension); + + void Post() override; + void InitialPropagate() override; + + private: + void PropagateNode(int node); + void PropagateVehicle(int vehicle); + const RoutingModel* model_; + const RoutingDimension* const dimension_; + std::vector vehicle_demons_; + + // This translates pruning information to solver variables. + // This class should have been an interface + subclasses, + // but that would force pointers in the tasks_ vector, + // which means dynamic allocation. Here tasks_'s reserved size will + // adjust to usage and eventually no more dynamic allocation will be made. + class TaskTranslator { + public: + TaskTranslator(IntVar* start, int64 duration_min) + : start_(start), duration_min_(duration_min) {} + explicit TaskTranslator(IntervalVar* interval) : interval_(interval) {} + TaskTranslator() {} + + void SetStartMin(int64 value) { + if (start_ != nullptr) { + start_->SetMin(value); + } else if (interval_ != nullptr) { + interval_->SetStartMin(value); + } + } + void SetEndMax(int64 value) { + if (start_ != nullptr) { + start_->SetMax(value - duration_min_); + } else if (interval_ != nullptr) { + interval_->SetEndMax(value); + } + } + + private: + IntVar* start_ = nullptr; + int64 duration_min_; + IntervalVar* interval_ = nullptr; + }; + + // Route and interval variables are normalized to the following values. + std::vector task_translators_; + + // This is used to restrict bounds of tasks. + DisjunctivePropagator disjunctive_propagator_; + DisjunctivePropagator::Tasks tasks_; }; // Dimensions represent quantities accumulated at nodes along the routes. They @@ -1361,6 +1605,9 @@ class RoutingModelVisitor : public BaseObject { // version of transits_. Favour transits over state_dependent_transits when // possible, because purely functional callbacks allow more optimisations and // make the model faster and easier to solve. +// TODO(user): Break constraints need to know the service time of nodes +// for a given vehicle, it is passed as an external vector, it would be better +// to have this information here. class RoutingDimension { public: ~RoutingDimension(); @@ -1374,7 +1621,8 @@ class RoutingDimension { // vehicle (the class of a vehicle can be obtained with vehicle_to_class()). int64 GetTransitValueFromClass(int64 from_index, int64 to_index, int64 vehicle_class) const { - return class_evaluators_[vehicle_class](from_index, to_index); + return model_->TransitCallback(class_evaluators_[vehicle_class])(from_index, + to_index); } // Get the cumul, transit and slack variables for the given node (given as // int64 var index). @@ -1399,8 +1647,9 @@ class RoutingDimension { } // Returns the callback evaluating the transit value between two node indices // for a given vehicle. - const RoutingModel::TransitEvaluator2& transit_evaluator(int vehicle) const { - return class_evaluators_[vehicle_to_class_[vehicle]]; + const RoutingModel::TransitCallback2& transit_evaluator(int vehicle) const { + return model_->TransitCallback( + class_evaluators_[vehicle_to_class_[vehicle]]); } int vehicle_to_class(int vehicle) const { return vehicle_to_class_[vehicle]; } #endif // !defined(SWIGCSHARP) && !defined(SWIGJAVA) @@ -1426,112 +1675,44 @@ class RoutingDimension { void SetGlobalSpanCostCoefficient(int64 coefficient); #ifndef SWIG - // Sets a piecewise linear cost on the cumul variable of a given node. - // If f is a piecewsie linear function, the resulting cost at node n will be - // f(CumulVar(n)). - // As of 3/2017, only non-decreasing positive cost functions are supported. - void SetCumulVarPiecewiseLinearCost(RoutingModel::NodeIndex node, + // Sets a piecewise linear cost on the cumul variable of a given variable + // index. If f is a piecewise linear function, the resulting cost at 'index' + // will be f(CumulVar(index)). As of 3/2017, only non-decreasing positive cost + // functions are supported. + void SetCumulVarPiecewiseLinearCost(int64 index, const PiecewiseLinearFunction& cost); - // Same as SetCumulVarPiecewiseLinearCost applied to vehicle start node, - void SetStartCumulVarPiecewiseLinearCost(int vehicle, - const PiecewiseLinearFunction& cost); - // Same as SetCumulVarPiecewiseLinearCost applied to vehicle end node, - void SetEndCumulVarPiecewiseLinearCost(int vehicle, - const PiecewiseLinearFunction& cost); - // Same as above but using a variable index. - void SetCumulVarPiecewiseLinearCostFromIndex( - int64 index, const PiecewiseLinearFunction& cost); - // Returns true if a piecewise linear cost has been set for a given node. - bool HasCumulVarPiecewiseLinearCost(RoutingModel::NodeIndex node) const; - // Returns true if a piecewise linear cost has been set for a given vehicle - // start node. - bool HasStartCumulVarPiecewiseLinearCost(int vehicle) const; - // Returns true if a piecewise linear cost has been set for a given vehicle - // end node. - bool HasEndCumulVarPiecewiseLinearCost(int vehicle) const; // Returns true if a piecewise linear cost has been set for a given variable // index. - bool HasCumulVarPiecewiseLinearCostFromIndex(int64 index) const; - // Returns the piecewise linear cost of a cumul variable for a given node. - const PiecewiseLinearFunction* GetCumulVarPiecewiseLinearCost( - RoutingModel::NodeIndex node) const; - // Returns the piecewise linear cost of a cumul variable for a given vehicle - // start node. - const PiecewiseLinearFunction* GetStartCumulVarPiecewiseLinearCost( - int vehicle) const; - // Returns the piecewise linear cost of a cumul variable for a given vehicle - // end node. - const PiecewiseLinearFunction* GetEndCumulVarPiecewiseLinearCost( - int vehicle) const; + bool HasCumulVarPiecewiseLinearCost(int64 index) const; // Returns the piecewise linear cost of a cumul variable for a given variable - // index. - const PiecewiseLinearFunction* GetCumulVarPiecewiseLinearCostFromIndex( + // index. The returned pointer has the same validity as this class. + const PiecewiseLinearFunction* GetCumulVarPiecewiseLinearCost( int64 index) const; #endif - // Sets a soft upper bound to the cumul variable of a given node. If the - // value of the cumul variable is greater than the bound, a cost proportional - // to the difference between this value and the bound is added to the cost - // function of the model: + // Sets a soft upper bound to the cumul variable of a given variable index. If + // the value of the cumul variable is greater than the bound, a cost + // proportional to the difference between this value and the bound is added to + // the cost function of the model: // cumulVar <= upper_bound -> cost = 0 - // cumulVar > upper_bound -> cost = coefficient * (cumulVar - upper_bound). + // cumulVar > upper_bound -> cost = coefficient * (cumulVar - upper_bound) // This is also handy to model tardiness costs when the dimension represents // time. - void SetCumulVarSoftUpperBound(RoutingModel::NodeIndex node, - int64 upper_bound, int64 coefficient); - // Same as SetCumulVarSoftUpperBound applied to vehicle start node, - void SetStartCumulVarSoftUpperBound(int vehicle, int64 upper_bound, - int64 coefficient); - // Same as SetCumulVarSoftUpperBound applied to vehicle end node, - void SetEndCumulVarSoftUpperBound(int vehicle, int64 upper_bound, - int64 coefficient); - // Same as SetCumulVarSoftUpperBound but using a variable index. - void SetCumulVarSoftUpperBoundFromIndex(int64 index, int64 upper_bound, - int64 coefficient); - // Returns true if a soft upper bound has been set for a given node. - bool HasCumulVarSoftUpperBound(RoutingModel::NodeIndex node) const; - // Returns true if a soft upper bound has been set for a given vehicle start - // node. - bool HasStartCumulVarSoftUpperBound(int vehicle) const; - // Returns true if a soft upper bound has been set for a given vehicle end - // node. - bool HasEndCumulVarSoftUpperBound(int vehicle) const; + void SetCumulVarSoftUpperBound(int64 index, int64 upper_bound, + int64 coefficient); // Returns true if a soft upper bound has been set for a given variable index. - bool HasCumulVarSoftUpperBoundFromIndex(int64 index) const; - // Returns the soft upper bound of a cumul variable for a given node. The - // "hard" upper bound of the variable is returned if no soft upper bound has - // been set. - int64 GetCumulVarSoftUpperBound(RoutingModel::NodeIndex node) const; - // Returns the soft upper bound of a cumul variable for a given vehicle start - // node. The "hard" upper bound of the variable is returned if no soft upper - // bound has been set. - int64 GetStartCumulVarSoftUpperBound(int vehicle) const; - // Returns the soft upper bound of a cumul variable for a given vehicle end - // node. The "hard" upper bound of the variable is returned if no soft upper - // bound has been set. - int64 GetEndCumulVarSoftUpperBound(int vehicle) const; + bool HasCumulVarSoftUpperBound(int64 index) const; // Returns the soft upper bound of a cumul variable for a given variable // index. The "hard" upper bound of the variable is returned if no soft upper // bound has been set. - int64 GetCumulVarSoftUpperBoundFromIndex(int64 index) const; - // Returns the cost coefficient of the soft upper bound of a cumul variable - // for a given node. If no soft upper bound has been set, 0 is returned. - int64 GetCumulVarSoftUpperBoundCoefficient( - RoutingModel::NodeIndex node) const; + int64 GetCumulVarSoftUpperBound(int64 index) const; // Returns the cost coefficient of the soft upper bound of a cumul variable - // for a given vehicle start node. If no soft upper bound has been set, 0 is + // for a given variable index. If no soft upper bound has been set, 0 is // returned. - int64 GetStartCumulVarSoftUpperBoundCoefficient(int vehicle) const; - // Returns the cost coefficient of the soft upper bound of a cumul variable - // for a given vehicle end node. If no soft upper bound has been set, 0 is - // returned. - int64 GetEndCumulVarSoftUpperBoundCoefficient(int vehicle) const; - // Returns the cost coefficient of the soft upper bound of a cumul variable - // for a variable index. If no soft upper bound has been set, 0 is returned. - int64 GetCumulVarSoftUpperBoundCoefficientFromIndex(int64 index) const; + int64 GetCumulVarSoftUpperBoundCoefficient(int64 index) const; - // Sets a soft lower bound to the cumul variable of a given node. If the - // value of the cumul variable is less than the bound, a cost proportional + // Sets a soft lower bound to the cumul variable of a given variable index. If + // the value of the cumul variable is less than the bound, a cost proportional // to the difference between this value and the bound is added to the cost // function of the model: // cumulVar > lower_bound -> cost = 0 @@ -1541,65 +1722,37 @@ class RoutingDimension { // Note: Using soft lower and upper bounds or span costs together is, as of // 6/2014, not well supported in the sense that an optimal schedule is not // guaranteed. - void SetCumulVarSoftLowerBound(RoutingModel::NodeIndex node, - int64 lower_bound, int64 coefficient); - // Same as SetCumulVarSoftLowerBound applied to vehicle start node, - void SetStartCumulVarSoftLowerBound(int vehicle, int64 lower_bound, - int64 coefficient); - // Same as SetCumulVarSoftLowerBound applied to vehicle end node, - void SetEndCumulVarSoftLowerBound(int vehicle, int64 lower_bound, - int64 coefficient); - // Same as SetCumulVarSoftLowerBound but using a variable index. - void SetCumulVarSoftLowerBoundFromIndex(int64 index, int64 lower_bound, - int64 coefficient); - // Returns true if a soft lower bound has been set for a given node. - bool HasCumulVarSoftLowerBound(RoutingModel::NodeIndex node) const; - // Returns true if a soft lower bound has been set for a given vehicle start - // node. - bool HasStartCumulVarSoftLowerBound(int vehicle) const; - // Returns true if a soft lower bound has been set for a given vehicle end - // node. - bool HasEndCumulVarSoftLowerBound(int vehicle) const; + void SetCumulVarSoftLowerBound(int64 index, int64 lower_bound, + int64 coefficient); // Returns true if a soft lower bound has been set for a given variable index. - bool HasCumulVarSoftLowerBoundFromIndex(int64 index) const; - // Returns the soft lower bound of a cumul variable for a given node. The - // "hard" lower bound of the variable is returned if no soft lower bound has - // been set. - int64 GetCumulVarSoftLowerBound(RoutingModel::NodeIndex node) const; - // Returns the soft lower bound of a cumul variable for a given vehicle start - // node. The "hard" lower bound of the variable is returned if no soft lower - // bound has been set. - int64 GetStartCumulVarSoftLowerBound(int vehicle) const; - // Returns the soft lower bound of a cumul variable for a given vehicle end - // node. The "hard" lower bound of the variable is returned if no soft lower - // bound has been set. - int64 GetEndCumulVarSoftLowerBound(int vehicle) const; + bool HasCumulVarSoftLowerBound(int64 index) const; // Returns the soft lower bound of a cumul variable for a given variable // index. The "hard" lower bound of the variable is returned if no soft lower // bound has been set. - int64 GetCumulVarSoftLowerBoundFromIndex(int64 index) const; - // Returns the cost coefficient of the soft lower bound of a cumul variable - // for a given node. If no soft lower bound has been set, 0 is returned. - int64 GetCumulVarSoftLowerBoundCoefficient( - RoutingModel::NodeIndex node) const; - // Returns the cost coefficient of the soft lower bound of a cumul variable - // for a given vehicle start node. If no soft lower bound has been set, 0 is - // returned. - int64 GetStartCumulVarSoftLowerBoundCoefficient(int vehicle) const; + int64 GetCumulVarSoftLowerBound(int64 index) const; // Returns the cost coefficient of the soft lower bound of a cumul variable - // for a given vehicle end node. If no soft lower bound has been set, 0 is + // for a given variable index. If no soft lower bound has been set, 0 is // returned. - int64 GetEndCumulVarSoftLowerBoundCoefficient(int vehicle) const; - // Returns the cost coefficient of the soft lower bound of a cumul variable - // for a variable index. If no soft lower bound has been set, 0 is returned. - int64 GetCumulVarSoftLowerBoundCoefficientFromIndex(int64 index) const; + int64 GetCumulVarSoftLowerBoundCoefficient(int64 index) const; // Sets the breaks for a given vehicle. Breaks are represented by // IntervalVars. They may interrupt transits between nodes and increase - // the value of corresponding slack variables. However an interval cannot - // overlap the cumul variable of a node (the interval must either be before - // or after the node). - void SetBreakIntervalsOfVehicle(std::vector breaks, - int vehicle); + // the value of corresponding slack variables. However a break interval cannot + // overlap the transit interval of a node, which is + // [CumulVar(node), CumulVar(node) + node_visit_transits[node]), i.e. the + // break interval must either end before CumulVar(node) or start after + // CumulVar(node) + node_visit_transits[node]. + void SetBreakIntervalsOfVehicle(std::vector breaks, int vehicle, + std::vector node_visit_transits); +#if !defined(SWIGPYTHON) + // Returns true if the vehicle has break intervals. + bool VehicleHasBreakIntervals(int vehicle) const; + // Returns the break intervals set by SetBreakIntervalsOfVehicle(). + const std::vector& GetBreakIntervalsOfVehicle( + int vehicle) const; + // Returns the amount of visit transit set by SetBreakIntervalsOfVehicle(). + const std::vector& GetNodeVisitTransitsOfVehicle(int vehicle) const; +#endif // !defined(SWIGPYTHON) + // Returns the parent in the dependency tree if any or nullptr otherwise. const RoutingDimension* base_dimension() const { return base_dimension_; } // It makes sense to use the function only for self-dependent dimension. @@ -1616,11 +1769,31 @@ class RoutingDimension { // Accessors. #ifndef SWIG - const ::util::ReverseArcListGraph& GetPrecedenceGraph() const { + const ReverseArcListGraph& GetPrecedenceGraph() const { return precedence_graph_; } #endif + // Limits, in terms of maximum difference between the cumul variables, between + // the pickup and delivery alternatives belonging to a single pickup/delivery + // pair in the RoutingModel. + // The indices passed to the function respectively correspond to the position + // of the pickup in the vector of pickup alternatives, and delivery position + // in the delivery alternatives for this pickup/delivery pair. + // These limits should only be set when each node index appears in at most one + // pickup/delivery pair, i.e. each pickup (delivery) index is in a single + // pickup/delivery pair.first (pair.second). + typedef std::function PickupToDeliveryLimitFunction; + + void SetPickupToDeliveryLimitFunctionForPair( + PickupToDeliveryLimitFunction limit_function, int pair_index); + + bool HasPickupToDeliveryLimits() const; +#ifndef SWIG + int64 GetPickupToDeliveryLimitForPair(int pair_index, int pickup, + int delivery) const; +#endif // SWIG + int64 GetSpanUpperBoundForVehicle(int vehicle) const { return vehicle_span_upper_bounds_[vehicle]; } @@ -1643,7 +1816,6 @@ class RoutingDimension { private: struct SoftBound { - SoftBound() : var(nullptr), bound(0), coefficient(0) {} IntVar* var; int64 bound; int64 coefficient; @@ -1652,7 +1824,7 @@ class RoutingDimension { struct PiecewiseLinearCost { PiecewiseLinearCost() : var(nullptr), cost(nullptr) {} IntVar* var; - PiecewiseLinearFunction* cost; + std::unique_ptr cost; }; class SelfBased {}; @@ -1661,17 +1833,15 @@ class RoutingDimension { const RoutingDimension* base_dimension); RoutingDimension(RoutingModel* model, std::vector vehicle_capacities, const std::string& name, SelfBased); - void Initialize( - const std::vector& transit_evaluators, - const std::vector& - state_dependent_node_evaluators, - int64 slack_max); + void Initialize(const std::vector& transit_evaluators, + const std::vector& state_dependent_transit_evaluators, + int64 slack_max); void InitializeCumuls(); void InitializeTransits( - const std::vector& transit_evaluators, - const std::vector& - state_dependent_transit_evaluators, + const std::vector& transit_evaluators, + const std::vector& state_dependent_transit_evaluators, int64 slack_max); + void InitializeTransitVariables(int64 slack_max); // Sets up the cost variables related to cumul soft upper bounds. void SetupCumulVarSoftUpperBoundCosts( std::vector* cost_elements) const; @@ -1694,12 +1864,12 @@ class RoutingDimension { const std::vector vehicle_capacities_; std::vector transits_; std::vector fixed_transits_; - // "transit_evaluators_" does the indexing by vehicle, while - // "class_evaluators_" does the de-duplicated ownership. - std::vector class_evaluators_; + // Values in class_evaluators_ correspond to the evaluators in + // RoutingModel::transit_evaluators_ for each vehicle class. + std::vector class_evaluators_; std::vector vehicle_to_class_; #ifndef SWIG - ::util::ReverseArcListGraph precedence_graph_; + ReverseArcListGraph precedence_graph_; #endif // The transits of a dimension may depend on its cumuls or the cumuls of @@ -1707,12 +1877,23 @@ class RoutingDimension { // example for this is a time dimension. const RoutingDimension* const base_dimension_; - // "state_dependent_transit_evaluators_" does the indexing by vehicle, while - // "state_dependent_class_evaluators_" does the de-duplicated ownership. - std::vector - state_dependent_class_evaluators_; + // Values in state_dependent_class_evaluators_ correspond to the evaluators in + // RoutingModel::state_dependent_transit_evaluators_ for each vehicle class. + std::vector state_dependent_class_evaluators_; std::vector state_dependent_vehicle_to_class_; + // For each pickup/delivery pair_index for which limits have been set, + // pickup_to_delivery_limits_per_pair_index_[pair_index] contains the + // PickupToDeliveryLimitFunction for the pickup and deliveries in this pair. + std::vector + pickup_to_delivery_limits_per_pair_index_; + + // Used if some vehicle has breaks in this dimension, typically time. + // clang-format off + std::vector > vehicle_break_intervals_; + std::vector > vehicle_node_visit_transits_; + // clang-format on + std::vector slacks_; std::vector dependent_transits_; std::vector vehicle_span_upper_bounds_; @@ -1731,24 +1912,29 @@ class RoutingDimension { }; #ifndef SWIG -// Class to arrange nodes by by their distance and their angles from the +// Class to arrange indices by by their distance and their angles from the // depot. Used in the Sweep first solution heuristic. class SweepArranger { public: - explicit SweepArranger(const gtl::ITIVector>& points); + explicit SweepArranger(const std::vector>& points); virtual ~SweepArranger() {} - void ArrangeNodes(std::vector* nodes); + void ArrangeIndices(std::vector* indices); void SetSectors(int sectors) { sectors_ = sectors; } private: - gtl::ITIVector coordinates_; + std::vector coordinates_; int sectors_; DISALLOW_COPY_AND_ASSIGN(SweepArranger); }; #endif +// A decision builder which tries to assign values to variables as close as +// possible to target values first. +DecisionBuilder* MakeSetValuesFromTargets(Solver* solver, + std::vector variables, + std::vector targets); + // Routing Search // Decision builders building a solution using local search filters to evaluate @@ -1785,6 +1971,8 @@ class IntVarFilteredDecisionBuilder : public DecisionBuilder { // are "filter-feasible", returns false otherwise; in any case discards // all modifications. bool Commit(); + // Returns true if the search must be stopped. + virtual bool StopSearch() { return false; } // Modifies the current solution by setting the variable of index 'index' to // value 'value'. void SetValue(int64 index, int64 value) { @@ -1812,8 +2000,6 @@ class IntVarFilteredDecisionBuilder : public DecisionBuilder { IntVar* Var(int64 index) const { return vars_[index]; } private: - // Sets all variables currently bound to their value in the current solution. - void SetValuesFromDomains(); // Synchronizes filters with an assignment (the current solution). void SynchronizeFilters(); // Checks if filters accept a given modification to the current solution @@ -1826,7 +2012,7 @@ class IntVarFilteredDecisionBuilder : public DecisionBuilder { std::vector delta_indices_; std::vector is_in_delta_; const Assignment* const empty_; - std::vector filters_; + LocalSearchFilterManager filter_manager_; // Stats on search int64 number_of_decisions_; int64 number_of_rejects_; @@ -1843,15 +2029,21 @@ class RoutingFilteredDecisionBuilder : public IntVarFilteredDecisionBuilder { bool InitializeRoutes(); // Returns the end of the start chain of vehicle, int GetStartChainEnd(int vehicle) const { return start_chain_ends_[vehicle]; } + // Returns the start of the end chain of vehicle, + int GetEndChainStart(int vehicle) const { return end_chain_starts_[vehicle]; } // Make nodes in the same disjunction as 'node' unperformed. 'node' is a // variable index corresponding to a node. void MakeDisjunctionNodesUnperformed(int64 node); // Make all unassigned nodes unperformed. void MakeUnassignedNodesUnperformed(); + protected: + bool StopSearch() override { return model_->CheckLimit(); } + private: RoutingModel* const model_; std::vector start_chain_ends_; + std::vector end_chain_starts_; }; class CheapestInsertionFilteredDecisionBuilder @@ -1859,14 +2051,43 @@ class CheapestInsertionFilteredDecisionBuilder public: // Takes ownership of evaluator. CheapestInsertionFilteredDecisionBuilder( - RoutingModel* model, - ResultCallback3* evaluator, - ResultCallback1* penalty_evaluator, + RoutingModel* model, std::function evaluator, + std::function penalty_evaluator, const std::vector& filters); ~CheapestInsertionFilteredDecisionBuilder() override {} protected: typedef std::pair ValuedPosition; + struct StartEndValue { + int64 distance; + int vehicle; + + bool operator<(const StartEndValue& other) const { + return std::tie(distance, vehicle) < + std::tie(other.distance, other.vehicle); + } + }; + typedef std::pair Seed; + + // Computes and returns the distance of each uninserted node to every vehicle + // in "vehicles" as a std::vector>, + // start_end_distances_per_node. + // For each node, start_end_distances_per_node[node] is sorted in decreasing + // order. + // clang-format off + std::vector > + ComputeStartEndDistanceForVehicles(const std::vector& vehicles); + + // Initializes the priority_queue by inserting the best entry corresponding + // to each node, i.e. the last element of start_end_distances_per_node[node], + // which is supposed to be sorted in decreasing order. + // Queue is a priority queue containing Seeds. + template + void InitializePriorityQueue( + std::vector >* start_end_distances_per_node, + Queue* priority_queue); + // clang-format on + // Inserts 'node' just after 'predecessor', and just before 'successor', // resulting in the following subsequence: predecessor -> node -> successor. // If 'node' is part of a disjunction, other nodes of the disjunction are made @@ -1883,8 +2104,8 @@ class CheapestInsertionFilteredDecisionBuilder // if penalty callback is null or if the node cannot be unperformed. int64 GetUnperformedValue(int64 node_to_insert) const; - std::unique_ptr> evaluator_; - std::unique_ptr> penalty_evaluator_; + std::function evaluator_; + std::function penalty_evaluator_; }; // Filter-based decision builder which builds a solution by inserting @@ -1898,18 +2119,18 @@ class GlobalCheapestInsertionFilteredDecisionBuilder public: // Takes ownership of evaluators. GlobalCheapestInsertionFilteredDecisionBuilder( - RoutingModel* model, - ResultCallback3* evaluator, - ResultCallback1* penalty_evaluator, - const std::vector& filters); + RoutingModel* model, std::function evaluator, + std::function penalty_evaluator, + const std::vector& filters, bool is_sequential, + double farthest_seeds_ratio, double neighbors_ratio); ~GlobalCheapestInsertionFilteredDecisionBuilder() override {} bool BuildSolution() override; private: class PairEntry; class NodeEntry; - typedef std::unordered_set PairEntries; - typedef std::unordered_set NodeEntries; + typedef absl::flat_hash_set PairEntries; + typedef absl::flat_hash_set NodeEntries; // Inserts all non-inserted pickup and delivery pairs. Maintains a priority // queue of possible pair insertions, which is incrementally updated when a @@ -1918,12 +2139,50 @@ class GlobalCheapestInsertionFilteredDecisionBuilder // insertion position, after the pickup position, after the delivery insertion // position and after the delivery position. void InsertPairs(); - // Inserts all non-inserted individual nodes. Maintains a priority queue of - // possible insertions, which is incrementally updated when an insertion is - // committed. Incrementality is obtained by updating insertion positions on - // the two newly modified route arcs: after the node insertion position and - // after the node position. - void InsertNodes(); + + // Inserts non-inserted individual nodes on the given routes (or all routes if + // "vehicles" is an empty vector), by constructing routes in parallel. + // Maintains a priority queue of possible insertions, which is incrementally + // updated when an insertion is committed. + // Incrementality is obtained by updating insertion positions on the two newly + // modified route arcs: after the node insertion position and after the node + // position. + void InsertNodesOnRoutes(const std::vector& nodes, + const std::vector& vehicles); + + // Inserts non-inserted individual nodes on routes by constructing routes + // sequentially. + // For each new route, the vehicle to use and the first node to insert on it + // are given by calling InsertSeedNode(). The route is then completed with + // other nodes by calling InsertNodesOnRoutes({vehicle}). + void SequentialInsertNodes(const std::vector& nodes); + + // Goes through all vehicles in the model to check if they are already used + // (i.e. Value(start) != end) or not. + // Updates the three passed vectors accordingly. + void DetectUsedVehicles(std::vector* is_vehicle_used, + std::vector* used_vehicles, + std::vector* unused_vehicles); + + // Inserts the (farthest_seeds_ratio_ * model()->vehicles()) nodes farthest + // from the start/ends of the available vehicle routes as seeds on their + // closest route. + void InsertFarthestNodesAsSeeds(); + + // Inserts a "seed node" based on the given priority_queue of Seeds. + // A "seed" is the node used in order to start a new route. + // If the Seed at the top of the priority queue cannot be inserted, + // (node already inserted in the model, corresponding vehicle already used, or + // unsuccessful Commit()), start_end_distances_per_node is updated and used + // to insert a new entry for that node if necessary (next best vehicle). + // If a seed node is successfully inserted, updates is_vehicle_used and + // returns the vehice of the corresponding route. Returns -1 otherwise. + template + int InsertSeedNode( + std::vector>* start_end_distances_per_node, + Queue* priority_queue, std::vector* is_vehicle_used); + // clang-format on + // Initializes the priority queue and the pair entries with the current state // of the solution. void InitializePairPositions( @@ -1961,12 +2220,15 @@ class GlobalCheapestInsertionFilteredDecisionBuilder std::vector* pickup_to_entries, std::vector* delivery_to_entries); // Initializes the priority queue and the node entries with the current state - // of the solution. - void InitializePositions(AdjustablePriorityQueue* priority_queue, - std::vector* position_to_node_entries); + // of the solution on the given vehicle routes. + void InitializePositions(const std::vector& nodes, + AdjustablePriorityQueue* priority_queue, + std::vector* position_to_node_entries, + const std::vector& vehicles); // Updates all node entries inserting a node after node "insert_after" and // updates the priority queue accordingly. - void UpdatePositions(int vehicle, int64 insert_after, + void UpdatePositions(const std::vector& nodes, int vehicle, + int64 insert_after, AdjustablePriorityQueue* priority_queue, std::vector* node_entries); // Deletes an entry, removing it from the priority queue and the appropriate @@ -1974,19 +2236,71 @@ class GlobalCheapestInsertionFilteredDecisionBuilder void DeleteNodeEntry(NodeEntry* entry, AdjustablePriorityQueue* priority_queue, std::vector* node_entries); + + // Inserts neighbor_index in + // node_index_to_[pickup|delivery|single]_neighbors_per_cost_class_ + // [node_index][cost_class] according to whether neighbor is a pickup, + // a delivery, or neither. + void AddNeighborForCostClass(int cost_class, int64 node_index, + int64 neighbor_index, bool neighbor_is_pickup, + bool neighbor_is_delivery); + + // Returns true iff neighbor_index is in node_index's neighbors list + // corresponding to neighbor_is_pickup and neighbor_is_delivery. + bool IsNeighborForCostClass(int cost_class, int64 node_index, + int64 neighbor_index) const; + + // Returns a reference to the set of pickup neighbors of node_index. + const absl::flat_hash_set& GetPickupNeighborsOfNodeForCostClass( + int cost_class, int64 node_index) { + if (neighbors_ratio_ == 1) { + return pickup_nodes_; + } + return node_index_to_pickup_neighbors_by_cost_class_[node_index] + [cost_class]; + } + + // Same as above for delivery neighbors. + const absl::flat_hash_set& GetDeliveryNeighborsOfNodeForCostClass( + int cost_class, int64 node_index) { + if (neighbors_ratio_ == 1) { + return delivery_nodes_; + } + return node_index_to_delivery_neighbors_by_cost_class_[node_index] + [cost_class]; + } + + const bool is_sequential_; + const double farthest_seeds_ratio_; + const double neighbors_ratio_; + + // clang-format off + std::vector > > + node_index_to_single_neighbors_by_cost_class_; + std::vector > > + node_index_to_pickup_neighbors_by_cost_class_; + std::vector > > + node_index_to_delivery_neighbors_by_cost_class_; + // clang-format on + + // When neighbors_ratio is 1, we don't compute the neighborhood members above, + // and use the following sets in the code to avoid unnecessary computations + // and decrease the time and space complexities. + absl::flat_hash_set pickup_nodes_; + absl::flat_hash_set delivery_nodes_; }; // Filter-base decision builder which builds a solution by inserting // nodes at their cheapest position. The cost of a position is computed // an arc-based cost callback. Node selected for insertion are considered in -// the order they were created in the model. +// decreasing order of distance to the start/ends of the routes, i.e. farthest +// nodes are inserted first. class LocalCheapestInsertionFilteredDecisionBuilder : public CheapestInsertionFilteredDecisionBuilder { public: // Takes ownership of evaluator. LocalCheapestInsertionFilteredDecisionBuilder( - RoutingModel* model, - ResultCallback3* evaluator, + RoutingModel* model, std::function evaluator, const std::vector& filters); ~LocalCheapestInsertionFilteredDecisionBuilder() override {} bool BuildSolution() override; @@ -2029,12 +2343,24 @@ class CheapestAdditionFilteredDecisionBuilder private: const CheapestAdditionFilteredDecisionBuilder& builder_; }; - // Returns a sorted vector of nodes which can come next in the route after - // node 'from'. - // 'from' is a variable index corresponding to a node, 'sorted_nexts' is a - // vector of variable indices corresponding to nodes which can follow 'from'. - virtual void SortPossibleNexts(int64 from, - std::vector* sorted_nexts) = 0; + // Returns a vector of possible next indices of node from an iterator. + template + std::vector GetPossibleNextsFromIterator(int64 node, Iterator start, + Iterator end) const { + const int size = model()->Size(); + std::vector nexts; + for (Iterator it = start; it != end; ++it) { + const int64 next = *it; + if (next != node && (next >= size || !Contains(next))) { + nexts.push_back(next); + } + } + return nexts; + } + // Sorts a vector of successors of node. + virtual void SortSuccessors(int64 node, std::vector* successors) = 0; + virtual int64 FindTopSuccessor(int64 node, + const std::vector& successors) = 0; }; // A CheapestAdditionFilteredDecisionBuilder where the notion of 'cheapest arc' @@ -2044,16 +2370,17 @@ class EvaluatorCheapestAdditionFilteredDecisionBuilder public: // Takes ownership of evaluator. EvaluatorCheapestAdditionFilteredDecisionBuilder( - RoutingModel* model, - ResultCallback2* evaluator, + RoutingModel* model, std::function evaluator, const std::vector& filters); ~EvaluatorCheapestAdditionFilteredDecisionBuilder() override {} private: // Next nodes are sorted according to the current evaluator. - void SortPossibleNexts(int64 from, std::vector* sorted_nexts) override; + void SortSuccessors(int64 node, std::vector* successors) override; + int64 FindTopSuccessor(int64 node, + const std::vector& successors) override; - std::unique_ptr> evaluator_; + std::function evaluator_; }; // A CheapestAdditionFilteredDecisionBuilder where the notion of 'cheapest arc' @@ -2069,7 +2396,9 @@ class ComparatorCheapestAdditionFilteredDecisionBuilder private: // Next nodes are sorted according to the current comparator. - void SortPossibleNexts(int64 from, std::vector* sorted_nexts) override; + void SortSuccessors(int64 node, std::vector* successors) override; + int64 FindTopSuccessor(int64 node, + const std::vector& successors) override; Solver::VariableValueComparator comparator_; }; @@ -2089,27 +2418,36 @@ class SavingsFilteredDecisionBuilder : public RoutingFilteredDecisionBuilder { // neighbors leading to the smallest arc costs are considered. // Furthermore, if add_reverse_arcs is true, the neighborhood relationships // are always considered symmetrically. + // Finally, savings_arc_coefficient is a strictly positive parameter + // indicating the coefficient of the arc being considered in the saving + // formula. + // TODO(user): Add all parameters as struct to the class. SavingsFilteredDecisionBuilder( - RoutingModel* model, double savings_neighbors_ratio, - bool add_reverse_arcs, const std::vector& filters); - ~SavingsFilteredDecisionBuilder() override {} + RoutingModel* model, RoutingIndexManager* manager, + double savings_neighbors_ratio, bool add_reverse_arcs, + double savings_arc_coefficient, + const std::vector& filters); + ~SavingsFilteredDecisionBuilder() override; bool BuildSolution() override; - private: + protected: typedef std::pair Saving; - // Computes saving values for all node pairs and vehicle types (see - // ComputeVehicleTypes()). - // The saving index attached to each saving value is an index used to - // store and recover the node pair to which the value is linked (cf. the - // index conversion methods below). - std::vector ComputeSavings(); - // Builds a saving from a saving value, a cost class and two nodes. - Saving BuildSaving(int64 saving, int vehicle_type, int before_node, - int after_node) const { - return std::make_pair(saving, vehicle_type * size_squared_ + - before_node * Size() + after_node); - } + template + class SavingsContainer; + + struct VehicleClassEntry { + int vehicle_class; + int64 fixed_cost; + + bool operator<(const VehicleClassEntry& other) const { + return std::tie(fixed_cost, vehicle_class) < + std::tie(other.fixed_cost, other.vehicle_class); + } + }; + + virtual void BuildRoutesFromSavings() = 0; + // Returns the cost class from a saving. int64 GetVehicleTypeFromSaving(const Saving& saving) const { return saving.second / size_squared_; @@ -2125,21 +2463,126 @@ class SavingsFilteredDecisionBuilder : public RoutingFilteredDecisionBuilder { // Returns the saving value from a saving. int64 GetSavingValue(const Saving& saving) const { return saving.first; } + // Finds the best available vehicle of type "type" to start a new route to + // serve the arc before_node-->after_node. + // Since there are different vehicle classes for each vehicle type, each + // vehicle class having its own capacity constraints, we go through all + // vehicle types (in each case only studying the first available vehicle) to + // make sure this Saving is inserted if possible. + // If possible, the arc is committed to the best vehicle, and the vehicle + // index is returned. If this arc can't be served by any vehicle of this type, + // the function returns -1. + int StartNewRouteWithBestVehicleOfType(int type, int64 before_node, + int64 after_node); + + std::vector type_index_of_vehicle_; + // clang-format off + std::vector > sorted_vehicle_classes_per_type_; + std::vector > vehicles_per_vehicle_class_; + std::unique_ptr > savings_container_; + // clang-format on + + private: + // Used when add_reverse_arcs_ is true. + // Given the vector of adjacency lists of a graph, adds symetric arcs not + // already in the graph to the adjacencies (i.e. if n1-->n2 is present and not + // n2-->n1, then n1 is added to adjacency_matrix[n2]. + // clang-format off + void AddSymetricArcsToAdjacencyLists( + std::vector >* adjacency_lists); + // clang-format on + + // Computes saving values for all node pairs and vehicle types (see + // ComputeVehicleTypes()). + // The saving index attached to each saving value is an index used to + // store and recover the node pair to which the value is linked (cf. the + // index conversion methods below). + // The computed savings are stored and sorted using the savings_container_. + void ComputeSavings(); + // Builds a saving from a saving value, a vehicle type and two nodes. + Saving BuildSaving(int64 saving, int vehicle_type, int before_node, + int after_node) const { + return std::make_pair(saving, vehicle_type * size_squared_ + + before_node * Size() + after_node); + } + // Computes the vehicle type of every vehicle and stores it in // type_index_of_vehicle_. A "vehicle type" consists of the set of vehicles // having the same cost class and start/end nodes, therefore the same savings // value for each arc. - // The vehicles corresponding to each vehicle type index are stored in - // vehicles_per_vehicle_type_. + // The vehicle classes corresponding to each vehicle type index are stored and + // sorted by fixed cost in sorted_vehicle_classes_per_type_, and the vehicles + // for each vehicle class are stored in vehicles_per_vehicle_class_. void ComputeVehicleTypes(); + RoutingIndexManager* const manager_; const double savings_neighbors_ratio_; const bool add_reverse_arcs_; + const double savings_arc_coefficient_; int64 size_squared_; - std::vector type_index_of_vehicle_; - // clang-format off - std::vector > vehicles_per_vehicle_type_; - // clang-format on +}; + +class SequentialSavingsFilteredDecisionBuilder + : public SavingsFilteredDecisionBuilder { + public: + SequentialSavingsFilteredDecisionBuilder( + RoutingModel* model, RoutingIndexManager* manager, + double savings_neighbors_ratio, bool add_reverse_arcs, + double savings_arc_coefficient, + const std::vector& filters) + : SavingsFilteredDecisionBuilder(model, manager, savings_neighbors_ratio, + add_reverse_arcs, + savings_arc_coefficient, filters) {} + ~SequentialSavingsFilteredDecisionBuilder() override{}; + + private: + // Builds routes sequentially. + // Once a Saving is used to start a new route, we extend this route as much as + // possible from both ends by gradually inserting the best Saving at either + // end of the route. + void BuildRoutesFromSavings() override; +}; + +class ParallelSavingsFilteredDecisionBuilder + : public SavingsFilteredDecisionBuilder { + public: + ParallelSavingsFilteredDecisionBuilder( + RoutingModel* model, RoutingIndexManager* manager, + double savings_neighbors_ratio, bool add_reverse_arcs, + double savings_arc_coefficient, + const std::vector& filters) + : SavingsFilteredDecisionBuilder(model, manager, savings_neighbors_ratio, + add_reverse_arcs, + savings_arc_coefficient, filters) {} + ~ParallelSavingsFilteredDecisionBuilder() override{}; + + private: + // Goes through the ordered computed Savings to build routes in parallel. + // Given a Saving for a before-->after arc : + // -- If both before and after are uncontained, we start a new route. + // -- If only before is served and is the last node on its route, we try + // adding after at the end of the route. + // -- If only after is served and is first on its route, we try adding before + // as first node on this route. + // -- If both nodes are contained and are respectively the last and first + // nodes on their (different) routes, we merge the routes of the two nodes + // into one if possible. + void BuildRoutesFromSavings() override; + + // Merges the routes of first_vehicle and second_vehicle onto the vehicle with + // lower fixed cost. The routes respectively end at before_node and start at + // after_node, and are merged into one by adding the arc + // before_node-->after_node. + void MergeRoutes(int first_vehicle, int second_vehicle, int64 before_node, + int64 after_node); + + // First and last non start/end nodes served by each vehicle. + std::vector first_node_on_route_; + std::vector last_node_on_route_; + // For each first/last node served by a vehicle (besides start/end nodes of + // vehicle), this vector contains the index of the vehicle serving them. + // For other (intermediary) nodes, contains -1. + std::vector vehicle_of_first_or_last_node_; }; // Christofides addition heuristic. Initially created to solve TSPs, extended to @@ -2155,30 +2598,9 @@ class ChristofidesFilteredDecisionBuilder bool BuildSolution() override; }; -// Routing filters - -class RoutingLocalSearchFilter : public IntVarLocalSearchFilter { - public: - RoutingLocalSearchFilter(const std::vector& nexts, - std::function objective_callback); - ~RoutingLocalSearchFilter() override {} - void InjectObjectiveValue(int64 objective_value) override; - - protected: - bool CanPropagateObjectiveValue() const { - return objective_callback_ != nullptr; - } - void PropagateObjectiveValue(int64 objective_value); - - int64 injected_objective_value_; - - private: - std::function objective_callback_; -}; - // Generic path-based filter class. -class BasePathFilter : public RoutingLocalSearchFilter { +class BasePathFilter : public IntVarLocalSearchFilter { public: BasePathFilter(const std::vector& nexts, int next_domain_size, std::function objective_callback); @@ -2197,15 +2619,20 @@ class BasePathFilter : public RoutingLocalSearchFilter { int NumPaths() const { return starts_.size(); } int64 Start(int i) const { return starts_[i]; } int GetPath(int64 node) const { return paths_[node]; } + int Rank(int64 node) const { return ranks_[node]; } + bool IsDisabled() const { return status_ == DISABLED; } private: + enum Status { UNKNOWN, ENABLED, DISABLED }; + + virtual bool DisableFiltering() const { return false; } virtual void OnBeforeSynchronizePaths() {} virtual void OnAfterSynchronizePaths() {} virtual void OnSynchronizePathFromStart(int64 start) {} virtual void InitializeAcceptPath() {} virtual bool AcceptPath(int64 path_start, int64 chain_start, int64 chain_end) = 0; - virtual bool FinalizeAcceptPath() { return true; } + virtual bool FinalizeAcceptPath(const Assignment* delta) { return true; } // Detects path starts, used to track which node belongs to which path. void ComputePathStarts(std::vector* path_starts, std::vector* index_to_path); @@ -2222,19 +2649,128 @@ class BasePathFilter : public RoutingLocalSearchFilter { SparseBitset<> touched_paths_; SparseBitset<> touched_path_nodes_; std::vector ranks_; + + Status status_; +}; + +// This filter accepts deltas for which the assignment satisfies the constraints +// of the Solver. This is verified by keeping an internal copy of the assignment +// with all Next vars and their updated values, and calling RestoreAssignment() +// on the assignment+delta. +// TODO(user): Also call the solution finalizer on variables, with the +// exception of Next Vars (woud fail on large instances). +// WARNING: In the case of mandatory nodes, when all vehicles are currently +// being used in the solution but uninserted nodes still remain, this filter +// will reject the solution, even if the node could be inserted on one of these +// routes, because all Next vars of vehicle starts are already instantiated. +// TODO(user): Avoid such false negatives. +class CPFeasibilityFilter : public IntVarLocalSearchFilter { + public: + explicit CPFeasibilityFilter(const RoutingModel* routing_model); + ~CPFeasibilityFilter() override {} + std::string DebugString() const override { return "CPFeasibilityFilter"; } + bool Accept(const Assignment* delta, const Assignment* deltadelta) override; + void OnSynchronize(const Assignment* delta) override; + + private: + void AddDeltaToAssignment(const Assignment* delta, Assignment* assignment); + + static const int64 kUnassigned; + const RoutingModel* const model_; + Solver* const solver_; + Assignment* const assignment_; + Assignment* const temp_assignment_; + DecisionBuilder* const restore_; }; #if !defined(SWIG) -RoutingLocalSearchFilter* MakeNodeDisjunctionFilter( +IntVarLocalSearchFilter* MakeNodeDisjunctionFilter( const RoutingModel& routing_model, std::function objective_callback); -RoutingLocalSearchFilter* MakePathCumulFilter( +IntVarLocalSearchFilter* MakeVehicleAmortizedCostFilter( + const RoutingModel& routing_model, + Solver::ObjectiveWatcher objective_callback); +IntVarLocalSearchFilter* MakeTypeIncompatibilityFilter( + const RoutingModel& routing_model); +IntVarLocalSearchFilter* MakePathCumulFilter( const RoutingModel& routing_model, const RoutingDimension& dimension, std::function objective_callback); -RoutingLocalSearchFilter* MakeNodePrecedenceFilter( - const RoutingModel& routing_model, const RoutingModel::NodePairs& pairs); -RoutingLocalSearchFilter* MakeVehicleVarFilter( +IntVarLocalSearchFilter* MakePickupDeliveryFilter( + const RoutingModel& routing_model, const RoutingModel::IndexPairs& pairs, + const std::vector& vehicle_policies); +IntVarLocalSearchFilter* MakeVehicleVarFilter( const RoutingModel& routing_model); +IntVarLocalSearchFilter* MakeVehicleBreaksFilter( + const RoutingModel& routing_model, const RoutingDimension& dimension); +IntVarLocalSearchFilter* MakeCPFeasibilityFilter( + const RoutingModel* routing_model); + +// Utility class used in RouteDimensionCumulOptimizer to set the LP constraints +// and solve the problem. +class DimensionCumulOptimizerCore { + public: + explicit DimensionCumulOptimizerCore(const RoutingDimension* dimension) + : dimension_(dimension) {} + + bool OptimizeSingleRoute(int vehicle, + const std::function& next_accessor, + glop::LinearProgram* linear_program, + glop::LPSolver* lp_solver, + std::vector* cumul_values, int64* cost, + int64* transit_cost); + + const RoutingDimension* dimension() const { return dimension_; } + + private: + void SetRouteCumulConstraints( + int vehicle, const std::function& next_accessor, + glop::LinearProgram* linear_program, int64* route_transit_cost); + + bool FinalizeAndSolve(glop::LinearProgram* linear_program, + glop::LPSolver* lp_solver, + std::vector* cumul_values, int64* cost); + const RoutingDimension* const dimension_; + std::vector current_route_cumul_variables_; +}; + +// Class used to compute optimal values for dimension cumuls of routes, +// minimizing cumul soft lower and upper bound costs, and vehicle span costs of +// a route. +// In its methods, next_accessor is a callback returning the next node of a +// given node on a route. +class RouteDimensionCumulOptimizer { + public: + explicit RouteDimensionCumulOptimizer(const RoutingDimension* dimension); + // If feasible, computes the optimal cost of the route performed by a vehicle, + // minimizing cumul soft lower and upper bound costs and vehicle span costs, + // and stores it in "optimal_cost" (if not null). + // Returns true iff the route respects all constraints. + bool ComputeRouteCumulCost(int vehicle, + const std::function& next_accessor, + int64* optimal_cost); + // Same as ComputeRouteCumulCost, but the cost computed does not contain + // the part of the vehicle span cost due to fixed transits. + bool ComputeRouteCumulCostWithoutFixedTransits( + int vehicle, const std::function& next_accessor, + int64* optimal_cost_without_transits); + // If feasible, computes the optimal cumul values of the route performed by a + // vehicle, minimizing cumul soft lower and upper bound costs and vehicle span + // costs, stores them in "optimal_cumuls" (if not null), and returns true. + // Returns false if the route is not feasible. + bool ComputeRouteCumuls(int vehicle, + const std::function& next_accessor, + std::vector* optimal_cumuls); + + const RoutingDimension* dimension() const { + return optimizer_core_.dimension(); + } + + private: + std::vector> lp_solver_; + std::vector> linear_program_; + DimensionCumulOptimizerCore optimizer_core_; +}; #endif + } // namespace operations_research #endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_H_ diff --git a/ortools/constraint_solver/routing_enums.proto b/ortools/constraint_solver/routing_enums.proto index c84ea6b9335..e2917e0bc5d 100644 --- a/ortools/constraint_solver/routing_enums.proto +++ b/ortools/constraint_solver/routing_enums.proto @@ -23,9 +23,12 @@ package operations_research; // First solution strategies, used as starting point of local search. message FirstSolutionStrategy { enum Value { + // See the homonymous value in LocalSearchMetaheuristic. + UNSET = 0; + // Lets the solver detect which strategy to use according to the model being // solved. - AUTOMATIC = 0; + AUTOMATIC = 15; // --- Path addition heuristics --- // Starting from a route "start" node, connect it to the node which produces @@ -72,14 +75,20 @@ message FirstSolutionStrategy { // optional nodes (with finite penalty costs). BEST_INSERTION = 7; // Iteratively build a solution by inserting the cheapest node at its - // cheapest position; the cost of insertion is based on the the arc cost + // cheapest position; the cost of insertion is based on the arc cost // function. Is faster than BEST_INSERTION. PARALLEL_CHEAPEST_INSERTION = 8; + // Iteratively build a solution by constructing routes sequentially, for + // each route inserting the cheapest node at its cheapest position until the + // route is completed; the cost of insertion is based on the arc cost + // function. Is faster than PARALLEL_CHEAPEST_INSERTION. + SEQUENTIAL_CHEAPEST_INSERTION = 14; // Iteratively build a solution by inserting each node at its cheapest - // position; the cost of insertion is based on the the arc cost function. + // position; the cost of insertion is based on the arc cost function. // Differs from PARALLEL_CHEAPEST_INSERTION by the node selected for - // insertion; here nodes are considered in their order of creation. Is - // faster than PARALLEL_CHEAPEST_INSERTION. + // insertion; here nodes are considered in decreasing order of distance to + // the start/ends of the routes, i.e. farthest nodes are inserted first. + // Is faster than SEQUENTIAL_CHEAPEST_INSERTION. LOCAL_CHEAPEST_INSERTION = 9; // --- Variable-based heuristics --- @@ -100,8 +109,13 @@ message FirstSolutionStrategy { // descent, they will try to escape local minima. message LocalSearchMetaheuristic { enum Value { + // Means "not set". If the solver sees that, it'll behave like for + // AUTOMATIC. But this value won't override others upon a proto MergeFrom(), + // whereas "AUTOMATIC" will. + UNSET = 0; + // Lets the solver select the metaheuristic. - AUTOMATIC = 0; + AUTOMATIC = 6; // Accepts improving (cost-reducing) local search neighbors until a local // minimum is reached. @@ -116,8 +130,9 @@ message LocalSearchMetaheuristic { // Uses tabu search to escape local minima // (cf. http://en.wikipedia.org/wiki/Tabu_search). TABU_SEARCH = 4; - // Uses tabu search on the objective value of solution to escape local - // minima - OBJECTIVE_TABU_SEARCH = 5; + // Uses tabu search on a list of variables to escape local minima. The list + // of variables to use must be provided via the SetTabuVarsCallback + // callback. + GENERIC_TABU_SEARCH = 5; } } diff --git a/ortools/constraint_solver/routing_flags.cc b/ortools/constraint_solver/routing_flags.cc index 075e204297b..b9b51a7fb12 100644 --- a/ortools/constraint_solver/routing_flags.cc +++ b/ortools/constraint_solver/routing_flags.cc @@ -16,9 +16,13 @@ #include #include +#include "absl/time/time.h" #include "ortools/base/map_util.h" +#include "ortools/base/protoutil.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/routing_enums.pb.h" +#include "ortools/constraint_solver/routing_parameters.h" +#include "ortools/util/optional_boolean.pb.h" // --- Routing search flags --- @@ -43,6 +47,8 @@ DEFINE_bool(routing_no_make_active, false, "Routing: forbids use of MakeActive/SwapActive/MakeInactive " "neighborhoods."); DEFINE_bool(routing_no_lkh, false, "Routing: forbids use of LKH neighborhood."); +DEFINE_bool(routing_no_relocate_expensive_chain, false, + "Routing: forbids use of RelocateExpensiveChain operator."); DEFINE_bool(routing_no_tsp, true, "Routing: forbids use of TSPOpt neighborhood."); DEFINE_bool(routing_no_tsplns, true, @@ -59,8 +65,8 @@ DEFINE_double(routing_guided_local_search_lambda_coefficient, 0.1, DEFINE_bool(routing_simulated_annealing, false, "Routing: use simulated annealing."); DEFINE_bool(routing_tabu_search, false, "Routing: use tabu search."); -DEFINE_bool(routing_objective_tabu_search, false, - "Routing: use tabu search based on objective value."); +DEFINE_bool(routing_generic_tabu_search, false, + "Routing: use tabu search based on a list of values."); // Search limits DEFINE_int64(routing_solution_limit, kint64max, @@ -75,14 +81,28 @@ DEFINE_string(routing_first_solution, "", "in the code to get a full list."); DEFINE_bool(routing_use_filtered_first_solutions, true, "Use filtered version of first solution heuristics if available."); -DEFINE_double(savings_neighbors_ratio, 0, +DEFINE_double(savings_neighbors_ratio, 1, "Ratio of neighbors to consider for each node when " "constructing the savings."); DEFINE_bool(savings_add_reverse_arcs, false, "Add savings related to reverse arcs when finding the nearest " "neighbors of the nodes."); +DEFINE_double(savings_arc_coefficient, 1.0, + "Coefficient of the cost of the arc for which the saving value " + "is being computed."); +DEFINE_double(cheapest_insertion_farthest_seeds_ratio, 0, + "Ratio of available vehicles in the model on which farthest " + "nodes of the model are inserted as seeds."); +DEFINE_double(cheapest_insertion_neighbors_ratio, 1.0, + "Ratio of nodes considered as neighbors in the " + "GlobalCheapestInsertion heuristic."); DEFINE_bool(routing_dfs, false, "Routing: use a complete depth-first search."); DEFINE_int64(routing_optimization_step, 1, "Optimization step."); +DEFINE_int32(routing_number_of_solutions_to_collect, 1, + "Number of solutions to collect."); +DEFINE_int32(routing_relocate_expensive_chain_num_arcs_to_consider, 4, + "Number of arcs to consider in the RelocateExpensiveChain " + "neighborhood operator."); // Propagation control DEFINE_bool(routing_use_light_propagation, true, @@ -94,9 +114,6 @@ DEFINE_int64(routing_max_cache_size, 1000, "Maximum cache size when callback caching is on."); // Misc -DEFINE_bool(routing_fingerprint_arc_cost_evaluators, true, - "Compare arc-cost evaluators using the fingerprint of their " - "corresponding matrix instead of evaluator addresses."); DEFINE_bool(routing_trace, false, "Routing: trace search."); DEFINE_bool(routing_profile, false, "Routing: profile search."); @@ -123,6 +140,8 @@ void SetFirstSolutionStrategyFromFlags(RoutingSearchParameters* parameters) { {"BestInsertion", FirstSolutionStrategy::BEST_INSERTION}, {"GlobalCheapestInsertion", FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION}, + {"SequentialGlobalCheapestInsertion", + FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION}, {"LocalCheapestInsertion", FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION}, {"GlobalCheapestArc", FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC}, @@ -134,10 +153,15 @@ void SetFirstSolutionStrategyFromFlags(RoutingSearchParameters* parameters) { FLAGS_routing_first_solution, &strategy)) { parameters->set_first_solution_strategy(strategy); } - parameters->set_use_filtered_first_solution_strategy( - FLAGS_routing_use_filtered_first_solutions); + parameters->set_use_unfiltered_first_solution_strategy( + !FLAGS_routing_use_filtered_first_solutions); parameters->set_savings_neighbors_ratio(FLAGS_savings_neighbors_ratio); parameters->set_savings_add_reverse_arcs(FLAGS_savings_add_reverse_arcs); + parameters->set_savings_arc_coefficient(FLAGS_savings_arc_coefficient); + parameters->set_cheapest_insertion_farthest_seeds_ratio( + FLAGS_cheapest_insertion_farthest_seeds_ratio); + parameters->set_cheapest_insertion_neighbors_ratio( + FLAGS_cheapest_insertion_neighbors_ratio); } void SetLocalSearchMetaheuristicFromFlags(RoutingSearchParameters* parameters) { @@ -145,9 +169,9 @@ void SetLocalSearchMetaheuristicFromFlags(RoutingSearchParameters* parameters) { if (FLAGS_routing_tabu_search) { parameters->set_local_search_metaheuristic( LocalSearchMetaheuristic::TABU_SEARCH); - } else if (FLAGS_routing_objective_tabu_search) { + } else if (FLAGS_routing_generic_tabu_search) { parameters->set_local_search_metaheuristic( - LocalSearchMetaheuristic::OBJECTIVE_TABU_SEARCH); + LocalSearchMetaheuristic::GENERIC_TABU_SEARCH); } else if (FLAGS_routing_simulated_annealing) { parameters->set_local_search_metaheuristic( LocalSearchMetaheuristic::SIMULATED_ANNEALING); @@ -159,51 +183,89 @@ void SetLocalSearchMetaheuristicFromFlags(RoutingSearchParameters* parameters) { FLAGS_routing_guided_local_search_lambda_coefficient); } +namespace { +OptionalBoolean ToOptionalBoolean(bool x) { return x ? BOOL_TRUE : BOOL_FALSE; } +} // namespace + void AddLocalSearchNeighborhoodOperatorsFromFlags( RoutingSearchParameters* parameters) { CHECK(parameters != nullptr); RoutingSearchParameters::LocalSearchNeighborhoodOperators* const local_search_operators = parameters->mutable_local_search_operators(); - local_search_operators->set_use_relocate_pair(true); - local_search_operators->set_use_relocate(!FLAGS_routing_no_relocate); + + // TODO(user): Remove these overrides: they should be set by the caller, via + // a baseline RoutingSearchParameters obtained from DefaultSearchParameters(). + local_search_operators->set_use_relocate_pair(BOOL_TRUE); + local_search_operators->set_use_light_relocate_pair(BOOL_TRUE); + local_search_operators->set_use_exchange_pair(BOOL_TRUE); + local_search_operators->set_use_relocate_and_make_active(BOOL_FALSE); + local_search_operators->set_use_node_pair_swap_active(BOOL_FALSE); + local_search_operators->set_use_cross_exchange(BOOL_FALSE); + + local_search_operators->set_use_relocate( + ToOptionalBoolean(!FLAGS_routing_no_relocate)); local_search_operators->set_use_relocate_neighbors( - !FLAGS_routing_no_relocate_neighbors); - local_search_operators->set_use_exchange(!FLAGS_routing_no_exchange); - local_search_operators->set_use_cross(!FLAGS_routing_no_cross); - local_search_operators->set_use_two_opt(!FLAGS_routing_no_2opt); - local_search_operators->set_use_or_opt(!FLAGS_routing_no_oropt); - local_search_operators->set_use_lin_kernighan(!FLAGS_routing_no_lkh); - local_search_operators->set_use_tsp_opt(!FLAGS_routing_no_tsp); - local_search_operators->set_use_make_active(!FLAGS_routing_no_make_active); - local_search_operators->set_use_make_inactive( - !FLAGS_routing_use_chain_make_inactive && !FLAGS_routing_no_make_active); - local_search_operators->set_use_make_chain_inactive( - FLAGS_routing_use_chain_make_inactive && !FLAGS_routing_no_make_active); + ToOptionalBoolean(!FLAGS_routing_no_relocate_neighbors)); + local_search_operators->set_use_exchange( + ToOptionalBoolean(!FLAGS_routing_no_exchange)); + local_search_operators->set_use_cross( + ToOptionalBoolean(!FLAGS_routing_no_cross)); + local_search_operators->set_use_two_opt( + ToOptionalBoolean(!FLAGS_routing_no_2opt)); + local_search_operators->set_use_or_opt( + ToOptionalBoolean(!FLAGS_routing_no_oropt)); + local_search_operators->set_use_lin_kernighan( + ToOptionalBoolean(!FLAGS_routing_no_lkh)); + local_search_operators->set_use_relocate_expensive_chain( + ToOptionalBoolean(!FLAGS_routing_no_relocate_expensive_chain)); + local_search_operators->set_use_tsp_opt( + ToOptionalBoolean(!FLAGS_routing_no_tsp)); + local_search_operators->set_use_make_active( + ToOptionalBoolean(!FLAGS_routing_no_make_active)); + local_search_operators->set_use_make_inactive(ToOptionalBoolean( + !FLAGS_routing_use_chain_make_inactive && !FLAGS_routing_no_make_active)); + local_search_operators->set_use_make_chain_inactive(ToOptionalBoolean( + FLAGS_routing_use_chain_make_inactive && !FLAGS_routing_no_make_active)); local_search_operators->set_use_swap_active( - !FLAGS_routing_use_extended_swap_active && !FLAGS_routing_no_make_active); - local_search_operators->set_use_extended_swap_active( - FLAGS_routing_use_extended_swap_active && !FLAGS_routing_no_make_active); - local_search_operators->set_use_path_lns(!FLAGS_routing_no_lns); - local_search_operators->set_use_inactive_lns(!FLAGS_routing_no_lns); - local_search_operators->set_use_full_path_lns(!FLAGS_routing_no_fullpathlns); - local_search_operators->set_use_tsp_lns(!FLAGS_routing_no_tsplns); + ToOptionalBoolean(!FLAGS_routing_use_extended_swap_active && + !FLAGS_routing_no_make_active)); + local_search_operators->set_use_extended_swap_active(ToOptionalBoolean( + FLAGS_routing_use_extended_swap_active && !FLAGS_routing_no_make_active)); + local_search_operators->set_use_path_lns( + ToOptionalBoolean(!FLAGS_routing_no_lns)); + local_search_operators->set_use_inactive_lns( + ToOptionalBoolean(!FLAGS_routing_no_lns)); + local_search_operators->set_use_full_path_lns( + ToOptionalBoolean(!FLAGS_routing_no_fullpathlns)); + local_search_operators->set_use_tsp_lns( + ToOptionalBoolean(!FLAGS_routing_no_tsplns)); } void SetSearchLimitsFromFlags(RoutingSearchParameters* parameters) { CHECK(parameters != nullptr); parameters->set_use_depth_first_search(FLAGS_routing_dfs); parameters->set_optimization_step(FLAGS_routing_optimization_step); + parameters->set_number_of_solutions_to_collect( + FLAGS_routing_number_of_solutions_to_collect); parameters->set_solution_limit(FLAGS_routing_solution_limit); - parameters->set_time_limit_ms(FLAGS_routing_time_limit); - parameters->set_lns_time_limit_ms(FLAGS_routing_lns_time_limit); + if (FLAGS_routing_time_limit != kint64max) { + CHECK_OK(util_time::EncodeGoogleApiProto( + absl::Milliseconds(FLAGS_routing_time_limit), + parameters->mutable_time_limit())); + } + if (FLAGS_routing_lns_time_limit != kint64max) { + CHECK_OK(util_time::EncodeGoogleApiProto( + absl::Milliseconds(FLAGS_routing_lns_time_limit), + parameters->mutable_lns_time_limit())); + } } void SetMiscellaneousParametersFromFlags(RoutingSearchParameters* parameters) { CHECK(parameters != nullptr); - parameters->set_use_light_propagation(FLAGS_routing_use_light_propagation); - parameters->set_fingerprint_arc_cost_evaluators( - FLAGS_routing_fingerprint_arc_cost_evaluators); + parameters->set_use_full_propagation(!FLAGS_routing_use_light_propagation); parameters->set_log_search(FLAGS_routing_trace); + parameters->set_relocate_expensive_chain_num_arcs_to_consider( + FLAGS_routing_relocate_expensive_chain_num_arcs_to_consider); } RoutingSearchParameters BuildSearchParametersFromFlags() { @@ -213,6 +275,9 @@ RoutingSearchParameters BuildSearchParametersFromFlags() { AddLocalSearchNeighborhoodOperatorsFromFlags(¶meters); SetSearchLimitsFromFlags(¶meters); SetMiscellaneousParametersFromFlags(¶meters); + const std::string error = FindErrorInRoutingSearchParameters(parameters); + LOG_IF(DFATAL, !error.empty()) + << "Error in the routing search parameters built from flags: " << error; return parameters; } @@ -228,4 +293,5 @@ RoutingModelParameters BuildModelParametersFromFlags() { solver_parameters->set_profile_local_search(FLAGS_routing_profile); return parameters; } + } // namespace operations_research diff --git a/ortools/constraint_solver/routing_flags.h b/ortools/constraint_solver/routing_flags.h index 67816f5f6b7..8921a59f0ca 100644 --- a/ortools/constraint_solver/routing_flags.h +++ b/ortools/constraint_solver/routing_flags.h @@ -32,6 +32,7 @@ DECLARE_bool(routing_no_2opt); DECLARE_bool(routing_no_oropt); DECLARE_bool(routing_no_make_active); DECLARE_bool(routing_no_lkh); +DECLARE_bool(routing_no_relocate_expensive_chain); DECLARE_bool(routing_no_tsp); DECLARE_bool(routing_no_tsplns); DECLARE_bool(routing_use_chain_make_inactive); @@ -42,7 +43,7 @@ DECLARE_bool(routing_guided_local_search); DECLARE_double(routing_guided_local_search_lambda_coefficient); DECLARE_bool(routing_simulated_annealing); DECLARE_bool(routing_tabu_search); -DECLARE_bool(routing_objective_tabu_search); +DECLARE_bool(routing_generic_tabu_search); // Search limits DECLARE_int64(routing_solution_limit); @@ -52,8 +53,15 @@ DECLARE_int64(routing_lns_time_limit); // Search control DECLARE_string(routing_first_solution); DECLARE_bool(routing_use_filtered_first_solutions); +DECLARE_double(savings_neighbors_ratio); +DECLARE_bool(savings_add_reverse_arcs); +DECLARE_double(savings_arc_coefficient); +DECLARE_double(cheapest_insertion_farthest_seeds_ratio); +DECLARE_double(cheapest_insertion_neighbors_ratio); DECLARE_bool(routing_dfs); DECLARE_int64(routing_optimization_step); +DECLARE_int32(routing_number_of_solutions_to_collect); +DECLARE_int32(routing_relocate_expensive_chain_num_arcs_to_consider); // Propagation control DECLARE_bool(routing_use_light_propagation); @@ -63,7 +71,6 @@ DECLARE_bool(routing_cache_callbacks); DECLARE_int64(routing_max_cache_size); // Misc -DECLARE_bool(routing_fingerprint_arc_cost_evaluators); DECLARE_bool(routing_trace); DECLARE_bool(routing_profile); @@ -77,6 +84,8 @@ namespace operations_research { RoutingModelParameters BuildModelParametersFromFlags(); // Builds routing search parameters from flags. +// TODO(user): Make this return a StatusOr, verifying that the flags +// describe a valid set of routing search parameters. RoutingSearchParameters BuildSearchParametersFromFlags(); } // namespace operations_research diff --git a/ortools/constraint_solver/routing_flow.cc b/ortools/constraint_solver/routing_flow.cc new file mode 100644 index 00000000000..34da596d892 --- /dev/null +++ b/ortools/constraint_solver/routing_flow.cc @@ -0,0 +1,420 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ortools/constraint_solver/routing.h" + +#include "ortools/graph/min_cost_flow.h" + +namespace operations_research { + +namespace { +// Compute set of disjunctions involved in a pickup and delivery pair. +template +void AddDisjunctionsFromNodes(const RoutingModel& model, + const std::vector& nodes, + Disjunctions* disjunctions) { + for (int64 node : nodes) { + for (const auto disjunction : model.GetDisjunctionIndices(node)) { + disjunctions->insert(disjunction); + } + } +} +} // namespace + +bool RoutingModel::IsMatchingModel() const { + // TODO(user): Support overlapping disjunctions and disjunctions with + // a cardinality > 1. + absl::flat_hash_set disjunction_nodes; + for (DisjunctionIndex i(0); i < GetNumberOfDisjunctions(); ++i) { + if (GetDisjunctionMaxCardinality(i) > 1) return false; + for (int64 node : GetDisjunctionIndices(i)) { + if (!disjunction_nodes.insert(node).second) return false; + } + } + for (const auto& pd_pairs : GetPickupAndDeliveryPairs()) { + absl::flat_hash_set disjunctions; + AddDisjunctionsFromNodes(*this, pd_pairs.first, &disjunctions); + AddDisjunctionsFromNodes(*this, pd_pairs.second, &disjunctions); + // Pairs involving more than 2 disjunctions are not supported. + if (disjunctions.size() > 2) return false; + } + // Detect if a "unary" dimension prevents from having more than a single + // non-start/end node (or a single pickup and delivery pair) on a route. + // Binary dimensions are not considered because they would result in a + // quadratic check. + for (const RoutingDimension* const dimension : dimensions_) { + // TODO(user): Support vehicle-dependent dimension callbacks. + if (dimension->class_evaluators_.size() != 1) { + continue; + } + const TransitCallback1& transit = + UnaryTransitCallbackOrNull(dimension->class_evaluators_[0]); + if (transit == nullptr) { + continue; + } + int64 max_vehicle_capacity = 0; + for (int64 vehicle_capacity : dimension->vehicle_capacities()) { + max_vehicle_capacity = std::max(max_vehicle_capacity, vehicle_capacity); + } + std::vector transits(nexts_.size(), kint64max); + for (int i = 0; i < nexts_.size(); ++i) { + if (!IsStart(i) && !IsEnd(i)) { + transits[i] = std::min(transits[i], transit(i)); + } + } + int64 min_transit = kint64max; + // Find the minimal accumulated value resulting from a pickup and delivery + // pair. + for (const auto& pd_pairs : GetPickupAndDeliveryPairs()) { + const auto transit_cmp = [&transits](int i, int j) { + return transits[i] < transits[j]; + }; + min_transit = std::min( + min_transit, + // Min transit from pickup. + transits[*std::min_element(pd_pairs.first.begin(), + pd_pairs.first.end(), transit_cmp)] + + // Min transit from delivery. + transits[*std::min_element(pd_pairs.second.begin(), + pd_pairs.second.end(), transit_cmp)]); + } + // Find the minimal accumulated value resulting from a non-pickup/delivery + // node. + for (int i = 0; i < transits.size(); ++i) { + if (GetPickupIndexPairs(i).empty() && GetDeliveryIndexPairs(i).empty()) { + min_transit = std::min(min_transit, transits[i]); + } + } + // If there cannot be more than one node or pickup and delivery, a matching + // problem has been detected. + if (CapProd(min_transit, 2) > max_vehicle_capacity) return true; + } + return false; +} + +// Solve matching model using a min-cost flow. Here is the underlyihg flow: +// +// ---------- Source ------------- +// | (1,0) | (N,0) +// V V +// (vehicles) unperformed +// | (1,cost) | +// V | +// (nodes/pickup/deliveries) | (1,penalty) +// | (1,0) | +// V | +// disjunction <--------- +// | (1, 0) +// V +// Sink +// +// On arcs, (,) represents (capacity, cost). +// N: number of disjunctions +// + +namespace { +struct FlowArc { + int64 tail; + int64 head; + int64 capacity; + int64 cost; +}; +} // namespace + +bool RoutingModel::SolveMatchingModel(Assignment* assignment) { + VLOG(2) << "Solving with flow"; + assignment->Clear(); + + // Collect dimensions with costs. + // TODO(user): If the costs are soft cumul upper (resp. lower) bounds only, + // do not use the LP model. + const std::vector dimensions = + GetDimensionsWithSoftOrSpanCosts(); + std::vector optimizers; + optimizers.reserve(dimensions.size()); + for (RoutingDimension* dimension : dimensions) { + optimizers.emplace_back(dimension); + } + + int num_flow_nodes = 0; + std::vector> disjunction_to_flow_nodes; + std::vector disjunction_penalties; + std::vector in_disjunction(Size(), false); + // Create pickup and delivery pair flow nodes. + // TODO(user): Check pair alternatives correspond exactly to at most two + // disjunctions. + absl::flat_hash_map> flow_to_pd; + for (const auto& pd_pairs : GetPickupAndDeliveryPairs()) { + disjunction_to_flow_nodes.push_back({}); + absl::flat_hash_set disjunctions; + AddDisjunctionsFromNodes(*this, pd_pairs.first, &disjunctions); + AddDisjunctionsFromNodes(*this, pd_pairs.second, &disjunctions); + for (int64 pickup : pd_pairs.first) { + in_disjunction[pickup] = true; + for (int64 delivery : pd_pairs.second) { + in_disjunction[delivery] = true; + flow_to_pd[num_flow_nodes] = {pickup, delivery}; + disjunction_to_flow_nodes.back().push_back(num_flow_nodes); + num_flow_nodes++; + } + } + DCHECK_LE(disjunctions.size(), 2); + int64 penalty = 0; + if (disjunctions.size() < 2) { + penalty = kNoPenalty; + } else { + for (DisjunctionIndex index : disjunctions) { + const int64 d_penalty = GetDisjunctionPenalty(index); + if (d_penalty == kNoPenalty) { + penalty = kNoPenalty; + break; + } + penalty = CapAdd(penalty, d_penalty); + } + } + disjunction_penalties.push_back(penalty); + } + // Create non-pickup and delivery flow nodes. + absl::flat_hash_map flow_to_non_pd; + for (int node = 0; node < Size(); ++node) { + if (IsStart(node) || in_disjunction[node]) continue; + const std::vector& disjunctions = + GetDisjunctionIndices(node); + DCHECK_LE(disjunctions.size(), 1); + disjunction_to_flow_nodes.push_back({}); + disjunction_penalties.push_back( + disjunctions.empty() ? kNoPenalty + : GetDisjunctionPenalty(disjunctions.back())); + if (disjunctions.empty()) { + in_disjunction[node] = true; + flow_to_non_pd[num_flow_nodes] = node; + disjunction_to_flow_nodes.back().push_back(num_flow_nodes); + num_flow_nodes++; + } else { + for (int n : GetDisjunctionIndices(disjunctions.back())) { + in_disjunction[n] = true; + flow_to_non_pd[num_flow_nodes] = n; + disjunction_to_flow_nodes.back().push_back(num_flow_nodes); + num_flow_nodes++; + } + } + } + + std::vector arcs; + + // Build a flow node for each disjunction and corresponding arcs. + // Each node exits to the sink through a node, for which the outgoing + // capacity is one (only one of the nodes in the disjunction is performed). + absl::flat_hash_map flow_to_disjunction; + for (int i = 0; i < disjunction_to_flow_nodes.size(); ++i) { + const std::vector& flow_nodes = disjunction_to_flow_nodes[i]; + if (flow_nodes.size() == 1) { + flow_to_disjunction[flow_nodes.back()] = i; + } else { + flow_to_disjunction[num_flow_nodes] = i; + for (int64 flow_node : flow_nodes) { + arcs.push_back({flow_node, num_flow_nodes, 1, 0}); + } + num_flow_nodes++; + } + } + + // Build arcs from each vehicle to each non-vehicle flow node; the cost of + // each arc corresponds to: + // start(vehicle) -> pickup -> delivery -> end(vehicle) + // or + // start(vehicle) -> node -> end(vehicle) + std::vector vehicle_to_flow; + absl::flat_hash_map flow_to_vehicle; + for (int vehicle = 0; vehicle < vehicles(); ++vehicle) { + const int64 start = Start(vehicle); + const int64 end = End(vehicle); + for (const std::vector& flow_nodes : disjunction_to_flow_nodes) { + for (int64 flow_node : flow_nodes) { + std::pair pd_pair; + int64 node = -1; + int64 cost = 0; + bool add_arc = false; + if (gtl::FindCopy(flow_to_pd, flow_node, &pd_pair)) { + const int64 pickup = pd_pair.first; + const int64 delivery = pd_pair.second; + if (IsVehicleAllowedForIndex(vehicle, pickup) && + IsVehicleAllowedForIndex(vehicle, delivery)) { + add_arc = true; + cost = + CapAdd(GetArcCostForVehicle(start, pickup, vehicle), + CapAdd(GetArcCostForVehicle(pickup, delivery, vehicle), + GetArcCostForVehicle(delivery, end, vehicle))); + const std::unordered_map nexts = { + {start, pickup}, {pickup, delivery}, {delivery, end}}; + for (RouteDimensionCumulOptimizer& optimizer : optimizers) { + int64 cumul_cost_value = 0; + if (optimizer.ComputeRouteCumulCostWithoutFixedTransits( + vehicle, + [&nexts](int64 node) { return nexts.find(node)->second; }, + &cumul_cost_value)) { + cost = CapAdd(cost, cumul_cost_value); + } else { + add_arc = false; + break; + } + } + } + } else if (gtl::FindCopy(flow_to_non_pd, flow_node, &node)) { + if (IsVehicleAllowedForIndex(vehicle, node)) { + add_arc = true; + cost = CapAdd(GetArcCostForVehicle(start, node, vehicle), + GetArcCostForVehicle(node, end, vehicle)); + const std::unordered_map nexts = {{start, node}, + {node, end}}; + for (RouteDimensionCumulOptimizer& optimizer : optimizers) { + int64 cumul_cost_value = 0; + if (optimizer.ComputeRouteCumulCostWithoutFixedTransits( + vehicle, + [&nexts](int64 node) { return nexts.find(node)->second; }, + &cumul_cost_value)) { + cost = CapAdd(cost, cumul_cost_value); + } else { + add_arc = false; + break; + } + } + } + } else { + DCHECK(false); + } + if (add_arc) { + arcs.push_back({num_flow_nodes, flow_node, 1, cost}); + } + } + } + flow_to_vehicle[num_flow_nodes] = vehicle; + vehicle_to_flow.push_back(num_flow_nodes); + num_flow_nodes++; + } + // Create flow source and sink nodes. + const int source = num_flow_nodes + 1; + const int sink = source + 1; + // Source connected to vehicle nodes. + for (int vehicle = 0; vehicle < vehicles(); ++vehicle) { + arcs.push_back({source, vehicle_to_flow[vehicle], 1, 0}); + } + // Handle unperformed nodes. + // Create a node to catch unperformed nodes and connect it to source. + const int unperformed = num_flow_nodes; + const int64 flow_supply = disjunction_to_flow_nodes.size(); + arcs.push_back({source, unperformed, flow_supply, 0}); + for (const auto& flow_disjunction_element : flow_to_disjunction) { + const int flow_node = flow_disjunction_element.first; + const int64 penalty = + disjunction_penalties[flow_disjunction_element.second]; + if (penalty != kNoPenalty) { + arcs.push_back({unperformed, flow_node, 1, penalty}); + } + // Connect non-vehicle flow nodes to sinks. + arcs.push_back({flow_node, sink, 1, 0}); + } + + // Rescale costs for min-cost flow; assuming max cost resulting from the + // push-relabel flow algorithm is max_arc_cost * (num_nodes+1) * (num_nodes+1) + // (cost-scaling multiplies arc costs by num_nodes+1 and the flow itself can + // accumulate num_nodes+1 such arcs (with capacity being 1 for costed arcs)). + int64 scale_factor = 1; + const FlowArc& arc_with_max_cost = *std::max_element( + arcs.begin(), arcs.end(), + [](const FlowArc& a, const FlowArc& b) { return a.cost < b.cost; }); + // SimpleMinCostFlow adds a source and a sink node, so actual number of + // nodes to consider is num_flow_nodes + 3. + const int actual_flow_num_nodes = num_flow_nodes + 3; + if (log(static_cast(arc_with_max_cost.cost) + 1) + + 2 * log(actual_flow_num_nodes) > + log(std::numeric_limits::max())) { + scale_factor = CapProd(actual_flow_num_nodes, actual_flow_num_nodes); + } + + SimpleMinCostFlow flow; + // Add arcs to flow. + for (const FlowArc& arc : arcs) { + flow.AddArcWithCapacityAndUnitCost(arc.tail, arc.head, arc.capacity, + arc.cost / scale_factor); + } + + // Set flow supply (number of non-vehicle nodes or pairs). + flow.SetNodeSupply(source, flow_supply); + flow.SetNodeSupply(sink, -flow_supply); + + // TODO(user): Take time limit into account. + if (flow.Solve() != SimpleMinCostFlow::OPTIMAL) { + return false; + } + + // Map the flow result to assignment, only setting next variables. + std::vector used_vehicles(vehicles(), false); + absl::flat_hash_set used_nodes; + for (int i = 0; i < flow.NumArcs(); ++i) { + if (flow.Flow(i) > 0 && flow.Tail(i) != source && flow.Head(i) != sink) { + std::vector nodes; + std::pair pd_pair; + int node = -1; + int index = -1; + if (gtl::FindCopy(flow_to_pd, flow.Head(i), &pd_pair)) { + nodes.push_back(pd_pair.first); + nodes.push_back(pd_pair.second); + } else if (gtl::FindCopy(flow_to_non_pd, flow.Head(i), &node)) { + nodes.push_back(node); + } else if (gtl::FindCopy(flow_to_disjunction, flow.Head(i), &index)) { + for (int64 flow_node : disjunction_to_flow_nodes[index]) { + if (gtl::FindCopy(flow_to_pd, flow_node, &pd_pair)) { + nodes.push_back(pd_pair.first); + nodes.push_back(pd_pair.second); + } else if (gtl::FindCopy(flow_to_non_pd, flow_node, &node)) { + nodes.push_back(node); + } + } + } + int vehicle = -1; + if (flow.Tail(i) == unperformed) { + // Head is unperformed. + for (int node : nodes) { + assignment->Add(NextVar(node))->SetValue(node); + used_nodes.insert(node); + } + } else if (gtl::FindCopy(flow_to_vehicle, flow.Tail(i), &vehicle)) { + // Head is performed on a vehicle. + used_vehicles[vehicle] = true; + int current = Start(vehicle); + for (int node : nodes) { + assignment->Add(NextVar(current))->SetValue(node); + used_nodes.insert(node); + current = node; + } + assignment->Add(NextVar(current))->SetValue(End(vehicle)); + } + } + } + // Adding unused nodes. + for (int node = 0; node < Size(); ++node) { + if (!IsStart(node) && used_nodes.count(node) == 0) { + assignment->Add(NextVar(node))->SetValue(node); + } + } + // Adding unused vehicles. + for (int vehicle = 0; vehicle < vehicles(); ++vehicle) { + if (!used_vehicles[vehicle]) { + assignment->Add(NextVar(Start(vehicle)))->SetValue(End(vehicle)); + } + } + return true; +} + +} // namespace operations_research diff --git a/ortools/constraint_solver/routing_index_manager.cc b/ortools/constraint_solver/routing_index_manager.cc new file mode 100644 index 00000000000..d2995e88815 --- /dev/null +++ b/ortools/constraint_solver/routing_index_manager.cc @@ -0,0 +1,135 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ortools/constraint_solver/routing_index_manager.h" + +#include + +#include "absl/container/flat_hash_set.h" +#include "ortools/base/logging.h" +#include "ortools/base/map_util.h" + +namespace operations_research { + +const int64 RoutingIndexManager::kUnassigned = -1; + +RoutingIndexManager::RoutingIndexManager(int num_nodes, int num_vehicles, + NodeIndex depot) + : RoutingIndexManager(num_nodes, num_vehicles, + std::vector>( + num_vehicles, {depot, depot})) {} + +RoutingIndexManager::RoutingIndexManager(int num_nodes, int num_vehicles, + const std::vector& starts, + const std::vector& ends) { + CHECK_EQ(starts.size(), num_vehicles); + CHECK_EQ(ends.size(), num_vehicles); + std::vector> starts_ends(num_vehicles); + for (int v = 0; v < num_vehicles; ++v) { + starts_ends[v] = {starts[v], ends[v]}; + } + Initialize(num_nodes, num_vehicles, starts_ends); +} + +RoutingIndexManager::RoutingIndexManager( + int num_nodes, int num_vehicles, + const std::vector>& starts_ends) { + Initialize(num_nodes, num_vehicles, starts_ends); +} + +void RoutingIndexManager::Initialize( + int num_nodes, int num_vehicles, + const std::vector>& starts_ends) { + static const NodeIndex kZeroNode(0); + + num_nodes_ = num_nodes; + num_vehicles_ = num_vehicles; + CHECK_EQ(num_vehicles_, starts_ends.size()); + absl::flat_hash_set starts; + absl::flat_hash_set ends; + absl::flat_hash_set unique_depots; + for (const std::pair& start_end : starts_ends) { + const NodeIndex start = start_end.first; + const NodeIndex end = start_end.second; + CHECK_GE(start, 0); + CHECK_GE(end, 0); + CHECK_LE(start, num_nodes_); + CHECK_LE(end, num_nodes_); + starts.insert(start); + ends.insert(end); + unique_depots.insert(start); + unique_depots.insert(end); + } + num_unique_depots_ = unique_depots.size(); + const int size = num_nodes_ + num_vehicles_ - num_unique_depots_; + + index_to_node_.resize(size + num_vehicles_); + node_to_index_.resize(num_nodes_, kUnassigned); + vehicle_to_start_.resize(num_vehicles_); + vehicle_to_end_.resize(num_vehicles_); + int index = 0; + for (NodeIndex i = kZeroNode; i < num_nodes_; ++i) { + if (gtl::ContainsKey(starts, i) || !gtl::ContainsKey(ends, i)) { + index_to_node_[index] = i; + node_to_index_[i] = index; + ++index; + } + } + absl::flat_hash_set seen_starts; + for (int i = 0; i < num_vehicles_; ++i) { + const NodeIndex start = starts_ends[i].first; + if (!gtl::ContainsKey(seen_starts, start)) { + seen_starts.insert(start); + const int start_index = node_to_index_[start]; + vehicle_to_start_[i] = start_index; + CHECK_NE(kUnassigned, start_index); + } else { + vehicle_to_start_[i] = index; + index_to_node_[index] = start; + ++index; + } + } + for (int i = 0; i < num_vehicles_; ++i) { + NodeIndex end = starts_ends[i].second; + index_to_node_[index] = end; + vehicle_to_end_[i] = index; + CHECK_LE(size, index); + ++index; + } + + // Logging model information. + VLOG(1) << "Number of nodes: " << num_nodes_; + VLOG(1) << "Number of vehicles: " << num_vehicles_; + for (int index = 0; index < index_to_node_.size(); ++index) { + VLOG(2) << "Variable index " << index << " -> Node index " + << index_to_node_[index]; + } + for (NodeIndex node = kZeroNode; node < node_to_index_.size(); ++node) { + VLOG(2) << "Node index " << node << " -> Variable index " + << node_to_index_[node]; + } +} + +std::vector RoutingIndexManager::NodesToIndices( + const std::vector& nodes) const { + std::vector indices; + indices.reserve(nodes.size()); + for (const NodeIndex node : nodes) { + const int64 index = NodeToIndex(node); + CHECK_NE(kUnassigned, index); + indices.push_back(index); + } + return indices; +} + +} // namespace operations_research diff --git a/ortools/constraint_solver/routing_index_manager.h b/ortools/constraint_solver/routing_index_manager.h new file mode 100644 index 00000000000..4991273690c --- /dev/null +++ b/ortools/constraint_solver/routing_index_manager.h @@ -0,0 +1,97 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_INDEX_MANAGER_H_ +#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_INDEX_MANAGER_H_ + +#include +#include + +#include "ortools/base/int_type_indexed_vector.h" +#include "ortools/base/logging.h" +#include "ortools/constraint_solver/routing_types.h" + +namespace operations_research { + +// Manager for any NodeIndex <-> variable index conversion. The routing solver +// uses variable indices internally and through its API. These variable indices +// are tricky to manage directly because one Node can correspond to a multitude +// of variables, depending on the number of times they appear in the model, and +// if they're used as start and/or end points. This class aims to simplify +// variable index usage, allowing users to use NodeIndex instead. +// +// Usage: +// auto starts_ends = ...; // These are NodeIndex. +// RoutingIndexManager manager(/*nodes*/10, /*vehicles*/4, starts_ends); +// RoutingModel model(manager); +// +// Then, use 'manager.NodeToIndex(node)' whenever 'model' requires a variable +// index. +class RoutingIndexManager { + public: + typedef RoutingNodeIndex NodeIndex; + static const int64 kUnassigned; + + // Creates a NodeIndex to variable index mapping for a problem containing + // 'num_nodes', 'num_vehicles' and the given starts and ends for each vehicle. + // If used, any start/end arrays have to have exactly 'num_vehicles' elements. + RoutingIndexManager(int num_nodes, int num_vehicles, NodeIndex depot); + RoutingIndexManager(int num_nodes, int num_vehicles, + const std::vector& starts, + const std::vector& ends); + RoutingIndexManager( + int num_nodes, int num_vehicles, + const std::vector >& starts_ends); + ~RoutingIndexManager() {} + + int num_nodes() const { return num_nodes_; } + int num_vehicles() const { return num_vehicles_; } + int num_indices() const { return index_to_node_.size(); } + int64 GetStartIndex(int vehicle) const { return vehicle_to_start_[vehicle]; } + int64 GetEndIndex(int vehicle) const { return vehicle_to_end_[vehicle]; } + int NodeToIndex(NodeIndex node) const { + DCHECK_GE(node.value(), 0); + DCHECK_LT(node.value(), node_to_index_.size()); + return node_to_index_[node]; + } + std::vector NodesToIndices(const std::vector& nodes) const; + NodeIndex IndexToNode(int index) const { + DCHECK_GE(index, 0); + DCHECK_LT(index, index_to_node_.size()); + return index_to_node_[index]; + } + // TODO(user): Remove when removal of NodeIndex from RoutingModel is + // complete. + int num_unique_depots() const { return num_unique_depots_; } + std::vector GetIndexToNodeMap() const { return index_to_node_; } + gtl::ITIVector GetNodeToIndexMap() const { + return node_to_index_; + } + + private: + void Initialize( + int num_nodes, int num_vehicles, + const std::vector >& starts_ends); + + std::vector index_to_node_; + gtl::ITIVector node_to_index_; + std::vector vehicle_to_start_; + std::vector vehicle_to_end_; + int num_nodes_; + int num_vehicles_; + int num_unique_depots_; +}; + +} // namespace operations_research + +#endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_INDEX_MANAGER_H_ diff --git a/ortools/constraint_solver/routing_neighborhoods.cc b/ortools/constraint_solver/routing_neighborhoods.cc index 704a93b39d3..9ec8014f1d2 100644 --- a/ortools/constraint_solver/routing_neighborhoods.cc +++ b/ortools/constraint_solver/routing_neighborhoods.cc @@ -19,7 +19,7 @@ MakeRelocateNeighborsOperator::MakeRelocateNeighborsOperator( const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - RoutingTransitEvaluator2 arc_evaluator) + RoutingTransitCallback2 arc_evaluator) : PathWithPreviousNodesOperator(vars, secondary_vars, 2, std::move(start_empty_path_class)), arc_evaluator_(std::move(arc_evaluator)) {} @@ -92,13 +92,15 @@ MakePairActiveOperator::MakePairActiveOperator( const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingNodePairs& pairs) - : PathOperator(vars, secondary_vars, 2, std::move(start_empty_path_class)), + const RoutingIndexPairs& pairs) + : PathOperator(vars, secondary_vars, 2, false, + std::move(start_empty_path_class)), inactive_pair_(0), pairs_(pairs) {} bool MakePairActiveOperator::MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) { + // TODO(user): Support pairs with disjunctions. while (inactive_pair_ < pairs_.size()) { if (!IsInactive(pairs_[inactive_pair_].first[0]) || !IsInactive(pairs_[inactive_pair_].second[0]) || @@ -146,18 +148,19 @@ MakePairInactiveOperator::MakePairInactiveOperator( const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingNodePairs& node_pairs) + const RoutingIndexPairs& index_pairs) : PathWithPreviousNodesOperator(vars, secondary_vars, 1, std::move(start_empty_path_class)) { int64 max_pair_index = -1; - for (const auto& node_pair : node_pairs) { - max_pair_index = std::max(max_pair_index, node_pair.first[0]); - max_pair_index = std::max(max_pair_index, node_pair.second[0]); + for (const auto& index_pair : index_pairs) { + max_pair_index = std::max(max_pair_index, index_pair.first[0]); + max_pair_index = std::max(max_pair_index, index_pair.second[0]); } pairs_.resize(max_pair_index + 1, -1); - for (const auto& node_pair : node_pairs) { - pairs_[node_pair.first[0]] = node_pair.second[0]; - pairs_[node_pair.second[0]] = node_pair.first[0]; + // TODO(user): Support pairs with disjunctions. + for (const auto& index_pair : index_pairs) { + pairs_[index_pair.first[0]] = index_pair.second[0]; + pairs_[index_pair.second[0]] = index_pair.first[0]; } } @@ -178,49 +181,89 @@ PairRelocateOperator::PairRelocateOperator( const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingNodePairs& node_pairs) - : PathOperator(vars, secondary_vars, 3, std::move(start_empty_path_class)) { + const RoutingIndexPairs& index_pairs) + : PathWithPreviousNodesOperator(vars, secondary_vars, 3, + std::move(start_empty_path_class)) { int64 index_max = 0; for (const IntVar* const var : vars) { index_max = std::max(index_max, var->Max()); } - prevs_.resize(index_max + 1, -1); is_first_.resize(index_max + 1, false); int64 max_pair_index = -1; - for (const auto& node_pair : node_pairs) { - max_pair_index = std::max(max_pair_index, node_pair.first[0]); - max_pair_index = std::max(max_pair_index, node_pair.second[0]); + // TODO(user): Support pairs with disjunctions. + for (const auto& index_pair : index_pairs) { + max_pair_index = std::max(max_pair_index, index_pair.first[0]); + max_pair_index = std::max(max_pair_index, index_pair.second[0]); } pairs_.resize(max_pair_index + 1, -1); - for (const auto& node_pair : node_pairs) { - pairs_[node_pair.first[0]] = node_pair.second[0]; - pairs_[node_pair.second[0]] = node_pair.first[0]; - is_first_[node_pair.first[0]] = true; + for (const auto& index_pair : index_pairs) { + pairs_[index_pair.first[0]] = index_pair.second[0]; + pairs_[index_pair.second[0]] = index_pair.first[0]; + is_first_[index_pair.first[0]] = true; } } bool PairRelocateOperator::MakeNeighbor() { DCHECK_EQ(StartNode(1), StartNode(2)); const int64 first_pair_node = BaseNode(kPairFirstNode); - const int64 prev = prevs_[first_pair_node]; - if (prev < 0) { + if (IsPathStart(first_pair_node)) { return false; } - const int sibling = + int64 first_prev = Prev(first_pair_node); + const int second_pair_node = first_pair_node < pairs_.size() ? pairs_[first_pair_node] : -1; - if (sibling < 0) { + if (second_pair_node < 0) { return false; } if (!is_first_[first_pair_node]) { return false; } - const int64 prev_sibling = prevs_[sibling]; - if (prev_sibling < 0) { + if (IsPathStart(second_pair_node)) { + return false; + } + const int64 second_prev = Prev(second_pair_node); + + if (BaseNode(kPairFirstNodeDestination) == second_pair_node) { + // The second_pair_node -> first_pair_node link is forbidden. + return false; + } + + if (second_prev == first_pair_node && + BaseNode(kPairFirstNodeDestination) == first_prev && + BaseNode(kPairSecondNodeDestination) == first_prev) { + // If the current sequence is first_prev -> first_pair_node -> + // second_pair_node, and both 1st and 2nd are moved both to prev, the result + // of the move will be first_prev -> first_pair_node -> second_pair_node, + // which is no move. return false; } - return MoveChain(prev_sibling, sibling, - BaseNode(kPairSecondNodeDestination)) && - MoveChain(prev, first_pair_node, BaseNode(kPairFirstNodeDestination)); + + // Relocation is a success if at least one of the nodes moved and all the + // moves are successful. + bool moved = false; + + // Do not allow to move second_pair_node to its current prev. + if (second_prev != BaseNode(kPairSecondNodeDestination)) { + if (!MoveChain(second_prev, second_pair_node, + BaseNode(kPairSecondNodeDestination))) { + return false; + } + if (BaseNode(kPairSecondNodeDestination) == first_prev) { + first_prev = second_pair_node; + } + moved = true; + } + + // Do not allow to move first_pair_node to its current prev. + if (first_prev != BaseNode(kPairFirstNodeDestination)) { + if (!MoveChain(first_prev, first_pair_node, + BaseNode(kPairFirstNodeDestination))) { + return false; + } + moved = true; + } + + return moved; } int64 PairRelocateOperator::GetBaseNodeRestartPosition(int base_index) { @@ -233,34 +276,418 @@ int64 PairRelocateOperator::GetBaseNodeRestartPosition(int base_index) { } } -void PairRelocateOperator::OnNodeInitialization() { - for (int i = 0; i < number_of_nexts(); ++i) { - prevs_[Next(i)] = i; +LightPairRelocateOperator::LightPairRelocateOperator( + const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + const RoutingIndexPairs& index_pairs) + : PathWithPreviousNodesOperator(vars, secondary_vars, 2, + std::move(start_empty_path_class)) { + int64 max_pair_index = -1; + // TODO(user): Support pairs with disjunctions. + for (const auto& index_pair : index_pairs) { + max_pair_index = std::max(max_pair_index, index_pair.first[0]); + max_pair_index = std::max(max_pair_index, index_pair.second[0]); + } + pairs_.resize(max_pair_index + 1, -1); + for (const auto& index_pair : index_pairs) { + pairs_[index_pair.first[0]] = index_pair.second[0]; + pairs_[index_pair.second[0]] = index_pair.first[0]; + } +} + +bool LightPairRelocateOperator::MakeNeighbor() { + const int64 prev1 = BaseNode(0); + if (IsPathEnd(prev1)) return false; + const int64 node1 = Next(prev1); + if (IsPathEnd(node1) || node1 >= pairs_.size()) return false; + const int64 sibling1 = pairs_[node1]; + if (sibling1 == -1) return false; + const int64 node2 = BaseNode(1); + if (node2 == sibling1 || IsPathEnd(node2) || node2 >= pairs_.size()) { + return false; + } + const int64 sibling2 = pairs_[node2]; + if (sibling2 == -1) return false; + int64 prev_sibling1 = Prev(sibling1); + if (prev_sibling1 == node1) { + prev_sibling1 = prev1; + } else if (prev_sibling1 == node2) { + prev_sibling1 = node1; } + return MoveChain(prev1, node1, node2) && + (sibling2 == prev_sibling1 || + MoveChain(prev_sibling1, sibling1, sibling2)); } -NodePairSwapActiveOperator::NodePairSwapActiveOperator( +PairExchangeOperator::PairExchangeOperator( const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingNodePairs& node_pairs) + const RoutingIndexPairs& index_pairs) + : PathWithPreviousNodesOperator(vars, secondary_vars, 2, + std::move(start_empty_path_class)) { + int64 index_max = 0; + for (const IntVar* const var : vars) { + index_max = std::max(index_max, var->Max()); + } + is_first_.resize(index_max + 1, false); + int64 max_pair_index = -1; + // TODO(user): Support pairs with disjunctions. + for (const auto& index_pair : index_pairs) { + max_pair_index = std::max(max_pair_index, index_pair.first[0]); + max_pair_index = std::max(max_pair_index, index_pair.second[0]); + } + pairs_.resize(max_pair_index + 1, -1); + for (const auto& index_pair : index_pairs) { + pairs_[index_pair.first[0]] = index_pair.second[0]; + pairs_[index_pair.second[0]] = index_pair.first[0]; + is_first_[index_pair.first[0]] = true; + } +} + +bool PairExchangeOperator::MakeNeighbor() { + const int64 node1 = BaseNode(0); + int64 prev1, sibling1, sibling_prev1 = -1; + if (!GetPreviousAndSibling(node1, &prev1, &sibling1, &sibling_prev1)) { + return false; + } + const int64 node2 = BaseNode(1); + int64 prev2, sibling2, sibling_prev2 = -1; + if (!GetPreviousAndSibling(node2, &prev2, &sibling2, &sibling_prev2)) { + return false; + } + bool status = true; + // Exchanging node1 and node2. + if (node1 == prev2) { + status = MoveChain(prev2, node2, prev1); + if (sibling_prev1 == node2) sibling_prev1 = node1; + if (sibling_prev2 == node2) sibling_prev2 = node1; + } else if (node2 == prev1) { + status = MoveChain(prev1, node1, prev2); + if (sibling_prev1 == node1) sibling_prev1 = node2; + if (sibling_prev2 == node1) sibling_prev2 = node2; + } else { + status = MoveChain(prev1, node1, node2) && MoveChain(prev2, node2, prev1); + if (sibling_prev1 == node1) { + sibling_prev1 = node2; + } else if (sibling_prev1 == node2) { + sibling_prev1 = node1; + } + if (sibling_prev2 == node1) { + sibling_prev2 = node2; + } else if (sibling_prev2 == node2) { + sibling_prev2 = node1; + } + } + if (!status) return false; + // Exchanging sibling1 and sibling2. + if (sibling1 == sibling_prev2) { + status = MoveChain(sibling_prev2, sibling2, sibling_prev1); + } else if (sibling2 == sibling_prev1) { + status = MoveChain(sibling_prev1, sibling1, sibling_prev2); + } else { + status = MoveChain(sibling_prev1, sibling1, sibling2) && + MoveChain(sibling_prev2, sibling2, sibling_prev1); + } + return status; +} + +bool PairExchangeOperator::GetPreviousAndSibling( + int64 node, int64* previous, int64* sibling, + int64* sibling_previous) const { + if (IsPathStart(node)) return false; + *previous = Prev(node); + *sibling = node < pairs_.size() ? pairs_[node] : -1; + *sibling_previous = *sibling >= 0 ? Prev(*sibling) : -1; + return *sibling_previous >= 0 && is_first_[node]; +} + +PairExchangeRelocateOperator::PairExchangeRelocateOperator( + const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + const RoutingIndexPairs& index_pairs) + : PathWithPreviousNodesOperator(vars, secondary_vars, 6, + std::move(start_empty_path_class)) { + int64 index_max = 0; + for (const IntVar* const var : vars) { + index_max = std::max(index_max, var->Max()); + } + is_first_.resize(index_max + 1, false); + int64 max_pair_index = -1; + // TODO(user): Support pairs with disjunctions. + for (const auto& index_pair : index_pairs) { + max_pair_index = std::max(max_pair_index, index_pair.first[0]); + max_pair_index = std::max(max_pair_index, index_pair.second[0]); + } + pairs_.resize(max_pair_index + 1, -1); + for (const auto& index_pair : index_pairs) { + pairs_[index_pair.first[0]] = index_pair.second[0]; + pairs_[index_pair.second[0]] = index_pair.first[0]; + is_first_[index_pair.first[0]] = true; + } +} + +bool PairExchangeRelocateOperator::MakeNeighbor() { + DCHECK_EQ(StartNode(kSecondPairFirstNodeDestination), + StartNode(kSecondPairSecondNodeDestination)); + DCHECK_EQ(StartNode(kSecondPairFirstNode), + StartNode(kFirstPairFirstNodeDestination)); + DCHECK_EQ(StartNode(kSecondPairFirstNode), + StartNode(kFirstPairSecondNodeDestination)); + + if (StartNode(kFirstPairFirstNode) == StartNode(kSecondPairFirstNode)) { + SetNextBaseToIncrement(kSecondPairFirstNode); + return false; + } + // Through this method, [X][Y] represent the variable for the + // node Y of pair X. is in node, prev, dest. + int64 nodes[2][2]; + int64 prev[2][2]; + int64 dest[2][2]; + nodes[0][0] = BaseNode(kFirstPairFirstNode); + nodes[1][0] = BaseNode(kSecondPairFirstNode); + if (nodes[1][0] <= nodes[0][0]) { + // Exchange is symetric. + SetNextBaseToIncrement(kSecondPairFirstNode); + return false; + } + if (!GetPreviousAndSibling(nodes[0][0], &prev[0][0], &nodes[0][1], + &prev[0][1])) { + SetNextBaseToIncrement(kFirstPairFirstNode); + return false; + } + if (!GetPreviousAndSibling(nodes[1][0], &prev[1][0], &nodes[1][1], + &prev[1][1])) { + SetNextBaseToIncrement(kSecondPairFirstNode); + return false; + } + + if (!LoadAndCheckDest(0, 0, kFirstPairFirstNodeDestination, nodes, dest)) { + SetNextBaseToIncrement(kFirstPairFirstNodeDestination); + return false; + } + if (!LoadAndCheckDest(0, 1, kFirstPairSecondNodeDestination, nodes, dest)) { + SetNextBaseToIncrement(kFirstPairSecondNodeDestination); + return false; + } + if (StartNode(kSecondPairFirstNodeDestination) != + StartNode(kFirstPairFirstNode) || + !LoadAndCheckDest(1, 0, kSecondPairFirstNodeDestination, nodes, dest)) { + SetNextBaseToIncrement(kSecondPairFirstNodeDestination); + return false; + } + if (!LoadAndCheckDest(1, 1, kSecondPairSecondNodeDestination, nodes, dest)) { + SetNextBaseToIncrement(kSecondPairSecondNodeDestination); + return false; + } + + if (!MoveNode(0, 1, nodes, dest, prev)) { + SetNextBaseToIncrement(kFirstPairSecondNodeDestination); + return false; + } + if (!MoveNode(0, 0, nodes, dest, prev)) { + SetNextBaseToIncrement(kFirstPairSecondNodeDestination); + return false; + } + if (!MoveNode(1, 1, nodes, dest, prev)) { + return false; + } + if (!MoveNode(1, 0, nodes, dest, prev)) { + return false; + } + return true; +} + +bool PairExchangeRelocateOperator::MoveNode(int pair, int node, + int64 nodes[2][2], int64 dest[2][2], + int64 prev[2][2]) { + if (!MoveChain(prev[pair][node], nodes[pair][node], dest[pair][node])) { + return false; + } + // Update the other pair if needed. + if (prev[1 - pair][0] == dest[pair][node]) { + prev[1 - pair][0] = nodes[pair][node]; + } + if (prev[1 - pair][1] == dest[pair][node]) { + prev[1 - pair][1] = nodes[pair][node]; + } + return true; +} + +bool PairExchangeRelocateOperator::LoadAndCheckDest(int pair, int node, + int64 base_node, + int64 nodes[2][2], + int64 dest[2][2]) const { + dest[pair][node] = BaseNode(base_node); + // A destination cannot be a node that will be moved. + return !(nodes[0][0] == dest[pair][node] || nodes[0][1] == dest[pair][node] || + nodes[1][0] == dest[pair][node] || nodes[1][1] == dest[pair][node]); +} + +bool PairExchangeRelocateOperator::OnSamePathAsPreviousBase(int64 base_index) { + // Ensuring the destination of the first pair is on the route of the second. + // pair. + // Ensuring that destination of both nodes of a pair are on the same route. + return base_index == kFirstPairFirstNodeDestination || + base_index == kFirstPairSecondNodeDestination || + base_index == kSecondPairSecondNodeDestination; +} + +int64 PairExchangeRelocateOperator::GetBaseNodeRestartPosition(int base_index) { + if (base_index == kFirstPairSecondNodeDestination || + base_index == kSecondPairSecondNodeDestination) { + return BaseNode(base_index - 1); + } else { + return StartNode(base_index); + } +} + +bool PairExchangeRelocateOperator::GetPreviousAndSibling( + int64 node, int64* previous, int64* sibling, + int64* sibling_previous) const { + if (IsPathStart(node)) return false; + *previous = Prev(node); + *sibling = node < pairs_.size() ? pairs_[node] : -1; + *sibling_previous = *sibling >= 0 ? Prev(*sibling) : -1; + return *sibling_previous >= 0 && is_first_[node]; +} + +SwapIndexPairOperator::SwapIndexPairOperator( + const std::vector& vars, const std::vector& path_vars, + const RoutingIndexPairs& index_pairs) + : IntVarLocalSearchOperator(vars), + index_pairs_(index_pairs), + pair_index_(0), + first_index_(0), + second_index_(0), + number_of_nexts_(vars.size()), + ignore_path_vars_(path_vars.empty()) { + if (!ignore_path_vars_) { + AddVars(path_vars); + } +} + +bool SwapIndexPairOperator::MakeNextNeighbor(Assignment* delta, + Assignment* deltadelta) { + const int64 kNoPath = -1; + CHECK(delta != nullptr); + while (true) { + RevertChanges(true); + + if (pair_index_ < index_pairs_.size()) { + const int64 path = + ignore_path_vars_ ? 0LL : Value(first_active_ + number_of_nexts_); + const int64 prev_first = prevs_[first_active_]; + const int64 next_first = Value(first_active_); + // Making current active "pickup" unperformed. + SetNext(first_active_, first_active_, kNoPath); + // Inserting "pickup" alternative at the same position. + const int64 insert_first = index_pairs_[pair_index_].first[first_index_]; + SetNext(prev_first, insert_first, path); + SetNext(insert_first, next_first, path); + int64 prev_second = prevs_[second_active_]; + if (prev_second == first_active_) { + prev_second = insert_first; + } + DCHECK_EQ(path, ignore_path_vars_ + ? 0LL + : Value(second_active_ + number_of_nexts_)); + const int64 next_second = Value(second_active_); + // Making current active "delivery" unperformed. + SetNext(second_active_, second_active_, kNoPath); + // Inserting "delivery" alternative at the same position. + const int64 insert_second = + index_pairs_[pair_index_].second[second_index_]; + SetNext(prev_second, insert_second, path); + SetNext(insert_second, next_second, path); + // Move to next "pickup/delivery" alternative. + ++second_index_; + if (second_index_ >= index_pairs_[pair_index_].second.size()) { + second_index_ = 0; + ++first_index_; + if (first_index_ >= index_pairs_[pair_index_].first.size()) { + first_index_ = 0; + ++pair_index_; + UpdateActiveNodes(); + } + } + } else { + return false; + } + + if (ApplyChanges(delta, deltadelta)) { + VLOG(2) << "Delta (" << DebugString() << ") = " << delta->DebugString(); + return true; + } + } + return false; +} + +void SwapIndexPairOperator::OnStart() { + prevs_.resize(number_of_nexts_, -1); + for (int index = 0; index < number_of_nexts_; ++index) { + const int64 next = Value(index); + if (next >= prevs_.size()) prevs_.resize(next + 1, -1); + prevs_[next] = index; + } + pair_index_ = 0; + first_index_ = 0; + second_index_ = 0; + first_active_ = -1; + second_active_ = -1; + while (true) { + if (!UpdateActiveNodes()) break; + if (first_active_ != -1 && second_active_ != -1) { + break; + } + ++pair_index_; + } +} + +bool SwapIndexPairOperator::UpdateActiveNodes() { + if (pair_index_ < index_pairs_.size()) { + for (const int64 first : index_pairs_[pair_index_].first) { + if (Value(first) != first) { + first_active_ = first; + break; + } + } + for (const int64 second : index_pairs_[pair_index_].second) { + if (Value(second) != second) { + second_active_ = second; + break; + } + } + return true; + } + return false; +} + +IndexPairSwapActiveOperator::IndexPairSwapActiveOperator( + const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + const RoutingIndexPairs& index_pairs) : PathWithPreviousNodesOperator(vars, secondary_vars, 1, std::move(start_empty_path_class)), inactive_node_(0) { int64 max_pair_index = -1; - for (const auto& node_pair : node_pairs) { - max_pair_index = std::max(max_pair_index, node_pair.first[0]); - max_pair_index = std::max(max_pair_index, node_pair.second[0]); + // TODO(user): Support pairs with disjunctions. + for (const auto& index_pair : index_pairs) { + max_pair_index = std::max(max_pair_index, index_pair.first[0]); + max_pair_index = std::max(max_pair_index, index_pair.second[0]); } pairs_.resize(max_pair_index + 1, -1); - for (const auto& node_pair : node_pairs) { - pairs_[node_pair.first[0]] = node_pair.second[0]; - pairs_[node_pair.second[0]] = node_pair.first[0]; + for (const auto& index_pair : index_pairs) { + pairs_[index_pair.first[0]] = index_pair.second[0]; + pairs_[index_pair.second[0]] = index_pair.first[0]; } } -bool NodePairSwapActiveOperator::MakeNextNeighbor(Assignment* delta, - Assignment* deltadelta) { +bool IndexPairSwapActiveOperator::MakeNextNeighbor(Assignment* delta, + Assignment* deltadelta) { while (inactive_node_ < Size()) { if (!IsInactive(inactive_node_) || !PathOperator::MakeNextNeighbor(delta, deltadelta)) { @@ -273,7 +700,7 @@ bool NodePairSwapActiveOperator::MakeNextNeighbor(Assignment* delta, return false; } -bool NodePairSwapActiveOperator::MakeNeighbor() { +bool IndexPairSwapActiveOperator::MakeNeighbor() { const int64 base = BaseNode(0); if (IsPathEnd(base)) { return false; @@ -286,7 +713,7 @@ bool NodePairSwapActiveOperator::MakeNeighbor() { return false; } -void NodePairSwapActiveOperator::OnNodeInitialization() { +void IndexPairSwapActiveOperator::OnNodeInitialization() { PathWithPreviousNodesOperator::OnNodeInitialization(); for (int i = 0; i < Size(); ++i) { if (IsInactive(i) && i < pairs_.size() && pairs_[i] == -1) { @@ -297,4 +724,152 @@ void NodePairSwapActiveOperator::OnNodeInitialization() { inactive_node_ = Size(); } +RelocateExpensiveChain::RelocateExpensiveChain( + const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, int num_arcs_to_consider, + std::function arc_cost_for_path_start) + : PathOperator(vars, secondary_vars, 1, false, + std::move(start_empty_path_class)), + num_arcs_to_consider_(num_arcs_to_consider), + current_path_(0), + current_expensive_arc_indices_({-1, -1}), + arc_cost_for_path_start_(std::move(arc_cost_for_path_start)), + end_path_(0), + has_non_empty_paths_to_explore_(false) {} + +bool RelocateExpensiveChain::MakeNeighbor() { + const int first_arc_index = current_expensive_arc_indices_.first; + const int second_arc_index = current_expensive_arc_indices_.second; + DCHECK_LE(0, first_arc_index); + DCHECK_LT(first_arc_index, second_arc_index); + DCHECK_LT(second_arc_index, most_expensive_arc_starts_and_ranks_.size()); + + const std::pair& first_start_and_rank = + most_expensive_arc_starts_and_ranks_[first_arc_index]; + const std::pair& second_start_and_rank = + most_expensive_arc_starts_and_ranks_[second_arc_index]; + if (first_start_and_rank.second < second_start_and_rank.second) { + return MoveChain(first_start_and_rank.first, second_start_and_rank.first, + BaseNode(0)); + } + return MoveChain(second_start_and_rank.first, first_start_and_rank.first, + BaseNode(0)); +} + +bool RelocateExpensiveChain::MakeOneNeighbor() { + while (has_non_empty_paths_to_explore_) { + if (!PathOperator::MakeOneNeighbor()) { + ResetPosition(); + // Move on to the next expensive arcs on the same path. + if (IncrementCurrentArcIndices()) { + continue; + } + // Move on to the next non_empty path. + IncrementCurrentPath(); + has_non_empty_paths_to_explore_ = + current_path_ != end_path_ && + FindMostExpensiveChainsOnRemainingPaths(); + } else { + return true; + } + } + return false; +} + +void RelocateExpensiveChain::OnNodeInitialization() { + if (current_path_ >= path_starts().size()) { + // current_path_ was made empty by last move (and it was the last non-empty + // path), restart from 0. + current_path_ = 0; + } + end_path_ = current_path_; + has_non_empty_paths_to_explore_ = FindMostExpensiveChainsOnRemainingPaths(); +} + +void RelocateExpensiveChain::IncrementCurrentPath() { + const int num_paths = path_starts().size(); + if (++current_path_ == num_paths) { + current_path_ = 0; + } +} + +bool RelocateExpensiveChain::IncrementCurrentArcIndices() { + int& second_index = current_expensive_arc_indices_.second; + if (++second_index < most_expensive_arc_starts_and_ranks_.size()) { + return true; + } + int& first_index = current_expensive_arc_indices_.first; + if (first_index + 2 < most_expensive_arc_starts_and_ranks_.size()) { + first_index++; + second_index = first_index + 1; + return true; + } + return false; +} + +bool RelocateExpensiveChain::FindMostExpensiveChainsOnRemainingPaths() { + do { + if (FindMostExpensiveChainsOnCurrentPath()) { + return true; + } + IncrementCurrentPath(); + } while (current_path_ != end_path_); + return false; +} + +bool RelocateExpensiveChain::FindMostExpensiveChainsOnCurrentPath() { + const int64 current_path_start = path_starts()[current_path_]; + + if (IsPathEnd(OldNext(current_path_start))) { + // Empty path. + current_expensive_arc_indices_.first = + current_expensive_arc_indices_.second = -1; + return false; + } + + // TODO(user): Investigate the impact of using a limited size priority + // queue instead of vectors on performance. + std::vector most_expensive_arc_costs(num_arcs_to_consider_, -1); + most_expensive_arc_starts_and_ranks_.assign(num_arcs_to_consider_, {-1, -1}); + + int64 before_node = current_path_start; + int rank = 0; + while (!IsPathEnd(before_node)) { + const int64 after_node = OldNext(before_node); + const int64 arc_cost = + arc_cost_for_path_start_(before_node, after_node, current_path_start); + if (most_expensive_arc_costs.back() < arc_cost) { + // Insert this arc in most_expensive_* vectors. + most_expensive_arc_costs.back() = arc_cost; + most_expensive_arc_starts_and_ranks_.back().first = before_node; + most_expensive_arc_starts_and_ranks_.back().second = rank; + // Move the newly added element in the vectors to keep + // most_expensive_arc_costs sorted decreasingly. + int index_before_added_arc = num_arcs_to_consider_ - 2; + while (index_before_added_arc >= 0 && + most_expensive_arc_costs[index_before_added_arc] < arc_cost) { + std::swap(most_expensive_arc_costs[index_before_added_arc + 1], + most_expensive_arc_costs[index_before_added_arc]); + std::swap( + most_expensive_arc_starts_and_ranks_[index_before_added_arc + 1], + most_expensive_arc_starts_and_ranks_[index_before_added_arc]); + index_before_added_arc--; + } + } + before_node = after_node; + rank++; + } + // If there are less than num_arcs_to_consider_ arcs on the path, resize the + // vector of arc starts. + if (rank < num_arcs_to_consider_) { + most_expensive_arc_starts_and_ranks_.resize(rank); + } + + current_expensive_arc_indices_.first = 0; + current_expensive_arc_indices_.second = 1; + + return true; +} + } // namespace operations_research diff --git a/ortools/constraint_solver/routing_neighborhoods.h b/ortools/constraint_solver/routing_neighborhoods.h index b8a97517016..da4ddac4451 100644 --- a/ortools/constraint_solver/routing_neighborhoods.h +++ b/ortools/constraint_solver/routing_neighborhoods.h @@ -47,7 +47,7 @@ class MakeRelocateNeighborsOperator : public PathWithPreviousNodesOperator { const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - RoutingTransitEvaluator2 arc_evaluator); + RoutingTransitCallback2 arc_evaluator); ~MakeRelocateNeighborsOperator() override {} bool MakeNeighbor() override; @@ -64,11 +64,15 @@ class MakeRelocateNeighborsOperator : public PathWithPreviousNodesOperator { int64 destination); // Moves node after 'before_to_move' down the path until a position is found - // where NextVar domains are not violated, it it exists. Stops when reaching + // where NextVar domains are not violated, if it exists. Stops when reaching // position after 'up_to'. + // If the node was not moved (either because the current position does not + // violate any domains or because not such position could be found), returns + // -1. If the node was moved to a new position before up_to, returns up_to; + // if it was moved just after up_to returns the node which was after up_to. int64 Reposition(int64 before_to_move, int64 up_to); - RoutingTransitEvaluator2 arc_evaluator_; + RoutingTransitCallback2 arc_evaluator_; }; // Pair-based neighborhood operators, designed to move nodes by pairs (pairs @@ -99,7 +103,7 @@ class MakePairActiveOperator : public PathOperator { MakePairActiveOperator(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingNodePairs& pairs); + const RoutingIndexPairs& pairs); ~MakePairActiveOperator() override {} bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; bool MakeNeighbor() override; @@ -122,7 +126,7 @@ class MakePairActiveOperator : public PathOperator { void OnNodeInitialization() override; int inactive_pair_; - RoutingNodePairs pairs_; + RoutingIndexPairs pairs_; }; // Operator which makes pairs of active nodes inactive. @@ -131,7 +135,7 @@ class MakePairInactiveOperator : public PathWithPreviousNodesOperator { MakePairInactiveOperator(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingNodePairs& node_pairs); + const RoutingIndexPairs& index_pairs); bool MakeNeighbor() override; std::string DebugString() const override { return "MakePairInActive"; } @@ -147,12 +151,13 @@ class MakePairInactiveOperator : public PathWithPreviousNodesOperator { // is a pair of nodes): // 1 -> [A] -> 2 -> [B] -> 3 // 1 -> 2 -> [A] -> [B] -> 3 -class PairRelocateOperator : public PathOperator { +// The pair can be moved to another path. +class PairRelocateOperator : public PathWithPreviousNodesOperator { public: PairRelocateOperator(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingNodePairs& node_pairs); + const RoutingIndexPairs& index_pairs); ~PairRelocateOperator() override {} bool MakeNeighbor() override; @@ -166,31 +171,167 @@ class PairRelocateOperator : public PathOperator { int64 GetBaseNodeRestartPosition(int base_index) override; private: - void OnNodeInitialization() override; bool RestartAtPathStartOnSynchronize() override { return true; } std::vector pairs_; - std::vector prevs_; std::vector is_first_; static const int kPairFirstNode = 0; static const int kPairFirstNodeDestination = 1; static const int kPairSecondNodeDestination = 2; }; +class LightPairRelocateOperator : public PathWithPreviousNodesOperator { + public: + LightPairRelocateOperator(const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + const RoutingIndexPairs& index_pairs); + ~LightPairRelocateOperator() override {} + + bool MakeNeighbor() override; + std::string DebugString() const override { + return "LightPairRelocateOperator"; + } + + private: + std::vector pairs_; +}; + +// Operator which exchanges the position of two pairs; for both pairs the first +// node of the pair must be before the second node on the same path. +// Possible neighbors for the paths 1 -> A -> B -> 2 -> 3 and 4 -> C -> D -> 5 +// (where (1, 3) and (4, 5) are first and last nodes of the paths and can +// therefore not be moved, and (A, B) and (C,D) are pairs of nodes): +// 1 -> [C] -> [D] -> 2 -> 3, 4 -> [A] -> [B] -> 5 +class PairExchangeOperator : public PathWithPreviousNodesOperator { + public: + PairExchangeOperator(const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + const RoutingIndexPairs& index_pairs); + ~PairExchangeOperator() override {} + + bool MakeNeighbor() override; + std::string DebugString() const override { return "PairExchangeOperator"; } + + private: + bool RestartAtPathStartOnSynchronize() override { return true; } + bool GetPreviousAndSibling(int64 node, int64* previous, int64* sibling, + int64* sibling_previous) const; + + std::vector pairs_; + std::vector is_first_; +}; + +// Operator which exchanges the paths of two pairs (path have to be different). +// Pairs are inserted in all possible positions in their new path with the +// constraint that the second node must be placed after the first. +// Possible neighbors for the path 1 -> A -> B -> 2 -> 3, 4 -> C -> 5 -> D -> 6 +// 1 -> C -> D -> 2 -> 3 4 -> A -> B -> 5 -> 6 +// 1 -> C -> 2 -> D -> 3 4 -> A -> 5 -> B -> 6 +// 1 -> 2 -> C -> D -> 3 4 -> 5 -> A -> B -> 6 +// 1 -> C -> D -> 2 -> 3 4 -> A -> B -> 5 -> 6 +// 1 -> C -> 2 -> D -> 3 4 -> A -> 5 -> B -> 6 +// 1 -> 2 -> C -> D -> 3 4 -> 5 -> A -> B -> 6 +// 1 -> C -> D -> 2 -> 3 4 -> A -> B -> 5 -> 6 +// 1 -> C -> 2 -> D -> 3 4 -> A -> 5 -> B -> 6 +// 1 -> 2 -> C -> D -> 3 4 -> 5 -> A -> B -> 6 +class PairExchangeRelocateOperator : public PathWithPreviousNodesOperator { + public: + PairExchangeRelocateOperator(const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + const RoutingIndexPairs& index_pairs); + ~PairExchangeRelocateOperator() override {} + + bool MakeNeighbor() override; + std::string DebugString() const override { + return "PairExchangeRelocateOperator"; + } + + protected: + bool OnSamePathAsPreviousBase(int64 base_index) override; + int64 GetBaseNodeRestartPosition(int base_index) override; + + private: + bool RestartAtPathStartOnSynchronize() override { return true; } + bool GetPreviousAndSibling(int64 node, int64* previous, int64* sibling, + int64* sibling_previous) const; + bool MoveNode(int pair, int node, int64 nodes[2][2], int64 dest[2][2], + int64 prev[2][2]); + bool LoadAndCheckDest(int pair, int node, int64 base_node, int64 nodes[2][2], + int64 dest[2][2]) const; + + std::vector pairs_; + std::vector is_first_; + static const int kFirstPairFirstNode = 0; + static const int kSecondPairFirstNode = 1; + static const int kFirstPairFirstNodeDestination = 2; + static const int kFirstPairSecondNodeDestination = 3; + static const int kSecondPairFirstNodeDestination = 4; + static const int kSecondPairSecondNodeDestination = 5; +}; + +// Operator which iterates through each alternative of a set of pairs. If a +// pair has n and m alternatives, n.m alternatives will be explored. +// Possible neighbors for the path 1 -> A -> a -> 2 (where (1, 2) are first and +// last nodes of a path and A has B, C as alternatives and a has b as +// alternative): +// 1 -> A -> [b] -> 2 +// 1 -> [B] -> a -> 2 +// 1 -> [B] -> [b] -> 2 +// 1 -> [C] -> a -> 2 +// 1 -> [C] -> [b] -> 2 +class SwapIndexPairOperator : public IntVarLocalSearchOperator { + public: + SwapIndexPairOperator(const std::vector& vars, + const std::vector& path_vars, + const RoutingIndexPairs& index_pairs); + ~SwapIndexPairOperator() override {} + + bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; + void OnStart() override; + std::string DebugString() const override { return "SwapIndexPairOperator"; } + + private: + // Updates first_active_ and second_active_ to make them correspond to the + // active nodes of the node pair of index pair_index_. + bool UpdateActiveNodes(); + // Sets the to to be the node after from + void SetNext(int64 from, int64 to, int64 path) { + DCHECK_LT(from, number_of_nexts_); + SetValue(from, to); + if (!ignore_path_vars_) { + DCHECK_LT(from + number_of_nexts_, Size()); + SetValue(from + number_of_nexts_, path); + } + } + + const RoutingIndexPairs index_pairs_; + int pair_index_; + int first_index_; + int second_index_; + int64 first_active_; + int64 second_active_; + std::vector prevs_; + const int number_of_nexts_; + const bool ignore_path_vars_; +}; + // Operator which inserts inactive nodes into a path and makes a pair of // active nodes inactive. -class NodePairSwapActiveOperator : public PathWithPreviousNodesOperator { +class IndexPairSwapActiveOperator : public PathWithPreviousNodesOperator { public: - NodePairSwapActiveOperator(const std::vector& vars, - const std::vector& secondary_vars, - std::function start_empty_path_class, - const RoutingNodePairs& node_pairs); - ~NodePairSwapActiveOperator() override {} + IndexPairSwapActiveOperator(const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + const RoutingIndexPairs& index_pairs); + ~IndexPairSwapActiveOperator() override {} bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; bool MakeNeighbor() override; std::string DebugString() const override { - return "NodePairSwapActiveOperator"; + return "IndexPairSwapActiveOperator"; } private: @@ -200,6 +341,54 @@ class NodePairSwapActiveOperator : public PathWithPreviousNodesOperator { std::vector pairs_; }; +// ----- RelocateExpensiveChain ----- +// Operator which relocates the most expensive subchains (given a cost callback) +// in a path to a different position. +// The most expensive chain on a path is the one resulting from cutting the 2 +// most expensive arcs on this path. +class RelocateExpensiveChain : public PathOperator { + public: + RelocateExpensiveChain( + const std::vector& vars, + const std::vector& secondary_vars, + std::function start_empty_path_class, + int num_arcs_to_consider, + std::function arc_cost_for_path_start); + ~RelocateExpensiveChain() override {} + bool MakeNeighbor() override; + bool MakeOneNeighbor() override; + + std::string DebugString() const override { return "RelocateExpensiveChain"; } + + private: + void OnNodeInitialization() override; + void IncrementCurrentPath(); + bool IncrementCurrentArcIndices(); + // Returns false if current_path_ is empty. Otherwise sets + // current_expensive_chain_ to the pair of {"preceding", "last"} nodes + // corresponding to the most expensive chain on current_path_, and returns + // true. + bool FindMostExpensiveChainsOnCurrentPath(); + // Calls FindMostExpensiveChainOnCurrentPath() on remaining paths until one + // of them returns true. Returns false if all remaining paths are empty. + bool FindMostExpensiveChainsOnRemainingPaths(); + + int num_arcs_to_consider_; + int current_path_; + std::vector > most_expensive_arc_starts_and_ranks_; + // Indices in most_expensive_arc_starts_and_ranks_ corresponding to the first + // and second arcs currently being considered for removal. + std::pair + current_expensive_arc_indices_; + std::function + arc_cost_for_path_start_; + int end_path_; + // The following boolean indicates if there are any non-empty paths left to + // explore by the operator. + bool has_non_empty_paths_to_explore_; +}; + // Operator which inserts pairs of inactive nodes into a path and makes an // active node inactive. // There are two versions: @@ -213,7 +402,7 @@ class PairNodeSwapActiveOperator : public PathOperator { PairNodeSwapActiveOperator(const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingNodePairs& node_pairs); + const RoutingIndexPairs& index_pairs); ~PairNodeSwapActiveOperator() override {} bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; @@ -239,7 +428,7 @@ class PairNodeSwapActiveOperator : public PathOperator { void OnNodeInitialization() override; int inactive_pair_; - RoutingNodePairs pairs_; + RoutingIndexPairs pairs_; }; // ========================================================================== @@ -250,10 +439,11 @@ PairNodeSwapActiveOperator::PairNodeSwapActiveOperator( const std::vector& vars, const std::vector& secondary_vars, std::function start_empty_path_class, - const RoutingNodePairs& node_pairs) - : PathOperator(vars, secondary_vars, 2, std::move(start_empty_path_class)), + const RoutingIndexPairs& index_pairs) + : PathOperator(vars, secondary_vars, 2, false, + std::move(start_empty_path_class)), inactive_pair_(0), - pairs_(node_pairs) {} + pairs_(index_pairs) {} template int64 PairNodeSwapActiveOperator::GetBaseNodeRestartPosition( diff --git a/ortools/constraint_solver/routing_parameters.cc b/ortools/constraint_solver/routing_parameters.cc new file mode 100644 index 00000000000..8e17044deea --- /dev/null +++ b/ortools/constraint_solver/routing_parameters.cc @@ -0,0 +1,221 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ortools/constraint_solver/routing_parameters.h" + +#include "absl/strings/str_cat.h" +#include "absl/time/time.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/duration.pb.h" +#include "google/protobuf/message.h" +#include "google/protobuf/text_format.h" +#include "ortools/base/logging.h" +#include "ortools/base/protoutil.h" +#include "ortools/base/statusor.h" +#include "ortools/constraint_solver/constraint_solver.h" +#include "ortools/constraint_solver/routing_enums.pb.h" +#include "ortools/constraint_solver/solver_parameters.pb.h" +#include "ortools/util/optional_boolean.pb.h" + +namespace operations_research { + +RoutingModelParameters DefaultRoutingModelParameters() { + RoutingModelParameters parameters; + ConstraintSolverParameters* const solver_parameters = + parameters.mutable_solver_parameters(); + *solver_parameters = Solver::DefaultSolverParameters(); + solver_parameters->set_compress_trail( + ConstraintSolverParameters::COMPRESS_WITH_ZLIB); + solver_parameters->set_skip_locally_optimal_paths(true); + parameters.set_reduce_vehicle_cost_model(true); + return parameters; +} + +// static +RoutingSearchParameters DefaultRoutingSearchParameters() { + static const char* const kSearchParameters = + "first_solution_strategy: AUTOMATIC " + "use_unfiltered_first_solution_strategy: false " + "savings_neighbors_ratio: 1 " + "savings_add_reverse_arcs: false " + "savings_arc_coefficient: 1 " + "savings_parallel_routes: false " + "cheapest_insertion_farthest_seeds_ratio: 0 " + "cheapest_insertion_neighbors_ratio: 1 " + "local_search_operators {" + " use_relocate: BOOL_TRUE" + " use_relocate_pair: BOOL_TRUE" + " use_light_relocate_pair: BOOL_TRUE" + " use_relocate_neighbors: BOOL_FALSE" + " use_exchange: BOOL_TRUE" + " use_exchange_pair: BOOL_TRUE" + " use_cross: BOOL_TRUE" + " use_cross_exchange: BOOL_FALSE" + " use_relocate_expensive_chain: BOOL_TRUE" + " use_two_opt: BOOL_TRUE" + " use_or_opt: BOOL_TRUE" + " use_lin_kernighan: BOOL_TRUE" + " use_tsp_opt: BOOL_FALSE" + " use_make_active: BOOL_TRUE" + " use_relocate_and_make_active: BOOL_FALSE" // costly if true by default + " use_make_inactive: BOOL_TRUE" + " use_make_chain_inactive: BOOL_FALSE" + " use_swap_active: BOOL_TRUE" + " use_extended_swap_active: BOOL_FALSE" + " use_node_pair_swap_active: BOOL_TRUE" + " use_path_lns: BOOL_FALSE" + " use_full_path_lns: BOOL_FALSE" + " use_tsp_lns: BOOL_FALSE" + " use_inactive_lns: BOOL_FALSE" + "}" + "relocate_expensive_chain_num_arcs_to_consider: 4 " + "local_search_metaheuristic: AUTOMATIC " + "guided_local_search_lambda_coefficient: 0.1 " + "use_depth_first_search: false " + "optimization_step: 1 " + "number_of_solutions_to_collect: 1 " + // No "time_limit" by default. + "solution_limit: 0x7fffffffffffffff " // kint64max + "lns_time_limit: { seconds:0 nanos:100000000 } " // 0.1s + "use_full_propagation: false " + "log_search: false"; + RoutingSearchParameters parameters; + if (!google::protobuf::TextFormat::ParseFromString(kSearchParameters, + ¶meters)) { + LOG(DFATAL) << "Unsupported default search parameters: " + << kSearchParameters; + } + const std::string error = FindErrorInRoutingSearchParameters(parameters); + LOG_IF(DFATAL, !error.empty()) + << "The default search parameters aren't valid: " << error; + return parameters; +} + +namespace { +bool IsValidNonNegativeDuration(const google::protobuf::Duration& d) { + const auto status_or_duration = util_time::DecodeGoogleApiProto(d); + return status_or_duration.ok() && + status_or_duration.ValueOrDie() >= absl::ZeroDuration(); +} +} // namespace + +std::string FindErrorInRoutingSearchParameters( + const RoutingSearchParameters& search_parameters) { + using absl::StrCat; + // Check that all local search operators are set to either BOOL_TRUE or + // BOOL_FALSE (and not BOOL_UNSPECIFIED). + { + using Reflection = google::protobuf::Reflection; + using Descriptor = google::protobuf::Descriptor; + using FieldDescriptor = google::protobuf::FieldDescriptor; + const RoutingSearchParameters::LocalSearchNeighborhoodOperators& operators = + search_parameters.local_search_operators(); + const Reflection* ls_reflection = operators.GetReflection(); + const Descriptor* ls_descriptor = operators.GetDescriptor(); + for (int /*this is NOT the field's tag number*/ field_index = 0; + field_index < ls_descriptor->field_count(); ++field_index) { + const FieldDescriptor* field = ls_descriptor->field(field_index); + if (field->type() != FieldDescriptor::TYPE_ENUM || + field->enum_type() != OptionalBoolean_descriptor()) { + DLOG(FATAL) + << "In RoutingSearchParameters::LocalSearchNeighborhoodOperators," + << " field '" << field->name() << "' is not an OptionalBoolean."; + return "The file 'routing_search_parameters.proto' itself is invalid!"; + } + const int value = ls_reflection->GetEnum(operators, field)->number(); + if (!OptionalBoolean_IsValid(value) || value == 0) { + return StrCat("local_search_neighborhood_operator.", field->name(), + " should be set to BOOL_TRUE or BOOL_FALSE instead of ", + OptionalBoolean_Name(static_cast(value)), + " (value: ", value, ")"); + } + } + } + { + const double ratio = search_parameters.savings_neighbors_ratio(); + if (std::isnan(ratio) || ratio <= 0 || ratio > 1) { + return StrCat("Invalid savings_neighbors_ratio:", ratio); + } + } + { + const double coefficient = search_parameters.savings_arc_coefficient(); + if (std::isnan(coefficient) || coefficient <= 0 || + std::isinf(coefficient)) { + return StrCat("Invalid savings_arc_coefficient:", coefficient); + } + } + { + const double ratio = + search_parameters.cheapest_insertion_farthest_seeds_ratio(); + if (std::isnan(ratio) || ratio < 0 || ratio > 1) { + return StrCat("Invalid cheapest_insertion_farthest_seeds_ratio:", ratio); + } + } + { + const double ratio = search_parameters.cheapest_insertion_neighbors_ratio(); + if (std::isnan(ratio) || ratio <= 0 || ratio > 1) { + return StrCat("Invalid cheapest_insertion_neighbors_ratio: ", ratio); + } + } + { + const int32 num_arcs = + search_parameters.relocate_expensive_chain_num_arcs_to_consider(); + if (num_arcs < 2 || num_arcs > 1e6) { + return StrCat("Invalid relocate_expensive_chain_num_arcs_to_consider: ", + num_arcs, ". Must be between 2 and 10^6 (included)."); + } + } + { + const double gls_coefficient = + search_parameters.guided_local_search_lambda_coefficient(); + if (std::isnan(gls_coefficient) || gls_coefficient < 0 || + std::isinf(gls_coefficient)) { + return StrCat("Invalid guided_local_search_lambda_coefficient: ", + gls_coefficient); + } + } + { + const int64 step = search_parameters.optimization_step(); + if (step < 1) return StrCat("Invalid optimization_step:", step); + } + { + const int32 num = search_parameters.number_of_solutions_to_collect(); + if (num < 1) return StrCat("Invalid number_of_solutions_to_collect:", num); + } + { + const int64 lim = search_parameters.solution_limit(); + if (lim < 1) return StrCat("Invalid solution_limit:", lim); + } + if (!IsValidNonNegativeDuration(search_parameters.time_limit())) { + return "Invalid time_limit: " + + search_parameters.time_limit().ShortDebugString(); + } + if (!IsValidNonNegativeDuration(search_parameters.lns_time_limit())) { + return "Invalid lns_time_limit: " + + search_parameters.lns_time_limit().ShortDebugString(); + } + if (!FirstSolutionStrategy::Value_IsValid( + search_parameters.first_solution_strategy())) { + return StrCat("Invalid first_solution_strategy: ", + search_parameters.first_solution_strategy()); + } + if (!LocalSearchMetaheuristic::Value_IsValid( + search_parameters.local_search_metaheuristic())) { + return StrCat("Invalid metaheuristic: ", + search_parameters.local_search_metaheuristic()); + } + + return ""; // = Valid (No error). +} + +} // namespace operations_research diff --git a/ortools/base/stringprintf.h b/ortools/constraint_solver/routing_parameters.h similarity index 53% rename from ortools/base/stringprintf.h rename to ortools/constraint_solver/routing_parameters.h index bc972b944cc..70b708a902e 100644 --- a/ortools/base/stringprintf.h +++ b/ortools/constraint_solver/routing_parameters.h @@ -11,19 +11,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef OR_TOOLS_BASE_STRINGPRINTF_H_ -#define OR_TOOLS_BASE_STRINGPRINTF_H_ +#ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_PARAMETERS_H_ +#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_PARAMETERS_H_ #include +#include "ortools/constraint_solver/routing_parameters.pb.h" namespace operations_research { -std::string StringPrintf(const char* const format, ...); -void SStringPrintf(std::string* const dst, const char* const format, ...); -void StringAppendF(std::string* const dst, const char* const format, ...); + +RoutingModelParameters DefaultRoutingModelParameters(); +RoutingSearchParameters DefaultRoutingSearchParameters(); + +// Returns an empty std::string if the routing search parameters are valid, and +// a non-empty, human readable error description if they're not. +std::string FindErrorInRoutingSearchParameters( + const RoutingSearchParameters& search_parameters); + } // namespace operations_research -namespace absl { -std::string StrFormat(const char* const format, ...); -void StrAppendFormat(std::string* const dst, const char* const format, ...); -} // namespace absl -#endif // OR_TOOLS_BASE_STRINGPRINTF_H_ +#endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_PARAMETERS_H_ diff --git a/ortools/constraint_solver/routing_parameters.proto b/ortools/constraint_solver/routing_parameters.proto index 03775410655..cd9ea4d8d95 100644 --- a/ortools/constraint_solver/routing_parameters.proto +++ b/ortools/constraint_solver/routing_parameters.proto @@ -20,27 +20,54 @@ option java_package = "com.google.ortools.constraintsolver"; option java_multiple_files = true; option csharp_namespace = "Google.OrTools.ConstraintSolver"; +import "google/protobuf/duration.proto"; import "ortools/constraint_solver/routing_enums.proto"; import "ortools/constraint_solver/solver_parameters.proto"; +import "ortools/util/optional_boolean.proto"; package operations_research; // Parameters defining the search used to solve vehicle routing problems. +// +// If a parameter is unset (or, equivalently, set to its default value), +// then the routing library will pick its preferred value for that parameter +// automatically: this should be the case for most parameters. +// To see those "default" parameters, call GetDefaultRoutingSearchParameters(). message RoutingSearchParameters { // First solution strategies, used as starting point of local search. FirstSolutionStrategy.Value first_solution_strategy = 1; - // These are advanced first solutions strategy settings which should not be - // modified unless you know what you are doing. + + // --- Advanced first solutions strategy settings --- + // Don't touch these unless you know what you are doing. + // // Use filtered version of first solution strategy if available. - bool use_filtered_first_solution_strategy = 2; + bool use_unfiltered_first_solution_strategy = 2; // Parameters specific to the Savings first solution heuristic. - // Ratio (between 0 and 1) of neighbors to consider for each node when - // constructing the savings. If <= 0 or greater than 1, its value is - // considered to be 1.0. + // Ratio (in ]0, 1]) of neighbors to consider for each node when constructing + // the savings. If unspecified, its value is considered to be 1.0. double savings_neighbors_ratio = 14; // Add savings related to reverse arcs when finding the nearest neighbors // of the nodes. bool savings_add_reverse_arcs = 15; + // Coefficient of the cost of the arc for which the saving value is being + // computed: + // Saving(a-->b) = Cost(a-->end) + Cost(start-->b) + // - savings_arc_coefficient * Cost(a-->b) + // This parameter must be greater than 0, and its default value is 1. + double savings_arc_coefficient = 18; + // When true, the routes are built in parallel, sequentially otherwise. + bool savings_parallel_routes = 19; + + // Ratio (between 0 and 1) of available vehicles in the model on which + // farthest nodes of the model are inserted as seeds in the + // GlobalCheapestInsertion first solution heuristic. + double cheapest_insertion_farthest_seeds_ratio = 16; + + // Ratio (in ]0, 1]) of neighbors to consider for each node when creating + // new insertions in the parallel/sequential cheapest insertion heuristic. + // If not overridden, its default value is 1, meaning all neighbors will be + // considered. + double cheapest_insertion_neighbors_ratio = 21; // Local search neighborhood operators used to build a solutions neighborhood. message LocalSearchNeighborhoodOperators { @@ -53,16 +80,25 @@ message RoutingSearchParameters { // 1 -> 3 -> 4 -> [2] -> 5 // 1 -> 2 -> 4 -> [3] -> 5 // 1 -> [4] -> 2 -> 3 -> 5 - bool use_relocate = 1; + OptionalBoolean use_relocate = 1; // Operator which moves a pair of pickup and delivery nodes to another // position where the first node of the pair must be before the second node - // on the same path. + // on the same path. Compared to the light_relocate_pair operator, tries all + // possible positions of insertion of a pair (not only after another pair). // Possible neighbors for the path 1 -> A -> B -> 2 -> 3 (where (1, 3) are // first and last nodes of the path and can therefore not be moved, and // (A, B) is a pair of nodes): // 1 -> [A] -> 2 -> [B] -> 3 // 1 -> 2 -> [A] -> [B] -> 3 - bool use_relocate_pair = 2; + OptionalBoolean use_relocate_pair = 2; + // Operator which moves a pair of pickup and delivery nodes after another + // pair. + // Possible neighbors for paths 1 -> A -> B -> 2, 3 -> C -> D -> 4 (where + // (1, 2) and (3, 4) are first and last nodes of paths and can therefore not + // be moved, and (A, B) and (C, D) are pair of nodes): + // 1 -> 2, 3 -> C -> [A] -> D -> [B] -> 4 + // 1 -> A -> [C] -> B -> [D] -> 2, 3 -> 4 + OptionalBoolean use_light_relocate_pair = 24; // Relocate neighborhood which moves chains of neighbors. // The operator starts by relocating a node n after a node m, then continues // moving nodes which were after n as long as the "cost" added is less than @@ -85,7 +121,7 @@ message RoutingSearchParameters { // 1 -> A -> B -> C -> [E] -> D -> 2 // This operator is extremelly useful to move chains of nodes which are // located at the same place (for instance nodes part of a same stop). - bool use_relocate_neighbors = 3; + OptionalBoolean use_relocate_neighbors = 3; // Operator which exchanges the positions of two nodes. // Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5 // (where (1, 5) are first and last nodes of the path and can therefore not @@ -93,7 +129,15 @@ message RoutingSearchParameters { // 1 -> [3] -> [2] -> 4 -> 5 // 1 -> [4] -> 3 -> [2] -> 5 // 1 -> 2 -> [4] -> [3] -> 5 - bool use_exchange = 4; + OptionalBoolean use_exchange = 4; + // Operator which exchanges the positions of two pair of nodes. Pairs + // correspond to the pickup and delivery pairs defined in the routing model. + // Possible neighbor for the paths + // 1 -> A -> B -> 2 -> 3 and 4 -> C -> D -> 5 + // (where (1, 3) and (4, 5) are first and last nodes of the paths and can + // therefore not be moved, and (A, B) and (C,D) are pairs of nodes): + // 1 -> [C] -> [D] -> 2 -> 3, 4 -> [A] -> [B] -> 5 + OptionalBoolean use_exchange_pair = 22; // Operator which cross exchanges the starting chains of 2 paths, including // exchanging the whole paths. // First and last nodes are not moved. @@ -103,9 +147,20 @@ message RoutingSearchParameters { // 1 -> [7] -> 3 -> 4 -> 5 6 -> [2] -> 8 // 1 -> [7] -> 4 -> 5 6 -> [2 -> 3] -> 8 // 1 -> [7] -> 5 6 -> [2 -> 3 -> 4] -> 8 - bool use_cross = 5; - // Not implemented as of 10/2015. - bool use_cross_exchange = 6; + OptionalBoolean use_cross = 5; + // Not implemented yet. TODO(b/68128619): Implement. + OptionalBoolean use_cross_exchange = 6; + // Operator which detects the relocate_expensive_chain_num_arcs_to_consider + // most expensive arcs on a path, and moves the chain resulting from cutting + // pairs of arcs among these to another position. + // Possible neighbors for paths 1 -> 2 (empty) and + // 3 -> A ------> B --> C -----> D -> 4 (where A -> B and C -> D are the 2 + // most expensive arcs, and the chain resulting from breaking them is + // B -> C): + // 1 -> [B -> C] -> 2 3 -> A -> D -> 4 + // 1 -> 2 3 -> [B -> C] -> A -> D -> 4 + // 1 -> 2 3 -> A -> D -> [B -> C] -> 4 + OptionalBoolean use_relocate_expensive_chain = 23; // --- Intra-route operators --- // Operator which reverves a sub-chain of a path. It is called TwoOpt // because it breaks two arcs on the path; resulting paths are called @@ -116,7 +171,7 @@ message RoutingSearchParameters { // 1 -> [3 -> 2] -> 4 -> 5 // 1 -> [4 -> 3 -> 2] -> 5 // 1 -> 2 -> [4 -> 3] -> 5 - bool use_two_opt = 7; + OptionalBoolean use_two_opt = 7; // Operator which moves sub-chains of a path of length 1, 2 and 3 to another // position in the same path. // When the length of the sub-chain is 1, the operator simply moves a node @@ -128,19 +183,19 @@ message RoutingSearchParameters { // 1 -> [3 -> 4] -> 2 -> 5 // The OR_OPT operator is a limited version of 3-Opt (breaks 3 arcs on a // path). - bool use_or_opt = 8; + OptionalBoolean use_or_opt = 8; // Lin-Kernighan operator. // While the accumulated local gain is positive, performs a 2-OPT or a 3-OPT // move followed by a series of 2-OPT moves. Returns a neighbor for which // the global gain is positive. - bool use_lin_kernighan = 9; + OptionalBoolean use_lin_kernighan = 9; // Sliding TSP operator. // Uses an exact dynamic programming algorithm to solve the TSP // corresponding to path sub-chains. // For a subchain 1 -> 2 -> 3 -> 4 -> 5 -> 6, solves the TSP on // nodes A, 2, 3, 4, 5, where A is a merger of nodes 1 and 6 such that // cost(A,i) = cost(1,i) and cost(i,A) = cost(i,6). - bool use_tsp_opt = 10; + OptionalBoolean use_tsp_opt = 10; // --- Operators on inactive nodes --- // Operator which inserts an inactive node into a path. // Possible neighbors for the path 1 -> 2 -> 3 -> 4 with 5 inactive @@ -148,7 +203,7 @@ message RoutingSearchParameters { // 1 -> [5] -> 2 -> 3 -> 4 // 1 -> 2 -> [5] -> 3 -> 4 // 1 -> 2 -> 3 -> [5] -> 4 - bool use_make_active = 11; + OptionalBoolean use_make_active = 11; // Operator which relocates a node while making an inactive one active. // As of 3/2017, the operator is limited to two kinds of moves: // - Relocating a node and replacing it by an inactive node. @@ -159,26 +214,26 @@ message RoutingSearchParameters { // Possible neighbor for path 1 -> 5, 2 -> 3 -> 6 and 4 inactive // (where 1,2 and 5,6 are first and last nodes of paths) is: // 1 -> 4 -> 3 -> 5, 2 -> 6. - bool use_relocate_and_make_active = 21; + OptionalBoolean use_relocate_and_make_active = 21; // Operator which makes path nodes inactive. // Possible neighbors for the path 1 -> 2 -> 3 -> 4 (where 1 and 4 are first // and last nodes of the path) are: // 1 -> 3 -> 4 with 2 inactive // 1 -> 2 -> 4 with 3 inactive - bool use_make_inactive = 12; + OptionalBoolean use_make_inactive = 12; // Operator which makes a "chain" of path nodes inactive. // Possible neighbors for the path 1 -> 2 -> 3 -> 4 (where 1 and 4 are first // and last nodes of the path) are: // 1 -> 3 -> 4 with 2 inactive // 1 -> 2 -> 4 with 3 inactive // 1 -> 4 with 2 and 3 inactive - bool use_make_chain_inactive = 13; + OptionalBoolean use_make_chain_inactive = 13; // Operator which replaces an active node by an inactive one. // Possible neighbors for the path 1 -> 2 -> 3 -> 4 with 5 inactive // (where 1 and 4 are first and last nodes of the path) are: // 1 -> [5] -> 3 -> 4 with 2 inactive // 1 -> 2 -> [5] -> 4 with 3 inactive - bool use_swap_active = 14; + OptionalBoolean use_swap_active = 14; // Operator which makes an inactive node active and an active one inactive. // It is similar to SwapActiveOperator excepts that it tries to insert the // inactive node in all possible positions instead of just the position of @@ -189,7 +244,7 @@ message RoutingSearchParameters { // 1 -> 3 -> [5] -> 4 with 2 inactive // 1 -> [5] -> 2 -> 4 with 3 inactive // 1 -> 2 -> [5] -> 4 with 3 inactive - bool use_extended_swap_active = 15; + OptionalBoolean use_extended_swap_active = 15; // Operator which makes an inactive node active and an active pair of nodes // inactive OR makes an inactive pair of nodes active and an active node // inactive. @@ -201,7 +256,7 @@ message RoutingSearchParameters { // (where 1 and 3 are first and last nodes of the path and (4,5) is a pair // of nodes) are: // 1 -> [4] -> [5] -> 3 with 2 inactive - bool use_node_pair_swap_active = 20; + OptionalBoolean use_node_pair_swap_active = 20; // --- Large neighborhood search operators --- // Operator which relaxes two sub-chains of three consecutive arcs each. // Each sub-chain is defined by a start node and the next three arcs. Those @@ -210,63 +265,77 @@ message RoutingSearchParameters { // n^2 neighbors, n being the number of nodes. // Note that the two sub-chains can be part of the same path; they even may // overlap. - bool use_path_lns = 16; + OptionalBoolean use_path_lns = 16; // Operator which relaxes one entire path and all unactive nodes. - bool use_full_path_lns = 17; + OptionalBoolean use_full_path_lns = 17; // TSP-base LNS. // Randomly merges consecutive nodes until n "meta"-nodes remain and solves // the corresponding TSP. // This defines an "unlimited" neighborhood which must be stopped by search // limits. To force diversification, the operator iteratively forces each // node to serve as base of a meta-node. - bool use_tsp_lns = 18; + OptionalBoolean use_tsp_lns = 18; // Operator which relaxes all inactive nodes and one sub-chain of six // consecutive arcs. That way the path can be improved by inserting inactive // nodes or swaping arcs. - bool use_inactive_lns = 19; + OptionalBoolean use_inactive_lns = 19; } LocalSearchNeighborhoodOperators local_search_operators = 3; + // Number of expensive arcs to consider cutting in the RelocateExpensiveChain + // neighborhood operator (see + // LocalSearchNeighborhoodOperators.use_relocate_expensive_chain()). + // This parameter must be greater than 2. + // NOTE(user): The number of neighbors generated by the operator for + // relocate_expensive_chain_num_arcs_to_consider = K is around + // K*(K-1)/2 * number_of_routes * number_of_nodes. + int32 relocate_expensive_chain_num_arcs_to_consider = 20; + // Local search metaheuristics used to guide the search. LocalSearchMetaheuristic.Value local_search_metaheuristic = 4; // These are advanced settings which should not be modified unless you know // what you are doing. // Lambda coefficient used to penalize arc costs when GUIDED_LOCAL_SEARCH is - // used. + // used. Must be positive. double guided_local_search_lambda_coefficient = 5; // --- Search control --- + // // If true, the solver should use depth-first search rather than local search // to solve the problem. bool use_depth_first_search = 6; - // Minimum step by which the solution must be improved in local search. + // Minimum step by which the solution must be improved in local search. 0 + // means "unspecified". int64 optimization_step = 7; + // Number of solutions to collect during the search. Corresponds to the best + // solutions found during the search. 0 means "unspecified". + int32 number_of_solutions_to_collect = 17; // -- Search limits -- - // Limit to the number of solutions generated during the search. + // Limit to the number of solutions generated during the search. 0 means + // "unspecified". int64 solution_limit = 8; - // Limit in milliseconds to the time spent in the search. - int64 time_limit_ms = 9; - // Limit in milliseconds to the time spent in the completion search for each - // local search neighbor. - int64 lns_time_limit_ms = 10; + // Limit to the time spent in the search. + google.protobuf.Duration time_limit = 9; + // Limit to the time spent in the completion search for each local search + // neighbor. + google.protobuf.Duration lns_time_limit = 10; // --- Propagation control --- // These are advanced settings which should not be modified unless you know // what you are doing. - // Use constraints with light propagation in routing model. Extra propagation - // is only necessary when using depth-first search or for models which - // require strong propagation to finalize the value of secondary variables. + // + // Use constraints with full propagation in routing model (instead of 'light' + // propagation only). Full propagation is only necessary when using + // depth-first search or for models which require strong propagation to + // finalize the value of secondary variables. // Changing this setting to true will slow down the search in most cases and // increase memory consumption in all cases. - bool use_light_propagation = 11; + bool use_full_propagation = 11; // --- Miscellaneous --- // Some of these are advanced settings which should not be modified unless you // know what you are doing. - // If true, arc cost evaluators will be fingerprinted. The fingerprint will - // be used when computing vehicle cost equivalent classes, otherwise the - // address of the evaluator will be used. - bool fingerprint_arc_cost_evaluators = 12; + // // Activates search logging. For each solution found during the search, the // following will be displayed: its objective value, the maximum objective // value since the beginning of the search, the elapsed time since the diff --git a/ortools/constraint_solver/routing_search.cc b/ortools/constraint_solver/routing_search.cc index 0dc4e2f0b96..90f46de0e6f 100644 --- a/ortools/constraint_solver/routing_search.cc +++ b/ortools/constraint_solver/routing_search.cc @@ -18,10 +18,12 @@ #include #include +#include #include -#include -#include #include + +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "ortools/base/small_map.h" #include "ortools/base/small_ordered_set.h" #include "ortools/constraint_solver/routing.h" @@ -37,49 +39,29 @@ DEFINE_bool(routing_shift_insertion_cost_by_penalty, true, namespace operations_research { -// --- Routing-specific local search filters --- - -// RoutingLocalSearchFilter - -RoutingLocalSearchFilter::RoutingLocalSearchFilter( - const std::vector& nexts, - Solver::ObjectiveWatcher objective_callback) - : IntVarLocalSearchFilter(nexts), - injected_objective_value_(0), - objective_callback_(std::move(objective_callback)) {} - -void RoutingLocalSearchFilter::InjectObjectiveValue(int64 objective_value) { - injected_objective_value_ = objective_value; -} - -void RoutingLocalSearchFilter::PropagateObjectiveValue(int64 objective_value) { - if (objective_callback_ != nullptr) { - objective_callback_(objective_value); - } -} - namespace { // Node disjunction filter class. -class NodeDisjunctionFilter : public RoutingLocalSearchFilter { +class NodeDisjunctionFilter : public IntVarLocalSearchFilter { public: NodeDisjunctionFilter(const RoutingModel& routing_model, Solver::ObjectiveWatcher objective_callback) - : RoutingLocalSearchFilter(routing_model.Nexts(), - std::move(objective_callback)), + : IntVarLocalSearchFilter(routing_model.Nexts(), + std::move(objective_callback)), routing_model_(routing_model), active_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0), inactive_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0), - penalty_value_(0) {} + synchronized_objective_value_(kint64min), + accepted_objective_value_(kint64min) {} bool Accept(const Assignment* delta, const Assignment* deltadelta) override { const int64 kUnassigned = -1; const Assignment::IntContainer& container = delta->IntVarContainer(); const int delta_size = container.Size(); - small_map> + gtl::small_map> disjunction_active_deltas; - small_map> + gtl::small_map> disjunction_inactive_deltas; bool lns_detected = false; // Update active/inactive count per disjunction for each element of delta. @@ -94,7 +76,7 @@ class NodeDisjunctionFilter : public RoutingLocalSearchFilter { lns_detected = true; } for (const RoutingModel::DisjunctionIndex disjunction_index : - routing_model_.GetDisjunctionIndicesFromVariableIndex(index)) { + routing_model_.GetDisjunctionIndices(index)) { const bool active_state_changed = !IsVarSynced(index) || (Value(index) == index) != is_inactive; if (active_state_changed) { @@ -133,8 +115,7 @@ class NodeDisjunctionFilter : public RoutingLocalSearchFilter { } } // Update penalty costs for disjunctions. - int64 new_objective_value = - CapAdd(injected_objective_value_, penalty_value_); + accepted_objective_value_ = synchronized_objective_value_; for (const std::pair disjunction_inactive_delta : disjunction_inactive_deltas) { const int64 penalty = routing_model_.GetDisjunctionPenalty( @@ -159,17 +140,20 @@ class NodeDisjunctionFilter : public RoutingLocalSearchFilter { } else if (current_inactive_nodes <= max_inactive_cardinality) { // Add penalty if there were not too many inactive nodes before the // move. - new_objective_value = CapAdd(new_objective_value, penalty); + accepted_objective_value_ = + CapAdd(accepted_objective_value_, penalty); } } else if (current_inactive_nodes > max_inactive_cardinality) { // Remove penalty if there were too many inactive nodes before the // move and there are not too many after the move. - new_objective_value = CapSub(new_objective_value, penalty); + accepted_objective_value_ = + CapSub(accepted_objective_value_, penalty); } } } - - PropagateObjectiveValue(new_objective_value); + if (lns_detected) accepted_objective_value_ = 0; + PropagateObjectiveValue( + CapAdd(accepted_objective_value_, injected_objective_value_)); if (lns_detected) { return true; } else { @@ -180,24 +164,30 @@ class NodeDisjunctionFilter : public RoutingLocalSearchFilter { if (delta->Objective() == cost_var) { cost_max = std::min(cost_max, delta->ObjectiveMax()); } - return new_objective_value <= cost_max; + return accepted_objective_value_ <= cost_max; } } std::string DebugString() const override { return "NodeDisjunctionFilter"; } + int64 GetSynchronizedObjectiveValue() const override { + return synchronized_objective_value_; + } + int64 GetAcceptedObjectiveValue() const override { + return accepted_objective_value_; + } private: void OnSynchronize(const Assignment* delta) override { - penalty_value_ = 0; + synchronized_objective_value_ = 0; for (RoutingModel::DisjunctionIndex i(0); i < active_per_disjunction_.size(); ++i) { active_per_disjunction_[i] = 0; inactive_per_disjunction_[i] = 0; - const std::vector& disjunction_nodes = + const std::vector& disjunction_indices = routing_model_.GetDisjunctionIndices(i); - for (const int64 node : disjunction_nodes) { - const bool node_synced = IsVarSynced(node); - if (node_synced) { - if (Value(node) != node) { + for (const int64 index : disjunction_indices) { + const bool index_synced = IsVarSynced(index); + if (index_synced) { + if (Value(index) != index) { ++active_per_disjunction_[i]; } else { ++inactive_per_disjunction_[i]; @@ -208,44 +198,98 @@ class NodeDisjunctionFilter : public RoutingLocalSearchFilter { const int max_cardinality = routing_model_.GetDisjunctionMaxCardinality(i); if (inactive_per_disjunction_[i] > - disjunction_nodes.size() - max_cardinality && + disjunction_indices.size() - max_cardinality && penalty > 0) { - penalty_value_ = CapAdd(penalty_value_, penalty); + synchronized_objective_value_ = + CapAdd(synchronized_objective_value_, penalty); } } - PropagateObjectiveValue(CapAdd(injected_objective_value_, penalty_value_)); + PropagateObjectiveValue( + CapAdd(injected_objective_value_, synchronized_objective_value_)); } const RoutingModel& routing_model_; gtl::ITIVector active_per_disjunction_; gtl::ITIVector inactive_per_disjunction_; - int64 penalty_value_; + int64 synchronized_objective_value_; + int64 accepted_objective_value_; }; } // namespace -RoutingLocalSearchFilter* MakeNodeDisjunctionFilter( +IntVarLocalSearchFilter* MakeNodeDisjunctionFilter( const RoutingModel& routing_model, Solver::ObjectiveWatcher objective_callback) { return routing_model.solver()->RevAlloc( new NodeDisjunctionFilter(routing_model, std::move(objective_callback))); } +LocalSearchFilterManager::LocalSearchFilterManager( + const std::vector& filters, IntVar* objective) + : filters_(filters), + objective_(objective), + is_incremental_(false), + synchronized_value_(kint64min), + accepted_value_(kint64min) { + for (LocalSearchFilter* filter : filters_) { + is_incremental_ |= filter->IsIncremental(); + } + // TODO(user): static reordering should take place here. +} + +// TODO(user): the behaviour of Accept relies on the initial order of +// filters having at most one filter with negative objective values, +// this could be fixed by having filters return their general bounds. +bool LocalSearchFilterManager::Accept(const Assignment* delta, + const Assignment* deltadelta) { + int64 objective_max = kint64max; + if (objective_ != nullptr) { + objective_max = objective_->Max(); + if (delta->Objective() == objective_) { + objective_max = std::min(objective_max, delta->ObjectiveMax()); + } + } + accepted_value_ = 0; + bool ok = true; + for (LocalSearchFilter* filter : filters_) { + if (!ok && !filter->IsIncremental()) continue; + ok = filter->Accept(delta, deltadelta) && ok; + if (ok) { + accepted_value_ = + CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue()); + ok = accepted_value_ <= objective_max; + } + } + return ok; +} + +void LocalSearchFilterManager::Synchronize(const Assignment* assignment, + const Assignment* delta) { + synchronized_value_ = 0; + for (LocalSearchFilter* filter : filters_) { + filter->Synchronize(assignment, delta); + synchronized_value_ = + CapAdd(synchronized_value_, filter->GetSynchronizedObjectiveValue()); + } +} + const int64 BasePathFilter::kUnassigned = -1; BasePathFilter::BasePathFilter(const std::vector& nexts, int next_domain_size, Solver::ObjectiveWatcher objective_callback) - : RoutingLocalSearchFilter(nexts, std::move(objective_callback)), + : IntVarLocalSearchFilter(nexts, std::move(objective_callback)), node_path_starts_(next_domain_size, kUnassigned), paths_(nexts.size(), -1), new_nexts_(nexts.size(), kUnassigned), touched_paths_(nexts.size()), touched_path_nodes_(next_domain_size), - ranks_(next_domain_size, -1) {} + ranks_(next_domain_size, -1), + status_(BasePathFilter::UNKNOWN) {} bool BasePathFilter::Accept(const Assignment* delta, const Assignment* deltadelta) { + if (IsDisabled()) return true; PropagateObjectiveValue(injected_objective_value_); for (const int touched : delta_touched_) { new_nexts_[touched] = kUnassigned; @@ -313,7 +357,7 @@ bool BasePathFilter::Accept(const Assignment* delta, } } // Order is important: FinalizeAcceptPath() must always be called. - return FinalizeAcceptPath() && accept; + return FinalizeAcceptPath(delta) && accept; } void BasePathFilter::ComputePathStarts(std::vector* path_starts, @@ -389,6 +433,11 @@ void BasePathFilter::SynchronizeFullAssignment() { } void BasePathFilter::OnSynchronize(const Assignment* delta) { + if (status_ == BasePathFilter::UNKNOWN) { + status_ = + DisableFiltering() ? BasePathFilter::DISABLED : BasePathFilter::ENABLED; + } + if (IsDisabled()) return; if (delta == nullptr || delta->Empty() || starts_.empty()) { SynchronizeFullAssignment(); return; @@ -448,6 +497,284 @@ void BasePathFilter::UpdatePathRanksFromStart(int start) { namespace { +class VehicleAmortizedCostFilter : public BasePathFilter { + public: + VehicleAmortizedCostFilter(const RoutingModel& routing_model, + Solver::ObjectiveWatcher objective_callback); + ~VehicleAmortizedCostFilter() override {} + std::string DebugString() const override { + return "VehicleAmortizedCostFilter"; + } + int64 GetSynchronizedObjectiveValue() const override { + return current_vehicle_cost_; + } + int64 GetAcceptedObjectiveValue() const override { + return delta_vehicle_cost_; + } + + private: + void OnSynchronizePathFromStart(int64 start) override; + void OnAfterSynchronizePaths() override; + void InitializeAcceptPath() override; + bool AcceptPath(int64 path_start, int64 chain_start, + int64 chain_end) override; + bool FinalizeAcceptPath(const Assignment* delta) override; + + const IntVar* const objective_; + int64 current_vehicle_cost_; + int64 delta_vehicle_cost_; + std::vector current_route_lengths_; + std::vector start_to_end_; + std::vector start_to_vehicle_; + std::vector vehicle_to_start_; + const std::vector& linear_cost_factor_of_vehicle_; + const std::vector& quadratic_cost_factor_of_vehicle_; +}; + +VehicleAmortizedCostFilter::VehicleAmortizedCostFilter( + const RoutingModel& routing_model, + Solver::ObjectiveWatcher objective_callback) + : BasePathFilter(routing_model.Nexts(), + routing_model.Size() + routing_model.vehicles(), + std::move(objective_callback)), + objective_(routing_model.CostVar()), + current_vehicle_cost_(0), + delta_vehicle_cost_(0), + current_route_lengths_(Size(), -1), + linear_cost_factor_of_vehicle_( + routing_model.GetAmortizedLinearCostFactorOfVehicles()), + quadratic_cost_factor_of_vehicle_( + routing_model.GetAmortizedQuadraticCostFactorOfVehicles()) { + start_to_end_.resize(Size(), -1); + start_to_vehicle_.resize(Size(), -1); + vehicle_to_start_.resize(routing_model.vehicles()); + for (int v = 0; v < routing_model.vehicles(); v++) { + const int64 start = routing_model.Start(v); + start_to_vehicle_[start] = v; + start_to_end_[start] = routing_model.End(v); + vehicle_to_start_[v] = start; + } +} + +void VehicleAmortizedCostFilter::OnSynchronizePathFromStart(int64 start) { + const int64 end = start_to_end_[start]; + CHECK_GE(end, 0); + const int route_length = Rank(end) - 1; + CHECK_GE(route_length, 0); + current_route_lengths_[start] = route_length; +} + +void VehicleAmortizedCostFilter::OnAfterSynchronizePaths() { + current_vehicle_cost_ = 0; + for (int vehicle = 0; vehicle < vehicle_to_start_.size(); vehicle++) { + const int64 start = vehicle_to_start_[vehicle]; + DCHECK_EQ(vehicle, start_to_vehicle_[start]); + + const int route_length = current_route_lengths_[start]; + DCHECK_GE(route_length, 0); + + if (route_length == 0) { + // The path is empty. + continue; + } + + const int64 linear_cost_factor = linear_cost_factor_of_vehicle_[vehicle]; + const int64 route_length_cost = + CapProd(quadratic_cost_factor_of_vehicle_[vehicle], + route_length * route_length); + + current_vehicle_cost_ = CapAdd( + current_vehicle_cost_, CapSub(linear_cost_factor, route_length_cost)); + } + PropagateObjectiveValue( + CapAdd(current_vehicle_cost_, injected_objective_value_)); +} + +void VehicleAmortizedCostFilter::InitializeAcceptPath() { + delta_vehicle_cost_ = current_vehicle_cost_; +} + +bool VehicleAmortizedCostFilter::AcceptPath(int64 path_start, int64 chain_start, + int64 chain_end) { + // Number of nodes previously between chain_start and chain_end + const int previous_chain_nodes = Rank(chain_end) - 1 - Rank(chain_start); + CHECK_GE(previous_chain_nodes, 0); + int new_chain_nodes = 0; + int64 node = GetNext(chain_start); + while (node != chain_end) { + new_chain_nodes++; + node = GetNext(node); + } + + const int previous_route_length = current_route_lengths_[path_start]; + CHECK_GE(previous_route_length, 0); + const int new_route_length = + previous_route_length - previous_chain_nodes + new_chain_nodes; + + const int vehicle = start_to_vehicle_[path_start]; + CHECK_GE(vehicle, 0); + DCHECK_EQ(path_start, vehicle_to_start_[vehicle]); + + // Update the cost related to used vehicles. + // TODO(user): Handle possible overflows. + if (previous_route_length == 0) { + // The route was empty before, it is no longer the case (changed path). + CHECK_GT(new_route_length, 0); + delta_vehicle_cost_ = + CapAdd(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]); + } else if (new_route_length == 0) { + // The route is now empty. + delta_vehicle_cost_ = + CapSub(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]); + } + + // Update the cost related to the sum of the squares of the route lengths. + const int64 quadratic_cost_factor = + quadratic_cost_factor_of_vehicle_[vehicle]; + delta_vehicle_cost_ = + CapAdd(delta_vehicle_cost_, + CapProd(quadratic_cost_factor, + previous_route_length * previous_route_length)); + delta_vehicle_cost_ = CapSub( + delta_vehicle_cost_, + CapProd(quadratic_cost_factor, new_route_length * new_route_length)); + + return true; +} + +bool VehicleAmortizedCostFilter::FinalizeAcceptPath(const Assignment* delta) { + int64 objective_max = objective_->Max(); + if (delta->Objective() == objective_) { + objective_max = std::min(objective_max, delta->ObjectiveMax()); + } + const int64 new_objective_value = + CapAdd(injected_objective_value_, delta_vehicle_cost_); + PropagateObjectiveValue(new_objective_value); + + return new_objective_value <= objective_max; +} + +} // namespace + +IntVarLocalSearchFilter* MakeVehicleAmortizedCostFilter( + const RoutingModel& routing_model, + Solver::ObjectiveWatcher objective_callback) { + return routing_model.solver()->RevAlloc(new VehicleAmortizedCostFilter( + routing_model, std::move(objective_callback))); +} + +namespace { + +class TypeIncompatibilityFilter : public BasePathFilter { + public: + explicit TypeIncompatibilityFilter(const RoutingModel& model); + ~TypeIncompatibilityFilter() override {} + std::string DebugString() const override { + return "TypeIncompatibilityFilter"; + } + + private: + void OnSynchronizePathFromStart(int64 start) override; + bool AcceptPath(int64 path_start, int64 chain_start, + int64 chain_end) override; + + const RoutingModel& routing_model_; + std::vector start_to_route_; + std::vector> type_counts_per_route_; +}; + +TypeIncompatibilityFilter::TypeIncompatibilityFilter(const RoutingModel& model) + : BasePathFilter(model.Nexts(), model.Size() + model.vehicles(), nullptr), + routing_model_(model) { + const int num_vehicles = model.vehicles(); + type_counts_per_route_.resize(num_vehicles); + const int num_visit_types = model.GetNumberOfVisitTypes(); + start_to_route_.resize(Size(), -1); + for (int route = 0; route < num_vehicles; route++) { + type_counts_per_route_[route].resize(num_visit_types, 0); + const int64 start = model.Start(route); + start_to_route_[start] = route; + } +} + +void TypeIncompatibilityFilter::OnSynchronizePathFromStart(int64 start) { + const int route = start_to_route_[start]; + CHECK_GE(route, 0); + std::vector& type_counts = type_counts_per_route_[route]; + std::fill(type_counts.begin(), type_counts.end(), 0); + const int num_types = type_counts.size(); + + int64 node = start; + while (node < Size()) { + DCHECK(IsVarSynced(node)); + const int type = routing_model_.GetVisitType(node); + if (type >= 0) { + CHECK_LT(type, num_types); + type_counts[type]++; + } + node = Value(node); + } +} + +bool TypeIncompatibilityFilter::AcceptPath(int64 path_start, int64 chain_start, + int64 chain_end) { + const int route = start_to_route_[path_start]; + CHECK_GE(route, 0); + std::vector new_type_counts = type_counts_per_route_[route]; + const int num_types = new_type_counts.size(); + std::vector types_to_check; + + // Go through the new nodes on the path and increment their type counts. + int64 node = GetNext(chain_start); + while (node != chain_end) { + const int type = routing_model_.GetVisitType(node); + if (type >= 0) { + CHECK_LT(type, num_types); + const int previous_count = new_type_counts[type]++; + if (previous_count == 0) { + // New type on the route, mark to check its incompatibilities. + types_to_check.push_back(type); + } + } + node = GetNext(node); + } + + // Update new_type_counts by decrementing the occurrence of the types of the + // nodes no longer on the route. + node = Value(chain_start); + while (node != chain_end) { + const int type = routing_model_.GetVisitType(node); + if (type >= 0) { + CHECK_LT(type, num_types); + int& type_count = new_type_counts[type]; + CHECK_GE(type_count, 1); + type_count--; + } + node = Value(node); + } + + // Check the incompatibilities for types in types_to_check. + for (int type : types_to_check) { + for (int incompatible_type : + routing_model_.GetTypeIncompatibilities(type)) { + if (new_type_counts[incompatible_type] > 0) { + return false; + } + } + } + return true; +} + +} // namespace + +IntVarLocalSearchFilter* MakeTypeIncompatibilityFilter( + const RoutingModel& routing_model) { + return routing_model.solver()->RevAlloc( + new TypeIncompatibilityFilter(routing_model)); +} + +namespace { + int64 GetNextValueFromForbiddenIntervals( int64 value, const SortedDisjointIntervalList& forbidden_intervals) { int64 next_value = value; @@ -483,7 +810,7 @@ class ChainCumulFilter : public BasePathFilter { const std::vector cumuls_; std::vector start_to_vehicle_; std::vector start_to_end_; - std::vector evaluators_; + std::vector evaluators_; const std::vector vehicle_capacities_; std::vector current_path_cumul_mins_; std::vector current_max_of_path_end_cumul_mins_; @@ -587,6 +914,12 @@ class PathCumulFilter : public BasePathFilter { std::string DebugString() const override { return "PathCumulFilter(" + name_ + ")"; } + int64 GetSynchronizedObjectiveValue() const override { + return synchronized_objective_value_; + } + int64 GetAcceptedObjectiveValue() const override { + return accepted_objective_value_; + } private: // This structure stores the "best" path cumul value for a solution, the path @@ -653,7 +986,7 @@ class PathCumulFilter : public BasePathFilter { } bool AcceptPath(int64 path_start, int64 chain_start, int64 chain_end) override; - bool FinalizeAcceptPath() override; + bool FinalizeAcceptPath(const Assignment* delta) override; void OnBeforeSynchronizePaths() override; bool FilterSpanCost() const { return global_span_cost_coefficient_ != 0; } @@ -663,6 +996,10 @@ class PathCumulFilter : public BasePathFilter { has_vehicle_span_upper_bounds_; } + bool FilterBreakCost(int vehicle) const { + return dimension_.VehicleHasBreakIntervals(vehicle); + } + bool FilterCumulSoftBounds() const { return !cumul_soft_bounds_.empty(); } int64 GetCumulSoftCost(int64 node, int64 cumul_value) const; @@ -671,6 +1008,21 @@ class PathCumulFilter : public BasePathFilter { return !cumul_piecewise_linear_costs_.empty(); } + bool FilterWithDimensionCumulOptimizerForVehicle(int vehicle) const { + // The DimensionCumulOptimizer is used to compute a more precise value of + // the cost related to the cumul values (soft bounds and span costs). + // Therefore, we only use the optimizer when the costs are actually used to + // filter the solutions, i.e. when we propagate the cost (during local + // search). + // NOTE: The optimizer might eventually catch an infeasibility not caught + // elsewhere in the filter. If that becomes the case, consider removing the + // "CanPropagateObjectiveValue() &&" from the clause. + return CanPropagateObjectiveValue() && + (dimension_.GetSpanCostCoefficientForVehicle(vehicle) > 0) && + !FilterCumulPiecewiseLinearCosts() && + (FilterCumulSoftBounds() || FilterCumulSoftLowerBounds()); + } + int64 GetCumulPiecewiseLinearCost(int64 node, int64 cumul_value) const; bool FilterCumulSoftLowerBounds() const { @@ -685,22 +1037,36 @@ class PathCumulFilter : public BasePathFilter { void InitializeSupportedPathCumul(SupportedPathCumul* supported_cumul, int64 default_value); + // Given the vector of minimum cumuls on the path, determines if the pickup to + // delivery limits for this dimension (if there are any) can be respected by + // this path. + // Returns true if for every pickup/delivery nodes visited on this path, + // min_cumul_value(delivery) - max_cumul_value(pickup) is less than the limit + // set for this pickup to delivery. + bool PickupToDeliveryLimitsRespected( + const PathTransits& path_transits, int path, + const std::vector& min_path_cumuls) const; + // Compute the max start cumul value for a given path given an end cumul - // value. + // value. Does not take time windows into account. int64 ComputePathMaxStartFromEndCumul(const PathTransits& path_transits, - int path, int end_cumul) const; + int path, int64 end_cumul) const; + const RoutingModel& routing_model_; + const RoutingDimension& dimension_; const std::vector cumuls_; const std::vector& forbidden_intervals_; const std::vector slacks_; std::vector start_to_vehicle_; - std::vector evaluators_; + std::vector evaluators_; std::vector vehicle_span_upper_bounds_; bool has_vehicle_span_upper_bounds_; int64 total_current_cumul_cost_value_; + int64 synchronized_objective_value_; + int64 accepted_objective_value_; // Map between paths and path soft cumul bound costs. The paths are indexed // by the index of the start node of the path. - std::unordered_map current_cumul_cost_values_; + absl::flat_hash_map current_cumul_cost_values_; int64 cumul_cost_delta_; const int64 global_span_cost_coefficient_; std::vector cumul_soft_bounds_; @@ -720,9 +1086,11 @@ class PathCumulFilter : public BasePathFilter { PathTransits delta_path_transits_; int64 delta_max_end_cumul_; // Note: small_ordered_set only support non-hash sets. - small_ordered_set> delta_paths_; + gtl::small_ordered_set> delta_paths_; const std::string name_; + RouteDimensionCumulOptimizer optimizer_; + bool lns_detected_; }; @@ -731,6 +1099,8 @@ PathCumulFilter::PathCumulFilter(const RoutingModel& routing_model, Solver::ObjectiveWatcher objective_callback) : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size(), std::move(objective_callback)), + routing_model_(routing_model), + dimension_(dimension), cumuls_(dimension.cumuls()), forbidden_intervals_(dimension.forbidden_intervals()), slacks_(dimension.slacks()), @@ -738,6 +1108,8 @@ PathCumulFilter::PathCumulFilter(const RoutingModel& routing_model, vehicle_span_upper_bounds_(dimension.vehicle_span_upper_bounds()), has_vehicle_span_upper_bounds_(false), total_current_cumul_cost_value_(0), + synchronized_objective_value_(0), + accepted_objective_value_(0), current_cumul_cost_values_(), cumul_cost_delta_(0), global_span_cost_coefficient_(dimension.global_span_cost_coefficient()), @@ -748,6 +1120,7 @@ PathCumulFilter::PathCumulFilter(const RoutingModel& routing_model, vehicle_capacities_(dimension.vehicle_capacities()), delta_max_end_cumul_(kint64min), name_(dimension.name()), + optimizer_(&dimension), lns_detected_(false) { for (const int64 upper_bound : vehicle_span_upper_bounds_) { if (upper_bound != kint64max) { @@ -775,27 +1148,26 @@ PathCumulFilter::PathCumulFilter(const RoutingModel& routing_model, } } for (int i = 0; i < cumuls_.size(); ++i) { - if (dimension.HasCumulVarSoftUpperBoundFromIndex(i)) { + if (dimension.HasCumulVarSoftUpperBound(i)) { has_cumul_soft_bounds = true; - cumul_soft_bounds_[i].bound = - dimension.GetCumulVarSoftUpperBoundFromIndex(i); + cumul_soft_bounds_[i].bound = dimension.GetCumulVarSoftUpperBound(i); cumul_soft_bounds_[i].coefficient = - dimension.GetCumulVarSoftUpperBoundCoefficientFromIndex(i); + dimension.GetCumulVarSoftUpperBoundCoefficient(i); } - if (dimension.HasCumulVarSoftLowerBoundFromIndex(i)) { + if (dimension.HasCumulVarSoftLowerBound(i)) { has_cumul_soft_lower_bounds = true; cumul_soft_lower_bounds_[i].bound = - dimension.GetCumulVarSoftLowerBoundFromIndex(i); + dimension.GetCumulVarSoftLowerBound(i); cumul_soft_lower_bounds_[i].coefficient = - dimension.GetCumulVarSoftLowerBoundCoefficientFromIndex(i); + dimension.GetCumulVarSoftLowerBoundCoefficient(i); } - if (dimension.HasCumulVarPiecewiseLinearCostFromIndex(i)) { + if (dimension.HasCumulVarPiecewiseLinearCost(i)) { has_cumul_piecewise_linear_costs = true; cumul_piecewise_linear_costs_[i] = - dimension.GetCumulVarPiecewiseLinearCostFromIndex(i); + dimension.GetCumulVarPiecewiseLinearCost(i); } IntVar* const cumul_var = cumuls_[i]; - if (cumul_var->Min() > 0 && cumul_var->Max() < kint64max) { + if (cumul_var->Min() > 0 || cumul_var->Max() < kint64max) { has_cumul_hard_bounds = true; } } @@ -886,6 +1258,9 @@ void PathCumulFilter::OnBeforeSynchronizePaths() { for (int r = 0; r < NumPaths(); ++r) { int64 node = Start(r); const int vehicle = start_to_vehicle_[Start(r)]; + const bool filter_with_optimizer = + FilterWithDimensionCumulOptimizerForVehicle(vehicle); + // First pass: evaluating route length to reserve memory to store route // information. int number_of_route_arcs = 0; @@ -897,9 +1272,18 @@ void PathCumulFilter::OnBeforeSynchronizePaths() { // Second pass: update cumul, transit and cost values. node = Start(r); int64 cumul = cumuls_[node]->Min(); - int64 current_cumul_cost_value = GetCumulSoftCost(node, cumul); - current_cumul_cost_value = CapAdd( - current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul)); + int64 current_cumul_cost_value = 0; + if (filter_with_optimizer) { + const bool cumuls_optimized = + optimizer_.ComputeRouteCumulCostWithoutFixedTransits( + vehicle, [this](int64 node) { return Value(node); }, + ¤t_cumul_cost_value); + DCHECK(cumuls_optimized); + } else { + current_cumul_cost_value = GetCumulSoftCost(node, cumul); + current_cumul_cost_value = CapAdd( + current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul)); + } int64 total_transit = 0; while (node < Size()) { const int64 next = Value(node); @@ -912,12 +1296,15 @@ void PathCumulFilter::OnBeforeSynchronizePaths() { forbidden_intervals_[next]); cumul = std::max(cumuls_[next]->Min(), cumul); node = next; - current_cumul_cost_value = - CapAdd(current_cumul_cost_value, GetCumulSoftCost(node, cumul)); - current_cumul_cost_value = CapAdd( - current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul)); + if (!filter_with_optimizer) { + current_cumul_cost_value = + CapAdd(current_cumul_cost_value, GetCumulSoftCost(node, cumul)); + current_cumul_cost_value = + CapAdd(current_cumul_cost_value, + GetCumulPiecewiseLinearCost(node, cumul)); + } } - if (FilterSlackCost()) { + if (FilterSlackCost() && !filter_with_optimizer) { const int64 start = ComputePathMaxStartFromEndCumul(current_path_transits_, r, cumul); current_cumul_cost_value = @@ -925,7 +1312,7 @@ void PathCumulFilter::OnBeforeSynchronizePaths() { CapProd(vehicle_span_cost_coefficients_[vehicle], CapSub(CapSub(cumul, start), total_transit))); } - if (FilterCumulSoftLowerBounds()) { + if (FilterCumulSoftLowerBounds() && !filter_with_optimizer) { current_cumul_cost_value = CapAdd(current_cumul_cost_value, GetPathCumulSoftLowerBoundCost(current_path_transits_, r)); @@ -954,12 +1341,14 @@ void PathCumulFilter::OnBeforeSynchronizePaths() { // Initialize this before considering any deltas (neighbor). delta_max_end_cumul_ = kint64min; lns_detected_ = false; + synchronized_objective_value_ = + CapAdd(total_current_cumul_cost_value_, + CapProd(global_span_cost_coefficient_, + CapSub(current_max_end_.cumul_value, + current_min_start_.cumul_value))); if (CanPropagateObjectiveValue()) { - const int64 new_objective_value = CapAdd( - CapAdd(injected_objective_value_, total_current_cumul_cost_value_), - CapProd(global_span_cost_coefficient_, - CapSub(current_max_end_.cumul_value, - current_min_start_.cumul_value))); + const int64 new_objective_value = + CapAdd(injected_objective_value_, synchronized_objective_value_); PropagateObjectiveValue(new_objective_value); } } @@ -968,18 +1357,22 @@ bool PathCumulFilter::AcceptPath(int64 path_start, int64 chain_start, int64 chain_end) { int64 node = path_start; int64 cumul = cumuls_[node]->Min(); - cumul_cost_delta_ = CapAdd(cumul_cost_delta_, GetCumulSoftCost(node, cumul)); - cumul_cost_delta_ = - CapAdd(cumul_cost_delta_, GetCumulPiecewiseLinearCost(node, cumul)); + int64 cumul_cost_delta = 0; int64 total_transit = 0; const int path = delta_path_transits_.AddPaths(1); const int vehicle = start_to_vehicle_[path_start]; const int64 capacity = vehicle_capacities_[vehicle]; + const bool filter_with_optimizer = + FilterWithDimensionCumulOptimizerForVehicle(vehicle); + if (!filter_with_optimizer) { + cumul_cost_delta = CapAdd(GetCumulSoftCost(node, cumul), + GetCumulPiecewiseLinearCost(node, cumul)); + } // Evaluating route length to reserve memory to store transit information. int number_of_route_arcs = 0; while (node < Size()) { const int64 next = GetNext(node); - // TODO(user): This shouldn't be needed anymore as the such deltas should + // TODO(user): This shouldn't be needed anymore as such deltas should // have been filtered already. if (next == kUnassigned) { // LNS detected, return true since other paths were ok up to now. @@ -990,6 +1383,9 @@ bool PathCumulFilter::AcceptPath(int64 path_start, int64 chain_start, node = next; } delta_path_transits_.ReserveTransits(path, number_of_route_arcs); + std::vector min_path_cumuls; + min_path_cumuls.reserve(number_of_route_arcs + 1); + min_path_cumuls.push_back(cumul); // Check that the path is feasible with regards to cumul bounds, scanning // the paths from start to end (caching path node sequences and transits // for further span cost filtering). @@ -1007,39 +1403,76 @@ bool PathCumulFilter::AcceptPath(int64 path_start, int64 chain_start, return false; } cumul = std::max(cumuls_[next]->Min(), cumul); + min_path_cumuls.push_back(cumul); node = next; - cumul_cost_delta_ = - CapAdd(cumul_cost_delta_, GetCumulSoftCost(node, cumul)); - cumul_cost_delta_ = - CapAdd(cumul_cost_delta_, GetCumulPiecewiseLinearCost(node, cumul)); - } - if (FilterSlackCost()) { - const int64 start = - ComputePathMaxStartFromEndCumul(delta_path_transits_, path, cumul); - const int64 path_cumul_range = CapSub(cumul, start); - if (path_cumul_range > vehicle_span_upper_bounds_[vehicle]) { + if (!filter_with_optimizer) { + cumul_cost_delta = + CapAdd(cumul_cost_delta, GetCumulSoftCost(node, cumul)); + cumul_cost_delta = + CapAdd(cumul_cost_delta, GetCumulPiecewiseLinearCost(node, cumul)); + } + } + const int64 min_end = cumul; + + if (!PickupToDeliveryLimitsRespected(delta_path_transits_, path, + min_path_cumuls)) { + return false; + } + if (FilterSlackCost() || FilterBreakCost(vehicle)) { + const int64 max_start_from_min_end = + ComputePathMaxStartFromEndCumul(delta_path_transits_, path, min_end); + int64 min_total_slack = + CapSub(CapSub(min_end, max_start_from_min_end), total_transit); + if (!filter_with_optimizer) { + if (FilterBreakCost(vehicle)) { + // Compute a lower bound of the amount of break that must be made inside + // the route. We compute a mandatory interval (might be empty) + // [max_start, min_end[ during which the route will have to happen, + // then the duration of break that must happen during this interval. + int64 min_total_break = 0; + int64 max_path_end = cumuls_[routing_model_.End(vehicle)]->Max(); + const int64 max_start = ComputePathMaxStartFromEndCumul( + delta_path_transits_, path, max_path_end); + for (const IntervalVar* br : + dimension_.GetBreakIntervalsOfVehicle(vehicle)) { + if (max_start < br->EndMin() && br->StartMax() < min_end) { + min_total_break = CapAdd(min_total_break, br->DurationMin()); + } + } + min_total_slack = std::max(min_total_slack, min_total_break); + } + cumul_cost_delta = CapAdd( + cumul_cost_delta, + CapProd(vehicle_span_cost_coefficients_[vehicle], min_total_slack)); + } + if (CapAdd(total_transit, min_total_slack) > + vehicle_span_upper_bounds_[vehicle]) { return false; } - cumul_cost_delta_ = CapAdd( - cumul_cost_delta_, CapProd(vehicle_span_cost_coefficients_[vehicle], - CapSub(path_cumul_range, total_transit))); } - if (FilterCumulSoftLowerBounds()) { - cumul_cost_delta_ = - CapAdd(cumul_cost_delta_, + if (FilterCumulSoftLowerBounds() && !filter_with_optimizer) { + cumul_cost_delta = + CapAdd(cumul_cost_delta, GetPathCumulSoftLowerBoundCost(delta_path_transits_, path)); } + if (filter_with_optimizer && + !optimizer_.ComputeRouteCumulCostWithoutFixedTransits( + vehicle, [this](int64 node) { return GetNext(node); }, + &cumul_cost_delta)) { + return false; + } if (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() || FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts()) { delta_paths_.insert(GetPath(path_start)); delta_max_end_cumul_ = std::max(delta_max_end_cumul_, cumul); - cumul_cost_delta_ = - CapSub(cumul_cost_delta_, current_cumul_cost_values_[path_start]); + cumul_cost_delta = + CapSub(cumul_cost_delta, current_cumul_cost_values_[path_start]); } + cumul_cost_delta_ = CapAdd(cumul_cost_delta_, cumul_cost_delta); return true; } -bool PathCumulFilter::FinalizeAcceptPath() { +bool PathCumulFilter::FinalizeAcceptPath(const Assignment* delta) { if ((!FilterSpanCost() && !FilterCumulSoftBounds() && !FilterSlackCost() && !FilterCumulSoftLowerBounds() && !FilterCumulPiecewiseLinearCosts()) || lns_detected_) { @@ -1110,10 +1543,11 @@ bool PathCumulFilter::FinalizeAcceptPath() { delta_path_transits_.Clear(); lns_detected_ = false; // Filtering on objective value, including the injected part of it. + accepted_objective_value_ = + CapAdd(cumul_cost_delta_, CapProd(global_span_cost_coefficient_, + CapSub(new_max_end, new_min_start))); const int64 new_objective_value = - CapAdd(CapAdd(injected_objective_value_, cumul_cost_delta_), - CapProd(global_span_cost_coefficient_, - CapSub(new_max_end, new_min_start))); + CapAdd(injected_objective_value_, accepted_objective_value_); PropagateObjectiveValue(new_objective_value); // Only compare to max as a cost lower bound is computed. return new_objective_value <= cost_var_->Max(); @@ -1126,8 +1560,70 @@ void PathCumulFilter::InitializeSupportedPathCumul( supported_cumul->path_values.resize(NumPaths(), default_value); } +bool PathCumulFilter::PickupToDeliveryLimitsRespected( + const PathTransits& path_transits, int path, + const std::vector& min_path_cumuls) const { + if (!dimension_.HasPickupToDeliveryLimits()) { + return true; + } + const int num_pairs = routing_model_.GetPickupAndDeliveryPairs().size(); + DCHECK_GT(num_pairs, 0); + std::vector> visited_delivery_and_min_cumul_per_pair( + num_pairs, {-1, -1}); + + const int path_size = path_transits.PathSize(path); + CHECK_EQ(min_path_cumuls.size(), path_size); + + int64 max_cumul = min_path_cumuls.back(); + for (int i = path_transits.PathSize(path) - 2; i >= 0; i--) { + const int node_index = path_transits.Node(path, i); + max_cumul = CapSub(max_cumul, path_transits.Transit(path, i)); + max_cumul = std::min(cumuls_[node_index]->Max(), max_cumul); + + const std::vector>& pickup_index_pairs = + routing_model_.GetPickupIndexPairs(node_index); + const std::vector>& delivery_index_pairs = + routing_model_.GetDeliveryIndexPairs(node_index); + if (!pickup_index_pairs.empty()) { + // The node is a pickup. Check that it is not a delivery and that it + // appears in a single pickup/delivery pair (as required when limits are + // set on dimension cumuls for pickup and deliveries). + DCHECK(delivery_index_pairs.empty()); + DCHECK_EQ(pickup_index_pairs.size(), 1); + const int pair_index = pickup_index_pairs[0].first; + // Get the delivery visited for this pair. + const int delivery_index = + visited_delivery_and_min_cumul_per_pair[pair_index].first; + if (delivery_index < 0) { + // No delivery visited after this pickup for this pickup/delivery pair. + continue; + } + const int64 cumul_diff_limit = dimension_.GetPickupToDeliveryLimitForPair( + pair_index, pickup_index_pairs[0].second, delivery_index); + if (CapSub(visited_delivery_and_min_cumul_per_pair[pair_index].second, + max_cumul) > cumul_diff_limit) { + return false; + } + } + if (!delivery_index_pairs.empty()) { + // The node is a delivery. Check that it's not a pickup and it belongs to + // a single pair. + DCHECK(pickup_index_pairs.empty()); + DCHECK_EQ(delivery_index_pairs.size(), 1); + const int pair_index = delivery_index_pairs[0].first; + std::pair& delivery_index_and_cumul = + visited_delivery_and_min_cumul_per_pair[pair_index]; + int& delivery_index = delivery_index_and_cumul.first; + DCHECK_EQ(delivery_index, -1); + delivery_index = delivery_index_pairs[0].second; + delivery_index_and_cumul.second = min_path_cumuls[i]; + } + } + return true; +} + int64 PathCumulFilter::ComputePathMaxStartFromEndCumul( - const PathTransits& path_transits, int path, int end_cumul) const { + const PathTransits& path_transits, int path, int64 end_cumul) const { int64 cumul = end_cumul; for (int i = path_transits.PathSize(path) - 2; i >= 0; --i) { cumul = CapSub(cumul, path_transits.Transit(path, i)); @@ -1138,7 +1634,7 @@ int64 PathCumulFilter::ComputePathMaxStartFromEndCumul( } // namespace -RoutingLocalSearchFilter* MakePathCumulFilter( +IntVarLocalSearchFilter* MakePathCumulFilter( const RoutingModel& routing_model, const RoutingDimension& dimension, Solver::ObjectiveWatcher objective_callback) { for (const int64 upper_bound : dimension.vehicle_span_upper_bounds()) { @@ -1161,15 +1657,15 @@ RoutingLocalSearchFilter* MakePathCumulFilter( } const std::vector& cumuls = dimension.cumuls(); for (int i = 0; i < cumuls.size(); ++i) { - if (dimension.HasCumulVarSoftUpperBoundFromIndex(i)) { + if (dimension.HasCumulVarSoftUpperBound(i)) { return routing_model.solver()->RevAlloc( new PathCumulFilter(routing_model, dimension, objective_callback)); } - if (dimension.HasCumulVarSoftLowerBoundFromIndex(i)) { + if (dimension.HasCumulVarSoftLowerBound(i)) { return routing_model.solver()->RevAlloc( new PathCumulFilter(routing_model, dimension, objective_callback)); } - if (dimension.HasCumulVarPiecewiseLinearCostFromIndex(i)) { + if (dimension.HasCumulVarPiecewiseLinearCost(i)) { return routing_model.solver()->RevAlloc( new PathCumulFilter(routing_model, dimension, objective_callback)); } @@ -1196,37 +1692,67 @@ RoutingLocalSearchFilter* MakePathCumulFilter( namespace { -// Node precedence filter, resulting from pickup and delivery pairs. -class NodePrecedenceFilter : public BasePathFilter { +// Filter for pickup/delivery precedences. +class PickupDeliveryFilter : public BasePathFilter { public: - NodePrecedenceFilter(const std::vector& nexts, int next_domain_size, - const RoutingModel::NodePairs& pairs); - ~NodePrecedenceFilter() override {} + PickupDeliveryFilter(const std::vector& nexts, int next_domain_size, + const RoutingModel::IndexPairs& pairs, + const std::vector& + vehicle_policies); + ~PickupDeliveryFilter() override {} bool AcceptPath(int64 path_start, int64 chain_start, int64 chain_end) override; - std::string DebugString() const override { return "NodePrecedenceFilter"; } + std::string DebugString() const override { return "PickupDeliveryFilter"; } private: + bool AcceptPathDefault(int64 path_start); + template + bool AcceptPathOrdered(int64 path_start); + std::vector pair_firsts_; std::vector pair_seconds_; + const RoutingModel::IndexPairs pairs_; SparseBitset<> visited_; + std::deque visited_deque_; + const std::vector vehicle_policies_; }; -NodePrecedenceFilter::NodePrecedenceFilter(const std::vector& nexts, - int next_domain_size, - const RoutingModel::NodePairs& pairs) +PickupDeliveryFilter::PickupDeliveryFilter( + const std::vector& nexts, int next_domain_size, + const RoutingModel::IndexPairs& pairs, + const std::vector& vehicle_policies) : BasePathFilter(nexts, next_domain_size, nullptr), pair_firsts_(next_domain_size, kUnassigned), pair_seconds_(next_domain_size, kUnassigned), - visited_(Size()) { - for (const auto& node_pair : pairs) { - pair_firsts_[node_pair.first[0]] = node_pair.second[0]; - pair_seconds_[node_pair.second[0]] = node_pair.first[0]; + pairs_(pairs), + visited_(Size()), + vehicle_policies_(vehicle_policies) { + for (int i = 0; i < pairs.size(); ++i) { + const auto& index_pair = pairs[i]; + for (int first : index_pair.first) { + pair_firsts_[first] = i; + } + for (int second : index_pair.second) { + pair_seconds_[second] = i; + } } } -bool NodePrecedenceFilter::AcceptPath(int64 path_start, int64 chain_start, +bool PickupDeliveryFilter::AcceptPath(int64 path_start, int64 chain_start, int64 chain_end) { + switch (vehicle_policies_[GetPath(path_start)]) { + case RoutingModel::PickupAndDeliveryPolicy::ANY: + return AcceptPathDefault(path_start); + case RoutingModel::PickupAndDeliveryPolicy::LIFO: + return AcceptPathOrdered(path_start); + case RoutingModel::PickupAndDeliveryPolicy::FIFO: + return AcceptPathOrdered(path_start); + default: + return true; + } +} + +bool PickupDeliveryFilter::AcceptPathDefault(int64 path_start) { visited_.ClearAll(); int64 node = path_start; int64 path_length = 1; @@ -1235,11 +1761,31 @@ bool NodePrecedenceFilter::AcceptPath(int64 path_start, int64 chain_start, if (path_length > Size()) { return false; } - if (pair_firsts_[node] != kUnassigned && visited_[pair_firsts_[node]]) { - return false; + if (pair_firsts_[node] != kUnassigned) { + // Checking on pair firsts is not actually necessary (inconsistencies + // will get caught when checking pair seconds); doing it anyway to + // cut checks early. + for (int second : pairs_[pair_firsts_[node]].second) { + if (visited_[second]) { + return false; + } + } } - if (pair_seconds_[node] != kUnassigned && !visited_[pair_seconds_[node]]) { - return false; + if (pair_seconds_[node] != kUnassigned) { + bool found_first = false; + bool some_synced = false; + for (int first : pairs_[pair_seconds_[node]].first) { + if (visited_[first]) { + found_first = true; + break; + } + if (IsVarSynced(first)) { + some_synced = true; + } + } + if (!found_first && some_synced) { + return false; + } } visited_.Set(node); const int64 next = GetNext(node); @@ -1251,20 +1797,89 @@ bool NodePrecedenceFilter::AcceptPath(int64 path_start, int64 chain_start, ++path_length; } for (const int64 node : visited_.PositionsSetAtLeastOnce()) { - if (pair_firsts_[node] != kUnassigned && !visited_[pair_firsts_[node]]) { + if (pair_firsts_[node] != kUnassigned) { + bool found_second = false; + bool some_synced = false; + for (int second : pairs_[pair_firsts_[node]].second) { + if (visited_[second]) { + found_second = true; + break; + } + if (IsVarSynced(second)) { + some_synced = true; + } + } + if (!found_second && some_synced) { + return false; + } + } + } + return true; +} + +template +bool PickupDeliveryFilter::AcceptPathOrdered(int64 path_start) { + visited_deque_.clear(); + int64 node = path_start; + int64 path_length = 1; + while (node < Size()) { + // Detect sub-cycles (path is longer than longest possible path). + if (path_length > Size()) { return false; } + if (pair_firsts_[node] != kUnassigned) { + if (lifo) { + visited_deque_.push_back(node); + } else { + visited_deque_.push_front(node); + } + } + if (pair_seconds_[node] != kUnassigned) { + bool found_first = false; + bool some_synced = false; + for (int first : pairs_[pair_seconds_[node]].first) { + if (!visited_deque_.empty() && visited_deque_.back() == first) { + found_first = true; + break; + } + if (IsVarSynced(first)) { + some_synced = true; + } + } + if (!found_first && some_synced) { + return false; + } else if (!visited_deque_.empty()) { + visited_deque_.pop_back(); + } + } + const int64 next = GetNext(node); + if (next == kUnassigned) { + // LNS detected, return true since path was ok up to now. + return true; + } + node = next; + ++path_length; + } + while (!visited_deque_.empty()) { + for (int second : pairs_[pair_firsts_[visited_deque_.back()]].second) { + if (IsVarSynced(second)) { + return false; + } + } + visited_deque_.pop_back(); } return true; } } // namespace -RoutingLocalSearchFilter* MakeNodePrecedenceFilter( - const RoutingModel& routing_model, const RoutingModel::NodePairs& pairs) { - return routing_model.solver()->RevAlloc(new NodePrecedenceFilter( +IntVarLocalSearchFilter* MakePickupDeliveryFilter( + const RoutingModel& routing_model, const RoutingModel::IndexPairs& pairs, + const std::vector& + vehicle_policies) { + return routing_model.solver()->RevAlloc(new PickupDeliveryFilter( routing_model.Nexts(), routing_model.Size() + routing_model.vehicles(), - pairs)); + pairs, vehicle_policies)); } namespace { @@ -1280,6 +1895,9 @@ class VehicleVarFilter : public BasePathFilter { std::string DebugString() const override { return "VehicleVariableFilter"; } private: + bool DisableFiltering() const override; + bool IsVehicleVariableConstrained(int index) const; + std::vector start_to_vehicle_; std::vector vehicle_vars_; const int64 unconstrained_vehicle_var_domain_size_; @@ -1299,24 +1917,16 @@ VehicleVarFilter::VehicleVarFilter(const RoutingModel& routing_model) // Avoid filtering if variable domains are unconstrained. bool VehicleVarFilter::Accept(const Assignment* delta, const Assignment* deltadelta) { + if (IsDisabled()) return true; const Assignment::IntContainer& container = delta->IntVarContainer(); const int size = container.Size(); bool all_unconstrained = true; for (int i = 0; i < size; ++i) { int64 index = -1; - if (FindIndex(container.Element(i).Var(), &index)) { - const IntVar* const vehicle_var = vehicle_vars_[index]; - // If vehicle variable contains -1 (optional node), then we need to - // add it to the "unconstrained" domain. Impact we don't filter mandatory - // nodes made inactive here, but it is covered by other filters. - const int adjusted_unconstrained_vehicle_var_domain_size = - vehicle_var->Min() >= 0 ? unconstrained_vehicle_var_domain_size_ - : unconstrained_vehicle_var_domain_size_ + 1; - if (vehicle_var->Size() != - adjusted_unconstrained_vehicle_var_domain_size) { - all_unconstrained = false; - break; - } + if (FindIndex(container.Element(i).Var(), &index) && + IsVehicleVariableConstrained(index)) { + all_unconstrained = false; + break; } } if (all_unconstrained) return true; @@ -1336,17 +1946,205 @@ bool VehicleVarFilter::AcceptPath(int64 path_start, int64 chain_start, return true; } +bool VehicleVarFilter::DisableFiltering() const { + for (int i = 0; i < vehicle_vars_.size(); ++i) { + if (IsVehicleVariableConstrained(i)) return false; + } + return true; +} + +bool VehicleVarFilter::IsVehicleVariableConstrained(int index) const { + const IntVar* const vehicle_var = vehicle_vars_[index]; + // If vehicle variable contains -1 (optional node), then we need to + // add it to the "unconstrained" domain. Impact we don't filter mandatory + // nodes made inactive here, but it is covered by other filters. + const int adjusted_unconstrained_vehicle_var_domain_size = + vehicle_var->Min() >= 0 ? unconstrained_vehicle_var_domain_size_ + : unconstrained_vehicle_var_domain_size_ + 1; + return vehicle_var->Size() != adjusted_unconstrained_vehicle_var_domain_size; +} + } // namespace -RoutingLocalSearchFilter* MakeVehicleVarFilter( +IntVarLocalSearchFilter* MakeVehicleVarFilter( const RoutingModel& routing_model) { return routing_model.solver()->RevAlloc(new VehicleVarFilter(routing_model)); } -// TODO(user): Implement same-vehicle filter. Could be merged with node -// precedence filter. +namespace { +class VehicleBreaksFilter : public BasePathFilter { + public: + VehicleBreaksFilter(const RoutingModel& routing_model, + const RoutingDimension& dimension); + std::string DebugString() const override { return "VehicleBreaksFilter"; } + bool AcceptPath(int64 path_start, int64 chain_start, + int64 chain_end) override; -// --- First solution decision builders --- + private: + // Handles to model. + const RoutingModel& model_; + const RoutingDimension& dimension_; + // Strong energy-based filtering algorithm. + DisjunctivePropagator disjunctive_propagator_; + DisjunctivePropagator::Tasks tasks_; + // Used to check whether propagation changed a vector. + std::vector old_start_min_; + std::vector old_end_max_; + std::vector start_to_vehicle_; +}; + +VehicleBreaksFilter::VehicleBreaksFilter(const RoutingModel& routing_model, + const RoutingDimension& dimension) + : BasePathFilter(routing_model.Nexts(), + routing_model.Size() + routing_model.vehicles(), nullptr), + model_(routing_model), + dimension_(dimension) { + start_to_vehicle_.resize(Size(), -1); + for (int i = 0; i < routing_model.vehicles(); ++i) { + start_to_vehicle_[routing_model.Start(i)] = i; + } +} + +bool VehicleBreaksFilter::AcceptPath(int64 path_start, int64 chain_start, + int64 chain_end) { + // Translate path and breaks to tasks. + const int vehicle = start_to_vehicle_[path_start]; + if (!dimension_.VehicleHasBreakIntervals(vehicle)) return true; + tasks_.start_min.clear(); + tasks_.duration_min.clear(); + tasks_.end_max.clear(); + tasks_.is_preemptible.clear(); + tasks_.forbidden_intervals.clear(); + bool has_forbidden_intervals = false; + int64 current = path_start; + while (true) { + // Add tasks from visits. + const bool node_is_last = model_.IsEnd(current); + const int64 visit_duration = + node_is_last + ? 0LL + : dimension_.GetNodeVisitTransitsOfVehicle(vehicle)[current]; + tasks_.start_min.push_back(dimension_.CumulVar(current)->Min()); + tasks_.duration_min.push_back(visit_duration); + tasks_.end_max.push_back( + CapAdd(dimension_.CumulVar(current)->Max(), visit_duration)); + tasks_.is_preemptible.push_back(false); + tasks_.forbidden_intervals.push_back( + &(dimension_.forbidden_intervals()[current])); + has_forbidden_intervals |= + tasks_.forbidden_intervals.back()->NumIntervals() > 0; + if (node_is_last) break; + + // Add tasks from transits. + const int next = GetNext(current); + tasks_.start_min.push_back(CapAdd(tasks_.start_min.back(), visit_duration)); + tasks_.duration_min.push_back(CapSub( + dimension_.transit_evaluator(vehicle)(current, next), visit_duration)); + tasks_.end_max.push_back(dimension_.CumulVar(next)->Max()); + tasks_.is_preemptible.push_back(true); + tasks_.forbidden_intervals.push_back(nullptr); + + current = next; + } + tasks_.num_chain_tasks = tasks_.start_min.size(); + // Add tasks from breaks. + for (IntervalVar* interval : dimension_.GetBreakIntervalsOfVehicle(vehicle)) { + if (!interval->MustBePerformed()) continue; + tasks_.start_min.push_back(interval->StartMin()); + tasks_.duration_min.push_back(interval->DurationMin()); + tasks_.end_max.push_back(interval->EndMax()); + tasks_.is_preemptible.push_back(false); + tasks_.forbidden_intervals.push_back(nullptr); + } + // Reduce bounds until failure or fixed point is reached. + if (!has_forbidden_intervals) tasks_.forbidden_intervals.clear(); + + bool is_feasible = true; + while (true) { + old_start_min_ = tasks_.start_min; + old_end_max_ = tasks_.end_max; + is_feasible = disjunctive_propagator_.Propagate(&tasks_); + // If fixed point reached, stop. + if ((old_start_min_ == tasks_.start_min) && + (old_end_max_ == tasks_.end_max)) { + break; + } + } + return is_feasible; +} + +} // namespace + +IntVarLocalSearchFilter* MakeVehicleBreaksFilter( + const RoutingModel& routing_model, const RoutingDimension& dimension) { + return routing_model.solver()->RevAlloc( + new VehicleBreaksFilter(routing_model, dimension)); +} + +const int64 CPFeasibilityFilter::kUnassigned = -1; + +CPFeasibilityFilter::CPFeasibilityFilter(const RoutingModel* routing_model) + : IntVarLocalSearchFilter(routing_model->Nexts()), + model_(routing_model), + solver_(routing_model->solver()), + assignment_(solver_->MakeAssignment()), + temp_assignment_(solver_->MakeAssignment()), + restore_(solver_->MakeRestoreAssignment(temp_assignment_)) { + assignment_->Add(routing_model->Nexts()); +} + +bool CPFeasibilityFilter::Accept(const Assignment* delta, + const Assignment* deltadelta) { + temp_assignment_->Copy(assignment_); + AddDeltaToAssignment(delta, temp_assignment_); + return solver_->Solve(restore_); +} + +void CPFeasibilityFilter::OnSynchronize(const Assignment* delta) { + AddDeltaToAssignment(delta, assignment_); +} + +void CPFeasibilityFilter::AddDeltaToAssignment(const Assignment* delta, + Assignment* assignment) { + if (delta == nullptr) { + return; + } + Assignment::IntContainer* const container = + assignment->MutableIntVarContainer(); + const Assignment::IntContainer& delta_container = delta->IntVarContainer(); + const int delta_size = delta_container.Size(); + + for (int i = 0; i < delta_size; i++) { + const IntVarElement& delta_element = delta_container.Element(i); + IntVar* const var = delta_element.Var(); + int64 index = kUnassigned; + CHECK(FindIndex(var, &index)); + DCHECK_EQ(var, Var(index)); + const int64 value = delta_element.Value(); + + container->AddAtPosition(var, index)->SetValue(value); + if (model_->IsStart(index)) { + if (model_->IsEnd(value)) { + // Do not restore unused routes. + container->MutableElement(index)->Deactivate(); + } else { + // Re-activate the route's start in case it was deactivated before. + container->MutableElement(index)->Activate(); + } + } + } +} + +IntVarLocalSearchFilter* MakeCPFeasibilityFilter( + const RoutingModel* routing_model) { + return routing_model->solver()->RevAlloc( + new CPFeasibilityFilter(routing_model)); +} + +// TODO(user): Implement same-vehicle filter. Could be merged with node +// precedence filter. + +// --- First solution decision builders --- // IntVarFilteredDecisionBuilder @@ -1358,7 +2156,7 @@ IntVarFilteredDecisionBuilder::IntVarFilteredDecisionBuilder( delta_(solver->MakeAssignment()), is_in_delta_(vars_.size(), false), empty_(solver->MakeAssignment()), - filters_(filters), + filter_manager_(filters, nullptr), number_of_decisions_(0), number_of_rejects_(0) { assignment_->MutableIntVarContainer()->Resize(vars_.size()); @@ -1411,32 +2209,12 @@ bool IntVarFilteredDecisionBuilder::Commit() { return accept; } -void IntVarFilteredDecisionBuilder::SetValuesFromDomains() { - Assignment::IntContainer* const container = - assignment_->MutableIntVarContainer(); - for (int index = 0; index < vars_.size(); ++index) { - IntVar* const var = vars_[index]; - if (var->Bound()) { - container->AddAtPosition(var, index)->SetValue(var->Min()); - } - } -} - void IntVarFilteredDecisionBuilder::SynchronizeFilters() { - for (LocalSearchFilter* const filter : filters_) { - filter->Synchronize(assignment_, delta_); - } + filter_manager_.Synchronize(assignment_, delta_); } bool IntVarFilteredDecisionBuilder::FilterAccept() { - // All incremental filters must be called. - bool ok = true; - for (LocalSearchFilter* const filter : filters_) { - if (filter->IsIncremental() || ok) { - ok = filter->Accept(delta_, empty_) && ok; - } - } - return ok; + return filter_manager_.Accept(delta_, empty_); } // RoutingFilteredDecisionBuilder @@ -1458,6 +2236,8 @@ bool RoutingFilteredDecisionBuilder::InitializeRoutes() { // Start by adding partial start chains to current assignment. start_chain_ends_.clear(); start_chain_ends_.resize(model()->vehicles(), -1); + end_chain_starts_.clear(); + end_chain_starts_.resize(model()->vehicles(), -1); for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { int64 node = model()->Start(vehicle); while (!model()->IsEnd(node) && Var(node)->Bound()) { @@ -1494,6 +2274,7 @@ bool RoutingFilteredDecisionBuilder::InitializeRoutes() { // Set each route to be the concatenation of the chain at its starts and the // chain at its end, without nodes in between. for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { + end_chain_starts_[vehicle] = starts[model()->End(vehicle)]; int64 node = start_chain_ends_[vehicle]; if (!model()->IsEnd(node)) { int64 next = starts[model()->End(vehicle)]; @@ -1532,15 +2313,62 @@ void RoutingFilteredDecisionBuilder::MakeUnassignedNodesUnperformed() { CheapestInsertionFilteredDecisionBuilder:: CheapestInsertionFilteredDecisionBuilder( RoutingModel* model, - ResultCallback3* evaluator, - ResultCallback1* penalty_evaluator, + std::function evaluator, + std::function penalty_evaluator, const std::vector& filters) : RoutingFilteredDecisionBuilder(model, filters), - evaluator_(evaluator), - penalty_evaluator_(penalty_evaluator) { - evaluator_->CheckIsRepeatable(); - if (penalty_evaluator_ != nullptr) { - penalty_evaluator_->CheckIsRepeatable(); + evaluator_(std::move(evaluator)), + penalty_evaluator_(std::move(penalty_evaluator)) {} + +std::vector< + std::vector> +CheapestInsertionFilteredDecisionBuilder::ComputeStartEndDistanceForVehicles( + const std::vector& vehicles) { + std::vector> start_end_distances_per_node( + model()->Size()); + + for (int node = 0; node < model()->Size(); node++) { + if (Contains(node)) continue; + std::vector& start_end_distances = + start_end_distances_per_node[node]; + + for (const int vehicle : vehicles) { + const int64 start = model()->Start(vehicle); + const int64 end = model()->End(vehicle); + + // We compute the distance of node to the start/end nodes of the route. + const int64 distance = + CapAdd(model()->GetArcCostForVehicle(start, node, vehicle), + model()->GetArcCostForVehicle(node, end, vehicle)); + start_end_distances.push_back({distance, vehicle}); + } + // Sort the distances for the node to all start/ends of available vehicles + // in decreasing order. + std::sort(start_end_distances.begin(), start_end_distances.end(), + [](const StartEndValue& first, const StartEndValue& second) { + return second < first; + }); + } + return start_end_distances_per_node; +} + +template +void CheapestInsertionFilteredDecisionBuilder::InitializePriorityQueue( + std::vector>* start_end_distances_per_node, + Queue* priority_queue) { + const int num_nodes = model()->Size(); + DCHECK_EQ(start_end_distances_per_node->size(), num_nodes); + + for (int node = 0; node < num_nodes; node++) { + std::vector& start_end_distances = + (*start_end_distances_per_node)[node]; + if (start_end_distances.empty()) { + continue; + } + // Put the best StartEndValue for this node in the priority queue. + const StartEndValue& start_end_value = start_end_distances.back(); + priority_queue->push(std::make_pair(start_end_value, node)); + start_end_distances.pop_back(); } } @@ -1561,9 +2389,9 @@ void CheapestInsertionFilteredDecisionBuilder::AppendEvaluatedPositionsAfter( const int64 insert_before = (insert_after == start) ? next_after_start : Value(insert_after); valued_positions->push_back(std::make_pair( - CapAdd(evaluator_->Run(insert_after, node_to_insert, vehicle), - CapSub(evaluator_->Run(node_to_insert, insert_before, vehicle), - evaluator_->Run(insert_after, insert_before, vehicle))), + CapAdd(evaluator_(insert_after, node_to_insert, vehicle), + CapSub(evaluator_(node_to_insert, insert_before, vehicle), + evaluator_(insert_after, insert_before, vehicle))), insert_after)); insert_after = insert_before; } @@ -1572,7 +2400,7 @@ void CheapestInsertionFilteredDecisionBuilder::AppendEvaluatedPositionsAfter( int64 CheapestInsertionFilteredDecisionBuilder::GetUnperformedValue( int64 node_to_insert) const { if (penalty_evaluator_ != nullptr) { - return penalty_evaluator_->Run(node_to_insert); + return penalty_evaluator_(node_to_insert); } return kint64max; } @@ -1611,6 +2439,11 @@ class GlobalCheapestInsertionFilteredDecisionBuilder::PairEntry { if (value_ != other.value_) { return value_ > other.value_; } + if ((vehicle_ == -1) != (other.vehicle_ == -1)) { + // Entries have same value, but only one of the two makes a pair of nodes + // performed. Prefer the insertion (vehicle != -1). + return vehicle_ == -1; + } if (pickup_insert_after_ != other.pickup_insert_after_) { return pickup_insert_after_ > other.pickup_insert_after_; } @@ -1657,6 +2490,11 @@ class GlobalCheapestInsertionFilteredDecisionBuilder::NodeEntry { if (value_ != other.value_) { return value_ > other.value_; } + if ((vehicle_ == -1) != (other.vehicle_ == -1)) { + // Entries have same value, but only one of the two makes a node + // performed. Prefer the insertion (vehicle != -1). + return vehicle_ == -1; + } if (insert_after_ != other.insert_after_) { return insert_after_ > other.insert_after_; } @@ -1683,18 +2521,182 @@ class GlobalCheapestInsertionFilteredDecisionBuilder::NodeEntry { GlobalCheapestInsertionFilteredDecisionBuilder:: GlobalCheapestInsertionFilteredDecisionBuilder( RoutingModel* model, - ResultCallback3* evaluator, - ResultCallback1* penalty_evaluator, - const std::vector& filters) - : CheapestInsertionFilteredDecisionBuilder(model, evaluator, - penalty_evaluator, filters) {} + std::function evaluator, + std::function penalty_evaluator, + const std::vector& filters, bool is_sequential, + double farthest_seeds_ratio, double neighbors_ratio) + : CheapestInsertionFilteredDecisionBuilder( + model, std::move(evaluator), std::move(penalty_evaluator), filters), + is_sequential_(is_sequential), + farthest_seeds_ratio_(std::min(farthest_seeds_ratio, 1.0)), + neighbors_ratio_(neighbors_ratio) { + CHECK_GT(neighbors_ratio, 0); + CHECK_LE(neighbors_ratio, 1); + + const int64 size = model->Size(); + + if (neighbors_ratio == 1) { + for (int64 node = 0; node < size; node++) { + if (!model->GetPickupIndexPairs(node).empty()) { + pickup_nodes_.insert(node); + } + if (!model->GetDeliveryIndexPairs(node).empty()) { + delivery_nodes_.insert(node); + } + } + return; + } + + // TODO(user): Refactor the neighborhood computations in RoutingModel. + node_index_to_single_neighbors_by_cost_class_.resize(size); + node_index_to_pickup_neighbors_by_cost_class_.resize(size); + node_index_to_delivery_neighbors_by_cost_class_.resize(size); + const int num_cost_classes = model->GetCostClassesCount(); + for (int64 node_index = 0; node_index < size; node_index++) { + node_index_to_single_neighbors_by_cost_class_[node_index].resize( + num_cost_classes); + node_index_to_pickup_neighbors_by_cost_class_[node_index].resize( + num_cost_classes); + node_index_to_delivery_neighbors_by_cost_class_[node_index].resize( + num_cost_classes); + } + + const int64 num_neighbors = std::max(1.0, neighbors_ratio * size); + + for (int64 node_index = 0; node_index < size; ++node_index) { + DCHECK(!model->IsEnd(node_index)); + const bool node_is_pickup = !model->GetPickupIndexPairs(node_index).empty(); + const bool node_is_delivery = + !model->GetDeliveryIndexPairs(node_index).empty(); + + // TODO(user): Use the model's IndexNeighborFinder when available. + for (int cost_class = 0; cost_class < num_cost_classes; cost_class++) { + if (!model->HasVehicleWithCostClassIndex( + RoutingCostClassIndex(cost_class))) { + // No vehicle with this cost class, avoid unnecessary computations. + continue; + } + std::vector> costed_after_nodes; + costed_after_nodes.reserve(size); + for (int after_node = 0; after_node < size; ++after_node) { + if (after_node != node_index) { + costed_after_nodes.push_back(std::make_pair( + model->GetArcCostForClass(node_index, after_node, cost_class), + after_node)); + } + } + if (num_neighbors < size) { + std::nth_element(costed_after_nodes.begin(), + costed_after_nodes.begin() + num_neighbors - 1, + costed_after_nodes.end()); + costed_after_nodes.resize(num_neighbors); + } + + for (const auto& costed_neighbor : costed_after_nodes) { + const int64 neighbor = costed_neighbor.second; + AddNeighborForCostClass( + cost_class, node_index, neighbor, + !model->GetPickupIndexPairs(neighbor).empty(), + !model->GetDeliveryIndexPairs(neighbor).empty()); + + // Add reverse neighborhood. + if (!model->IsEnd(neighbor)) { + AddNeighborForCostClass(cost_class, neighbor, node_index, + node_is_pickup, node_is_delivery); + } + } + } + } +} + +void GlobalCheapestInsertionFilteredDecisionBuilder::AddNeighborForCostClass( + int cost_class, int64 node_index, int64 neighbor_index, + bool neighbor_is_pickup, bool neighbor_is_delivery) { + if (neighbor_is_pickup) { + node_index_to_pickup_neighbors_by_cost_class_[node_index][cost_class] + .insert(neighbor_index); + } + if (neighbor_is_delivery) { + node_index_to_delivery_neighbors_by_cost_class_[node_index][cost_class] + .insert(neighbor_index); + } + if (!neighbor_is_pickup && !neighbor_is_delivery) { + node_index_to_single_neighbors_by_cost_class_[node_index][cost_class] + .insert(neighbor_index); + } +} + +bool GlobalCheapestInsertionFilteredDecisionBuilder::IsNeighborForCostClass( + int cost_class, int64 node_index, int64 neighbor_index) const { + if (neighbors_ratio_ == 1) { + return true; + } + if (!model()->GetPickupIndexPairs(neighbor_index).empty()) { + return gtl::ContainsKey( + node_index_to_pickup_neighbors_by_cost_class_[node_index][cost_class], + neighbor_index); + } + if (!model()->GetDeliveryIndexPairs(neighbor_index).empty()) { + return gtl::ContainsKey( + node_index_to_delivery_neighbors_by_cost_class_[node_index][cost_class], + neighbor_index); + } + return gtl::ContainsKey( + node_index_to_single_neighbors_by_cost_class_[node_index][cost_class], + neighbor_index); +} bool GlobalCheapestInsertionFilteredDecisionBuilder::BuildSolution() { if (!InitializeRoutes()) { return false; } + // Insert partially inserted pairs. + std::vector pair_nodes; + for (const RoutingModel::IndexPair& index_pair : + model()->GetPickupAndDeliveryPairs()) { + bool has_inserted_pickup = false; + for (int64 pickup : index_pair.first) { + if (Contains(pickup)) { + has_inserted_pickup = true; + break; + } + } + bool has_inserted_delivery = false; + for (int64 delivery : index_pair.second) { + if (Contains(delivery)) { + has_inserted_delivery = true; + break; + } + } + if (has_inserted_pickup && !has_inserted_delivery) { + for (int64 delivery : index_pair.second) { + pair_nodes.push_back(delivery); + } + } + if (!has_inserted_pickup && has_inserted_delivery) { + for (int64 pickup : index_pair.first) { + pair_nodes.push_back(pickup); + } + } + } + if (!pair_nodes.empty()) { + InsertNodesOnRoutes(pair_nodes, {}); + } + // TODO(user): Adapt the pair insertions to also support seed and + // sequential insertion. InsertPairs(); - InsertNodes(); + std::vector nodes; + for (int node = 0; node < model()->Size(); ++node) { + if (!Contains(node)) { + nodes.push_back(node); + } + } + InsertFarthestNodesAsSeeds(); + if (is_sequential_) { + SequentialInsertNodes(nodes); + } else { + InsertNodesOnRoutes(nodes, {}); + } MakeUnassignedNodesUnperformed(); return Commit(); } @@ -1706,6 +2708,12 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InsertPairs() { InitializePairPositions(&priority_queue, &pickup_to_entries, &delivery_to_entries); while (!priority_queue.IsEmpty()) { + if (StopSearch()) { + for (PairEntry* const entry : *priority_queue.Raw()) { + delete entry; + } + return; + } PairEntry* const entry = priority_queue.Top(); if (Contains(entry->pickup_to_insert()) || Contains(entry->delivery_to_insert())) { @@ -1756,31 +2764,61 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InsertPairs() { } } -void GlobalCheapestInsertionFilteredDecisionBuilder::InsertNodes() { +void GlobalCheapestInsertionFilteredDecisionBuilder::InsertNodesOnRoutes( + const std::vector& nodes, const std::vector& vehicles) { AdjustablePriorityQueue priority_queue; std::vector position_to_node_entries; - InitializePositions(&priority_queue, &position_to_node_entries); + InitializePositions(nodes, &priority_queue, &position_to_node_entries, + vehicles); + const bool all_routes = + vehicles.empty() || vehicles.size() == model()->vehicles(); + while (!priority_queue.IsEmpty()) { NodeEntry* const node_entry = priority_queue.Top(); + if (StopSearch()) { + for (NodeEntry* const entry : *priority_queue.Raw()) { + delete entry; + } + return; + } if (Contains(node_entry->node_to_insert())) { DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries); } else { if (node_entry->vehicle() == -1) { // Pair is unperformed. - SetValue(node_entry->node_to_insert(), node_entry->node_to_insert()); - if (!Commit()) { + if (all_routes) { + // Make node unperformed. + SetValue(node_entry->node_to_insert(), node_entry->node_to_insert()); + if (!Commit()) { + DeleteNodeEntry(node_entry, &priority_queue, + &position_to_node_entries); + } + } else { + DCHECK_EQ(node_entry->value(), 0); + // In this case, all routes are not being considered simultaneously, + // so we do not make nodes unperformed (they might be better performed + // on some other route later). + // Furthermore, since in this case the node penalty is necessarily + // taken into account in the NodeEntry, the values "cost - penalty" + // for all nodes are now positive for all remaining entries in the + // priority queue, so we can empty the priority queue. DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries); + while (!priority_queue.IsEmpty()) { + NodeEntry* const to_delete = priority_queue.Top(); + DeleteNodeEntry(to_delete, &priority_queue, + &position_to_node_entries); + } } } else { InsertBetween(node_entry->node_to_insert(), node_entry->insert_after(), Value(node_entry->insert_after())); if (Commit()) { const int vehicle = node_entry->vehicle(); - UpdatePositions(vehicle, node_entry->node_to_insert(), + UpdatePositions(nodes, vehicle, node_entry->node_to_insert(), + &priority_queue, &position_to_node_entries); + UpdatePositions(nodes, vehicle, node_entry->insert_after(), &priority_queue, &position_to_node_entries); - UpdatePositions(vehicle, node_entry->insert_after(), &priority_queue, - &position_to_node_entries); } else { DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries); @@ -1790,6 +2828,132 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InsertNodes() { } } +void GlobalCheapestInsertionFilteredDecisionBuilder::SequentialInsertNodes( + const std::vector& nodes) { + std::vector is_vehicle_used; + std::vector used_vehicles; + std::vector unused_vehicles; + + DetectUsedVehicles(&is_vehicle_used, &used_vehicles, &unused_vehicles); + if (!used_vehicles.empty()) { + InsertNodesOnRoutes(nodes, used_vehicles); + } + + std::vector> start_end_distances_per_node = + ComputeStartEndDistanceForVehicles(unused_vehicles); + std::priority_queue, std::greater> + first_node_queue; + InitializePriorityQueue(&start_end_distances_per_node, &first_node_queue); + + int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue, + &is_vehicle_used); + + while (vehicle >= 0) { + InsertNodesOnRoutes(nodes, {vehicle}); + vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue, + &is_vehicle_used); + } +} + +void GlobalCheapestInsertionFilteredDecisionBuilder::DetectUsedVehicles( + std::vector* is_vehicle_used, std::vector* used_vehicles, + std::vector* unused_vehicles) { + is_vehicle_used->clear(); + is_vehicle_used->resize(model()->vehicles()); + + used_vehicles->clear(); + used_vehicles->reserve(model()->vehicles()); + + unused_vehicles->clear(); + unused_vehicles->reserve(model()->vehicles()); + + for (int vehicle = 0; vehicle < model()->vehicles(); vehicle++) { + if (Value(model()->Start(vehicle)) != model()->End(vehicle)) { + (*is_vehicle_used)[vehicle] = true; + used_vehicles->push_back(vehicle); + } else { + (*is_vehicle_used)[vehicle] = false; + unused_vehicles->push_back(vehicle); + } + } +} + +void GlobalCheapestInsertionFilteredDecisionBuilder:: + InsertFarthestNodesAsSeeds() { + if (farthest_seeds_ratio_ <= 0) return; + // Insert at least 1 farthest Seed if the parameter is positive. + const int num_seeds = + static_cast(std::ceil(farthest_seeds_ratio_ * model()->vehicles())); + + std::vector is_vehicle_used; + std::vector used_vehicles; + std::vector unused_vehicles; + DetectUsedVehicles(&is_vehicle_used, &used_vehicles, &unused_vehicles); + std::vector> start_end_distances_per_node = + ComputeStartEndDistanceForVehicles(unused_vehicles); + + // Priority queue where the Seeds with a larger distance are given higher + // priority. + std::priority_queue farthest_node_queue; + InitializePriorityQueue(&start_end_distances_per_node, &farthest_node_queue); + + int inserted_seeds = 0; + while (!farthest_node_queue.empty() && inserted_seeds < num_seeds) { + InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue, + &is_vehicle_used); + inserted_seeds++; + } +} + +template +int GlobalCheapestInsertionFilteredDecisionBuilder::InsertSeedNode( + std::vector>* start_end_distances_per_node, + Queue* priority_queue, std::vector* is_vehicle_used) { + while (!priority_queue->empty()) { + if (StopSearch()) break; + const Seed& seed = priority_queue->top(); + + const int seed_node = seed.second; + const int seed_vehicle = seed.first.vehicle; + + std::vector& other_start_end_values = + (*start_end_distances_per_node)[seed_node]; + + if (Contains(seed_node)) { + // The node is already inserted, it is therefore no longer considered as + // a potential seed. + priority_queue->pop(); + other_start_end_values.clear(); + continue; + } + if (!(*is_vehicle_used)[seed_vehicle]) { + // Try to insert this seed_node on this vehicle's route. + const int64 start = model()->Start(seed_vehicle); + const int64 end = model()->End(seed_vehicle); + DCHECK_EQ(Value(start), end); + InsertBetween(seed_node, start, end); + if (Commit()) { + priority_queue->pop(); + (*is_vehicle_used)[seed_vehicle] = true; + other_start_end_values.clear(); + return seed_vehicle; + } + } + // Either the vehicle is already used, or the Commit() wasn't successful. + // In both cases, we remove this Seed from the priority queue, and insert + // the next StartEndValue from start_end_distances_per_node[seed_node] + // in the priority queue. + priority_queue->pop(); + if (!other_start_end_values.empty()) { + const StartEndValue& next_seed_value = other_start_end_values.back(); + priority_queue->push(std::make_pair(next_seed_value, seed_node)); + other_start_end_values.pop_back(); + } + } + // No seed node was inserted. + return -1; +} + void GlobalCheapestInsertionFilteredDecisionBuilder::InitializePairPositions( AdjustablePriorityQueue< GlobalCheapestInsertionFilteredDecisionBuilder::PairEntry>* @@ -1803,67 +2967,77 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InitializePairPositions( pickup_to_entries->resize(model()->Size()); delivery_to_entries->clear(); delivery_to_entries->resize(model()->Size()); - for (const RoutingModel::NodePair& node_pair : + for (const RoutingModel::IndexPair& index_pair : model()->GetPickupAndDeliveryPairs()) { - const int64 pickup = node_pair.first[0]; - const int64 delivery = node_pair.second[0]; - if (Contains(pickup) || Contains(delivery)) { - continue; - } - // Add insertion entry making pair unperformed. - const int64 pickup_penalty = GetUnperformedValue(pickup); - const int64 delivery_penalty = GetUnperformedValue(delivery); - int64 penalty = - FLAGS_routing_shift_insertion_cost_by_penalty ? kint64max : 0; - if (pickup_penalty != kint64max && delivery_penalty != kint64max) { - PairEntry* const entry = new PairEntry(pickup, -1, delivery, -1, -1); - if (FLAGS_routing_shift_insertion_cost_by_penalty) { - entry->set_value(0); - penalty = CapAdd(pickup_penalty, delivery_penalty); - } else { - entry->set_value(CapAdd(pickup_penalty, delivery_penalty)); - penalty = 0; - } - priority_queue->Add(entry); - } - // Add all other insertion entries with pair performed. - std::vector, std::pair>> - valued_positions; - for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { - std::vector valued_pickup_positions; - const int64 start = model()->Start(vehicle); - AppendEvaluatedPositionsAfter(pickup, start, Value(start), vehicle, - &valued_pickup_positions); - for (const ValuedPosition& valued_pickup_position : - valued_pickup_positions) { - const int64 pickup_position = valued_pickup_position.second; - CHECK(!model()->IsEnd(pickup_position)); - std::vector valued_delivery_positions; - AppendEvaluatedPositionsAfter(delivery, pickup, Value(pickup_position), - vehicle, &valued_delivery_positions); - for (const ValuedPosition& valued_delivery_position : - valued_delivery_positions) { - valued_positions.push_back(std::make_pair( - std::make_pair(CapAdd(valued_pickup_position.first, - valued_delivery_position.first), - vehicle), - std::make_pair(pickup_position, - valued_delivery_position.second))); + for (int64 pickup : index_pair.first) { + for (int64 delivery : index_pair.second) { + if (Contains(pickup) || Contains(delivery)) { + continue; + } + int64 penalty = + FLAGS_routing_shift_insertion_cost_by_penalty ? kint64max : 0; + // Add insertion entry making pair unperformed. When the pair is part + // of a disjunction we do not try to make any of its pairs unperformed + // as it requires having an entry with all pairs being unperformed. + // TODO(user): Adapt the code to make pair disjunctions unperformed. + if (index_pair.first.size() == 1 && index_pair.second.size() == 1) { + const int64 pickup_penalty = GetUnperformedValue(pickup); + const int64 delivery_penalty = GetUnperformedValue(delivery); + if (pickup_penalty != kint64max && delivery_penalty != kint64max) { + PairEntry* const entry = + new PairEntry(pickup, -1, delivery, -1, -1); + if (FLAGS_routing_shift_insertion_cost_by_penalty) { + entry->set_value(0); + penalty = CapAdd(pickup_penalty, delivery_penalty); + } else { + entry->set_value(CapAdd(pickup_penalty, delivery_penalty)); + penalty = 0; + } + priority_queue->Add(entry); + } + } + // Add all other insertion entries with pair performed. + std::vector, std::pair>> + valued_positions; + for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { + std::vector valued_pickup_positions; + const int64 start = model()->Start(vehicle); + AppendEvaluatedPositionsAfter(pickup, start, Value(start), vehicle, + &valued_pickup_positions); + for (const ValuedPosition& valued_pickup_position : + valued_pickup_positions) { + const int64 pickup_position = valued_pickup_position.second; + CHECK(!model()->IsEnd(pickup_position)); + std::vector valued_delivery_positions; + AppendEvaluatedPositionsAfter(delivery, pickup, + Value(pickup_position), vehicle, + &valued_delivery_positions); + for (const ValuedPosition& valued_delivery_position : + valued_delivery_positions) { + valued_positions.push_back(std::make_pair( + std::make_pair(CapAdd(valued_pickup_position.first, + valued_delivery_position.first), + vehicle), + std::make_pair(pickup_position, + valued_delivery_position.second))); + } + } + } + for (const std::pair, std::pair>& + valued_position : valued_positions) { + PairEntry* const entry = new PairEntry( + pickup, valued_position.second.first, delivery, + valued_position.second.second, valued_position.first.second); + entry->set_value(CapSub(valued_position.first.first, penalty)); + pickup_to_entries->at(valued_position.second.first).insert(entry); + if (valued_position.second.first != valued_position.second.second) { + delivery_to_entries->at(valued_position.second.second) + .insert(entry); + } + priority_queue->Add(entry); } } } - for (const std::pair, std::pair>& - valued_position : valued_positions) { - PairEntry* const entry = new PairEntry( - pickup, valued_position.second.first, delivery, - valued_position.second.second, valued_position.first.second); - entry->set_value(CapSub(valued_position.first.first, penalty)); - pickup_to_entries->at(valued_position.second.first).insert(entry); - if (valued_position.second.first != valued_position.second.second) { - delivery_to_entries->at(valued_position.second.second).insert(entry); - } - priority_queue->Add(entry); - } } } @@ -1880,7 +3054,7 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdatePickupPositions( // the entries which are being kept and must be updated. using Pair = std::pair; using Insertion = std::pair; - std::unordered_set> existing_insertions; + absl::flat_hash_set existing_insertions; std::vector to_remove; for (PairEntry* const pair_entry : pickup_to_entries->at(pickup_insert_after)) { @@ -1901,27 +3075,36 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdatePickupPositions( } // Create new entries for which the pickup is to be inserted after // pickup_insert_after. + const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value(); const int64 pickup_insert_before = Value(pickup_insert_after); - for (const RoutingModel::NodePair& node_pair : - model()->GetPickupAndDeliveryPairs()) { - const int64 pickup = node_pair.first[0]; - const int64 delivery = node_pair.second[0]; - if (!Contains(pickup) && !Contains(delivery)) { - int64 delivery_insert_after = pickup; - while (!model()->IsEnd(delivery_insert_after)) { - const std::pair insertion = {{pickup, delivery}, - delivery_insert_after}; - if (!gtl::ContainsKey(existing_insertions, insertion)) { - PairEntry* const entry = - new PairEntry(pickup, pickup_insert_after, delivery, - delivery_insert_after, vehicle); - pickup_to_entries->at(pickup_insert_after).insert(entry); - delivery_to_entries->at(delivery_insert_after).insert(entry); + for (int64 pickup : + GetPickupNeighborsOfNodeForCostClass(cost_class, pickup_insert_after)) { + if (Contains(pickup)) { + continue; + } + for (const auto& pickup_index_pair : model()->GetPickupIndexPairs(pickup)) { + const RoutingModel::IndexPair& index_pair = + model()->GetPickupAndDeliveryPairs()[pickup_index_pair.first]; + for (int64 delivery : index_pair.second) { + if (Contains(delivery)) { + continue; } - if (delivery_insert_after == pickup) { - delivery_insert_after = pickup_insert_before; - } else { - delivery_insert_after = Value(delivery_insert_after); + int64 delivery_insert_after = pickup; + while (!model()->IsEnd(delivery_insert_after)) { + const std::pair insertion = {{pickup, delivery}, + delivery_insert_after}; + if (!gtl::ContainsKey(existing_insertions, insertion)) { + PairEntry* const entry = + new PairEntry(pickup, pickup_insert_after, delivery, + delivery_insert_after, vehicle); + pickup_to_entries->at(pickup_insert_after).insert(entry); + delivery_to_entries->at(delivery_insert_after).insert(entry); + } + if (delivery_insert_after == pickup) { + delivery_insert_after = pickup_insert_before; + } else { + delivery_insert_after = Value(delivery_insert_after); + } } } } @@ -1930,15 +3113,15 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdatePickupPositions( // accordingly if the entry already existed or add it to the queue if it's // new. const int64 old_pickup_value = - evaluator_->Run(pickup_insert_after, pickup_insert_before, vehicle); + evaluator_(pickup_insert_after, pickup_insert_before, vehicle); for (PairEntry* const pair_entry : pickup_to_entries->at(pickup_insert_after)) { DCHECK_EQ(pickup_insert_after, pair_entry->pickup_insert_after()); const int64 pickup_value = - CapSub(CapAdd(evaluator_->Run(pickup_insert_after, - pair_entry->pickup_to_insert(), vehicle), - evaluator_->Run(pair_entry->pickup_to_insert(), - pickup_insert_before, vehicle)), + CapSub(CapAdd(evaluator_(pickup_insert_after, + pair_entry->pickup_to_insert(), vehicle), + evaluator_(pair_entry->pickup_to_insert(), + pickup_insert_before, vehicle)), old_pickup_value); const int64 delivery_insert_after = pair_entry->delivery_insert_after(); const int64 delivery_insert_before = @@ -1946,12 +3129,11 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdatePickupPositions( ? pickup_insert_before : Value(delivery_insert_after); const int64 delivery_value = CapSub( - CapAdd(evaluator_->Run(delivery_insert_after, - pair_entry->delivery_to_insert(), vehicle), - evaluator_->Run(pair_entry->delivery_to_insert(), - delivery_insert_before, vehicle)), - evaluator_->Run(delivery_insert_after, delivery_insert_before, - vehicle)); + CapAdd(evaluator_(delivery_insert_after, + pair_entry->delivery_to_insert(), vehicle), + evaluator_(pair_entry->delivery_to_insert(), + delivery_insert_before, vehicle)), + evaluator_(delivery_insert_after, delivery_insert_before, vehicle)); const int64 penalty = FLAGS_routing_shift_insertion_cost_by_penalty ? CapAdd(GetUnperformedValue(pair_entry->pickup_to_insert()), @@ -1980,7 +3162,7 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdateDeliveryPositions( // the entries which are being kept and must be updated. using Pair = std::pair; using Insertion = std::pair; - std::unordered_set> existing_insertions; + absl::flat_hash_set existing_insertions; std::vector to_remove; for (PairEntry* const pair_entry : delivery_to_entries->at(delivery_insert_after)) { @@ -2001,24 +3183,34 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdateDeliveryPositions( } // Create new entries for which the delivery is to be inserted after // delivery_insert_after. + const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value(); const int64 delivery_insert_before = Value(delivery_insert_after); - for (const RoutingModel::NodePair& node_pair : - model()->GetPickupAndDeliveryPairs()) { - const int64 pickup = node_pair.first[0]; - const int64 delivery = node_pair.second[0]; - if (!Contains(pickup) && !Contains(delivery)) { - int64 pickup_insert_after = model()->Start(vehicle); - while (pickup_insert_after != delivery_insert_after) { - std::pair insertion = {{pickup, delivery}, - pickup_insert_after}; - if (!gtl::ContainsKey(existing_insertions, insertion)) { - PairEntry* const entry = - new PairEntry(pickup, pickup_insert_after, delivery, - delivery_insert_after, vehicle); - pickup_to_entries->at(pickup_insert_after).insert(entry); - delivery_to_entries->at(delivery_insert_after).insert(entry); + for (int64 delivery : GetDeliveryNeighborsOfNodeForCostClass( + cost_class, delivery_insert_after)) { + if (Contains(delivery)) { + continue; + } + for (const auto& delivery_index_pair : + model()->GetDeliveryIndexPairs(delivery)) { + const RoutingModel::IndexPair& index_pair = + model()->GetPickupAndDeliveryPairs()[delivery_index_pair.first]; + for (int64 pickup : index_pair.first) { + if (Contains(pickup)) { + continue; + } + int64 pickup_insert_after = model()->Start(vehicle); + while (pickup_insert_after != delivery_insert_after) { + std::pair insertion = {{pickup, delivery}, + pickup_insert_after}; + if (!gtl::ContainsKey(existing_insertions, insertion)) { + PairEntry* const entry = + new PairEntry(pickup, pickup_insert_after, delivery, + delivery_insert_after, vehicle); + pickup_to_entries->at(pickup_insert_after).insert(entry); + delivery_to_entries->at(delivery_insert_after).insert(entry); + } + pickup_insert_after = Value(pickup_insert_after); } - pickup_insert_after = Value(pickup_insert_after); } } } @@ -2026,24 +3218,23 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdateDeliveryPositions( // accordingly if the entry already existed or add it to the queue if it's // new. const int64 old_delivery_value = - evaluator_->Run(delivery_insert_after, delivery_insert_before, vehicle); + evaluator_(delivery_insert_after, delivery_insert_before, vehicle); for (PairEntry* const pair_entry : delivery_to_entries->at(delivery_insert_after)) { DCHECK_EQ(delivery_insert_after, pair_entry->delivery_insert_after()); const int64 pickup_value = CapSub( - CapAdd( - evaluator_->Run(pair_entry->pickup_insert_after(), - pair_entry->pickup_to_insert(), vehicle), - evaluator_->Run(pair_entry->pickup_to_insert(), - Value(pair_entry->pickup_insert_after()), vehicle)), - evaluator_->Run(pair_entry->pickup_insert_after(), - Value(pair_entry->pickup_insert_after()), vehicle)); - const int64 delivery_value = CapSub( - CapAdd(evaluator_->Run(delivery_insert_after, - pair_entry->delivery_to_insert(), vehicle), - evaluator_->Run(pair_entry->delivery_to_insert(), - delivery_insert_before, vehicle)), - old_delivery_value); + CapAdd(evaluator_(pair_entry->pickup_insert_after(), + pair_entry->pickup_to_insert(), vehicle), + evaluator_(pair_entry->pickup_to_insert(), + Value(pair_entry->pickup_insert_after()), vehicle)), + evaluator_(pair_entry->pickup_insert_after(), + Value(pair_entry->pickup_insert_after()), vehicle)); + const int64 delivery_value = + CapSub(CapAdd(evaluator_(delivery_insert_after, + pair_entry->delivery_to_insert(), vehicle), + evaluator_(pair_entry->delivery_to_insert(), + delivery_insert_before, vehicle)), + old_delivery_value); const int64 penalty = FLAGS_routing_shift_insertion_cost_by_penalty ? CapAdd(GetUnperformedValue(pair_entry->pickup_to_insert()), @@ -2077,15 +3268,21 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::DeletePairEntry( } void GlobalCheapestInsertionFilteredDecisionBuilder::InitializePositions( + const std::vector& nodes, AdjustablePriorityQueue< GlobalCheapestInsertionFilteredDecisionBuilder::NodeEntry>* priority_queue, std::vector* - position_to_node_entries) { + position_to_node_entries, + const std::vector& vehicles) { priority_queue->Clear(); position_to_node_entries->clear(); position_to_node_entries->resize(model()->Size()); - for (int node = 0; node < model()->Size(); ++node) { + + const int num_vehicles = + vehicles.empty() ? model()->vehicles() : vehicles.size(); + + for (int node : nodes) { if (Contains(node)) { continue; } @@ -2095,7 +3292,10 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InitializePositions( // Add insertion entry making node unperformed. if (node_penalty != kint64max) { NodeEntry* const node_entry = new NodeEntry(node, -1, -1); - if (FLAGS_routing_shift_insertion_cost_by_penalty) { + if (FLAGS_routing_shift_insertion_cost_by_penalty || + num_vehicles < model()->vehicles()) { + // In the case where we're not considering all routes simultaneously, + // always shift insertion costs by penalty. node_entry->set_value(0); penalty = node_penalty; } else { @@ -2105,7 +3305,9 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InitializePositions( priority_queue->Add(node_entry); } // Add all insertion entries making node performed. - for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { + for (int v = 0; v < num_vehicles; v++) { + const int vehicle = vehicles.empty() ? v : vehicles[v]; + std::vector valued_positions; const int64 start = model()->Start(vehicle); AppendEvaluatedPositionsAfter(node, start, Value(start), vehicle, @@ -2122,7 +3324,7 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InitializePositions( } void GlobalCheapestInsertionFilteredDecisionBuilder::UpdatePositions( - int vehicle, int64 insert_after, + const std::vector& nodes, int vehicle, int64 insert_after, AdjustablePriorityQueue< GlobalCheapestInsertionFilteredDecisionBuilder::NodeEntry>* priority_queue, @@ -2133,9 +3335,10 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdatePositions( bool update = true; if (node_entries->at(insert_after).empty()) { update = false; - for (int node_to_insert = 0; node_to_insert < model()->Size(); - ++node_to_insert) { - if (!Contains(node_to_insert)) { + const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value(); + for (int node_to_insert : nodes) { + if (!Contains(node_to_insert) && + IsNeighborForCostClass(cost_class, insert_after, node_to_insert)) { NodeEntry* const node_entry = new NodeEntry(node_to_insert, insert_after, vehicle); node_entries->at(insert_after).insert(node_entry); @@ -2160,15 +3363,14 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdatePositions( // new. DCHECK_GE(model()->Size(), node_entries->at(insert_after).size()); const int64 insert_before = Value(insert_after); - const int64 old_value = evaluator_->Run(insert_after, insert_before, vehicle); + const int64 old_value = evaluator_(insert_after, insert_before, vehicle); for (NodeEntry* const node_entry : node_entries->at(insert_after)) { DCHECK_EQ(node_entry->insert_after(), insert_after); - const int64 value = - CapSub(CapAdd(evaluator_->Run(insert_after, - node_entry->node_to_insert(), vehicle), - evaluator_->Run(node_entry->node_to_insert(), - insert_before, vehicle)), - old_value); + const int64 value = CapSub( + CapAdd( + evaluator_(insert_after, node_entry->node_to_insert(), vehicle), + evaluator_(node_entry->node_to_insert(), insert_before, vehicle)), + old_value); const int64 penalty = FLAGS_routing_shift_insertion_cost_by_penalty ? GetUnperformedValue(node_entry->node_to_insert()) @@ -2200,10 +3402,10 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::DeleteNodeEntry( LocalCheapestInsertionFilteredDecisionBuilder:: LocalCheapestInsertionFilteredDecisionBuilder( RoutingModel* model, - ResultCallback3* evaluator, + std::function evaluator, const std::vector& filters) - : CheapestInsertionFilteredDecisionBuilder(model, evaluator, nullptr, - filters) {} + : CheapestInsertionFilteredDecisionBuilder(model, std::move(evaluator), + nullptr, filters) {} bool LocalCheapestInsertionFilteredDecisionBuilder::BuildSolution() { if (!InitializeRoutes()) { @@ -2217,50 +3419,65 @@ bool LocalCheapestInsertionFilteredDecisionBuilder::BuildSolution() { // current node has one). std::vector delivery_insertion_positions; // Iterating on pickup and delivery pairs - const RoutingModel::NodePairs& node_pairs = + const RoutingModel::IndexPairs& index_pairs = model()->GetPickupAndDeliveryPairs(); - for (const auto& node_pair : node_pairs) { - const int64 pickup = node_pair.first[0]; - const int64 delivery = node_pair.second[0]; - // If either is already in the solution, let it be inserted in the standard - // node insertion loop. - if (Contains(pickup) || Contains(delivery)) { - continue; - } - visited[pickup] = true; - visited[delivery] = true; - ComputeEvaluatorSortedPositions(pickup, &insertion_positions); - for (const int64 pickup_insertion : insertion_positions) { - const int pickup_insertion_next = Value(pickup_insertion); - ComputeEvaluatorSortedPositionsOnRouteAfter( - delivery, pickup, pickup_insertion_next, - &delivery_insertion_positions); - bool found = false; - for (const int64 delivery_insertion : delivery_insertion_positions) { - InsertBetween(pickup, pickup_insertion, pickup_insertion_next); - const int64 delivery_insertion_next = - (delivery_insertion == pickup_insertion) - ? pickup - : (delivery_insertion == pickup) ? pickup_insertion_next - : Value(delivery_insertion); - InsertBetween(delivery, delivery_insertion, delivery_insertion_next); - if (Commit()) { - found = true; - break; + for (const auto& index_pair : index_pairs) { + for (int64 pickup : index_pair.first) { + for (int64 delivery : index_pair.second) { + // If either is already in the solution, let it be inserted in the + // standard node insertion loop. + if (Contains(pickup) || Contains(delivery)) { + continue; + } + if (StopSearch()) return false; + visited[pickup] = true; + visited[delivery] = true; + ComputeEvaluatorSortedPositions(pickup, &insertion_positions); + for (const int64 pickup_insertion : insertion_positions) { + const int pickup_insertion_next = Value(pickup_insertion); + ComputeEvaluatorSortedPositionsOnRouteAfter( + delivery, pickup, pickup_insertion_next, + &delivery_insertion_positions); + bool found = false; + for (const int64 delivery_insertion : delivery_insertion_positions) { + InsertBetween(pickup, pickup_insertion, pickup_insertion_next); + const int64 delivery_insertion_next = + (delivery_insertion == pickup_insertion) + ? pickup + : (delivery_insertion == pickup) + ? pickup_insertion_next + : Value(delivery_insertion); + InsertBetween(delivery, delivery_insertion, + delivery_insertion_next); + if (Commit()) { + found = true; + break; + } + } + if (found) { + break; + } } - } - if (found) { - break; } } } - // Iterating on remaining nodes. - for (int node = 0; node < model()->Size(); ++node) { - if (Contains(node) || visited[node]) { - continue; - } + + std::vector all_vehicles(model()->vehicles()); + std::iota(std::begin(all_vehicles), std::end(all_vehicles), 0); + + std::vector> start_end_distances_per_node = + ComputeStartEndDistanceForVehicles(all_vehicles); + + std::priority_queue node_queue; + InitializePriorityQueue(&start_end_distances_per_node, &node_queue); + + while (!node_queue.empty()) { + const int node = node_queue.top().second; + node_queue.pop(); + if (Contains(node) || visited[node]) continue; ComputeEvaluatorSortedPositions(node, &insertion_positions); for (const int64 insertion : insertion_positions) { + if (StopSearch()) return false; InsertBetween(node, insertion, Value(insertion)); if (Commit()) { break; @@ -2318,10 +3535,16 @@ bool CheapestAdditionFilteredDecisionBuilder::BuildSolution() { return false; } const int kUnassigned = -1; - const RoutingModel::NodePairs& pairs = model()->GetPickupAndDeliveryPairs(); - std::vector deliveries(Size(), kUnassigned); - for (const RoutingModel::NodePair& pair : pairs) { - deliveries[pair.first[0]] = pair.second[0]; + const RoutingModel::IndexPairs& pairs = model()->GetPickupAndDeliveryPairs(); + std::vector> deliveries(Size()); + std::vector> pickups(Size()); + for (const RoutingModel::IndexPair& pair : pairs) { + for (int first : pair.first) { + for (int second : pair.second) { + deliveries[first].push_back(second); + pickups[second].push_back(first); + } + } } // To mimic the behavior of PathSelector (cf. search.cc), iterating on // routes with partial route at their start first then on routes with largest @@ -2333,7 +3556,6 @@ bool CheapestAdditionFilteredDecisionBuilder::BuildSolution() { std::sort(sorted_vehicles.begin(), sorted_vehicles.end(), PartialRoutesAndLargeVehicleIndicesFirst(*this)); // Neighbors of the node currently being extended. - std::vector neighbors; for (const int vehicle : sorted_vehicles) { int64 last_node = GetStartChainEnd(vehicle); bool extend_route = true; @@ -2345,42 +3567,93 @@ bool CheapestAdditionFilteredDecisionBuilder::BuildSolution() { extend_route = false; bool found = true; int64 index = last_node; - int64 end = model()->End(vehicle); + int64 end = GetEndChainStart(vehicle); // Extend the route until either the end node of the vehicle is reached // or no node or node pair can be added. Deliveries in pickup and - // delivery pairs are added at the end of the route in reverse order - // of the pickups. + // delivery pairs are added at the same time as pickups, at the end of the + // route, in reverse order of the pickups. Deliveries are never added + // alone. while (found && !model()->IsEnd(index)) { found = false; - SortPossibleNexts(index, &neighbors); - for (const int64 next : neighbors) { + std::vector neighbors; + if (index < model()->Nexts().size()) { + std::unique_ptr it( + model()->Nexts()[index]->MakeDomainIterator(false)); + auto next_values = InitAndGetValues(it.get()); + neighbors = GetPossibleNextsFromIterator(index, next_values.begin(), + next_values.end()); + } + for (int i = 0; !found && i < neighbors.size(); ++i) { + int64 next = -1; + switch (i) { + case 0: + next = FindTopSuccessor(index, neighbors); + break; + case 1: + SortSuccessors(index, &neighbors); + ABSL_FALLTHROUGH_INTENDED; + default: + next = neighbors[i]; + } if (model()->IsEnd(next) && next != end) { continue; } - // Insert "next" after "index", and before "end" if it is not the end - // already. - SetValue(index, next); - const int delivery = next < Size() ? deliveries[next] : kUnassigned; - if (!model()->IsEnd(next)) { - SetValue(next, end); - MakeDisjunctionNodesUnperformed(next); - if (delivery != kUnassigned) { - SetValue(next, delivery); - SetValue(delivery, end); - MakeDisjunctionNodesUnperformed(delivery); + // Only add a delivery if one of its pickups has been added already. + if (!model()->IsEnd(next) && !pickups[next].empty()) { + bool contains_pickups = false; + for (int64 pickup : pickups[next]) { + if (Contains(pickup)) { + contains_pickups = true; + break; + } } + if (!contains_pickups) { + continue; + } + } + std::vector next_deliveries; + if (next < deliveries.size()) { + next_deliveries = GetPossibleNextsFromIterator( + next, deliveries[next].begin(), deliveries[next].end()); } - if (Commit()) { - index = next; - found = true; - if (delivery != kUnassigned) { - if (model()->IsEnd(end) && last_node != delivery) { - last_node = delivery; - extend_route = true; + if (next_deliveries.empty()) next_deliveries = {kUnassigned}; + for (int j = 0; !found && j < next_deliveries.size(); ++j) { + if (StopSearch()) return false; + int delivery = -1; + switch (j) { + case 0: + delivery = FindTopSuccessor(next, next_deliveries); + break; + case 1: + SortSuccessors(next, &next_deliveries); + ABSL_FALLTHROUGH_INTENDED; + default: + delivery = next_deliveries[j]; + } + // Insert "next" after "index", and before "end" if it is not the + // end already. + SetValue(index, next); + if (!model()->IsEnd(next)) { + SetValue(next, end); + MakeDisjunctionNodesUnperformed(next); + if (delivery != kUnassigned) { + SetValue(next, delivery); + SetValue(delivery, end); + MakeDisjunctionNodesUnperformed(delivery); } - end = delivery; } - break; + if (Commit()) { + index = next; + found = true; + if (delivery != kUnassigned) { + if (model()->IsEnd(end) && last_node != delivery) { + last_node = delivery; + extend_route = true; + } + end = delivery; + } + break; + } } } } @@ -2408,36 +3681,40 @@ bool CheapestAdditionFilteredDecisionBuilder:: EvaluatorCheapestAdditionFilteredDecisionBuilder:: EvaluatorCheapestAdditionFilteredDecisionBuilder( - RoutingModel* model, ResultCallback2* evaluator, + RoutingModel* model, std::function evaluator, const std::vector& filters) : CheapestAdditionFilteredDecisionBuilder(model, filters), - evaluator_(evaluator) { - evaluator_->CheckIsRepeatable(); + evaluator_(std::move(evaluator)) {} + +int64 EvaluatorCheapestAdditionFilteredDecisionBuilder::FindTopSuccessor( + int64 node, const std::vector& successors) { + int64 best_evaluation = kint64max; + int64 best_successor = -1; + for (int64 successor : successors) { + const int64 evaluation = + (successor >= 0) ? evaluator_(node, successor) : kint64max; + if (evaluation < best_evaluation || + (evaluation == best_evaluation && successor > best_successor)) { + best_evaluation = evaluation; + best_successor = successor; + } + } + return best_successor; } -void EvaluatorCheapestAdditionFilteredDecisionBuilder::SortPossibleNexts( - int64 from, std::vector* sorted_nexts) { - CHECK(sorted_nexts != nullptr); - const std::vector& nexts = model()->Nexts(); - sorted_nexts->clear(); - const int size = model()->Size(); - if (from < size) { - std::vector> valued_neighbors; - IntVar* const next = nexts[from]; - std::unique_ptr it(next->MakeDomainIterator(false)); - for (const int64 value : InitAndGetValues(it.get())) { - if (value != from && (value >= size || !Contains(value))) { - // Tie-breaking on largest node index to mimic the behavior of - // CheapestValueSelector (search.cc). - valued_neighbors.push_back( - std::make_pair(evaluator_->Run(from, value), -value)); - } - } - std::sort(valued_neighbors.begin(), valued_neighbors.end()); - sorted_nexts->reserve(valued_neighbors.size()); - for (const std::pair neighbor : valued_neighbors) { - sorted_nexts->push_back(-neighbor.second); - } +void EvaluatorCheapestAdditionFilteredDecisionBuilder::SortSuccessors( + int64 node, std::vector* successors) { + std::vector> values; + values.reserve(successors->size()); + for (int64 successor : *successors) { + // Tie-breaking on largest node index to mimic the behavior of + // CheapestValueSelector (search.cc). + values.push_back({evaluator_(node, successor), -successor}); + } + std::sort(values.begin(), values.end()); + successors->clear(); + for (auto value : values) { + successors->push_back(-value.second); } } @@ -2450,38 +3727,468 @@ ComparatorCheapestAdditionFilteredDecisionBuilder:: : CheapestAdditionFilteredDecisionBuilder(model, filters), comparator_(std::move(comparator)) {} -void ComparatorCheapestAdditionFilteredDecisionBuilder::SortPossibleNexts( - int64 from, std::vector* sorted_nexts) { - CHECK(sorted_nexts != nullptr); - const std::vector& nexts = model()->Nexts(); - sorted_nexts->clear(); - const int size = model()->Size(); - if (from < size) { - IntVar* const next = nexts[from]; - std::unique_ptr it(next->MakeDomainIterator(false)); - for (const int64 value : InitAndGetValues(it.get())) { - if (value != from && (value >= size || !Contains(value))) { - sorted_nexts->push_back(value); +int64 ComparatorCheapestAdditionFilteredDecisionBuilder::FindTopSuccessor( + int64 node, const std::vector& successors) { + return *std::min_element(successors.begin(), successors.end(), + [this, node](int successor1, int successor2) { + return comparator_(node, successor1, successor2); + }); +} + +void ComparatorCheapestAdditionFilteredDecisionBuilder::SortSuccessors( + int64 node, std::vector* successors) { + std::sort(successors->begin(), successors->end(), + [this, node](int successor1, int successor2) { + return comparator_(node, successor1, successor2); + }); +} + +// Class storing and allowing access to the savings according to the number of +// vehicle types. +// The savings are stored and sorted in sorted_savings_per_vehicle_type_. +// Furthermore, when there is more than one vehicle type, the savings for a same +// before-->after arc are sorted in costs_and_savings_per_arc_[arc] by +// increasing cost(s-->before-->after-->e), where s and e are the start and end +// of the route, in order to make sure the arc is served by the route with the +// closest depot (start/end) possible. +// When there is only one vehicle "type" (i.e. all vehicles have the same +// start/end and cost class), each arc has a single saving value associated to +// it, so we ignore this last step to avoid unnecessary computations, and only +// work with sorted_savings_per_vehicle_type_[0]. +// In case of multiple vehicle types, the best savings for each arc, i.e. the +// savings corresponding to the closest vehicle type, are inserted and sorted in +// sorted_savings_. +// +// This class also handles skipped Savings: +// The vectors skipped_savings_starting/ending_at_ contain all the Savings that +// weren't added to the model, which we want to consider for later: +// 1) When a Saving before-->after with both nodes uncontained cannot be used to +// start a new route (no more available vehicles or could not commit on any +// of those available). +// 2) When only one of the nodes of the Saving is contained but on a different +// vehicle type. +// In these cases, the Update() method is called with update_best_saving = true, +// which in turn calls SkipSavingForArc() (within +// UpdateNextAndSkippedSavingsForArcWithType()) to mark the Saving for this arc +// (with the correct type in the second case) as "skipped", by storing it in +// skipped_savings_starting_at_[before] and skipped_savings_ending_at_[after]. +// +// UpdateNextAndSkippedSavingsForArcWithType() also updates the next_savings_ +// vector, which stores the savings to go through once we've iterated through +// all sorted_savings_. +// In the first case above, where neither nodes are contained, we skip the +// current Saving (current_saving_), and add the next best Saving for this arc +// to next_savings_ (in case this skipped Saving is never considered). +// In the second case with a specific type, we search for the Saving with the +// correct type for this arc, and add it to both next_savings_ and the skipped +// Savings. +// +// The skipped Savings are then re-considered when one of their ends gets +// inserted: +// When another Saving other_node-->before (or after-->other_node) gets +// inserted, all skipped Savings in skipped_savings_starting_at_[before] (or +// skipped_savings_ending_at_[after]) are once again considered by calling +// ReinjectSkippedSavingsStartingAt() (or ReinjectSkippedSavingsEndingAt()). +// Then, when calling GetSaving(), we iterate through the reinjected Savings in +// order of insertion in the vectors while there are reinjected savings. +template +class SavingsFilteredDecisionBuilder::SavingsContainer { + public: + explicit SavingsContainer(const SavingsFilteredDecisionBuilder* savings_db, + int vehicle_types) + : savings_db_(savings_db), + vehicle_types_(vehicle_types), + index_in_sorted_savings_(0), + single_vehicle_type_(vehicle_types == 1), + using_incoming_reinjected_saving_(false), + sorted_(false), + to_update_(true) {} + + void InitializeContainer(int64 size, int64 saving_neighbors) { + sorted_savings_per_vehicle_type_.clear(); + sorted_savings_per_vehicle_type_.resize(vehicle_types_); + for (std::vector& savings : sorted_savings_per_vehicle_type_) { + savings.reserve(size * saving_neighbors); + } + + sorted_savings_.clear(); + costs_and_savings_per_arc_.clear(); + arc_indices_per_before_node_.clear(); + + if (!single_vehicle_type_) { + costs_and_savings_per_arc_.reserve(size * saving_neighbors); + arc_indices_per_before_node_.resize(size); + for (int before_node = 0; before_node < size; before_node++) { + arc_indices_per_before_node_[before_node].reserve(saving_neighbors); } } - std::sort(sorted_nexts->begin(), sorted_nexts->end(), - [this, from](int next1, int next2) { - return comparator_(from, next1, next2); - }); + skipped_savings_starting_at_.clear(); + skipped_savings_starting_at_.resize(size); + skipped_savings_ending_at_.clear(); + skipped_savings_ending_at_.resize(size); + incoming_reinjected_savings_ = nullptr; + outgoing_reinjected_savings_ = nullptr; + incoming_new_reinjected_savings_ = nullptr; + outgoing_new_reinjected_savings_ = nullptr; + } + + void AddNewSaving(const Saving& saving, int64 total_cost, int64 before_node, + int64 after_node, int vehicle_type) { + CHECK(!sorted_savings_per_vehicle_type_.empty()) + << "Container not initialized!"; + sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving); + UpdateArcIndicesCostsAndSavings(before_node, after_node, + {total_cost, saving}); + } + + void Sort() { + CHECK(!sorted_) << "Container already sorted!"; + + for (std::vector& savings : sorted_savings_per_vehicle_type_) { + std::sort(savings.begin(), savings.end()); + } + + if (single_vehicle_type_) { + const auto& savings = sorted_savings_per_vehicle_type_[0]; + sorted_savings_.resize(savings.size()); + std::transform(savings.begin(), savings.end(), sorted_savings_.begin(), + [](const Saving& saving) { + return SavingAndArc({saving, /*arc_index*/ -1}); + }); + } else { + // For each arc, sort the savings by decreasing total cost + // start-->a-->b-->end. + // The best saving for each arc is therefore the last of its vector. + sorted_savings_.reserve(costs_and_savings_per_arc_.size()); + for (int arc_index = 0; arc_index < costs_and_savings_per_arc_.size(); + arc_index++) { + std::vector>& costs_and_savings = + costs_and_savings_per_arc_[arc_index]; + DCHECK(!costs_and_savings.empty()); + + std::sort( + costs_and_savings.begin(), costs_and_savings.end(), + [](const std::pair& cs1, + const std::pair& cs2) { return cs1 > cs2; }); + + // Insert all Savings for this arc with the lowest cost into + // sorted_savings_. + // TODO(user): Also do this when reiterating on next_savings_. + const int64 cost = costs_and_savings.back().first; + while (!costs_and_savings.empty() && + costs_and_savings.back().first == cost) { + sorted_savings_.push_back( + {costs_and_savings.back().second, arc_index}); + costs_and_savings.pop_back(); + } + } + std::sort(sorted_savings_.begin(), sorted_savings_.end()); + next_saving_type_and_index_for_arc_.clear(); + next_saving_type_and_index_for_arc_.resize( + costs_and_savings_per_arc_.size(), {-1, -1}); + } + sorted_ = true; + index_in_sorted_savings_ = 0; + to_update_ = false; } -} + + bool HasSaving() { + return index_in_sorted_savings_ < sorted_savings_.size() || + HasReinjectedSavings(); + } + + Saving GetSaving() { + CHECK(sorted_) << "Calling GetSaving() before Sort() !"; + CHECK(!to_update_) + << "Update() should be called between two calls to GetSaving() !"; + + to_update_ = true; + + if (HasReinjectedSavings()) { + if (incoming_reinjected_savings_ != nullptr && + outgoing_reinjected_savings_ != nullptr) { + // Get the best Saving among the two. + SavingAndArc& incoming_saving = incoming_reinjected_savings_->front(); + SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front(); + if (incoming_saving < outgoing_saving) { + current_saving_ = incoming_saving; + using_incoming_reinjected_saving_ = true; + } else { + current_saving_ = outgoing_saving; + using_incoming_reinjected_saving_ = false; + } + } else { + if (incoming_reinjected_savings_ != nullptr) { + current_saving_ = incoming_reinjected_savings_->front(); + using_incoming_reinjected_saving_ = true; + } + if (outgoing_reinjected_savings_ != nullptr) { + current_saving_ = outgoing_reinjected_savings_->front(); + using_incoming_reinjected_saving_ = false; + } + } + } else { + current_saving_ = sorted_savings_[index_in_sorted_savings_]; + } + return current_saving_.saving; + } + + void Update(bool update_best_saving, int type = -1) { + CHECK(to_update_) << "Container already up to date!"; + if (update_best_saving) { + const int64 arc_index = current_saving_.arc_index; + UpdateNextAndSkippedSavingsForArcWithType(arc_index, type); + } + if (!HasReinjectedSavings()) { + index_in_sorted_savings_++; + + if (index_in_sorted_savings_ == sorted_savings_.size()) { + sorted_savings_ = next_savings_; + index_in_sorted_savings_ = 0; + + std::sort(sorted_savings_.begin(), sorted_savings_.end()); + next_savings_.clear(); + next_saving_type_and_index_for_arc_.clear(); + next_saving_type_and_index_for_arc_.resize( + costs_and_savings_per_arc_.size(), {-1, -1}); + } + } + UpdateReinjectedSavings(); + to_update_ = false; + } + + void UpdateWithType(int type) { + CHECK(!single_vehicle_type_); + Update(/*update_best_saving*/ true, type); + } + + const std::vector& GetSortedSavingsForVehicleType(int type) { + CHECK(sorted_) << "Savings not sorted yet!"; + CHECK_LT(type, vehicle_types_); + return sorted_savings_per_vehicle_type_[type]; + } + + void ReinjectSkippedSavingsStartingAt(int64 node) { + CHECK(outgoing_new_reinjected_savings_ == nullptr); + outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]); + } + + void ReinjectSkippedSavingsEndingAt(int64 node) { + CHECK(incoming_new_reinjected_savings_ == nullptr); + incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]); + } + + private: + struct SavingAndArc { + Saving saving; + int64 arc_index; + + bool operator<(const SavingAndArc& other) const { + return std::tie(saving, arc_index) < + std::tie(other.saving, other.arc_index); + } + }; + + // Skips the Saving for the arc before_node-->after_node, by adding it to the + // skipped_savings_ vector of the nodes, if they're uncontained. + void SkipSavingForArc(const SavingAndArc& saving_and_arc) { + const Saving& saving = saving_and_arc.saving; + const int64 before_node = savings_db_->GetBeforeNodeFromSaving(saving); + const int64 after_node = savings_db_->GetAfterNodeFromSaving(saving); + if (!savings_db_->Contains(before_node)) { + skipped_savings_starting_at_[before_node].push_back(saving_and_arc); + } + if (!savings_db_->Contains(after_node)) { + skipped_savings_ending_at_[after_node].push_back(saving_and_arc); + } + } + + // Called within Update() when update_best_saving is true, this method updates + // the next_savings_ and skipped savings vectors for a given arc_index and + // vehicle type. + // When a Saving with the right type has already been added to next_savings_ + // for this arc, no action is needed on next_savings_. + // Otherwise, if such a Saving exists, GetNextSavingForArcWithType() will find + // and assign it to next_saving, which is then used to update next_savings_. + // Finally, the right Saving is skipped for this arc: if looking for a + // specific type (i.e. type != -1), next_saving (which has the correct type) + // is skipped, otherwise the current_saving_ is. + void UpdateNextAndSkippedSavingsForArcWithType(int64 arc_index, int type) { + if (single_vehicle_type_) { + // No next Saving, skip the current Saving. + CHECK_EQ(type, -1); + SkipSavingForArc(current_saving_); + return; + } + CHECK_GE(arc_index, 0); + auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index]; + const int previous_index = type_and_index.second; + const int previous_type = type_and_index.first; + bool next_saving_added = false; + Saving next_saving; + + if (previous_index >= 0) { + // Next Saving already added for this arc. + DCHECK_GE(previous_type, 0); + if (type == -1 || previous_type == type) { + // Not looking for a specific type, or correct type already in + // next_savings_. + next_saving_added = true; + next_saving = next_savings_[previous_index].saving; + } + } + + if (!next_saving_added && + GetNextSavingForArcWithType(arc_index, type, &next_saving)) { + type_and_index.first = savings_db_->GetVehicleTypeFromSaving(next_saving); + if (previous_index >= 0) { + // Update the previous saving. + next_savings_[previous_index] = {next_saving, arc_index}; + } else { + // Insert the new next Saving for this arc. + type_and_index.second = next_savings_.size(); + next_savings_.push_back({next_saving, arc_index}); + } + next_saving_added = true; + } + + // Skip the Saving based on the vehicle type. + if (type == -1) { + // Skip the current Saving. + SkipSavingForArc(current_saving_); + } else { + // Skip the Saving with the correct type, already added to next_savings_ + // if it was found. + if (next_saving_added) { + SkipSavingForArc({next_saving, arc_index}); + } + } + } + + void UpdateReinjectedSavings() { + UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_, + &incoming_reinjected_savings_, + using_incoming_reinjected_saving_); + UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_, + &outgoing_reinjected_savings_, + !using_incoming_reinjected_saving_); + incoming_new_reinjected_savings_ = nullptr; + outgoing_new_reinjected_savings_ = nullptr; + } + + void UpdateGivenReinjectedSavings( + std::deque* new_reinjected_savings, + std::deque** reinjected_savings, + bool using_reinjected_savings) { + if (new_reinjected_savings == nullptr) { + // No new reinjected savings, update the previous ones if needed. + if (*reinjected_savings != nullptr && using_reinjected_savings) { + CHECK(!(*reinjected_savings)->empty()); + (*reinjected_savings)->pop_front(); + if ((*reinjected_savings)->empty()) { + *reinjected_savings = nullptr; + } + } + return; + } + + // New savings reinjected. + // Forget about the previous reinjected savings and add the new ones if + // there are any. + if (*reinjected_savings != nullptr) { + (*reinjected_savings)->clear(); + } + *reinjected_savings = nullptr; + if (!new_reinjected_savings->empty()) { + *reinjected_savings = new_reinjected_savings; + } + } + + bool HasReinjectedSavings() { + return outgoing_reinjected_savings_ != nullptr || + incoming_reinjected_savings_ != nullptr; + } + + void UpdateArcIndicesCostsAndSavings( + int64 before_node, int64 after_node, + const std::pair& cost_and_saving) { + if (single_vehicle_type_) { + return; + } + absl::flat_hash_map& arc_indices = + arc_indices_per_before_node_[before_node]; + const auto& arc_inserted = arc_indices.insert( + std::make_pair(after_node, costs_and_savings_per_arc_.size())); + const int index = arc_inserted.first->second; + if (arc_inserted.second) { + costs_and_savings_per_arc_.push_back({cost_and_saving}); + } else { + DCHECK_LT(index, costs_and_savings_per_arc_.size()); + costs_and_savings_per_arc_[index].push_back(cost_and_saving); + } + } + + bool GetNextSavingForArcWithType(int64 arc_index, int type, + Saving* next_saving) { + std::vector>& costs_and_savings = + costs_and_savings_per_arc_[arc_index]; + + bool found_saving = false; + while (!costs_and_savings.empty() && !found_saving) { + const Saving& saving = costs_and_savings.back().second; + if (type == -1 || savings_db_->GetVehicleTypeFromSaving(saving) == type) { + *next_saving = saving; + found_saving = true; + } + costs_and_savings.pop_back(); + } + return found_saving; + } + + const SavingsFilteredDecisionBuilder* const savings_db_; + const int vehicle_types_; + int64 index_in_sorted_savings_; + std::vector> sorted_savings_per_vehicle_type_; + std::vector sorted_savings_; + std::vector next_savings_; + std::vector> + next_saving_type_and_index_for_arc_; + SavingAndArc current_saving_; + const bool single_vehicle_type_; + std::vector>> + costs_and_savings_per_arc_; + std::vector> + arc_indices_per_before_node_; + std::vector> skipped_savings_starting_at_; + std::vector> skipped_savings_ending_at_; + std::deque* outgoing_reinjected_savings_; + std::deque* incoming_reinjected_savings_; + bool using_incoming_reinjected_saving_; + std::deque* outgoing_new_reinjected_savings_; + std::deque* incoming_new_reinjected_savings_; + bool sorted_; + bool to_update_; +}; // SavingsFilteredDecisionBuilder SavingsFilteredDecisionBuilder::SavingsFilteredDecisionBuilder( - RoutingModel* model, double savings_neighbors_ratio, bool add_reverse_arcs, + RoutingModel* model, RoutingIndexManager* manager, + double savings_neighbors_ratio, bool add_reverse_arcs, + double savings_arc_coefficient, const std::vector& filters) : RoutingFilteredDecisionBuilder(model, filters), - savings_neighbors_ratio_(savings_neighbors_ratio > 0 - ? std::min(savings_neighbors_ratio, 1.0) - : 1), + manager_(manager), + savings_neighbors_ratio_(savings_neighbors_ratio), add_reverse_arcs_(add_reverse_arcs), - size_squared_(0) {} + savings_arc_coefficient_(savings_arc_coefficient), + size_squared_(0) { + DCHECK_GT(savings_neighbors_ratio_, 0); + DCHECK_LE(savings_neighbors_ratio_, 1); + DCHECK_GT(savings_arc_coefficient_, 0); +} + +SavingsFilteredDecisionBuilder::~SavingsFilteredDecisionBuilder() {} bool SavingsFilteredDecisionBuilder::BuildSolution() { if (!InitializeRoutes()) { @@ -2489,77 +4196,290 @@ bool SavingsFilteredDecisionBuilder::BuildSolution() { } const int size = model()->Size(); size_squared_ = size * size; - std::vector savings = ComputeSavings(); - const int vehicle_types = vehicles_per_vehicle_type_.size(); + ComputeSavings(); + BuildRoutesFromSavings(); + MakeUnassignedNodesUnperformed(); + return Commit(); +} + +int SavingsFilteredDecisionBuilder::StartNewRouteWithBestVehicleOfType( + int type, int64 before_node, int64 after_node) { + std::set& sorted_classes = + sorted_vehicle_classes_per_type_[type]; + auto vehicle_class_it = sorted_classes.begin(); + + while (vehicle_class_it != sorted_classes.end()) { + if (StopSearch()) break; + const int vehicle_class = vehicle_class_it->vehicle_class; + auto& vehicles = vehicles_per_vehicle_class_[vehicle_class]; + CHECK(!vehicles.empty()); + + const int vehicle = vehicles.front(); + const int64 start = model()->Start(vehicle); + const int64 end = model()->End(vehicle); + SetValue(start, before_node); + SetValue(before_node, after_node); + SetValue(after_node, end); + if (Commit()) { + vehicles.pop_front(); + if (vehicles.empty()) { + sorted_classes.erase(vehicle_class_it); + } + return vehicle; + } + vehicle_class_it++; + } + // This arc could not be served by any vehicle of this type. + return -1; +} + +void SavingsFilteredDecisionBuilder::ComputeVehicleTypes() { + type_index_of_vehicle_.clear(); + const int nodes = model()->nodes(); + const int nodes_squared = nodes * nodes; + const int vehicles = model()->vehicles(); + type_index_of_vehicle_.resize(vehicles); + + sorted_vehicle_classes_per_type_.clear(); + vehicles_per_vehicle_class_.clear(); + vehicles_per_vehicle_class_.resize(model()->GetVehicleClassesCount()); + + absl::flat_hash_map type_to_type_index; + + for (int v = 0; v < vehicles; v++) { + const int start = manager_->IndexToNode(model()->Start(v)).value(); + const int end = manager_->IndexToNode(model()->End(v)).value(); + const int cost_class = model()->GetCostClassIndexOfVehicle(v).value(); + const int64 type = cost_class * nodes_squared + start * nodes + end; + + const auto& vehicle_type_added = type_to_type_index.insert( + std::make_pair(type, type_to_type_index.size())); + + const int index = vehicle_type_added.first->second; + + const int vehicle_class = model()->GetVehicleClassIndexOfVehicle(v).value(); + const VehicleClassEntry class_entry = {vehicle_class, + model()->GetFixedCostOfVehicle(v)}; + + if (vehicle_type_added.second) { + // Type was not indexed yet. + DCHECK_EQ(sorted_vehicle_classes_per_type_.size(), index); + sorted_vehicle_classes_per_type_.push_back({class_entry}); + } else { + // Type already indexed. + DCHECK_LT(index, sorted_vehicle_classes_per_type_.size()); + sorted_vehicle_classes_per_type_[index].insert(class_entry); + } + vehicles_per_vehicle_class_[vehicle_class].push_back(v); + type_index_of_vehicle_[v] = index; + } +} + +void SavingsFilteredDecisionBuilder::AddSymetricArcsToAdjacencyLists( + std::vector>* adjacency_lists) { + for (int64 node = 0; node < adjacency_lists->size(); node++) { + for (int64 neighbor : (*adjacency_lists)[node]) { + if (model()->IsStart(neighbor) || model()->IsEnd(neighbor)) { + continue; + } + (*adjacency_lists)[neighbor].push_back(node); + } + } + std::transform(adjacency_lists->begin(), adjacency_lists->end(), + adjacency_lists->begin(), [](std::vector vec) { + std::sort(vec.begin(), vec.end()); + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); + return vec; + }); +} + +// Computes the savings related to each pair of non-start and non-end nodes. +// The savings value for an arc a-->b for a vehicle starting at node s and +// ending at node e is: +// saving = cost(s-->a-->e) + cost(s-->b-->e) - cost(s-->a-->b-->e), i.e. +// saving = cost(a-->e) + cost(s-->b) - cost(a-->b) +// The saving value also considers a coefficient for the cost of the arc +// a-->b, which results in: +// saving = cost(a-->e) + cost(s-->b) - [savings_arc_coefficient_ * cost(a-->b)] +// The higher this saving value, the better the arc. +// Here, the value stored for the savings is -saving, which are therefore +// considered in decreasing order. +void SavingsFilteredDecisionBuilder::ComputeSavings() { + ComputeVehicleTypes(); + const int size = model()->Size(); + + const int64 saving_neighbors = std::max(1.0, size * savings_neighbors_ratio_); + + const int num_vehicle_types = sorted_vehicle_classes_per_type_.size(); + + savings_container_ = + absl::make_unique>(this, num_vehicle_types); + savings_container_->InitializeContainer(size, saving_neighbors); + + std::vector> adjacency_lists(size); + + for (int type = 0; type < num_vehicle_types; ++type) { + const std::set& vehicle_classes = + sorted_vehicle_classes_per_type_[type]; + if (vehicle_classes.empty()) { + continue; + } + const int vehicle_class = (vehicle_classes.begin())->vehicle_class; + const auto& vehicles = vehicles_per_vehicle_class_[vehicle_class]; + DCHECK(!vehicles.empty()); + const int vehicle = vehicles[0]; + + const int64 cost_class = + model()->GetCostClassIndexOfVehicle(vehicle).value(); + const int64 start = model()->Start(vehicle); + const int64 end = model()->End(vehicle); + const int64 fixed_cost = model()->GetFixedCostOfVehicle(vehicle); + DCHECK_EQ(fixed_cost, (vehicle_classes.begin())->fixed_cost); + + // Compute the neighbors for each non-start/end node not already inserted in + // the model. + for (int before_node = 0; before_node < size; ++before_node) { + if (model()->IsStart(before_node) || model()->IsEnd(before_node) || + Contains(before_node)) { + continue; + } + std::vector> costed_after_nodes; + costed_after_nodes.reserve(size); + for (int after_node = 0; after_node < size; ++after_node) { + if (!model()->IsEnd(after_node) && !model()->IsStart(after_node) && + after_node != before_node && !Contains(after_node)) { + costed_after_nodes.push_back(std::make_pair( + model()->GetArcCostForClass(before_node, after_node, cost_class), + after_node)); + } + } + if (saving_neighbors < size) { + std::nth_element(costed_after_nodes.begin(), + costed_after_nodes.begin() + saving_neighbors, + costed_after_nodes.end()); + costed_after_nodes.resize(saving_neighbors); + } + adjacency_lists[before_node].resize(costed_after_nodes.size()); + std::transform(costed_after_nodes.begin(), costed_after_nodes.end(), + adjacency_lists[before_node].begin(), + [](std::pair cost_and_node) { + return cost_and_node.second; + }); + } + if (add_reverse_arcs_) { + AddSymetricArcsToAdjacencyLists(&adjacency_lists); + } + + // Build the savings for this vehicle type given the adjacency_lists. + for (int before_node = 0; before_node < size; ++before_node) { + if (model()->IsStart(before_node) || model()->IsEnd(before_node) || + Contains(before_node)) { + continue; + } + const int64 before_to_end_cost = + model()->GetArcCostForClass(before_node, end, cost_class); + const int64 start_to_before_cost = + CapSub(model()->GetArcCostForClass(start, before_node, cost_class), + fixed_cost); + for (int64 after_node : adjacency_lists[before_node]) { + if (model()->IsStart(after_node) || model()->IsEnd(after_node) || + before_node == after_node || Contains(after_node)) { + continue; + } + const int64 arc_cost = + model()->GetArcCostForClass(before_node, after_node, cost_class); + const int64 start_to_after_cost = + CapSub(model()->GetArcCostForClass(start, after_node, cost_class), + fixed_cost); + const int64 after_to_end_cost = + model()->GetArcCostForClass(after_node, end, cost_class); + + const double weighed_arc_cost_fp = savings_arc_coefficient_ * arc_cost; + const int64 weighed_arc_cost = + weighed_arc_cost_fp < kint64max + ? static_cast(weighed_arc_cost_fp) + : kint64max; + const int64 saving_value = CapSub( + CapAdd(before_to_end_cost, start_to_after_cost), weighed_arc_cost); + + const Saving saving = + BuildSaving(-saving_value, type, before_node, after_node); + + const int64 total_cost = + CapAdd(CapAdd(start_to_before_cost, arc_cost), after_to_end_cost); + + savings_container_->AddNewSaving(saving, total_cost, before_node, + after_node, type); + } + } + } + + savings_container_->Sort(); +} + +// SequentialSavingsFilteredDecisionBuilder + +void SequentialSavingsFilteredDecisionBuilder::BuildRoutesFromSavings() { + const int vehicle_types = sorted_vehicle_classes_per_type_.size(); DCHECK_GT(vehicle_types, 0); + const int size = model()->Size(); // Store savings for each incoming and outgoing node and by vehicle type. This // is necessary to quickly extend partial chains without scanning all savings. - std::vector> in_savings_indices(size * vehicle_types); - std::vector> out_savings_indices(size * vehicle_types); - for (int i = 0; i < savings.size(); ++i) { - const Saving& saving = savings[i]; - const int vehicle_type_offset = GetVehicleTypeFromSaving(saving) * size; - const int before_node = GetBeforeNodeFromSaving(saving); - in_savings_indices[vehicle_type_offset + before_node].push_back(i); - const int after_node = GetAfterNodeFromSaving(saving); - out_savings_indices[vehicle_type_offset + after_node].push_back(i); - } - // For each vehicle type, sort vehicles by decreasing vehicle fixed cost. - // Vehicles with the same fixed cost are sorted by decreasing vehicle index. - std::vector fixed_cost_of_vehicle(model()->vehicles()); - for (int vehicle = 0; vehicle < model()->vehicles(); vehicle++) { - fixed_cost_of_vehicle[vehicle] = model()->GetFixedCostOfVehicle(vehicle); - } + std::vector> in_savings_ptr(size * vehicle_types); + std::vector> out_savings_ptr(size * vehicle_types); for (int type = 0; type < vehicle_types; type++) { - std::vector& sorted_vehicles = vehicles_per_vehicle_type_[type]; - std::stable_sort(sorted_vehicles.begin(), sorted_vehicles.end(), - [&fixed_cost_of_vehicle](int v1, int v2) { - return fixed_cost_of_vehicle[v1] < - fixed_cost_of_vehicle[v2]; - }); - std::reverse(sorted_vehicles.begin(), sorted_vehicles.end()); + const int vehicle_type_offset = type * size; + const std::vector& sorted_savings_for_type = + savings_container_->GetSortedSavingsForVehicleType(type); + for (const Saving& saving : sorted_savings_for_type) { + DCHECK_EQ(GetVehicleTypeFromSaving(saving), type); + const int before_node = GetBeforeNodeFromSaving(saving); + in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving); + const int after_node = GetAfterNodeFromSaving(saving); + out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving); + } } // Build routes from savings. - for (const Saving& saving : savings) { + while (savings_container_->HasSaving()) { // First find the best saving to start a new route. - const int type = GetVehicleTypeFromSaving(saving); - std::vector& sorted_vehicles = vehicles_per_vehicle_type_[type]; - if (sorted_vehicles.empty()) continue; - int vehicle = sorted_vehicles.back(); - + const Saving saving = savings_container_->GetSaving(); int before_node = GetBeforeNodeFromSaving(saving); int after_node = GetAfterNodeFromSaving(saving); - if (!Contains(before_node) && !Contains(after_node)) { - const int64 start = model()->Start(vehicle); - const int64 end = model()->End(vehicle); - SetValue(start, before_node); - SetValue(before_node, after_node); - SetValue(after_node, end); - if (Commit()) { + const bool nodes_not_contained = + !Contains(before_node) && !Contains(after_node); + + bool committed = false; + + if (nodes_not_contained) { + // Find the right vehicle to start the route with this Saving. + const int type = GetVehicleTypeFromSaving(saving); + const int vehicle = + StartNewRouteWithBestVehicleOfType(type, before_node, after_node); + + if (vehicle >= 0) { + committed = true; + const int64 start = model()->Start(vehicle); + const int64 end = model()->End(vehicle); // Then extend the route from both ends of the partial route. - sorted_vehicles.pop_back(); int in_index = 0; int out_index = 0; const int saving_offset = type * size; - while (in_index < - in_savings_indices[saving_offset + after_node].size() || + while (in_index < in_savings_ptr[saving_offset + after_node].size() || out_index < - out_savings_indices[saving_offset + before_node].size()) { + out_savings_ptr[saving_offset + before_node].size()) { + if (StopSearch()) return; // First determine how to extend the route. int before_before_node = -1; int after_after_node = -1; - if (in_index < - in_savings_indices[saving_offset + after_node].size()) { + if (in_index < in_savings_ptr[saving_offset + after_node].size()) { const Saving& in_saving = - savings[in_savings_indices[saving_offset + after_node] - [in_index]]; + *(in_savings_ptr[saving_offset + after_node][in_index]); if (out_index < - out_savings_indices[saving_offset + before_node].size()) { + out_savings_ptr[saving_offset + before_node].size()) { const Saving& out_saving = - savings[out_savings_indices[saving_offset + before_node] - [out_index]]; + *(out_savings_ptr[saving_offset + before_node][out_index]); if (GetSavingValue(in_saving) < GetSavingValue(out_saving)) { // Should extend after after_node after_after_node = GetAfterNodeFromSaving(in_saving); @@ -2574,8 +4494,7 @@ bool SavingsFilteredDecisionBuilder::BuildSolution() { } else { // Should extend before before_node before_before_node = GetBeforeNodeFromSaving( - savings[out_savings_indices[saving_offset + before_node] - [out_index]]); + *(out_savings_ptr[saving_offset + before_node][out_index])); } // Extend the route if (after_after_node != -1) { @@ -2612,139 +4531,230 @@ bool SavingsFilteredDecisionBuilder::BuildSolution() { } } } + savings_container_->Update(nodes_not_contained && !committed); } - MakeUnassignedNodesUnperformed(); - return Commit(); } -void SavingsFilteredDecisionBuilder::ComputeVehicleTypes() { - type_index_of_vehicle_.clear(); - const int nodes = model()->nodes(); - const int nodes_squared = nodes * nodes; - const int vehicles = model()->vehicles(); - type_index_of_vehicle_.resize(vehicles); - - vehicles_per_vehicle_type_.clear(); - std::unordered_map type_to_type_index; +// ParallelSavingsFilteredDecisionBuilder - for (int v = 0; v < vehicles; v++) { - const int start = model()->IndexToNode(model()->Start(v)).value(); - const int end = model()->IndexToNode(model()->End(v)).value(); - const int cost_class = model()->GetCostClassIndexOfVehicle(v).value(); - const int64 type = cost_class * nodes_squared + start * nodes + end; +void ParallelSavingsFilteredDecisionBuilder::BuildRoutesFromSavings() { + // Initialize the vehicles of the first/last non start/end nodes served by + // each route. + const int64 size = model()->Size(); + const int vehicles = model()->vehicles(); - const auto& vehicle_type_added = type_to_type_index.insert( - std::make_pair(type, type_to_type_index.size())); + first_node_on_route_.resize(vehicles, -1); + last_node_on_route_.resize(vehicles, -1); + vehicle_of_first_or_last_node_.resize(size, -1); - const int index = vehicle_type_added.first->second; + for (int vehicle = 0; vehicle < vehicles; vehicle++) { + const int64 start = model()->Start(vehicle); + const int64 end = model()->End(vehicle); + if (!Contains(start)) { + continue; + } + int64 node = Value(start); + if (node != end) { + vehicle_of_first_or_last_node_[node] = vehicle; + first_node_on_route_[vehicle] = node; - if (vehicle_type_added.second) { - // Type was not indexed yet. - DCHECK_EQ(vehicles_per_vehicle_type_.size(), index); - vehicles_per_vehicle_type_.push_back({v}); - } else { - // Type already indexed. - DCHECK_LT(index, vehicles_per_vehicle_type_.size()); - vehicles_per_vehicle_type_[index].push_back(v); + int64 next = Value(node); + while (next != end) { + node = next; + next = Value(node); + } + vehicle_of_first_or_last_node_[node] = vehicle; + last_node_on_route_[vehicle] = node; } - type_index_of_vehicle_[v] = index; } -} -// Computes and returns the savings related to each pair of non-start and -// non-end nodes. The savings value for an arc a-->b for a vehicle starting at -// node s and ending at node e is: -// saving = cost(s-->a-->e) + cost(s-->b-->e) - cost(s-->a-->b-->e), i.e. -// saving = cost(a-->e) + cost(s-->b) - cost(a-->b) -// The higher this saving value, the better the arc. -// Here, the value stored for the savings in the output vector is -saving, and -// the vector is therefore sorted in increasing order (the lower -saving, -// the better). -std::vector -SavingsFilteredDecisionBuilder::ComputeSavings() { - ComputeVehicleTypes(); - const int size = model()->Size(); - - const int64 saving_neighbors = std::max(1.0, size * savings_neighbors_ratio_); - - const int num_vehicle_types = vehicles_per_vehicle_type_.size(); - std::vector savings; - savings.reserve(num_vehicle_types * size * saving_neighbors); + while (savings_container_->HasSaving()) { + if (StopSearch()) return; + const Saving saving = savings_container_->GetSaving(); + const int64 before_node = GetBeforeNodeFromSaving(saving); + const int64 after_node = GetAfterNodeFromSaving(saving); + const int type = GetVehicleTypeFromSaving(saving); - for (int type = 0; type < num_vehicle_types; ++type) { - const std::vector& vehicles = vehicles_per_vehicle_type_[type]; - if (vehicles.empty()) { + if (!Contains(before_node) && !Contains(after_node)) { + // Neither nodes are contained, start a new route. + bool committed = false; + + const int vehicle = + StartNewRouteWithBestVehicleOfType(type, before_node, after_node); + + if (vehicle >= 0) { + committed = true; + // Store before_node and after_node as first and last nodes of the route + vehicle_of_first_or_last_node_[before_node] = vehicle; + vehicle_of_first_or_last_node_[after_node] = vehicle; + first_node_on_route_[vehicle] = before_node; + last_node_on_route_[vehicle] = after_node; + savings_container_->ReinjectSkippedSavingsStartingAt(after_node); + savings_container_->ReinjectSkippedSavingsEndingAt(before_node); + } + savings_container_->Update(!committed); continue; } - const int vehicle = vehicles.front(); - const int64 cost_class = - model()->GetCostClassIndexOfVehicle(vehicle).value(); - const int64 start = model()->Start(vehicle); - const int64 end = model()->End(vehicle); - const int64 fixed_cost = model()->GetFixedCostOfVehicle(vehicle); - // TODO(user): deal with the add_reverse_arcs_ flag more efficiently. - std::vector arc_added; - if (add_reverse_arcs_) { - arc_added.resize(size * size, false); + if (Contains(before_node) && Contains(after_node)) { + // Merge the two routes if before_node is last and after_node first of its + // route, the two nodes aren't already on the same route, and the vehicle + // types are compatible. + const int v1 = vehicle_of_first_or_last_node_[before_node]; + const int64 last_node = v1 == -1 ? -1 : last_node_on_route_[v1]; + + const int v2 = vehicle_of_first_or_last_node_[after_node]; + const int64 first_node = v2 == -1 ? -1 : first_node_on_route_[v2]; + + if (before_node == last_node && after_node == first_node && v1 != v2 && + type_index_of_vehicle_[v1] == type_index_of_vehicle_[v2]) { + CHECK_EQ(Value(before_node), model()->End(v1)); + CHECK_EQ(Value(model()->Start(v2)), after_node); + + // We try merging the two routes. + // TODO(user): Try to use skipped savings to start new routes when + // a vehicle becomes available after a merge (not trivial because it can + // result in an infinite loop). + MergeRoutes(v1, v2, before_node, after_node); + } } - for (int before_node = 0; before_node < size; ++before_node) { - if (!Contains(before_node) && !model()->IsEnd(before_node) && - !model()->IsStart(before_node)) { - const int64 in_saving = - model()->GetArcCostForClass(before_node, end, cost_class); - std::vector> - costed_after_nodes; - costed_after_nodes.reserve(size); - for (int after_node = 0; after_node < size; ++after_node) { - if (after_node != before_node && !Contains(after_node) && - !model()->IsEnd(after_node) && !model()->IsStart(after_node)) { - costed_after_nodes.push_back( - std::make_pair(model()->GetArcCostForClass( - before_node, after_node, cost_class), - after_node)); + + if (Contains(before_node) && !Contains(after_node)) { + const int vehicle = vehicle_of_first_or_last_node_[before_node]; + const int64 last_node = vehicle == -1 ? -1 : last_node_on_route_[vehicle]; + + if (before_node == last_node) { + const int64 end = model()->End(vehicle); + CHECK_EQ(Value(before_node), end); + + const int route_type = type_index_of_vehicle_[vehicle]; + if (type != route_type) { + // The saving doesn't correspond to the type of the vehicle serving + // before_node. We update the container with the correct type. + savings_container_->UpdateWithType(route_type); + continue; + } + + // Try adding after_node on route of before_node. + SetValue(before_node, after_node); + SetValue(after_node, end); + if (Commit()) { + if (first_node_on_route_[vehicle] != before_node) { + // before_node is no longer the start or end of its route + DCHECK_NE(Value(model()->Start(vehicle)), before_node); + vehicle_of_first_or_last_node_[before_node] = -1; } + vehicle_of_first_or_last_node_[after_node] = vehicle; + last_node_on_route_[vehicle] = after_node; + savings_container_->ReinjectSkippedSavingsStartingAt(after_node); } - if (saving_neighbors < size) { - std::nth_element(costed_after_nodes.begin(), - costed_after_nodes.begin() + saving_neighbors, - costed_after_nodes.end()); - costed_after_nodes.resize(saving_neighbors); + } + } + + if (!Contains(before_node) && Contains(after_node)) { + const int vehicle = vehicle_of_first_or_last_node_[after_node]; + const int64 first_node = + vehicle == -1 ? -1 : first_node_on_route_[vehicle]; + + if (after_node == first_node) { + const int64 start = model()->Start(vehicle); + CHECK_EQ(Value(start), after_node); + + const int route_type = type_index_of_vehicle_[vehicle]; + if (type != route_type) { + // The saving doesn't correspond to the type of the vehicle serving + // after_node. We update the container with the correct type. + savings_container_->UpdateWithType(route_type); + continue; } - for (const auto& costed_after_node : costed_after_nodes) { - const int64 after_node = costed_after_node.second; - if (add_reverse_arcs_ && arc_added[before_node * size + after_node]) { - DCHECK(arc_added[after_node * size + before_node]); - continue; - } - const int64 saving = - CapSub(CapAdd(in_saving, model()->GetArcCostForClass( - start, after_node, cost_class)), - CapAdd(costed_after_node.first, fixed_cost)); - savings.push_back( - BuildSaving(-saving, type, before_node, after_node)); - - if (add_reverse_arcs_) { - // Also add after->before savings. - arc_added[before_node * size + after_node] = true; - arc_added[after_node * size + before_node] = true; - const int64 second_cost = model()->GetArcCostForClass( - after_node, before_node, cost_class); - const int64 second_saving = CapSub( - CapAdd(model()->GetArcCostForClass(after_node, end, cost_class), - model()->GetArcCostForClass(start, before_node, - cost_class)), - CapAdd(second_cost, fixed_cost)); - savings.push_back( - BuildSaving(-second_saving, type, after_node, before_node)); + // Try adding before_node on route of after_node. + SetValue(before_node, after_node); + SetValue(start, before_node); + if (Commit()) { + if (last_node_on_route_[vehicle] != after_node) { + // after_node is no longer the start or end of its route + DCHECK_NE(Value(after_node), model()->End(vehicle)); + vehicle_of_first_or_last_node_[after_node] = -1; } + vehicle_of_first_or_last_node_[before_node] = vehicle; + first_node_on_route_[vehicle] = before_node; + savings_container_->ReinjectSkippedSavingsEndingAt(before_node); } } } + savings_container_->Update(/*update_best_saving*/ false); + } +} + +void ParallelSavingsFilteredDecisionBuilder::MergeRoutes(int first_vehicle, + int second_vehicle, + int64 before_node, + int64 after_node) { + if (StopSearch()) return; + const int64 new_first_node = first_node_on_route_[first_vehicle]; + DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle); + CHECK_EQ(Value(model()->Start(first_vehicle)), new_first_node); + const int64 new_last_node = last_node_on_route_[second_vehicle]; + DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle); + CHECK_EQ(Value(new_last_node), model()->End(second_vehicle)); + + // Select the vehicle with lower fixed cost to merge the routes. + int used_vehicle = first_vehicle; + int unused_vehicle = second_vehicle; + if (model()->GetFixedCostOfVehicle(first_vehicle) > + model()->GetFixedCostOfVehicle(second_vehicle)) { + used_vehicle = second_vehicle; + unused_vehicle = first_vehicle; + } + + SetValue(before_node, after_node); + SetValue(model()->Start(unused_vehicle), model()->End(unused_vehicle)); + if (used_vehicle == first_vehicle) { + SetValue(new_last_node, model()->End(used_vehicle)); + } else { + SetValue(model()->Start(used_vehicle), new_first_node); + } + bool committed = Commit(); + if (!committed && + model()->GetVehicleClassIndexOfVehicle(first_vehicle).value() != + model()->GetVehicleClassIndexOfVehicle(second_vehicle).value()) { + // Try committing on other vehicle instead. + std::swap(used_vehicle, unused_vehicle); + SetValue(before_node, after_node); + SetValue(model()->Start(unused_vehicle), model()->End(unused_vehicle)); + if (used_vehicle == first_vehicle) { + SetValue(new_last_node, model()->End(used_vehicle)); + } else { + SetValue(model()->Start(used_vehicle), new_first_node); + } + committed = Commit(); + } + if (committed) { + // Make unused_vehicle available + const int vehicle_class = + model()->GetVehicleClassIndexOfVehicle(unused_vehicle).value(); + auto& vehicles = vehicles_per_vehicle_class_[vehicle_class]; + if (vehicles.empty()) { + // Add the vehicle class entry to the set (it was removed when + // vehicles_per_vehicle_class_[vehicle_class] got empty). + const int type = type_index_of_vehicle_[unused_vehicle]; + const auto& insertion = sorted_vehicle_classes_per_type_[type].insert( + {vehicle_class, model()->GetFixedCostOfVehicle(unused_vehicle)}); + DCHECK(insertion.second); + } + vehicles.push_front(unused_vehicle); + + // Update the first and last nodes on vehicles. + first_node_on_route_[unused_vehicle] = -1; + last_node_on_route_[unused_vehicle] = -1; + vehicle_of_first_or_last_node_[before_node] = -1; + vehicle_of_first_or_last_node_[after_node] = -1; + first_node_on_route_[used_vehicle] = new_first_node; + last_node_on_route_[used_vehicle] = new_last_node; + vehicle_of_first_or_last_node_[new_last_node] = used_vehicle; + vehicle_of_first_or_last_node_[new_first_node] = used_vehicle; } - std::sort(savings.begin(), savings.end()); - return savings; } // ChristofidesFilteredDecisionBuilder @@ -2804,6 +4814,7 @@ bool ChristofidesFilteredDecisionBuilder::BuildSolution() { int prev = GetStartChainEnd(vehicle); const int end = model()->End(vehicle); for (int i = 1; i < path.size() - 1 && prev != end; ++i) { + if (StopSearch()) return false; int next = indices[path[i]]; if (!Contains(next)) { SetValue(prev, next); @@ -2845,8 +4856,8 @@ class GuidedSlackFinalizer : public DecisionBuilder { GuidedSlackFinalizer::GuidedSlackFinalizer( const RoutingDimension* dimension, RoutingModel* model, std::function initializer) - : dimension_(CHECK_NOTNULL(dimension)), - model_(CHECK_NOTNULL(model)), + : dimension_(ABSL_DIE_IF_NULL(dimension)), + model_(ABSL_DIE_IF_NULL(model)), initializer_(std::move(initializer)), is_initialized_(dimension->slacks().size(), false), initial_values_(dimension->slacks().size(), kint64min), @@ -2944,8 +4955,10 @@ int64 RoutingDimension::ShortestTransitionSlack(int64 node) const { const int64 serving_vehicle = model_->VehicleVar(node)->Value(); CHECK_EQ(serving_vehicle, model_->VehicleVar(next)->Value()); const RoutingModel::StateDependentTransit transit_from_next = - state_dependent_class_evaluators_ - [state_dependent_vehicle_to_class_[serving_vehicle]](next, next_next); + model_->StateDependentTransitCallback( + state_dependent_class_evaluators_ + [state_dependent_vehicle_to_class_[serving_vehicle]])(next, + next_next); // We have that transit[i+1] is a function of cumul[i+1]. const int64 next_cumul_min = CumulVar(next)->Min(); const int64 next_cumul_max = CumulVar(next)->Max(); @@ -2960,12 +4973,15 @@ int64 RoutingDimension::ShortestTransitionSlack(int64 node) const { // In the current implementation TransitVar(i) = transit[i] + slack[i], so we // have to find the transit from the evaluators. const int64 current_cumul = CumulVar(node)->Value(); - const int64 current_state_independent_transit = - class_evaluators_[vehicle_to_class_[serving_vehicle]](node, next); + const int64 current_state_independent_transit = model_->TransitCallback( + class_evaluators_[vehicle_to_class_[serving_vehicle]])(node, next); const int64 current_state_dependent_transit = - state_dependent_class_evaluators_ - [state_dependent_vehicle_to_class_[serving_vehicle]](node, next) - .transit->Query(current_cumul); + model_ + ->StateDependentTransitCallback( + state_dependent_class_evaluators_ + [state_dependent_vehicle_to_class_[serving_vehicle]])(node, + next) + .transit->Query(current_cumul); const int64 optimal_slack = optimal_next_cumul - current_cumul - current_state_independent_transit - current_state_dependent_transit; diff --git a/ortools/constraint_solver/routing_types.h b/ortools/constraint_solver/routing_types.h index acfe8c420eb..474e740d2a9 100644 --- a/ortools/constraint_solver/routing_types.h +++ b/ortools/constraint_solver/routing_types.h @@ -14,9 +14,9 @@ #ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_TYPES_H_ #define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_TYPES_H_ +#include #include #include -#include "ortools/base/callback.h" #include "ortools/base/int_type.h" #include "ortools/base/integral_types.h" @@ -37,12 +37,13 @@ DEFINE_INT_TYPE(RoutingDimensionIndex, int); DEFINE_INT_TYPE(RoutingDisjunctionIndex, int); DEFINE_INT_TYPE(RoutingVehicleClassIndex, int); -typedef ResultCallback2 +typedef std::function RoutingNodeEvaluator2; -typedef std::function RoutingTransitEvaluator2; +typedef std::function RoutingTransitCallback1; +typedef std::function RoutingTransitCallback2; // NOTE(user): keep the "> >" for SWIG. -typedef std::pair, std::vector > RoutingNodePair; -typedef std::vector RoutingNodePairs; +typedef std::pair, std::vector > RoutingIndexPair; +typedef std::vector RoutingIndexPairs; } // namespace operations_research diff --git a/ortools/constraint_solver/sat_constraint.cc b/ortools/constraint_solver/sat_constraint.cc index da0f9497e72..df8fe381769 100644 --- a/ortools/constraint_solver/sat_constraint.cc +++ b/ortools/constraint_solver/sat_constraint.cc @@ -15,77 +15,6 @@ namespace operations_research { -void SatTableConstraint::Post() { - BooleanVariableManager* manager = sat_constraint_.VariableManager(); - sat::SatSolver* sat_solver = sat_constraint_.SatSolver(); - - // First register the variable. - DCHECK_EQ(vars_.size(), tuples_.Arity()); - std::vector reg_indices; - for (IntVar* int_var : vars_) { - reg_indices.push_back(manager->RegisterIntVar(int_var)); - } - - // Then create an extra BooleanVariable per tuple. - const sat::BooleanVariable first_tuple_var(sat_solver->NumVariables()); - sat_solver->SetNumVariables(sat_solver->NumVariables() + tuples_.NumTuples()); - - std::vector clause; - std::vector> column_values; - for (int i = 0; i < tuples_.Arity(); ++i) { - column_values.clear(); - IntVarLiteralGetter literal_getter = - manager->AssociatedBooleanVariables(reg_indices[i]); - for (int tuple_index = 0; tuple_index < tuples_.NumTuples(); - ++tuple_index) { - // Add the implications not(int_var == value) => not(tuple_var). - clause.clear(); - clause.push_back(sat::Literal(first_tuple_var + tuple_index, false)); - clause.push_back(literal_getter.IsEqualTo(tuples_.Value(tuple_index, i))); - column_values.push_back( - std::make_pair(tuples_.Value(tuple_index, i), tuple_index)); - sat_solver->AddProblemClause(clause); - } - - // We need to process all the tuple with the same value for the current - // variable, so we sort them. - clause.clear(); - std::sort(column_values.begin(), column_values.end()); - - // Loop over all the current variable value. - const IntVar* int_var = vars_[i]; - int column_index = 0; - for (int value = int_var->Min(); value <= int_var->Max(); ++value) { - // It is possible that the tuples contains out of range value, so we - // remove them. - while (column_index < column_values.size() && - column_values[column_index].first < value) { - ++column_index; - } - - // If a value doesn't appear, then we can fix a Boolean variable to false. - if (column_index >= column_values.size() || - value != column_values[column_index].first) { - sat_solver->AddUnitClause(literal_getter.IsNotEqualTo(value)); - continue; - } - - // Otherwise, we create a clause to indicates that we can't have this - // value if all the tuple containing it are false. - clause.clear(); - clause.push_back(literal_getter.IsNotEqualTo(value)); - for (; column_index < column_values.size(); ++column_index) { - const std::pair& entry = column_values[column_index]; - if (entry.first != value) break; - clause.push_back(sat::Literal(first_tuple_var + entry.second, true)); - } - sat_solver->AddProblemClause(clause); - } - } - - sat_constraint_.Post(); -} - int BooleanVariableManager::RegisterIntVar(IntVar* int_var) { const int reg_index = gtl::LookupOrInsert(®istration_index_map_, int_var, registered_int_vars_.size()); diff --git a/ortools/constraint_solver/sat_constraint.h b/ortools/constraint_solver/sat_constraint.h index 7ef805273aa..1e0a41c60bf 100644 --- a/ortools/constraint_solver/sat_constraint.h +++ b/ortools/constraint_solver/sat_constraint.h @@ -28,9 +28,8 @@ #ifndef OR_TOOLS_CONSTRAINT_SOLVER_SAT_CONSTRAINT_H_ #define OR_TOOLS_CONSTRAINT_SOLVER_SAT_CONSTRAINT_H_ -#include -#include - +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "ortools/base/hash.h" #include "ortools/base/map_util.h" #include "ortools/constraint_solver/constraint_solver.h" @@ -126,8 +125,9 @@ class BooleanVariableManager { sat::SatSolver* solver_; std::vector registered_int_vars_; std::vector associated_variables_; - std::unordered_map registration_index_map_; - gtl::ITIVector> variable_meaning_; + absl::flat_hash_map registration_index_map_; + gtl::ITIVector> + variable_meaning_; DISALLOW_COPY_AND_ASSIGN(BooleanVariableManager); }; @@ -256,34 +256,6 @@ class SatConstraint : public Constraint { DISALLOW_COPY_AND_ASSIGN(SatConstraint); }; -class SatTableConstraint : public Constraint { - public: - // Note that we need to copy the arguments. - SatTableConstraint(Solver* s, const std::vector& vars, - const IntTupleSet& tuples) - : Constraint(s), vars_(vars), tuples_(tuples), sat_constraint_(s) {} - - void Post() override; - void InitialPropagate() override { sat_constraint_.InitialPropagate(); } - - private: - const std::vector vars_; - const IntTupleSet tuples_; - - // TODO(user): share this between different constraint. We need to pay - // attention and call Post()/InitialPropagate() after all other constraint - // have been posted though. - SatConstraint sat_constraint_; - - DISALLOW_COPY_AND_ASSIGN(SatTableConstraint); -}; - -inline Constraint* BuildSatTableConstraint(Solver* solver, - const std::vector& vars, - const IntTupleSet& tuples) { - return solver->RevAlloc(new SatTableConstraint(solver, vars, tuples)); -} - } // namespace operations_research #endif // OR_TOOLS_CONSTRAINT_SOLVER_SAT_CONSTRAINT_H_ diff --git a/ortools/constraint_solver/sched_constraints.cc b/ortools/constraint_solver/sched_constraints.cc index 8b8c64a42f2..5627c345ce4 100644 --- a/ortools/constraint_solver/sched_constraints.cc +++ b/ortools/constraint_solver/sched_constraints.cc @@ -24,10 +24,10 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/string_array.h" @@ -61,9 +61,8 @@ class TreeArrayConstraint : public Constraint { } std::string DebugStringInternal(const std::string& name) const { - return StringPrintf("Cover(%s) == %s", - JoinDebugStringPtr(vars_, ", ").c_str(), - target_var_->DebugString().c_str()); + return absl::StrFormat("Cover(%s) == %s", JoinDebugStringPtr(vars_, ", "), + target_var_->DebugString()); } void AcceptInternal(const std::string& name, @@ -182,7 +181,7 @@ class TreeArrayConstraint : public Constraint { return target_var_->MayBePerformed() ? target_var_->EndMax() : 0; } - // Returns the the performed status of the 'position' nth interval + // Returns the performed status of the 'position' nth interval // var of the problem. PerformedStatus VarPerformed(int position) const { IntervalVar* const var = vars_[position]; @@ -195,7 +194,7 @@ class TreeArrayConstraint : public Constraint { } } - // Returns the the performed status of the target var. + // Returns the performed status of the target var. PerformedStatus TargetVarPerformed() const { if (target_var_->MustBePerformed()) { return PERFORMED; @@ -309,7 +308,7 @@ class CoverConstraint : public TreeArrayConstraint { break; case PERFORMED: target_var_->SetPerformed(true); - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; case UNDECIDED: target_var_->SetStartRange(RootStartMin(), RootStartMax()); target_var_->SetEndRange(RootEndMin(), RootEndMax()); @@ -347,7 +346,7 @@ class CoverConstraint : public TreeArrayConstraint { break; case PERFORMED: vars_[position]->SetPerformed(true); - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; case UNDECIDED: vars_[position]->SetStartRange(new_start_min, new_start_max); vars_[position]->SetEndRange(new_end_min, new_end_max); @@ -376,7 +375,7 @@ class CoverConstraint : public TreeArrayConstraint { break; case PERFORMED: must_be_performed_count++; - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; case UNDECIDED: may_be_performed_count++; candidate = i; @@ -452,7 +451,7 @@ class CoverConstraint : public TreeArrayConstraint { parent_depth, parent, &bucket_start_min, &bucket_start_max, &bucket_end_min, &bucket_end_max, &one_undecided); if (bucket_start_min > StartMin(parent_depth, parent) || - bucket_start_max < StartMax(parent_depth, parent_depth) || + bucket_start_max < StartMax(parent_depth, parent) || bucket_end_min > EndMin(parent_depth, parent) || bucket_end_max < EndMax(parent_depth, parent) || status_up != Performed(parent_depth, parent)) { @@ -566,8 +565,8 @@ class IntervalEquality : public Constraint { } std::string DebugString() const override { - return StringPrintf("Equality(%s, %s)", var1_->DebugString().c_str(), - var2_->DebugString().c_str()); + return absl::StrFormat("Equality(%s, %s)", var1_->DebugString(), + var2_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { diff --git a/ortools/constraint_solver/sched_expr.cc b/ortools/constraint_solver/sched_expr.cc index 49440cb3ba8..5001d82abbe 100644 --- a/ortools/constraint_solver/sched_expr.cc +++ b/ortools/constraint_solver/sched_expr.cc @@ -14,10 +14,10 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -52,7 +52,7 @@ class IntervalVarStartExpr : public BaseIntExpr { void WhenRange(Demon* d) override { interval_->WhenStartRange(d); } std::string DebugString() const override { - return absl::StrFormat("start(%s)", interval_->DebugString().c_str()); + return absl::StrFormat("start(%s)", interval_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -91,7 +91,7 @@ class IntervalVarEndExpr : public BaseIntExpr { void WhenRange(Demon* d) override { interval_->WhenEndRange(d); } std::string DebugString() const override { - return absl::StrFormat("end(%s)", interval_->DebugString().c_str()); + return absl::StrFormat("end(%s)", interval_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -132,7 +132,7 @@ class IntervalVarDurationExpr : public BaseIntExpr { void WhenRange(Demon* d) override { interval_->WhenDurationRange(d); } std::string DebugString() const override { - return absl::StrFormat("duration(%s)", interval_->DebugString().c_str()); + return absl::StrFormat("duration(%s)", interval_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -154,7 +154,7 @@ IntExpr* BuildStartExpr(IntervalVar* var) { IntExpr* const expr = s->RegisterIntExpr(s->RevAlloc(new IntervalVarStartExpr(var))); if (var->HasName()) { - expr->set_name(absl::StrFormat("start<%s>", var->name().c_str())); + expr->set_name(absl::StrFormat("start<%s>", var->name())); } return expr; } @@ -164,7 +164,7 @@ IntExpr* BuildDurationExpr(IntervalVar* var) { IntExpr* const expr = s->RegisterIntExpr(s->RevAlloc(new IntervalVarDurationExpr(var))); if (var->HasName()) { - expr->set_name(absl::StrFormat("duration<%s>", var->name().c_str())); + expr->set_name(absl::StrFormat("duration<%s>", var->name())); } return expr; } @@ -174,7 +174,7 @@ IntExpr* BuildEndExpr(IntervalVar* var) { IntExpr* const expr = s->RegisterIntExpr(s->RevAlloc(new IntervalVarEndExpr(var))); if (var->HasName()) { - expr->set_name(absl::StrFormat("end<%s>", var->name().c_str())); + expr->set_name(absl::StrFormat("end<%s>", var->name())); } return expr; } diff --git a/ortools/constraint_solver/sched_search.cc b/ortools/constraint_solver/sched_search.cc index a47ad317ee9..20fcdf267c2 100644 --- a/ortools/constraint_solver/sched_search.cc +++ b/ortools/constraint_solver/sched_search.cc @@ -15,9 +15,9 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/string_array.h" @@ -61,11 +61,11 @@ std::string SequenceVar::DebugString() const { int ranked = 0; int not_ranked = 0; ComputeStatistics(&ranked, ¬_ranked, &unperformed); - return StringPrintf("%s(horizon = %" GG_LL_FORMAT "d..%" GG_LL_FORMAT - "d, duration = %" GG_LL_FORMAT "d..%" GG_LL_FORMAT - "d, not ranked = %d, ranked = %d, nexts = [%s])", - name().c_str(), hmin, hmax, dmin, dmax, not_ranked, - ranked, JoinDebugStringPtr(nexts_, ", ").c_str()); + return absl::StrFormat("%s(horizon = %" GG_LL_FORMAT "d..%" GG_LL_FORMAT + "d, duration = %" GG_LL_FORMAT "d..%" GG_LL_FORMAT + "d, not ranked = %d, ranked = %d, nexts = [%s])", + name(), hmin, hmax, dmin, dmax, not_ranked, ranked, + JoinDebugStringPtr(nexts_, ", ")); } void SequenceVar::Accept(ModelVisitor* const visitor) const { @@ -105,7 +105,7 @@ void SequenceVar::HorizonRange(int64* const hmin, int64* const hmax) const { void SequenceVar::ActiveHorizonRange(int64* const hmin, int64* const hmax) const { - std::unordered_set decided; + absl::flat_hash_set decided; for (int i = 0; i < intervals_.size(); ++i) { if (intervals_[i]->CannotBePerformed()) { decided.insert(i); @@ -190,7 +190,7 @@ void SequenceVar::ComputePossibleFirstsAndLasts( std::vector* const possible_lasts) { possible_firsts->clear(); possible_lasts->clear(); - std::unordered_set to_check; + absl::flat_hash_set to_check; for (int i = 0; i < intervals_.size(); ++i) { if (intervals_[i]->MayBePerformed()) { to_check.insert(i); @@ -408,8 +408,8 @@ class ScheduleOrPostpone : public Decision { } std::string DebugString() const override { - return StringPrintf("ScheduleOrPostpone(%s at %" GG_LL_FORMAT "d)", - var_->DebugString().c_str(), est_.Value()); + return absl::StrFormat("ScheduleOrPostpone(%s at %" GG_LL_FORMAT "d)", + var_->DebugString(), est_.Value()); } private: @@ -518,8 +518,8 @@ class ScheduleOrExpedite : public Decision { } std::string DebugString() const override { - return StringPrintf("ScheduleOrExpedite(%s at %" GG_LL_FORMAT "d)", - var_->DebugString().c_str(), est_.Value()); + return absl::StrFormat("ScheduleOrExpedite(%s at %" GG_LL_FORMAT "d)", + var_->DebugString(), est_.Value()); } private: @@ -599,8 +599,8 @@ class RankFirst : public Decision { } std::string DebugString() const override { - return StringPrintf("RankFirst(%s, %d)", sequence_->DebugString().c_str(), - index_); + return absl::StrFormat("RankFirst(%s, %d)", sequence_->DebugString(), + index_); } private: @@ -623,8 +623,8 @@ class RankLast : public Decision { } std::string DebugString() const override { - return StringPrintf("RankLast(%s, %d)", sequence_->DebugString().c_str(), - index_); + return absl::StrFormat("RankLast(%s, %d)", sequence_->DebugString(), + index_); } private: diff --git a/ortools/constraint_solver/search.cc b/ortools/constraint_solver/search.cc index 4b3edd1cf06..26ba10af492 100644 --- a/ortools/constraint_solver/search.cc +++ b/ortools/constraint_solver/search.cc @@ -17,22 +17,24 @@ #include #include #include -#include #include #include +#include "absl/base/casts.h" +#include "absl/container/flat_hash_map.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/bitmap.h" -#include "ortools/base/casts.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/map_util.h" #include "ortools/base/random.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -76,7 +78,7 @@ std::string SearchLog::DebugString() const { return "SearchLog"; } void SearchLog::EnterSearch() { const std::string buffer = - StringPrintf("Start search (%s)", MemoryUsage().c_str()); + absl::StrFormat("Start search (%s)", MemoryUsage()); OutputLine(buffer); timer_->Restart(); min_right_depth_ = kint32max; @@ -88,12 +90,11 @@ void SearchLog::ExitSearch() { if (ms == 0) { ms = 1; } - const std::string buffer = StringPrintf( + const std::string buffer = absl::StrFormat( "End search (time = %" GG_LL_FORMAT "d ms, branches = %" GG_LL_FORMAT "d, failures = %" GG_LL_FORMAT "d, %s, speed = %" GG_LL_FORMAT "d branches/s)", - ms, branches, solver()->failures(), MemoryUsage().c_str(), - branches * 1000 / ms); + ms, branches, solver()->failures(), MemoryUsage(), branches * 1000 / ms); OutputLine(buffer); } @@ -109,44 +110,47 @@ bool SearchLog::AtSolution() { objective_updated = true; } else if (var_ != nullptr) { current = var_->Value(); - StringAppendF(&obj_str, "%" GG_LL_FORMAT "d, ", current); + absl::StrAppendFormat(&obj_str, "%" GG_LL_FORMAT "d, ", current); objective_updated = true; } if (objective_updated) { if (current >= objective_min_) { - StringAppendF(&obj_str, "objective minimum = %" GG_LL_FORMAT "d, ", - objective_min_); + absl::StrAppendFormat( + &obj_str, "objective minimum = %" GG_LL_FORMAT "d, ", objective_min_); } else { objective_min_ = current; } if (current <= objective_max_) { - StringAppendF(&obj_str, "objective maximum = %" GG_LL_FORMAT "d, ", - objective_max_); + absl::StrAppendFormat( + &obj_str, "objective maximum = %" GG_LL_FORMAT "d, ", objective_max_); } else { objective_max_ = current; } } std::string log; - StringAppendF(&log, - "Solution #%d (%stime = %" GG_LL_FORMAT - "d ms, branches = %" GG_LL_FORMAT - "d," - " failures = %" GG_LL_FORMAT "d, depth = %d", - nsol_++, obj_str.c_str(), timer_->GetInMs(), - solver()->branches(), solver()->failures(), depth); + absl::StrAppendFormat(&log, + "Solution #%d (%stime = %" GG_LL_FORMAT + "d ms, branches = %" GG_LL_FORMAT + "d," + " failures = %" GG_LL_FORMAT "d, depth = %d", + nsol_++, obj_str, timer_->GetInMs(), + solver()->branches(), solver()->failures(), depth); + if (!solver()->SearchContext().empty()) { + absl::StrAppendFormat(&log, ", %s", solver()->SearchContext()); + } if (solver()->neighbors() != 0) { - StringAppendF(&log, - ", neighbors = %" GG_LL_FORMAT - "d, filtered neighbors = %" GG_LL_FORMAT - "d," - " accepted neighbors = %" GG_LL_FORMAT "d", - solver()->neighbors(), solver()->filtered_neighbors(), - solver()->accepted_neighbors()); - } - StringAppendF(&log, ", %s", MemoryUsage().c_str()); + absl::StrAppendFormat(&log, + ", neighbors = %" GG_LL_FORMAT + "d, filtered neighbors = %" GG_LL_FORMAT + "d," + " accepted neighbors = %" GG_LL_FORMAT "d", + solver()->neighbors(), solver()->filtered_neighbors(), + solver()->accepted_neighbors()); + } + absl::StrAppendFormat(&log, ", %s", MemoryUsage()); const int progress = solver()->TopProgressPercent(); if (progress != SearchMonitor::kNoProgress) { - StringAppendF(&log, ", limit = %d%%", progress); + absl::StrAppendFormat(&log, ", limit = %d%%", progress); } log.append(")"); OutputLine(log); @@ -159,22 +163,22 @@ bool SearchLog::AtSolution() { void SearchLog::BeginFail() { Maintain(); } void SearchLog::NoMoreSolutions() { - std::string buffer = StringPrintf( + std::string buffer = absl::StrFormat( "Finished search tree (time = %" GG_LL_FORMAT "d ms, branches = %" GG_LL_FORMAT "d," " failures = %" GG_LL_FORMAT "d", timer_->GetInMs(), solver()->branches(), solver()->failures()); if (solver()->neighbors() != 0) { - StringAppendF(&buffer, - ", neighbors = %" GG_LL_FORMAT - "d, filtered neighbors = %" GG_LL_FORMAT - "d," - " accepted neigbors = %" GG_LL_FORMAT "d", - solver()->neighbors(), solver()->filtered_neighbors(), - solver()->accepted_neighbors()); - } - StringAppendF(&buffer, ", %s)", MemoryUsage().c_str()); + absl::StrAppendFormat(&buffer, + ", neighbors = %" GG_LL_FORMAT + "d, filtered neighbors = %" GG_LL_FORMAT + "d," + " accepted neigbors = %" GG_LL_FORMAT "d", + solver()->neighbors(), solver()->filtered_neighbors(), + solver()->accepted_neighbors()); + } + absl::StrAppendFormat(&buffer, ", %s)", MemoryUsage()); OutputLine(buffer); } @@ -192,29 +196,29 @@ void SearchLog::RefuteDecision(Decision* const decision) { } void SearchLog::OutputDecision() { - std::string buffer = StringPrintf( + std::string buffer = absl::StrFormat( "%" GG_LL_FORMAT "d branches, %" GG_LL_FORMAT "d ms, %" GG_LL_FORMAT "d failures", solver()->branches(), timer_->GetInMs(), solver()->failures()); if (min_right_depth_ != kint32max && max_depth_ != 0) { const int depth = solver()->SearchDepth(); - StringAppendF(&buffer, ", tree pos=%d/%d/%d minref=%d max=%d", - sliding_min_depth_, depth, sliding_max_depth_, - min_right_depth_, max_depth_); + absl::StrAppendFormat(&buffer, ", tree pos=%d/%d/%d minref=%d max=%d", + sliding_min_depth_, depth, sliding_max_depth_, + min_right_depth_, max_depth_); sliding_min_depth_ = depth; sliding_max_depth_ = depth; } if (obj_ != nullptr && objective_min_ != kint64max && objective_max_ != kint64min) { - StringAppendF(&buffer, - ", objective minimum = %" GG_LL_FORMAT - "d" - ", objective maximum = %" GG_LL_FORMAT "d", - objective_min_, objective_max_); + absl::StrAppendFormat(&buffer, + ", objective minimum = %" GG_LL_FORMAT + "d" + ", objective maximum = %" GG_LL_FORMAT "d", + objective_min_, objective_max_); } const int progress = solver()->TopProgressPercent(); if (progress != SearchMonitor::kNoProgress) { - StringAppendF(&buffer, ", limit = %d%%", progress); + absl::StrAppendFormat(&buffer, ", limit = %d%%", progress); } OutputLine(buffer); } @@ -229,11 +233,11 @@ void SearchLog::Maintain() { void SearchLog::BeginInitialPropagation() { tick_ = timer_->GetInMs(); } void SearchLog::EndInitialPropagation() { - const int64 delta = std::max(timer_->GetInMs() - tick_, 0LL); + const int64 delta = std::max(timer_->GetInMs() - tick_, int64{0}); const std::string buffer = - StringPrintf("Root node processed (time = %" GG_LL_FORMAT - "d ms, constraints = %d, %s)", - delta, solver()->constraints(), MemoryUsage().c_str()); + absl::StrFormat("Root node processed (time = %" GG_LL_FORMAT + "d ms, constraints = %d, %s)", + delta, solver()->constraints(), MemoryUsage()); OutputLine(buffer); } @@ -252,16 +256,16 @@ std::string SearchLog::MemoryUsage() { static const int64 kGigaByte = kMegaByte * kKiloByte; const int64 memory_usage = Solver::MemoryUsage(); if (memory_usage > kDisplayThreshold * kGigaByte) { - return StringPrintf("memory used = %.2lf GB", - memory_usage * 1.0 / kGigaByte); + return absl::StrFormat("memory used = %.2lf GB", + memory_usage * 1.0 / kGigaByte); } else if (memory_usage > kDisplayThreshold * kMegaByte) { - return StringPrintf("memory used = %.2lf MB", - memory_usage * 1.0 / kMegaByte); + return absl::StrFormat("memory used = %.2lf MB", + memory_usage * 1.0 / kMegaByte); } else if (memory_usage > kDisplayThreshold * kKiloByte) { - return StringPrintf("memory used = %2lf KB", - memory_usage * 1.0 / kKiloByte); + return absl::StrFormat("memory used = %2lf KB", + memory_usage * 1.0 / kKiloByte); } else { - return StringPrintf("memory used = %" GG_LL_FORMAT "d", memory_usage); + return absl::StrFormat("memory used = %" GG_LL_FORMAT "d", memory_usage); } } @@ -520,8 +524,8 @@ Decision* ComposeDecisionBuilder::Next(Solver* const s) { } std::string ComposeDecisionBuilder::DebugString() const { - return StringPrintf("ComposeDecisionBuilder(%s)", - JoinDebugStringPtr(builders_, ", ").c_str()); + return absl::StrFormat("ComposeDecisionBuilder(%s)", + JoinDebugStringPtr(builders_, ", ")); } } // namespace @@ -659,8 +663,8 @@ Decision* TryDecisionBuilder::Next(Solver* const solver) { } std::string TryDecisionBuilder::DebugString() const { - return StringPrintf("TryDecisionBuilder(%s)", - JoinDebugStringPtr(builders_, ", ").c_str()); + return absl::StrFormat("TryDecisionBuilder(%s)", + JoinDebugStringPtr(builders_, ", ")); } void TryDecisionBuilder::AdvanceToNextBuilder(Solver* const solver) { @@ -1311,8 +1315,7 @@ class VariableAssignmentSelector : public BaseVariableAssignmentSelector { }; std::string VariableAssignmentSelector::DebugString() const { - return StringPrintf("%s(%s)", name_.c_str(), - JoinDebugStringPtr(vars_, ", ").c_str()); + return absl::StrFormat("%s(%s)", name_, JoinDebugStringPtr(vars_, ", ")); } // ----- Base Global Evaluator-based selector ----- @@ -1332,8 +1335,7 @@ class BaseEvaluatorSelector : public BaseVariableAssignmentSelector { }; std::string DebugStringInternal(const std::string& name) const { - return StringPrintf("%s(%s)", name.c_str(), - JoinDebugStringPtr(vars_, ", ").c_str()); + return absl::StrFormat("%s(%s)", name, JoinDebugStringPtr(vars_, ", ")); } std::function evaluator_; @@ -1522,8 +1524,8 @@ AssignOneVariableValue::AssignOneVariableValue(IntVar* const v, int64 val) : var_(v), value_(val) {} std::string AssignOneVariableValue::DebugString() const { - return StringPrintf("[%s == %" GG_LL_FORMAT "d]", var_->DebugString().c_str(), - value_); + return absl::StrFormat("[%s == %" GG_LL_FORMAT "d]", var_->DebugString(), + value_); } void AssignOneVariableValue::Apply(Solver* const s) { var_->SetValue(value_); } @@ -1561,8 +1563,8 @@ AssignOneVariableValueOrFail::AssignOneVariableValueOrFail(IntVar* const v, : var_(v), value_(value) {} std::string AssignOneVariableValueOrFail::DebugString() const { - return StringPrintf("[%s == %" GG_LL_FORMAT "d]", var_->DebugString().c_str(), - value_); + return absl::StrFormat("[%s == %" GG_LL_FORMAT "d]", var_->DebugString(), + value_); } void AssignOneVariableValueOrFail::Apply(Solver* const s) { @@ -1603,11 +1605,11 @@ SplitOneVariable::SplitOneVariable(IntVar* const v, int64 val, std::string SplitOneVariable::DebugString() const { if (start_with_lower_half_) { - return StringPrintf("[%s <= %" GG_LL_FORMAT "d]", - var_->DebugString().c_str(), value_); + return absl::StrFormat("[%s <= %" GG_LL_FORMAT "d]", var_->DebugString(), + value_); } else { - return StringPrintf("[%s >= %" GG_LL_FORMAT "d]", - var_->DebugString().c_str(), value_); + return absl::StrFormat("[%s >= %" GG_LL_FORMAT "d]", var_->DebugString(), + value_); } } @@ -1678,8 +1680,8 @@ AssignVariablesValues::AssignVariablesValues(const std::vector& vars, std::string AssignVariablesValues::DebugString() const { std::string out; for (int i = 0; i < vars_.size(); ++i) { - StringAppendF(&out, "[%s == %" GG_LL_FORMAT "d]", - vars_[i]->DebugString().c_str(), values_[i]); + absl::StrAppendFormat(&out, "[%s == %" GG_LL_FORMAT "d]", + vars_[i]->DebugString(), values_[i]); } return out; } @@ -2726,7 +2728,8 @@ bool OptimizeVar::AtSolution() { } std::string OptimizeVar::Print() const { - return StringPrintf("objective value = %" GG_LL_FORMAT "d, ", var_->Value()); + return absl::StrFormat("objective value = %" GG_LL_FORMAT "d, ", + var_->Value()); } std::string OptimizeVar::DebugString() const { @@ -2736,9 +2739,9 @@ std::string OptimizeVar::DebugString() const { } else { out = "MinimizeVar("; } - StringAppendF(&out, - "%s, step = %" GG_LL_FORMAT "d, best = %" GG_LL_FORMAT "d)", - var_->DebugString().c_str(), step_, best_); + absl::StrAppendFormat( + &out, "%s, step = %" GG_LL_FORMAT "d, best = %" GG_LL_FORMAT "d)", + var_->DebugString(), step_, best_); return out; } @@ -2790,9 +2793,9 @@ std::string WeightedOptimizeVar::Print() const { std::string result(OptimizeVar::Print()); result.append("\nWeighted Objective:\n"); for (int i = 0; i < sub_objectives_.size(); ++i) { - StringAppendF(&result, "Variable %s,\tvalue %lld,\tweight %lld\n", - sub_objectives_[i]->name().c_str(), - sub_objectives_[i]->Value(), weights_[i]); + absl::StrAppendFormat(&result, "Variable %s,\tvalue %d,\tweight %d\n", + sub_objectives_[i]->name(), + sub_objectives_[i]->Value(), weights_[i]); } return result; } @@ -3303,8 +3306,7 @@ int64 GuidedLocalSearchPenaltiesTable::Value(const Arc& arc) const { } } -// Sparse GLS penalties implementation using a hash_map to store penalties. - +// Sparse GLS penalties implementation using hash_map to store penalties. class GuidedLocalSearchPenaltiesMap : public GuidedLocalSearchPenalties { public: explicit GuidedLocalSearchPenaltiesMap(int size); @@ -3316,7 +3318,7 @@ class GuidedLocalSearchPenaltiesMap : public GuidedLocalSearchPenalties { private: Bitmap penalized_; - std::unordered_map penalties_; + absl::flat_hash_map penalties_; }; GuidedLocalSearchPenaltiesMap::GuidedLocalSearchPenaltiesMap(int size) @@ -3376,7 +3378,7 @@ class GuidedLocalSearch : public Metaheuristic { int64 assignment_penalized_value_; int64 old_penalized_value_; const std::vector vars_; - std::unordered_map indices_; + absl::flat_hash_map indices_; const double penalty_factor_; std::unique_ptr penalties_; std::unique_ptr current_penalized_values_; @@ -3399,8 +3401,8 @@ GuidedLocalSearch::GuidedLocalSearch(Solver* const s, IntVar* objective, if (!vars.empty()) { // TODO(user): Remove scoped_array. assignment_.Add(vars_); - current_penalized_values_.reset(new int64[vars_.size()]); - delta_cache_.reset(new int64[vars_.size()]); + current_penalized_values_ = absl::make_unique(vars_.size()); + delta_cache_ = absl::make_unique(vars_.size()); memset(current_penalized_values_.get(), 0, vars_.size() * sizeof(*current_penalized_values_.get())); } @@ -3408,9 +3410,10 @@ GuidedLocalSearch::GuidedLocalSearch(Solver* const s, IntVar* objective, indices_[vars_[i]] = i; } if (FLAGS_cp_use_sparse_gls_penalties) { - penalties_.reset(new GuidedLocalSearchPenaltiesMap(vars_.size())); + penalties_ = absl::make_unique(vars_.size()); } else { - penalties_.reset(new GuidedLocalSearchPenaltiesTable(vars_.size())); + penalties_ = + absl::make_unique(vars_.size()); } } @@ -3434,7 +3437,8 @@ void GuidedLocalSearch::ApplyDecision(Decision* const d) { const int64 penalty = AssignmentElementPenalty(assignment_, i); current_penalized_values_[i] = penalty; delta_cache_[i] = penalty; - assignment_penalized_value_ += penalty; + assignment_penalized_value_ = + CapAdd(assignment_penalized_value_, penalty); } old_penalized_value_ = assignment_penalized_value_; incremental_ = false; @@ -3647,8 +3651,11 @@ int64 BinaryGuidedLocalSearch::PenalizedValue(int64 i, int64 j) { const Arc arc(i, j); const int64 penalty = penalties_->Value(arc); if (penalty != 0) { // objective_function_->Run(i, j) can be costly - const int64 penalized_value = + const double penalized_value_fp = penalty_factor_ * penalty * objective_function_(i, j); + const int64 penalized_value = (penalized_value_fp <= kint64max) + ? static_cast(penalized_value_fp) + : kint64max; if (maximize_) { return -penalized_value; } else { @@ -3735,8 +3742,11 @@ int64 TernaryGuidedLocalSearch::PenalizedValue(int64 i, int64 j, int64 k) { const Arc arc(i, j); const int64 penalty = penalties_->Value(arc); if (penalty != 0) { // objective_function_(i, j, k) can be costly - const int64 penalized_value = + const double penalized_value_fp = penalty_factor_ * penalty * objective_function_(i, j, k); + const int64 penalized_value = (penalized_value_fp <= kint64max) + ? static_cast(penalized_value_fp) + : kint64max; if (maximize_) { return -penalized_value; } else { @@ -3971,12 +3981,13 @@ void RegularLimit::UpdateLimits(int64 time, int64 branches, int64 failures, } std::string RegularLimit::DebugString() const { - return StringPrintf("RegularLimit(crossed = %i, wall_time = %" GG_LL_FORMAT - "d, " - "branches = %" GG_LL_FORMAT "d, failures = %" GG_LL_FORMAT - "d, solutions = %" GG_LL_FORMAT "d cumulative = %s", - crossed(), wall_time_, branches_, failures_, solutions_, - (cumulative_ ? "true" : "false")); + return absl::StrFormat("RegularLimit(crossed = %i, wall_time = %" GG_LL_FORMAT + "d, " + "branches = %" GG_LL_FORMAT + "d, failures = %" GG_LL_FORMAT + "d, solutions = %" GG_LL_FORMAT "d cumulative = %s", + crossed(), wall_time_, branches_, failures_, + solutions_, (cumulative_ ? "true" : "false")); } bool RegularLimit::CheckTime() { return TimeDelta() >= wall_time_; } @@ -3990,6 +4001,7 @@ int64 RegularLimit::TimeDelta() { int64 time_delta = s->wall_time() - wall_time_offset_; if (smart_time_check_ && check_count_ > kCheckWarmupIterations && time_delta > 0) { + // TODO(user): Fix potential overflow. int64 approximate_calls = (wall_time_ * check_count_) / time_delta; next_check_ = check_count_ + std::min(kMaxSkip, approximate_calls); } @@ -4192,7 +4204,7 @@ class SolveOnce : public DecisionBuilder { } std::string DebugString() const override { - return StringPrintf("SolveOnce(%s)", db_->DebugString().c_str()); + return absl::StrFormat("SolveOnce(%s)", db_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -4306,8 +4318,8 @@ class NestedOptimize : public DecisionBuilder { } std::string DebugString() const override { - return StringPrintf("NestedOptimize(db = %s, maximize = %d, step = %lld)", - db_->DebugString().c_str(), maximize_, step_); + return absl::StrFormat("NestedOptimize(db = %s, maximize = %d, step = %d)", + db_->DebugString(), maximize_, step_); } void Accept(ModelVisitor* const visitor) const override { @@ -4424,7 +4436,7 @@ class LubyRestart : public SearchMonitor { } std::string DebugString() const override { - return StringPrintf("LubyRestart(%i)", scale_factor_); + return absl::StrFormat("LubyRestart(%i)", scale_factor_); } private: @@ -4459,7 +4471,7 @@ class ConstantRestart : public SearchMonitor { } std::string DebugString() const override { - return StringPrintf("ConstantRestart(%i)", frequency_); + return absl::StrFormat("ConstantRestart(%i)", frequency_); } private: diff --git a/ortools/constraint_solver/softgcc.cc b/ortools/constraint_solver/softgcc.cc index d0c07deed03..8d48c455d38 100644 --- a/ortools/constraint_solver/softgcc.cc +++ b/ortools/constraint_solver/softgcc.cc @@ -13,7 +13,6 @@ #include -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/string_array.h" diff --git a/ortools/constraint_solver/solver_parameters.proto b/ortools/constraint_solver/solver_parameters.proto index 1183d967a02..5785f841bed 100644 --- a/ortools/constraint_solver/solver_parameters.proto +++ b/ortools/constraint_solver/solver_parameters.proto @@ -88,9 +88,6 @@ message ConstraintSolverParameters { // Print added constraints. bool print_added_constraints = 13; - // Export model to file. - string export_file = 14; - // // Control search. // @@ -104,11 +101,8 @@ message ConstraintSolverParameters { // // Control the implementation of the table constraint. // - bool use_compact_table = 100; bool use_small_table = 101; - bool use_sat_table = 102; - int32 ac4r_table_threshold = 103; - bool use_mdd_table = 104; + reserved 100, 102, 103, 104; // // Control the propagation of the cumulative constraint. @@ -129,4 +123,12 @@ message ConstraintSolverParameters { // Control the implementation of the element constraint. // bool use_element_rmq = 111; + + // + // Skip locally optimal pairs of paths in PathOperators. Setting this + // parameter to true might skip valid neighbors if there are constraints + // linking paths together (such as precedences). In any other case this + // should only speed up the search without omitting any neighbors. + // + bool skip_locally_optimal_paths = 113; }; diff --git a/ortools/constraint_solver/table.cc b/ortools/constraint_solver/table.cc index 476d58f5fa8..da4afd272f0 100644 --- a/ortools/constraint_solver/table.cc +++ b/ortools/constraint_solver/table.cc @@ -17,15 +17,15 @@ #include #include #include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/constraint_solver/sat_constraint.h" @@ -34,19 +34,6 @@ #include "ortools/util/tuple_set.h" namespace operations_research { -// External table code. -Constraint* BuildAc4TableConstraint(Solver* const solver, - const IntTupleSet& tuples, - const std::vector& vars); - -Constraint* BuildSatTableConstraint(Solver* solver, - const std::vector& vars, - const IntTupleSet& tuples); - -Constraint* BuildAc4MddResetTableConstraint(Solver* const solver, - const IntTupleSet& tuples, - const std::vector& vars); - namespace { // ----- Presolve helpers ----- // TODO(user): Move this out of this file. @@ -77,7 +64,8 @@ struct AffineTransformation { // y == a*x + b. } std::string DebugString() const { - return StringPrintf("(%" GG_LL_FORMAT "d * x + %" GG_LL_FORMAT "d)", a, b); + return absl::StrFormat("(%" GG_LL_FORMAT "d * x + %" GG_LL_FORMAT "d)", a, + b); } }; @@ -198,8 +186,8 @@ class BasePositiveTableConstraint : public Constraint { ~BasePositiveTableConstraint() override {} std::string DebugString() const override { - return StringPrintf("AllowedAssignments(arity = %d, tuple_count = %d)", - arity_, tuple_count_); + return absl::StrFormat("AllowedAssignments(arity = %d, tuple_count = %d)", + arity_, tuple_count_); } void Accept(ModelVisitor* const visitor) const override { @@ -249,7 +237,7 @@ class BasePositiveTableConstraint : public Constraint { class PositiveTableConstraint : public BasePositiveTableConstraint { public: - typedef std::unordered_map> ValueBitset; + typedef absl::flat_hash_map> ValueBitset; PositiveTableConstraint(Solver* const s, const std::vector& vars, const IntTupleSet& tuples) @@ -372,8 +360,8 @@ class PositiveTableConstraint : public BasePositiveTableConstraint { } std::string DebugString() const override { - return StringPrintf("PositiveTableConstraint([%s], %d tuples)", - JoinDebugStringPtr(vars_, ", ").c_str(), tuple_count_); + return absl::StrFormat("PositiveTableConstraint([%s], %d tuples)", + JoinDebugStringPtr(vars_, ", "), tuple_count_); } protected: @@ -631,8 +619,8 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint { } std::string DebugString() const override { - return StringPrintf("CompactPositiveTableConstraint([%s], %d tuples)", - JoinDebugStringPtr(vars_, ", ").c_str(), tuple_count_); + return absl::StrFormat("CompactPositiveTableConstraint([%s], %d tuples)", + JoinDebugStringPtr(vars_, ", "), tuple_count_); } private: @@ -1097,8 +1085,9 @@ class SmallCompactPositiveTableConstraint : public BasePositiveTableConstraint { } std::string DebugString() const override { - return StringPrintf("SmallCompactPositiveTableConstraint([%s], %d tuples)", - JoinDebugStringPtr(vars_, ", ").c_str(), tuple_count_); + return absl::StrFormat( + "SmallCompactPositiveTableConstraint([%s], %d tuples)", + JoinDebugStringPtr(vars_, ", "), tuple_count_); } private: @@ -1211,18 +1200,9 @@ class TransitionConstraint : public Constraint { tmp_vars[1] = vars_[var_index]; tmp_vars[2] = states[var_index + 1]; // We always build the compact versions of the tables. - const ConstraintSolverParameters& params = solver()->parameters(); if (num_tuples <= kBitsInUint64) { s->AddConstraint(s->RevAlloc(new SmallCompactPositiveTableConstraint( s, tmp_vars, transition_table_))); - } else if (params.use_sat_table() && - num_tuples > params.ac4r_table_threshold()) { - s->AddConstraint( - BuildSatTableConstraint(s, tmp_vars, transition_table_)); - } else if (params.use_mdd_table() && - num_tuples > params.ac4r_table_threshold()) { - s->AddConstraint( - BuildAc4MddResetTableConstraint(s, transition_table_, tmp_vars)); } else { s->AddConstraint(s->RevAlloc(new CompactPositiveTableConstraint( s, tmp_vars, transition_table_))); @@ -1245,11 +1225,11 @@ class TransitionConstraint : public Constraint { } std::string DebugString() const override { - return StringPrintf( + return absl::StrFormat( "TransitionConstraint([%s], %d transitions, initial = %" GG_LL_FORMAT "d, final = [%s])", - JoinDebugStringPtr(vars_, ", ").c_str(), transition_table_.NumTuples(), - initial_state_, absl::StrJoin(final_states_, ", ").c_str()); + JoinDebugStringPtr(vars_, ", "), transition_table_.NumTuples(), + initial_state_, absl::StrJoin(final_states_, ", ")); } private: @@ -1272,10 +1252,7 @@ const int TransitionConstraint::kTransitionTupleSize = 3; Constraint* Solver::MakeAllowedAssignments(const std::vector& vars, const IntTupleSet& tuples) { - if (parameters_.use_sat_table()) { - return BuildSatTableConstraint(this, vars, tuples); - } - if (parameters_.use_compact_table() && HasCompactDomains(vars)) { + if (HasCompactDomains(vars)) { if (tuples.NumTuples() < kBitsInUint64 && parameters_.use_small_table()) { return RevAlloc( new SmallCompactPositiveTableConstraint(this, vars, tuples)); @@ -1283,15 +1260,7 @@ Constraint* Solver::MakeAllowedAssignments(const std::vector& vars, return RevAlloc(new CompactPositiveTableConstraint(this, vars, tuples)); } } - if (tuples.NumTuples() > parameters_.ac4r_table_threshold()) { - if (parameters_.use_mdd_table()) { - return BuildAc4MddResetTableConstraint(this, tuples, vars); - } else { - return BuildAc4TableConstraint(this, tuples, vars); - } - } else { - return RevAlloc(new PositiveTableConstraint(this, vars, tuples)); - } + return RevAlloc(new PositiveTableConstraint(this, vars, tuples)); } Constraint* Solver::MakeTransitionConstraint( diff --git a/ortools/constraint_solver/timetabling.cc b/ortools/constraint_solver/timetabling.cc index 1d4963d1b12..3f21706d9f9 100644 --- a/ortools/constraint_solver/timetabling.cc +++ b/ortools/constraint_solver/timetabling.cc @@ -13,10 +13,10 @@ #include +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -47,8 +47,8 @@ class IntervalUnaryRelation : public Constraint { void InitialPropagate() override; std::string DebugString() const override { - return StringPrintf("(%s %s %" GG_LL_FORMAT "d)", t_->DebugString().c_str(), - kUnaryNames[rel_], d_); + return absl::StrFormat("(%s %s %" GG_LL_FORMAT "d)", t_->DebugString(), + kUnaryNames[rel_], d_); } void Accept(ModelVisitor* const visitor) const override { @@ -132,8 +132,8 @@ class IntervalBinaryRelation : public Constraint { void InitialPropagate() override; std::string DebugString() const override { - return StringPrintf("(%s %s %s)", t1_->DebugString().c_str(), - kBinaryNames[rel_], t2_->DebugString().c_str()); + return absl::StrFormat("(%s %s %s)", t1_->DebugString(), kBinaryNames[rel_], + t2_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -308,10 +308,10 @@ void TemporalDisjunction::InitialPropagate() { std::string TemporalDisjunction::DebugString() const { std::string out; - SStringPrintf(&out, "TemporalDisjunction(%s, %s", t1_->DebugString().c_str(), - t2_->DebugString().c_str()); + (out = absl::StrFormat("TemporalDisjunction(%s, %s", t1_->DebugString(), + t2_->DebugString())); if (alt_ != nullptr) { - StringAppendF(&out, " => %s", alt_->DebugString().c_str()); + absl::StrAppendFormat(&out, " => %s", alt_->DebugString()); } out += ") "; return out; diff --git a/ortools/constraint_solver/trace.cc b/ortools/constraint_solver/trace.cc index 1704730400f..b6d6f8ef613 100644 --- a/ortools/constraint_solver/trace.cc +++ b/ortools/constraint_solver/trace.cc @@ -15,15 +15,15 @@ #include #include #include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" @@ -457,8 +457,7 @@ class PrintTrace : public PropagationMonitor { } void BeginNextDecision(DecisionBuilder* const b) override { - DisplaySearch( - StringPrintf("DecisionBuilder(%s)", b->DebugString().c_str())); + DisplaySearch(absl::StrFormat("DecisionBuilder(%s)", b->DebugString())); IncreaseIndent(); contexes_.top().in_decision_builder = true; } @@ -475,18 +474,19 @@ class PrintTrace : public PropagationMonitor { DecreaseIndent(); LOG(INFO) << Indent() << "}"; } - DisplaySearch(StringPrintf("Failure at depth %d", solver()->SearchDepth())); + DisplaySearch( + absl::StrFormat("Failure at depth %d", solver()->SearchDepth())); } bool AtSolution() override { DisplaySearch( - StringPrintf("Solution found at depth %d", solver()->SearchDepth())); + absl::StrFormat("Solution found at depth %d", solver()->SearchDepth())); return false; } void ApplyDecision(Decision* const decision) override { DisplaySearch( - StringPrintf("ApplyDecision(%s)", decision->DebugString().c_str())); + absl::StrFormat("ApplyDecision(%s)", decision->DebugString())); IncreaseIndent(); contexes_.top().in_decision = true; } @@ -497,7 +497,7 @@ class PrintTrace : public PropagationMonitor { contexes_.top().in_objective = false; } DisplaySearch( - StringPrintf("RefuteDecision(%s)", decision->DebugString().c_str())); + absl::StrFormat("RefuteDecision(%s)", decision->DebugString())); IncreaseIndent(); contexes_.top().in_decision = true; } @@ -533,7 +533,7 @@ class PrintTrace : public PropagationMonitor { void BeginConstraintInitialPropagation( Constraint* const constraint) override { PushDelayedInfo( - StringPrintf("Constraint(%s)", constraint->DebugString().c_str())); + absl::StrFormat("Constraint(%s)", constraint->DebugString())); contexes_.top().in_constraint = true; } @@ -544,8 +544,7 @@ class PrintTrace : public PropagationMonitor { void BeginNestedConstraintInitialPropagation( Constraint* const parent, Constraint* const nested) override { - PushDelayedInfo( - StringPrintf("Constraint(%s)", nested->DebugString().c_str())); + PushDelayedInfo(absl::StrFormat("Constraint(%s)", nested->DebugString())); contexes_.top().in_constraint = true; } void EndNestedConstraintInitialPropagation(Constraint* const, @@ -559,7 +558,7 @@ class PrintTrace : public PropagationMonitor { void BeginDemonRun(Demon* const demon) override { if (demon->priority() != Solver::VAR_PRIORITY) { contexes_.top().in_demon = true; - PushDelayedInfo(StringPrintf("Demon(%s)", demon->DebugString().c_str())); + PushDelayedInfo(absl::StrFormat("Demon(%s)", demon->DebugString())); } } @@ -571,8 +570,7 @@ class PrintTrace : public PropagationMonitor { } void StartProcessingIntegerVariable(IntVar* const var) override { - PushDelayedInfo( - StringPrintf("StartProcessing(%s)", var->DebugString().c_str())); + PushDelayedInfo(absl::StrFormat("StartProcessing(%s)", var->DebugString())); } void EndProcessingIntegerVariable(IntVar* const var) override { @@ -589,152 +587,145 @@ class PrintTrace : public PropagationMonitor { void SetMin(IntExpr* const expr, int64 new_min) override { DisplayModification( - StringPrintf("SetMin(%s, %lld)", expr->DebugString().c_str(), new_min)); + absl::StrFormat("SetMin(%s, %d)", expr->DebugString(), new_min)); } void SetMax(IntExpr* const expr, int64 new_max) override { DisplayModification( - StringPrintf("SetMax(%s, %lld)", expr->DebugString().c_str(), new_max)); + absl::StrFormat("SetMax(%s, %d)", expr->DebugString(), new_max)); } void SetRange(IntExpr* const expr, int64 new_min, int64 new_max) override { - DisplayModification(StringPrintf("SetRange(%s, [%lld .. %lld])", - expr->DebugString().c_str(), new_min, - new_max)); + DisplayModification(absl::StrFormat("SetRange(%s, [%d .. %d])", + expr->DebugString(), new_min, new_max)); } // ----- IntVar modifiers ----- void SetMin(IntVar* const var, int64 new_min) override { DisplayModification( - StringPrintf("SetMin(%s, %lld)", var->DebugString().c_str(), new_min)); + absl::StrFormat("SetMin(%s, %d)", var->DebugString(), new_min)); } void SetMax(IntVar* const var, int64 new_max) override { DisplayModification( - StringPrintf("SetMax(%s, %lld)", var->DebugString().c_str(), new_max)); + absl::StrFormat("SetMax(%s, %d)", var->DebugString(), new_max)); } void SetRange(IntVar* const var, int64 new_min, int64 new_max) override { - DisplayModification(StringPrintf("SetRange(%s, [%lld .. %lld])", - var->DebugString().c_str(), new_min, - new_max)); + DisplayModification(absl::StrFormat("SetRange(%s, [%d .. %d])", + var->DebugString(), new_min, new_max)); } void RemoveValue(IntVar* const var, int64 value) override { - DisplayModification(StringPrintf("RemoveValue(%s, %lld)", - var->DebugString().c_str(), value)); + DisplayModification( + absl::StrFormat("RemoveValue(%s, %d)", var->DebugString(), value)); } void SetValue(IntVar* const var, int64 value) override { DisplayModification( - StringPrintf("SetValue(%s, %lld)", var->DebugString().c_str(), value)); + absl::StrFormat("SetValue(%s, %d)", var->DebugString(), value)); } void RemoveInterval(IntVar* const var, int64 imin, int64 imax) override { - DisplayModification(StringPrintf("RemoveInterval(%s, [%lld .. %lld])", - var->DebugString().c_str(), imin, imax)); + DisplayModification(absl::StrFormat("RemoveInterval(%s, [%d .. %d])", + var->DebugString(), imin, imax)); } void SetValues(IntVar* const var, const std::vector& values) override { - DisplayModification(StringPrintf("SetValues(%s, %s)", - var->DebugString().c_str(), - absl::StrJoin(values, ", ").c_str())); + DisplayModification(absl::StrFormat("SetValues(%s, %s)", var->DebugString(), + absl::StrJoin(values, ", "))); } void RemoveValues(IntVar* const var, const std::vector& values) override { - DisplayModification(StringPrintf("RemoveValues(%s, %s)", - var->DebugString().c_str(), - absl::StrJoin(values, ", ").c_str())); + DisplayModification(absl::StrFormat("RemoveValues(%s, %s)", + var->DebugString(), + absl::StrJoin(values, ", "))); } // ----- IntervalVar modifiers ----- void SetStartMin(IntervalVar* const var, int64 new_min) override { - DisplayModification(StringPrintf("SetStartMin(%s, %lld)", - var->DebugString().c_str(), new_min)); + DisplayModification( + absl::StrFormat("SetStartMin(%s, %d)", var->DebugString(), new_min)); } void SetStartMax(IntervalVar* const var, int64 new_max) override { - DisplayModification(StringPrintf("SetStartMax(%s, %lld)", - var->DebugString().c_str(), new_max)); + DisplayModification( + absl::StrFormat("SetStartMax(%s, %d)", var->DebugString(), new_max)); } void SetStartRange(IntervalVar* const var, int64 new_min, int64 new_max) override { - DisplayModification(StringPrintf("SetStartRange(%s, [%lld .. %lld])", - var->DebugString().c_str(), new_min, - new_max)); + DisplayModification(absl::StrFormat("SetStartRange(%s, [%d .. %d])", + var->DebugString(), new_min, new_max)); } void SetEndMin(IntervalVar* const var, int64 new_min) override { - DisplayModification(StringPrintf("SetEndMin(%s, %lld)", - var->DebugString().c_str(), new_min)); + DisplayModification( + absl::StrFormat("SetEndMin(%s, %d)", var->DebugString(), new_min)); } void SetEndMax(IntervalVar* const var, int64 new_max) override { - DisplayModification(StringPrintf("SetEndMax(%s, %lld)", - var->DebugString().c_str(), new_max)); + DisplayModification( + absl::StrFormat("SetEndMax(%s, %d)", var->DebugString(), new_max)); } void SetEndRange(IntervalVar* const var, int64 new_min, int64 new_max) override { - DisplayModification(StringPrintf("SetEndRange(%s, [%lld .. %lld])", - var->DebugString().c_str(), new_min, - new_max)); + DisplayModification(absl::StrFormat("SetEndRange(%s, [%d .. %d])", + var->DebugString(), new_min, new_max)); } void SetDurationMin(IntervalVar* const var, int64 new_min) override { - DisplayModification(StringPrintf("SetDurationMin(%s, %lld)", - var->DebugString().c_str(), new_min)); + DisplayModification( + absl::StrFormat("SetDurationMin(%s, %d)", var->DebugString(), new_min)); } void SetDurationMax(IntervalVar* const var, int64 new_max) override { - DisplayModification(StringPrintf("SetDurationMax(%s, %lld)", - var->DebugString().c_str(), new_max)); + DisplayModification( + absl::StrFormat("SetDurationMax(%s, %d)", var->DebugString(), new_max)); } void SetDurationRange(IntervalVar* const var, int64 new_min, int64 new_max) override { - DisplayModification(StringPrintf("SetDurationRange(%s, [%lld .. %lld])", - var->DebugString().c_str(), new_min, - new_max)); + DisplayModification(absl::StrFormat("SetDurationRange(%s, [%d .. %d])", + var->DebugString(), new_min, new_max)); } void SetPerformed(IntervalVar* const var, bool value) override { - DisplayModification(StringPrintf("SetPerformed(%s, %d)", - var->DebugString().c_str(), value)); + DisplayModification( + absl::StrFormat("SetPerformed(%s, %d)", var->DebugString(), value)); } void RankFirst(SequenceVar* const var, int index) override { DisplayModification( - StringPrintf("RankFirst(%s, %d)", var->DebugString().c_str(), index)); + absl::StrFormat("RankFirst(%s, %d)", var->DebugString(), index)); } void RankNotFirst(SequenceVar* const var, int index) override { - DisplayModification(StringPrintf("RankNotFirst(%s, %d)", - var->DebugString().c_str(), index)); + DisplayModification( + absl::StrFormat("RankNotFirst(%s, %d)", var->DebugString(), index)); } void RankLast(SequenceVar* const var, int index) override { DisplayModification( - StringPrintf("RankLast(%s, %d)", var->DebugString().c_str(), index)); + absl::StrFormat("RankLast(%s, %d)", var->DebugString(), index)); } void RankNotLast(SequenceVar* const var, int index) override { DisplayModification( - StringPrintf("RankNotLast(%s, %d)", var->DebugString().c_str(), index)); + absl::StrFormat("RankNotLast(%s, %d)", var->DebugString(), index)); } void RankSequence(SequenceVar* const var, const std::vector& rank_first, const std::vector& rank_last, const std::vector& unperformed) override { - DisplayModification(StringPrintf( + DisplayModification(absl::StrFormat( "RankSequence(%s, forward [%s], backward[%s], unperformed[%s])", - var->DebugString().c_str(), absl::StrJoin(rank_first, ", ").c_str(), - absl::StrJoin(rank_last, ", ").c_str(), - absl::StrJoin(unperformed, ", ").c_str())); + var->DebugString(), absl::StrJoin(rank_first, ", "), + absl::StrJoin(rank_last, ", "), absl::StrJoin(unperformed, ", "))); } void Install() override { @@ -808,7 +799,7 @@ class PrintTrace : public PropagationMonitor { // RefuteDecision() callbacks must be from the objective. // In that case, we push the in_objective context. CHECK(contexes_.top().TopLevel()); - DisplaySearch(StringPrintf("Objective -> %s", to_print.c_str())); + DisplaySearch(absl::StrFormat("Objective -> %s", to_print)); IncreaseIndent(); contexes_.top().in_objective = true; } diff --git a/ortools/constraint_solver/tree_monitor.cc b/ortools/constraint_solver/tree_monitor.cc index 46e839053e9..499be36c2bc 100644 --- a/ortools/constraint_solver/tree_monitor.cc +++ b/ortools/constraint_solver/tree_monitor.cc @@ -17,20 +17,21 @@ #include #include #include -#include #include #include #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "ortools/base/file.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/util/xml_helper.h" @@ -179,7 +180,7 @@ class TreeDecisionVisitor : public DecisionVisitor { // not support this. class TreeMonitor : public SearchMonitor { public: - typedef std::unordered_map IntVarMap; + typedef absl::flat_hash_map IntVarMap; TreeMonitor(Solver* const solver, const IntVar* const* vars, int size, const std::string& filename_tree, @@ -238,7 +239,7 @@ class TreeMonitor : public SearchMonitor { const std::string filename_visualizer_; int id_counter_; std::string last_decision_; - std::unordered_map last_value_; + absl::flat_hash_map last_value_; std::string last_variable_; int64 min_; int64 max_; @@ -311,7 +312,7 @@ class TreeNode { // Adds a new child, initializes it and returns the corresponding pointer. bool AddChild(int id, const std::string& name, - std::unordered_map const& last_value, + absl::flat_hash_map const& last_value, bool is_final_node, TreeMonitor::IntVarMap const& vars, TreeNode** child) { CHECK(child != nullptr); @@ -368,14 +369,15 @@ class TreeNode { current.size() == (current.back() - current[0] + 1)) { // Use %d .. %d format. visualization_writer->AddAttribute( - "domain", StringPrintf("%" GG_LL_FORMAT "d .. %" GG_LL_FORMAT "d", - current[0], current.back())); + "domain", + absl::StrFormat("%" GG_LL_FORMAT "d .. %" GG_LL_FORMAT "d", + current[0], current.back())); } else { // Use list of integers std::string domain; for (int j = 0; j < current.size(); ++j) { - StringAppendF(&domain, " %" GG_LL_FORMAT "d", current[j]); + absl::StrAppendFormat(&domain, " %" GG_LL_FORMAT "d", current[j]); } visualization_writer->AddAttribute( @@ -409,7 +411,7 @@ class TreeNode { void GenerateTreeXML(XmlHelper* const tree_writer) { CHECK(tree_writer != nullptr); - // The solution element is preceeded by a try element. + // The solution element is preceded by a try element. const char* kElementName[] = {"root", "try", "fail", "try"}; if (node_type_ == ROOT) { @@ -571,9 +573,9 @@ void TreeMonitor::Init(const IntVar* const* vars, int size) { } void TreeMonitor::EnterSearch() { - if (!root_node_.get()) { + if (!root_node_) { id_counter_ = 0; - root_node_.reset(new TreeNode(nullptr, id_counter_++)); + root_node_ = absl::make_unique(nullptr, id_counter_++); root_node_->set_node_type(TreeNode::ROOT); root_node_->SetDomain(vars_); current_node_ = root_node_.get(); @@ -688,7 +690,7 @@ std::string TreeMonitor::GenerateVisualizationXML() const { } std::string TreeMonitor::DebugString() const { - return StringPrintf("TreeMonitor:\n%s", GenerateTreeXML().c_str()); + return absl::StrFormat("TreeMonitor:\n%s", GenerateTreeXML()); } void TreeMonitor::ExitSearch() { diff --git a/ortools/constraint_solver/utilities.cc b/ortools/constraint_solver/utilities.cc index 082cdac7aa4..3f545131eb8 100644 --- a/ortools/constraint_solver/utilities.cc +++ b/ortools/constraint_solver/utilities.cc @@ -12,15 +12,15 @@ // limitations under the License. #include -#include -#include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/util/bitset.h" @@ -419,7 +419,7 @@ class PrintModelVisitor : public ModelVisitor { if (j != 0) { array.append(", "); } - StringAppendF(&array, "%lld", values.Value(i, j)); + absl::StrAppendFormat(&array, "%d", values.Value(i, j)); } array.append("]"); } @@ -429,7 +429,7 @@ class PrintModelVisitor : public ModelVisitor { void VisitIntegerExpressionArgument(const std::string& arg_name, IntExpr* const argument) override { - set_prefix(StringPrintf("%s: ", arg_name.c_str())); + set_prefix(absl::StrFormat("%s: ", arg_name)); Increase(); argument->Accept(this); Decrease(); @@ -450,7 +450,7 @@ class PrintModelVisitor : public ModelVisitor { // Visit interval argument. void VisitIntervalArgument(const std::string& arg_name, IntervalVar* const argument) override { - set_prefix(StringPrintf("%s: ", arg_name.c_str())); + set_prefix(absl::StrFormat("%s: ", arg_name)); Increase(); argument->Accept(this); Decrease(); @@ -470,7 +470,7 @@ class PrintModelVisitor : public ModelVisitor { // Visit sequence argument. void VisitSequenceArgument(const std::string& arg_name, SequenceVar* const argument) override { - set_prefix(StringPrintf("%s: ", arg_name.c_str())); + set_prefix(absl::StrFormat("%s: ", arg_name)); Increase(); argument->Accept(this); Decrease(); @@ -690,9 +690,9 @@ class ModelStatisticsVisitor : public ModelVisitor { extension_types_[extension_type]++; } - std::unordered_map constraint_types_; - std::unordered_map expression_types_; - std::unordered_map extension_types_; + absl::flat_hash_map constraint_types_; + absl::flat_hash_map expression_types_; + absl::flat_hash_map extension_types_; int num_constraints_; int num_variables_; int num_expressions_; @@ -700,7 +700,7 @@ class ModelStatisticsVisitor : public ModelVisitor { int num_intervals_; int num_sequences_; int num_extensions_; - std::unordered_set already_visited_; + absl::flat_hash_set already_visited_; }; // ---------- Variable Degree Visitor --------- @@ -708,7 +708,7 @@ class ModelStatisticsVisitor : public ModelVisitor { class VariableDegreeVisitor : public ModelVisitor { public: explicit VariableDegreeVisitor( - std::unordered_map* const map) + absl::flat_hash_map* const map) : map_(map) {} ~VariableDegreeVisitor() override {} @@ -800,7 +800,7 @@ class VariableDegreeVisitor : public ModelVisitor { object->Accept(this); } - std::unordered_map* const map_; + absl::flat_hash_map* const map_; }; } // namespace @@ -813,7 +813,7 @@ ModelVisitor* Solver::MakeStatisticsModelVisitor() { } ModelVisitor* Solver::MakeVariableDegreeVisitor( - std::unordered_map* const map) { + absl::flat_hash_map* const map) { return RevAlloc(new VariableDegreeVisitor(map)); } diff --git a/ortools/constraint_solver/visitor.cc b/ortools/constraint_solver/visitor.cc index 8047c15d5a6..157c732043d 100644 --- a/ortools/constraint_solver/visitor.cc +++ b/ortools/constraint_solver/visitor.cc @@ -13,10 +13,10 @@ #include #include -#include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" diff --git a/ortools/data/CMakeLists.txt b/ortools/data/CMakeLists.txt index 2c9883861cc..b6911ea1703 100644 --- a/ortools/data/CMakeLists.txt +++ b/ortools/data/CMakeLists.txt @@ -4,39 +4,48 @@ set(NAME ${PROJECT_NAME}_data) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE +# absl::strings +# gflags::gflags glog::glog # protobuf::libprotobuf -# gflags::gflags -# glog::glog # Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + absl::strings + gflags::gflags glog::glog + protobuf::libprotobuf + Cbc::Cbc + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::data ALIAS ${NAME}) diff --git a/ortools/data/jobshop_scheduling_parser.cc b/ortools/data/jobshop_scheduling_parser.cc index 3fa76bf656a..9fa537c9253 100644 --- a/ortools/data/jobshop_scheduling_parser.cc +++ b/ortools/data/jobshop_scheduling_parser.cc @@ -15,13 +15,12 @@ #include +#include "absl/strings/str_split.h" #include "google/protobuf/wrappers.pb.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/filelineiter.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/split.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/strtoint.h" #include "ortools/data/jobshop_scheduling.pb.h" @@ -133,7 +132,7 @@ void JsspParser::ProcessJsspLine(const std::string& line) { break; } case JOB_COUNT_READ: { - CHECK_EQ(words.size(), declared_machine_count_ * 2); + CHECK_GE(words.size(), declared_machine_count_ * 2); Job* const job = problem_.mutable_jobs(current_job_index_); for (int i = 0; i < declared_machine_count_; ++i) { const int machine_id = atoi32(words[2 * i]); diff --git a/ortools/data/jobshop_scheduling_parser.h b/ortools/data/jobshop_scheduling_parser.h index a9c99534b0c..cb2e2f904d5 100644 --- a/ortools/data/jobshop_scheduling_parser.h +++ b/ortools/data/jobshop_scheduling_parser.h @@ -14,7 +14,7 @@ #ifndef OR_TOOLS_DATA_JOBSHOP_SCHEDULING_PARSER_H_ #define OR_TOOLS_DATA_JOBSHOP_SCHEDULING_PARSER_H_ -#include "ortools/base/match.h" +#include "absl/strings/match.h" #include "ortools/data/jobshop_scheduling.pb.h" namespace operations_research { diff --git a/ortools/data/rcpsp_parser.cc b/ortools/data/rcpsp_parser.cc index d712e4fefae..7021e4a64a8 100644 --- a/ortools/data/rcpsp_parser.cc +++ b/ortools/data/rcpsp_parser.cc @@ -13,15 +13,13 @@ #include "ortools/data/rcpsp_parser.h" +#include "absl/strings/match.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" #include "ortools/base/filelineiter.h" -#include "ortools/base/numbers.h" -#include "ortools/base/split.h" -#include "ortools/base/stringpiece_utils.h" #include "ortools/base/strtoint.h" #include "ortools/data/rcpsp.pb.h" -using ::absl::delimiter::AnyOf; - namespace operations_research { namespace data { namespace rcpsp { @@ -41,9 +39,9 @@ bool RcpspParser::ParseFile(const std::string& file_name) { return false; } - const bool is_rcpsp_max = strings::EndsWith(file_name, ".sch") || - strings::EndsWith(file_name, ".SCH"); - const bool is_patterson = strings::EndsWith(file_name, ".rcp"); + const bool is_rcpsp_max = + absl::EndsWith(file_name, ".sch") || absl::EndsWith(file_name, ".SCH"); + const bool is_patterson = absl::EndsWith(file_name, ".rcp"); load_status_ = HEADER_SECTION; for (const std::string& line : FileLines(file_name)) { @@ -79,11 +77,11 @@ void RcpspParser::SetNumDeclaredTasks(int t) { } void RcpspParser::ProcessRcpspLine(const std::string& line) { - if (strings::StartsWith(line, "***")) return; - if (strings::StartsWith(line, "---")) return; + if (absl::StartsWith(line, "***")) return; + if (absl::StartsWith(line, "---")) return; const std::vector words = - absl::StrSplit(line, AnyOf(" :\t\r"), absl::SkipEmpty()); + absl::StrSplit(line, absl::ByAnyChar(" :\t\r"), absl::SkipEmpty()); if (words.empty()) return; @@ -256,7 +254,7 @@ void RcpspParser::ProcessRcpspLine(const std::string& line) { void RcpspParser::ProcessRcpspMaxLine(const std::string& line) { const std::vector words = - absl::StrSplit(line, AnyOf(" :\t[]\r"), absl::SkipEmpty()); + absl::StrSplit(line, absl::ByAnyChar(" :\t[]\r"), absl::SkipEmpty()); switch (load_status_) { case NOT_STARTED: { @@ -485,7 +483,7 @@ void RcpspParser::ProcessRcpspMaxLine(const std::string& line) { void RcpspParser::ProcessPattersonLine(const std::string& line) { const std::vector words = - absl::StrSplit(line, AnyOf(" :\t[]\r"), absl::SkipEmpty()); + absl::StrSplit(line, absl::ByAnyChar(" :\t[]\r"), absl::SkipEmpty()); if (words.empty()) return; diff --git a/ortools/data/set_covering_parser.cc b/ortools/data/set_covering_parser.cc index ff13543dec1..4800d346924 100644 --- a/ortools/data/set_covering_parser.cc +++ b/ortools/data/set_covering_parser.cc @@ -13,16 +13,14 @@ #include "ortools/data/set_covering_parser.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" #include "ortools/base/filelineiter.h" -#include "ortools/base/numbers.h" -#include "ortools/base/split.h" #include "ortools/base/strtoint.h" namespace operations_research { namespace scp { -using ::absl::delimiter::AnyOf; - ScpParser::ScpParser() : section_(INIT), line_(0), remaining_(0), current_(0) {} bool ScpParser::LoadProblem(const std::string& filename, Format format, @@ -43,7 +41,7 @@ void ScpParser::ProcessLine(const std::string& line, Format format, ScpData* data) { line_++; const std::vector words = - absl::StrSplit(line, AnyOf(" :\t\r"), absl::SkipEmpty()); + absl::StrSplit(line, absl::ByAnyChar(" :\t\r"), absl::SkipEmpty()); switch (section_) { case INIT: { if (words.size() != 2) { diff --git a/ortools/dotnet/Google.OrTools.runtime.linux-x64/Google.OrTools.runtime.linux-x64.csproj.in b/ortools/dotnet/Google.OrTools.runtime.linux-x64/Google.OrTools.runtime.linux-x64.csproj.in index 7ab9661253a..a7677e71b5f 100644 --- a/ortools/dotnet/Google.OrTools.runtime.linux-x64/Google.OrTools.runtime.linux-x64.csproj.in +++ b/ortools/dotnet/Google.OrTools.runtime.linux-x64/Google.OrTools.runtime.linux-x64.csproj.in @@ -64,6 +64,9 @@ sat/%(Filename)%(Extension) + + util/%(Filename)%(Extension) + util/%(Filename)%(Extension) diff --git a/ortools/dotnet/Google.OrTools.runtime.osx-x64/Google.OrTools.runtime.osx-x64.csproj.in b/ortools/dotnet/Google.OrTools.runtime.osx-x64/Google.OrTools.runtime.osx-x64.csproj.in index 47a9ffcc0f9..32a7ea45bec 100644 --- a/ortools/dotnet/Google.OrTools.runtime.osx-x64/Google.OrTools.runtime.osx-x64.csproj.in +++ b/ortools/dotnet/Google.OrTools.runtime.osx-x64/Google.OrTools.runtime.osx-x64.csproj.in @@ -62,6 +62,9 @@ sat/%(Filename)%(Extension) + + util/%(Filename)%(Extension) + util/%(Filename)%(Extension) diff --git a/ortools/dotnet/Google.OrTools.runtime.win-x64/Google.OrTools.runtime.win-x64.csproj.in b/ortools/dotnet/Google.OrTools.runtime.win-x64/Google.OrTools.runtime.win-x64.csproj.in index cea9d7f4ff0..8626b0e4303 100644 --- a/ortools/dotnet/Google.OrTools.runtime.win-x64/Google.OrTools.runtime.win-x64.csproj.in +++ b/ortools/dotnet/Google.OrTools.runtime.win-x64/Google.OrTools.runtime.win-x64.csproj.in @@ -62,6 +62,9 @@ sat/%(Filename)%(Extension) + + util/%(Filename)%(Extension) + util/%(Filename)%(Extension) diff --git a/ortools/flatzinc/checker.cc b/ortools/flatzinc/checker.cc index c40d2c46b2a..9c1dc542de4 100644 --- a/ortools/flatzinc/checker.cc +++ b/ortools/flatzinc/checker.cc @@ -14,9 +14,9 @@ #include "ortools/flatzinc/checker.h" #include -#include -#include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "ortools/base/map_util.h" #include "ortools/flatzinc/logging.h" #include "ortools/flatzinc/model.h" @@ -68,7 +68,7 @@ int64 EvalAt(const Argument& arg, int pos, bool CheckAllDifferentInt( const Constraint& ct, const std::function& evaluator) { - std::unordered_set visited; + absl::flat_hash_set visited; for (int i = 0; i < Size(ct.arguments[0]); ++i) { const int64 value = EvalAt(ct.arguments[0], i, evaluator); if (gtl::ContainsKey(visited, value)) { @@ -83,7 +83,7 @@ bool CheckAllDifferentInt( bool CheckAlldifferentExcept0( const Constraint& ct, const std::function& evaluator) { - std::unordered_set visited; + absl::flat_hash_set visited; for (int i = 0; i < Size(ct.arguments[0]); ++i) { const int64 value = EvalAt(ct.arguments[0], i, evaluator); if (value != 0 && gtl::ContainsKey(visited, value)) { @@ -260,7 +260,7 @@ bool CheckCircuit(const Constraint& ct, } } - std::unordered_set visited; + absl::flat_hash_set visited; int64 current = 0; for (int i = 0; i < Size(ct.arguments[0]); ++i) { const int64 next = EvalAt(ct.arguments[0], current, evaluator) + shift; @@ -337,7 +337,7 @@ bool CheckCumulative(const Constraint& ct, const int size = Size(ct.arguments[0]); CHECK_EQ(size, Size(ct.arguments[1])); CHECK_EQ(size, Size(ct.arguments[2])); - std::unordered_map usage; + absl::flat_hash_map usage; for (int i = 0; i < size; ++i) { const int64 start = EvalAt(ct.arguments[0], i, evaluator); const int64 duration = EvalAt(ct.arguments[1], i, evaluator); @@ -395,7 +395,7 @@ std::vector ComputeGlobalCardinalityCards( const Constraint& ct, const std::function& evaluator) { std::vector cards(Size(ct.arguments[1]), 0); - std::unordered_map positions; + absl::flat_hash_map positions; for (int i = 0; i < ct.arguments[1].values.size(); ++i) { const int64 value = ct.arguments[1].values[i]; CHECK(!gtl::ContainsKey(positions, value)); @@ -899,7 +899,7 @@ bool CheckNetworkFlowCost( bool CheckNvalue(const Constraint& ct, const std::function& evaluator) { const int64 count = Eval(ct.arguments[0], evaluator); - std::unordered_set all_values; + absl::flat_hash_set all_values; for (int i = 0; i < Size(ct.arguments[1]); ++i) { all_values.insert(EvalAt(ct.arguments[1], i, evaluator)); } @@ -962,8 +962,8 @@ bool CheckSlidingSum(const Constraint& ct, bool CheckSort(const Constraint& ct, const std::function& evaluator) { CHECK_EQ(Size(ct.arguments[0]), Size(ct.arguments[1])); - std::unordered_map init_count; - std::unordered_map sorted_count; + absl::flat_hash_map init_count; + absl::flat_hash_map sorted_count; for (int i = 0; i < Size(ct.arguments[0]); ++i) { init_count[EvalAt(ct.arguments[0], i, evaluator)]++; sorted_count[EvalAt(ct.arguments[1], i, evaluator)]++; @@ -982,7 +982,7 @@ bool CheckSort(const Constraint& ct, bool CheckSubCircuit(const Constraint& ct, const std::function& evaluator) { - std::unordered_set visited; + absl::flat_hash_set visited; // Find inactive nodes (pointing to themselves). int64 current = -1; for (int i = 0; i < Size(ct.arguments[0]); ++i) { @@ -1031,7 +1031,7 @@ bool CheckSymmetricAllDifferent( return true; } -using CallMap = std::unordered_map< +using CallMap = absl::flat_hash_map< std::string, std::function)>>; diff --git a/ortools/flatzinc/constraints.cc b/ortools/flatzinc/constraints.cc index 73a50d18f16..d63ba3e6afe 100644 --- a/ortools/flatzinc/constraints.cc +++ b/ortools/flatzinc/constraints.cc @@ -14,8 +14,8 @@ #include "ortools/flatzinc/constraints.h" #include -#include +#include "absl/container/flat_hash_set.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" @@ -216,7 +216,7 @@ void ExtractAtMostInt(fz::SolverData* data, fz::Constraint* ct) { void ExtractArrayBoolAnd(fz::SolverData* data, fz::Constraint* ct) { Solver* const solver = data->solver(); std::vector variables; - std::unordered_set added; + absl::flat_hash_set added; const std::vector tmp_vars = data->GetOrCreateVariableArray(ct->arguments[0]); for (IntVar* const to_add : tmp_vars) { @@ -269,7 +269,7 @@ void ExtractArrayBoolAnd(fz::SolverData* data, fz::Constraint* ct) { void ExtractArrayBoolOr(fz::SolverData* data, fz::Constraint* ct) { Solver* const solver = data->solver(); std::vector variables; - std::unordered_set added; + absl::flat_hash_set added; const std::vector tmp_vars = data->GetOrCreateVariableArray(ct->arguments[0]); for (IntVar* const to_add : tmp_vars) { diff --git a/ortools/flatzinc/cp_model_fz_solver.cc b/ortools/flatzinc/cp_model_fz_solver.cc index 17375f99102..24239378141 100644 --- a/ortools/flatzinc/cp_model_fz_solver.cc +++ b/ortools/flatzinc/cp_model_fz_solver.cc @@ -16,15 +16,14 @@ #include #include #include -#include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_split.h" +#include "absl/synchronization/mutex.h" #include "google/protobuf/text_format.h" -#include "ortools/base/join.h" #include "ortools/base/map_util.h" -#include "ortools/base/mutex.h" -#include "ortools/base/split.h" -#include "ortools/base/stringpiece_utils.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/threadpool.h" #include "ortools/base/timer.h" #include "ortools/flatzinc/checker.h" @@ -93,8 +92,8 @@ struct CpModelProtoWithMapping { SatParameters parameters; // Mapping from flatzinc variables to CpModelProto variables. - std::unordered_map fz_var_to_index; - std::unordered_map constant_value_to_index; + absl::flat_hash_map fz_var_to_index; + absl::flat_hash_map constant_value_to_index; }; int CpModelProtoWithMapping::LookupConstant(int64 value) { @@ -224,11 +223,18 @@ void CpModelProtoWithMapping::FillConstraint(const fz::Constraint& fz_ct, FillAMinusBInDomain({kint64min, -1}, fz_ct, ct); } else if (fz_ct.type == "bool_gt" || fz_ct.type == "int_gt") { FillAMinusBInDomain({1, kint64max}, fz_ct, ct); - } else if (fz_ct.type == "bool_eq" || fz_ct.type == "int_eq" || + } else if (fz_ct.type == "bool_eq" || fz_ct.type == "int_eq" || fz_ct.type == "bool2int") { FillAMinusBInDomain({0, 0}, fz_ct, ct); - } else if (fz_ct.type == "bool_ne" || fz_ct.type == "bool_not" || - fz_ct.type == "int_ne") { + } else if (fz_ct.type == "bool_ne" || fz_ct.type == "bool_not") { + auto* arg = ct->mutable_linear(); + arg->add_vars(LookupVar(fz_ct.arguments[0])); + arg->add_coeffs(1); + arg->add_vars(LookupVar(fz_ct.arguments[1])); + arg->add_coeffs(1); + arg->add_domain(1); + arg->add_domain(1); + } else if (fz_ct.type == "int_ne") { FillAMinusBInDomain({kint64min, -1, 1, kint64max}, fz_ct, ct); } else if (fz_ct.type == "int_lin_eq") { const int64 rhs = fz_ct.arguments[2].values[0]; @@ -276,8 +282,9 @@ void CpModelProtoWithMapping::FillConstraint(const fz::Constraint& fz_ct, fz_ct.arguments[1].values.end()}), arg); } else if (fz_ct.arguments[1].type == fz::Argument::INT_INTERVAL) { - FillDomain({{fz_ct.arguments[1].values[0], fz_ct.arguments[1].values[1]}}, - arg); + FillDomainInProto( + Domain(fz_ct.arguments[1].values[0], fz_ct.arguments[1].values[1]), + arg); } else { LOG(FATAL) << "Wrong format"; } @@ -330,7 +337,7 @@ void CpModelProtoWithMapping::FillConstraint(const fz::Constraint& fz_ct, arg->add_vars(-LookupVar(fz_ct.arguments[0]) - 1); } else if (fz_ct.type == "int_plus") { auto* arg = ct->mutable_linear(); - FillDomain({{0, 0}}, arg); + FillDomainInProto(Domain(0, 0), arg); arg->add_vars(LookupVar(fz_ct.arguments[0])); arg->add_coeffs(1); arg->add_vars(LookupVar(fz_ct.arguments[1])); @@ -357,7 +364,7 @@ void CpModelProtoWithMapping::FillConstraint(const fz::Constraint& fz_ct, arg->set_index(LookupVar(fz_ct.arguments[0])); arg->set_target(LookupVar(fz_ct.arguments[2])); - if (!strings::EndsWith(fz_ct.type, "no_offset")) { + if (!absl::EndsWith(fz_ct.type, "no_offset")) { // Add a dummy variable at position zero because flatzinc index start at // 1. // TODO(user): Make sure that zero is not in the index domain... @@ -367,7 +374,7 @@ void CpModelProtoWithMapping::FillConstraint(const fz::Constraint& fz_ct, } else { // Special case added by the presolve (not in flatzinc). We encode this // as a table constraint. - CHECK(!strings::EndsWith(fz_ct.type, "no_offset")); + CHECK(!absl::EndsWith(fz_ct.type, "no_offset")); auto* arg = ct->mutable_table(); // the constraint is: @@ -648,7 +655,7 @@ void CpModelProtoWithMapping::FillReifConstraint(const fz::Constraint& fz_ct, ConstraintProto* ct) { // Start by adding a non-reified version of the same constraint. fz::Constraint copy = fz_ct; - if (strings::EndsWith(fz_ct.type, "_reif")) { + if (absl::EndsWith(fz_ct.type, "_reif")) { copy.type = fz_ct.type.substr(0, fz_ct.type.size() - 5); // Remove _reif. } else { copy.type = fz_ct.type; @@ -881,7 +888,7 @@ void SolveFzWithCpModelProto(const fz::Model& fz_model, if (fz_ct == nullptr || !fz_ct->active) continue; ConstraintProto* ct = m.proto.add_constraints(); ct->set_name(fz_ct->type); - if (strings::EndsWith(fz_ct->type, "_reif") || + if (absl::EndsWith(fz_ct->type, "_reif") || fz_ct->type == "array_bool_or" || fz_ct->type == "array_bool_and") { m.FillReifConstraint(*fz_ct, ct); } else { diff --git a/ortools/flatzinc/flatzinc_constraints.cc b/ortools/flatzinc/flatzinc_constraints.cc index 254f07f3a1e..7be48ea69f5 100644 --- a/ortools/flatzinc/flatzinc_constraints.cc +++ b/ortools/flatzinc/flatzinc_constraints.cc @@ -13,10 +13,9 @@ #include "ortools/flatzinc/flatzinc_constraints.h" -#include - +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/flatzinc/logging.h" #include "ortools/util/string_array.h" @@ -107,7 +106,7 @@ class BooleanSumOdd : public Constraint { std::string DebugString() const override { return absl::StrFormat("BooleanSumOdd([%s])", - JoinDebugStringPtr(vars_, ", ").c_str()); + JoinDebugStringPtr(vars_, ", ")); } void Accept(ModelVisitor* const visitor) const override { @@ -154,8 +153,7 @@ class FixedModulo : public Constraint { std::string DebugString() const override { return absl::StrFormat("(%s %% %s == %" GG_LL_FORMAT "d)", - var_->DebugString().c_str(), - mod_->DebugString().c_str(), residual_); + var_->DebugString(), mod_->DebugString(), residual_); } private: @@ -202,7 +200,7 @@ class VariableParity : public Constraint { } std::string DebugString() const override { - return absl::StrFormat("VarParity(%s, %d)", var_->DebugString().c_str(), odd_); + return absl::StrFormat("VarParity(%s, %d)", var_->DebugString(), odd_); } void Accept(ModelVisitor* const visitor) const override { @@ -307,8 +305,8 @@ class IsBooleanSumInRange : public Constraint { std::string DebugString() const override { return absl::StrFormat("Sum([%s]) in [%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d] == %s", - JoinDebugStringPtr(vars_, ", ").c_str(), range_min_, - range_max_, target_->DebugString().c_str()); + JoinDebugStringPtr(vars_, ", "), range_min_, + range_max_, target_->DebugString()); } void Accept(ModelVisitor* const visitor) const override { @@ -426,7 +424,7 @@ class BooleanSumInRange : public Constraint { std::string DebugString() const override { return absl::StrFormat( "Sum([%s]) in [%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d]", - JoinDebugStringPtr(vars_, ", ").c_str(), range_min_, range_max_); + JoinDebugStringPtr(vars_, ", "), range_min_, range_max_); } void Accept(ModelVisitor* const visitor) const override { @@ -624,8 +622,7 @@ std::string StartVarDurationVarPerformedIntervalVar::DebugString() const { out = "IntervalVar(start = "; } absl::StrAppendFormat(&out, "%s, duration = %s, performed = true)", - start_->DebugString().c_str(), - duration_->DebugString().c_str()); + start_->DebugString(), duration_->DebugString()); return out; } @@ -843,7 +840,7 @@ class KDiffn : public Constraint { const int64 num_boxes_; const int64 num_dims_; Demon* delayed_demon_; - std::unordered_set to_propagate_; + absl::flat_hash_set to_propagate_; std::vector neighbors_; uint64 fail_stamp_; }; diff --git a/ortools/flatzinc/fz.cc b/ortools/flatzinc/fz.cc index c9d6874878d..b2b19708f63 100644 --- a/ortools/flatzinc/fz.cc +++ b/ortools/flatzinc/fz.cc @@ -26,7 +26,6 @@ #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/threadpool.h" #include "ortools/base/timer.h" #include "ortools/flatzinc/cp_model_fz_solver.h" @@ -35,8 +34,6 @@ #include "ortools/flatzinc/parser.h" #include "ortools/flatzinc/presolve.h" #include "ortools/flatzinc/reporting.h" -#include "ortools/flatzinc/solver.h" -#include "ortools/flatzinc/solver_util.h" DEFINE_int32(time_limit, 0, "time limit in ms."); DEFINE_bool(all_solutions, false, "Search for all solutions."); @@ -51,26 +48,10 @@ DEFINE_bool(statistics, false, "Print solver statistics after search."); DEFINE_bool(read_from_stdin, false, "Read the FlatZinc from stdin, not from a file."); DEFINE_int32(fz_seed, 0, "Random seed"); -DEFINE_int32(log_period, 10000000, "Search log period in the CP search."); -DEFINE_bool(use_impact, false, "Use impact based search in free search."); -DEFINE_double(restart_log_size, -1, - "Restart log size parameter for impact search."); -DEFINE_bool(last_conflict, false, - "Use last conflict search hints in free search."); -DEFINE_int32(luby_restart, -1, - "Luby restart factor, <= 0 = no luby in free search."); -DEFINE_int32(heuristic_period, 100, - "Period to call heuristics in free search."); -DEFINE_bool( - verbose_impact, false, - "Increase verbosity of the impact based search when used in free search."); -DEFINE_bool(verbose_mt, false, "Verbose Multi-Thread."); -DEFINE_bool(use_cp_sat, true, "Use the CP/SAT solver."); DEFINE_string(fz_model_name, "stdin", "Define problem name when reading from stdin."); DEFINE_string(params, "", "SatParameters as a text proto."); -DECLARE_bool(fz_use_sat); DECLARE_bool(log_prefix); using operations_research::ThreadPool; @@ -78,100 +59,8 @@ using operations_research::ThreadPool; namespace operations_research { namespace fz { -int FixedNumberOfSolutions() { - return FLAGS_num_solutions == 0 ? // Not fixed. - (FLAGS_num_solutions = FLAGS_all_solutions ? kint32max : 1) - : FLAGS_num_solutions; -} - -FlatzincParameters SingleThreadParameters() { - FlatzincParameters parameters; - parameters.all_solutions = FLAGS_all_solutions; - parameters.free_search = FLAGS_free_search; - parameters.heuristic_period = FLAGS_heuristic_period; - parameters.ignore_unknown = false; - parameters.last_conflict = FLAGS_last_conflict; - parameters.logging = FLAGS_fz_logging; - parameters.log_period = FLAGS_log_period; - parameters.luby_restart = FLAGS_luby_restart; - parameters.num_solutions = FixedNumberOfSolutions(); - parameters.random_seed = FLAGS_fz_seed; - parameters.restart_log_size = FLAGS_restart_log_size; - parameters.search_type = - FLAGS_use_impact ? FlatzincParameters::IBS : FlatzincParameters::DEFAULT; - parameters.statistics = FLAGS_statistics; - parameters.threads = FLAGS_threads; - parameters.thread_id = -1; - parameters.time_limit_in_ms = FLAGS_time_limit; - parameters.verbose_impact = FLAGS_verbose_impact; - return parameters; -} - -FlatzincParameters MultiThreadParameters(int thread_id) { - FlatzincParameters parameters = SingleThreadParameters(); - parameters.logging = thread_id == 0; - parameters.luby_restart = -1; - parameters.random_seed = FLAGS_fz_seed + thread_id * 10; - parameters.thread_id = thread_id; - switch (thread_id) { - case 0: { - parameters.free_search = false; - parameters.last_conflict = false; - parameters.search_type = - operations_research::fz::FlatzincParameters::DEFAULT; - parameters.restart_log_size = -1.0; - break; - } - case 1: { - parameters.free_search = true; - parameters.last_conflict = false; - parameters.search_type = - operations_research::fz::FlatzincParameters::MIN_SIZE; - parameters.restart_log_size = -1.0; - break; - } - case 2: { - parameters.free_search = true; - parameters.last_conflict = false; - parameters.search_type = operations_research::fz::FlatzincParameters::IBS; - parameters.restart_log_size = FLAGS_restart_log_size; - break; - } - case 3: { - parameters.free_search = true; - parameters.last_conflict = false; - parameters.search_type = - operations_research::fz::FlatzincParameters::FIRST_UNBOUND; - parameters.restart_log_size = -1.0; - parameters.heuristic_period = 10000000; - break; - } - case 4: { - parameters.free_search = true; - parameters.last_conflict = false; - parameters.search_type = - operations_research::fz::FlatzincParameters::DEFAULT; - parameters.restart_log_size = -1.0; - parameters.heuristic_period = 30; - parameters.run_all_heuristics = true; - break; - } - default: { - parameters.free_search = true; - parameters.last_conflict = false; - parameters.search_type = - thread_id % 2 == 0 - ? operations_research::fz::FlatzincParameters::RANDOM_MIN - : operations_research::fz::FlatzincParameters::RANDOM_MAX; - parameters.restart_log_size = -1.0; - parameters.luby_restart = 250; - } - } - return parameters; -} - void FixAndParseParameters(int* argc, char*** argv) { - FLAGS_log_prefix = false; + absl::SetFlag(&FLAGS_log_prefix, false); char all_param[] = "--all_solutions"; char free_param[] = "--free_search"; @@ -224,8 +113,8 @@ Model ParseFlatzincModel(const std::string& input, bool input_is_filename) { timer.Start(); // Read model. std::string problem_name = input_is_filename ? input : FLAGS_fz_model_name; - if (input_is_filename || strings::EndsWith(problem_name, ".fzn")) { - CHECK(strings::EndsWith(problem_name, ".fzn")); + if (input_is_filename || absl::EndsWith(problem_name, ".fzn")) { + CHECK(absl::EndsWith(problem_name, ".fzn")); problem_name.resize(problem_name.size() - 4); const size_t found = problem_name.find_last_of("/\\"); if (found != std::string::npos) { @@ -244,7 +133,7 @@ Model ParseFlatzincModel(const std::string& input, bool input_is_filename) { // Presolve the model. Presolver presolve; - presolve.CleanUpModelForTheCpSolver(&model, FLAGS_fz_use_sat); + presolve.CleanUpModelForTheCpSolver(&model, true); if (FLAGS_presolve) { FZLOG << "Presolve model" << FZENDL; timer.Reset(); @@ -260,42 +149,6 @@ Model ParseFlatzincModel(const std::string& input, bool input_is_filename) { return model; } -void Solve(const Model& model) { -#if defined(__GNUC__) - signal(SIGINT, &operations_research::fz::Interrupt::ControlCHandler); -#endif - - if (model.IsInconsistent()) { - MonoThreadReporting reporting(FLAGS_all_solutions, - FixedNumberOfSolutions()); - Solver::ReportInconsistentModel(model, SingleThreadParameters(), - &reporting); - return; - } - - if (FLAGS_threads == 0) { - Solver solver(model); - CHECK(solver.Extract()); - MonoThreadReporting reporting(FLAGS_all_solutions, - FixedNumberOfSolutions()); - solver.Solve(SingleThreadParameters(), &reporting); - } else { - MultiThreadReporting reporting(FLAGS_all_solutions, - FixedNumberOfSolutions(), FLAGS_verbose_mt); - { - ThreadPool pool("Parallel_FlatZinc", FLAGS_threads); - pool.StartWorkers(); - for (int thread_id = 0; thread_id < FLAGS_threads; ++thread_id) { - pool.Schedule([&model, thread_id, &reporting]() { - Solver solver(model); - CHECK(solver.Extract()); - solver.Solve(MultiThreadParameters(thread_id), &reporting); - }); - } - } - } -} - } // namespace fz } // namespace operations_research @@ -317,15 +170,32 @@ int main(int argc, char** argv) { } input = argv[1]; } + operations_research::fz::Model model = operations_research::fz::ParseFlatzincModel(input, !FLAGS_read_from_stdin); + operations_research::fz::FlatzincParameters parameters; + parameters.all_solutions = FLAGS_all_solutions; + parameters.free_search = FLAGS_free_search; + parameters.heuristic_period = -1; + parameters.ignore_unknown = false; + parameters.last_conflict = false; + parameters.logging = FLAGS_fz_logging; + parameters.log_period = 0; + parameters.luby_restart = false; + parameters.num_solutions = + FLAGS_num_solutions == 0 ? // Not fixed. + (FLAGS_num_solutions = FLAGS_all_solutions ? kint32max : 1) + : FLAGS_num_solutions; + parameters.random_seed = FLAGS_fz_seed; + parameters.search_type = operations_research::fz::FlatzincParameters::DEFAULT; + parameters.statistics = FLAGS_statistics; + parameters.threads = FLAGS_threads; + parameters.thread_id = -1; + parameters.time_limit_in_ms = FLAGS_time_limit; + parameters.verbose_impact = false; - if (FLAGS_use_cp_sat) { - operations_research::sat::SolveFzWithCpModelProto( - model, operations_research::fz::SingleThreadParameters(), FLAGS_params); - } else { - operations_research::fz::Solve(model); - } + operations_research::sat::SolveFzWithCpModelProto(model, parameters, + FLAGS_params); return EXIT_SUCCESS; } diff --git a/ortools/flatzinc/model.cc b/ortools/flatzinc/model.cc index affdf8a8194..0c3c58feaf9 100644 --- a/ortools/flatzinc/model.cc +++ b/ortools/flatzinc/model.cc @@ -14,13 +14,14 @@ #include "ortools/flatzinc/model.h" #include -#include #include -#include "ortools/base/join.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "ortools/base/map_util.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/flatzinc/logging.h" namespace operations_research { @@ -214,7 +215,7 @@ bool Domain::IntersectWithListOfIntegers(const std::vector& integers) { } else { // TODO(user): Investigate faster code for small arrays. std::sort(values.begin(), values.end()); - std::unordered_set other_values(integers.begin(), integers.end()); + absl::flat_hash_set other_values(integers.begin(), integers.end()); std::vector new_values; new_values.reserve(std::min(values.size(), integers.size())); bool changed = false; @@ -296,10 +297,10 @@ bool Domain::OverlapsIntList(const std::vector& vec) const { // TODO(user): Better algorithm, sort and compare increasingly. const std::vector& to_scan = values.size() <= vec.size() ? values : vec; - const std::unordered_set container = + const absl::flat_hash_set container = values.size() <= vec.size() - ? std::unordered_set(vec.begin(), vec.end()) - : std::unordered_set(values.begin(), values.end()); + ? absl::flat_hash_set(vec.begin(), vec.end()) + : absl::flat_hash_set(values.begin(), values.end()); for (int64 value : to_scan) { if (gtl::ContainsKey(container, value)) { return true; @@ -377,7 +378,7 @@ std::string Domain::DebugString() const { } else if (values.size() == 1) { return absl::StrCat(values.back()); } else { - return absl::StrFormat("[%s]", absl::StrJoin(values, ", ").c_str()); + return absl::StrFormat("[%s]", absl::StrJoin(values, ", ")); } } @@ -452,9 +453,9 @@ std::string Argument::DebugString() const { return absl::StrFormat("[%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d]", values[0], values[1]); case INT_LIST: - return absl::StrFormat("[%s]", absl::StrJoin(values, ", ").c_str()); + return absl::StrFormat("[%s]", absl::StrJoin(values, ", ")); case DOMAIN_LIST: - return absl::StrFormat("[%s]", JoinDebugString(domains, ", ").c_str()); + return absl::StrFormat("[%s]", JoinDebugString(domains, ", ")); case INT_VAR_REF: return variables[0]->name; case INT_VAR_REF_ARRAY: { @@ -621,7 +622,7 @@ std::string IntegerVariable::DebugString() const { return absl::StrFormat("% " GG_LL_FORMAT "d", domain.values.back()); } else { return absl::StrFormat( - "%s(%s%s%s)%s", name.c_str(), domain.DebugString().c_str(), + "%s(%s%s%s)%s", name, domain.DebugString(), temporary ? ", temporary" : "", defining_constraint != nullptr ? ", target_variable" : "", active ? "" : " [removed during presolve]"); @@ -638,11 +639,11 @@ std::string Constraint::DebugString() const { : "[removed during presolve]"); const std::string target = target_variable != nullptr - ? absl::StrFormat(" => %s", target_variable->name.c_str()) + ? absl::StrFormat(" => %s", target_variable->name) : ""; - return absl::StrFormat("%s(%s)%s %s %s", type.c_str(), - JoinDebugString(arguments, ", ").c_str(), target.c_str(), - strong.c_str(), presolve_status_str.c_str()); + return absl::StrFormat("%s(%s)%s %s %s", type, + JoinDebugString(arguments, ", "), target, strong, + presolve_status_str); } void Constraint::RemoveArg(int arg_pos) { @@ -777,14 +778,13 @@ void Annotation::AppendAllIntegerVariables( std::string Annotation::DebugString() const { switch (type) { case ANNOTATION_LIST: { - return absl::StrFormat("[%s]", JoinDebugString(annotations, ", ").c_str()); + return absl::StrFormat("[%s]", JoinDebugString(annotations, ", ")); } case IDENTIFIER: { return id; } case FUNCTION_CALL: { - return absl::StrFormat("%s(%s)", id.c_str(), - JoinDebugString(annotations, ", ").c_str()); + return absl::StrFormat("%s(%s)", id, JoinDebugString(annotations, ", ")); } case INTERVAL: { return absl::StrFormat("%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d", @@ -805,7 +805,7 @@ std::string Annotation::DebugString() const { return result; } case STRING_VALUE: { - return absl::StrFormat("\"%s\"", string_value.c_str()); + return absl::StrFormat("\"%s\"", string_value); } } LOG(FATAL) << "Unhandled case in DebugString " << static_cast(type); @@ -850,11 +850,11 @@ SolutionOutputSpecs SolutionOutputSpecs::VoidOutput() { std::string SolutionOutputSpecs::DebugString() const { if (variable != nullptr) { - return absl::StrFormat("output_var(%s)", variable->name.c_str()); + return absl::StrFormat("output_var(%s)", variable->name); } else { return absl::StrFormat("output_array([%s] [%s])", - JoinDebugString(bounds, ", ").c_str(), - JoinNameFieldPtr(flat_variables, ", ").c_str()); + JoinDebugString(bounds, ", "), + JoinNameFieldPtr(flat_variables, ", ")); } } @@ -920,27 +920,27 @@ void Model::Maximize(IntegerVariable* obj, } std::string Model::DebugString() const { - std::string output = absl::StrFormat("Model %s\nVariables\n", name_.c_str()); + std::string output = absl::StrFormat("Model %s\nVariables\n", name_); for (int i = 0; i < variables_.size(); ++i) { - absl::StrAppendFormat(&output, " %s\n", variables_[i]->DebugString().c_str()); + absl::StrAppendFormat(&output, " %s\n", variables_[i]->DebugString()); } output.append("Constraints\n"); for (int i = 0; i < constraints_.size(); ++i) { if (constraints_[i] != nullptr) { - absl::StrAppendFormat(&output, " %s\n", constraints_[i]->DebugString().c_str()); + absl::StrAppendFormat(&output, " %s\n", constraints_[i]->DebugString()); } } if (objective_ != nullptr) { - absl::StrAppendFormat(&output, "%s %s\n %s\n", maximize_ ? "Maximize" : "Minimize", - objective_->name.c_str(), - JoinDebugString(search_annotations_, ", ").c_str()); + absl::StrAppendFormat(&output, "%s %s\n %s\n", + maximize_ ? "Maximize" : "Minimize", objective_->name, + JoinDebugString(search_annotations_, ", ")); } else { absl::StrAppendFormat(&output, "Satisfy\n %s\n", - JoinDebugString(search_annotations_, ", ").c_str()); + JoinDebugString(search_annotations_, ", ")); } output.append("Output\n"); for (int i = 0; i < output_.size(); ++i) { - absl::StrAppendFormat(&output, " %s\n", output_[i].DebugString().c_str()); + absl::StrAppendFormat(&output, " %s\n", output_[i].DebugString()); } return output; @@ -982,7 +982,7 @@ void ModelStatistics::BuildStatistics() { for (Constraint* const ct : model_.constraints()) { if (ct != nullptr && ct->active) { constraints_per_type_[ct->type].push_back(ct); - std::unordered_set marked; + absl::flat_hash_set marked; for (const Argument& arg : ct->arguments) { for (IntegerVariable* const var : arg.variables) { marked.insert(var); diff --git a/ortools/flatzinc/model.h b/ortools/flatzinc/model.h index 57228122f0a..f5131729f34 100644 --- a/ortools/flatzinc/model.h +++ b/ortools/flatzinc/model.h @@ -16,11 +16,10 @@ #include #include -#include +#include "absl/container/flat_hash_map.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/graph/iterators.h" #include "ortools/util/string_array.h" @@ -414,7 +413,7 @@ class ModelStatistics { private: const Model& model_; std::map> constraints_per_type_; - std::unordered_map> + absl::flat_hash_map> constraints_per_variables_; }; } // namespace fz diff --git a/ortools/flatzinc/parser.tab.cc b/ortools/flatzinc/parser.tab.cc index ee4130bf629..ed157ef50b2 100644 --- a/ortools/flatzinc/parser.tab.cc +++ b/ortools/flatzinc/parser.tab.cc @@ -114,7 +114,8 @@ extern int orfz_debug; #if !defined(OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_) #define OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_ -#include "ortools/base/strutil.h" +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" #include "ortools/flatzinc/parser_util.h" // Tells flex to use the LexerInfo class to communicate with the bison parser. @@ -125,7 +126,7 @@ typedef operations_research::fz::LexerInfo YYSTYPE; #endif // OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_ -#line 118 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:355 */ +#line 119 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:355 */ /* Token type. */ #ifndef YYTOKENTYPE @@ -162,11 +163,12 @@ int orfz_parse(operations_research::fz::ParserContext* context, /* Copy the second part of user declarations. */ -#line 157 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:358 */ +#line 158 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:358 */ /* Unqualified %code blocks. */ -#line 37 "./ortools/flatzinc/parser.yy" /* yacc.c:359 */ +#line 38 "./ortools/flatzinc/parser.yy" /* yacc.c:359 */ -#include "ortools/base/stringpiece_utils.h" +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" #include "ortools/flatzinc/parser_util.cc" using operations_research::fz::Annotation; @@ -182,7 +184,7 @@ using operations_research::fz::SolutionOutputSpecs; using operations_research::fz::VariableRefOrValue; using operations_research::fz::VariableRefOrValueArray; -#line 177 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:359 */ +#line 179 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:359 */ #ifdef short #undef short @@ -457,13 +459,13 @@ static const yytype_uint8 yytranslate[] = { #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 103, 103, 110, 114, 115, 120, 123, 124, 127, 128, 129, 130, 133, - 134, 137, 138, 145, 146, 149, 168, 183, 194, 209, 220, 246, 279, 349, - 350, 353, 354, 355, 358, 362, 368, 369, 382, 400, 401, 402, 403, 410, - 411, 412, 413, 420, 421, 428, 429, 430, 433, 434, 437, 438, 439, 444, - 445, 448, 449, 450, 455, 456, 457, 462, 463, 467, 468, 474, 478, 484, - 485, 488, 515, 516, 519, 520, 521, 522, 523, 528, 559, 576, 601, 610, - 614, 617, 618, 621, 622, 623, 624, 634, 643, 649, 664, 672, 681}; + 0, 105, 105, 112, 116, 117, 122, 125, 126, 129, 130, 131, 132, 135, + 136, 139, 140, 147, 148, 151, 170, 185, 196, 211, 222, 248, 281, 351, + 352, 355, 356, 357, 360, 364, 370, 371, 384, 402, 403, 404, 405, 412, + 413, 414, 415, 422, 423, 430, 431, 432, 435, 436, 439, 440, 441, 446, + 447, 450, 451, 452, 457, 458, 459, 464, 465, 469, 470, 476, 480, 486, + 487, 490, 517, 518, 521, 522, 523, 524, 525, 530, 561, 578, 603, 612, + 616, 619, 620, 623, 624, 625, 626, 636, 645, 651, 666, 674, 683}; #endif #if YYDEBUG || YYERROR_VERBOSE || 1 @@ -1292,15 +1294,15 @@ int yyparse(operations_research::fz::ParserContext* context, YY_REDUCE_PRINT(yyn); switch (yyn) { case 4: -#line 114 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 116 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { yyerrok; } -#line 1423 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1425 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 19: -#line 149 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 151 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { // Declaration of a (named) constant: we simply register it in the // parser's context, and don't store it in the model. @@ -1319,11 +1321,11 @@ int yyparse(operations_research::fz::ParserContext* context, } delete annotations; } -#line 1447 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1449 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 20: -#line 169 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 171 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { std::vector* const annotations = (yyvsp[-4].annotations); // Declaration of a (named) constant array. See rule right above. @@ -1339,11 +1341,11 @@ int yyparse(operations_research::fz::ParserContext* context, delete assignments; delete annotations; } -#line 1466 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1468 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 21: -#line 184 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 186 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { std::vector* const annotations = (yyvsp[-3].annotations); // Declaration of a (named) constant array. See rule right above. @@ -1355,11 +1357,11 @@ int yyparse(operations_research::fz::ParserContext* context, context->integer_array_map[identifier] = std::vector(); delete annotations; } -#line 1481 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1483 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 22: -#line 195 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 197 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { std::vector* const annotations = (yyvsp[-4].annotations); // Declaration of a (named) constant array. See rule right above. @@ -1375,11 +1377,11 @@ int yyparse(operations_research::fz::ParserContext* context, delete assignments; delete annotations; } -#line 1500 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1502 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 23: -#line 210 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 212 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { std::vector* const annotations = (yyvsp[-3].annotations); // Declaration of a (named) constant array. See rule right above. @@ -1391,11 +1393,11 @@ int yyparse(operations_research::fz::ParserContext* context, context->float_array_map[identifier] = std::vector(); delete annotations; } -#line 1515 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1517 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 24: -#line 221 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 223 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { // Declaration of a (named) constant array: See rule above. CHECK_EQ((yyvsp[-12].integer_value), 1) @@ -1423,11 +1425,11 @@ int yyparse(operations_research::fz::ParserContext* context, delete assignments; delete annotations; } -#line 1545 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1547 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 25: -#line 246 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 248 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { // Declaration of a variable. If it's unassigned or assigned to a // constant, we'll create a new var stored in the model. If it's @@ -1438,7 +1440,7 @@ int yyparse(operations_research::fz::ParserContext* context, std::vector* const annotations = (yyvsp[-1].annotations); const VariableRefOrValue& assignment = (yyvsp[0].var_or_value); const bool introduced = ContainsId(annotations, "var_is_introduced") || - strings::StartsWith(identifier, "X_INTRODUCED"); + absl::StartsWith(identifier, "X_INTRODUCED"); IntegerVariable* var = nullptr; if (!assignment.defined) { var = model->AddVariable(identifier, domain, introduced); @@ -1460,11 +1462,11 @@ int yyparse(operations_research::fz::ParserContext* context, } delete annotations; } -#line 1583 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1585 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 26: -#line 280 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 282 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { // Declaration of a "variable array": these is exactly like N simple // variable declarations, where the identifier for declaration #i is @@ -1480,13 +1482,13 @@ int yyparse(operations_research::fz::ParserContext* context, assignments->variables.size() == num_vars); CHECK(assignments == nullptr || assignments->values.size() == num_vars); const bool introduced = ContainsId(annotations, "var_is_introduced") || - strings::StartsWith(identifier, "X_INTRODUCED"); + absl::StartsWith(identifier, "X_INTRODUCED"); std::vector vars(num_vars, nullptr); for (int i = 0; i < num_vars; ++i) { const std::string var_name = - absl::StrFormat("%s[%d]", identifier.c_str(), i + 1); + absl::StrFormat("%s[%d]", identifier, i + 1); if (assignments == nullptr) { vars[i] = model->AddVariable(var_name, domain, introduced); } else if (assignments->variables[i] == nullptr) { @@ -1534,78 +1536,78 @@ int yyparse(operations_research::fz::ParserContext* context, delete annotations; } } -#line 1655 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1657 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 27: -#line 349 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 351 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.var_or_value) = (yyvsp[0].var_or_value); } -#line 1661 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1663 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 28: -#line 350 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 352 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.var_or_value) = VariableRefOrValue::Undefined(); } -#line 1667 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1669 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 29: -#line 353 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 355 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.var_or_value_array) = (yyvsp[-1].var_or_value_array); } -#line 1673 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1675 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 30: -#line 354 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 356 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.var_or_value_array) = nullptr; } -#line 1679 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1681 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 31: -#line 355 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 357 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.var_or_value_array) = nullptr; } -#line 1685 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1687 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 32: -#line 358 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 360 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.var_or_value_array) = (yyvsp[-2].var_or_value_array); (yyval.var_or_value_array)->PushBack((yyvsp[0].var_or_value)); } -#line 1694 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1696 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 33: -#line 362 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 364 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.var_or_value_array) = new VariableRefOrValueArray(); (yyval.var_or_value_array)->PushBack((yyvsp[0].var_or_value)); } -#line 1703 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1705 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 34: -#line 368 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 370 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.var_or_value) = VariableRefOrValue::Value((yyvsp[0].integer_value)); } -#line 1709 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1711 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 35: -#line 369 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 371 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { // A reference to an existing integer constant or variable. const std::string& id = (yyvsp[0].string_value); @@ -1621,11 +1623,11 @@ int yyparse(operations_research::fz::ParserContext* context, *ok = false; } } -#line 1727 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1729 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 36: -#line 382 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 384 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { // A given element of an existing constant array or variable array. const std::string& id = (yyvsp[-3].string_value); @@ -1642,296 +1644,296 @@ int yyparse(operations_research::fz::ParserContext* context, *ok = false; } } -#line 1748 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1750 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 37: -#line 400 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 402 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::Boolean(); } -#line 1754 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1756 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 38: -#line 401 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 403 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::AllInt64(); } -#line 1760 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1762 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 39: -#line 402 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 404 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::Interval((yyvsp[-2].integer_value), (yyvsp[0].integer_value)); } -#line 1766 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1768 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 40: -#line 403 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 405 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { CHECK((yyvsp[-1].integers) != nullptr); (yyval.domain) = Domain::IntegerList(std::move(*(yyvsp[-1].integers))); delete (yyvsp[-1].integers); } -#line 1776 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1778 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 41: -#line 410 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 412 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::SetOfBoolean(); } -#line 1782 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1784 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 42: -#line 411 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 413 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::SetOfAllInt64(); } -#line 1788 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1790 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 43: -#line 412 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 414 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::SetOfInterval((yyvsp[-2].integer_value), (yyvsp[0].integer_value)); } -#line 1794 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1796 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 44: -#line 413 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 415 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { CHECK((yyvsp[-1].integers) != nullptr); (yyval.domain) = Domain::SetOfIntegerList(std::move(*(yyvsp[-1].integers))); delete (yyvsp[-1].integers); } -#line 1804 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1806 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 45: -#line 420 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 422 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::AllInt64(); } -#line 1810 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1812 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 46: -#line 421 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 423 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { const int64 lb = ConvertAsIntegerOrDie((yyvsp[-2].double_value)); const int64 ub = ConvertAsIntegerOrDie((yyvsp[0].double_value)); (yyval.domain) = Domain::Interval(lb, ub); } -#line 1820 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1822 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 47: -#line 428 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 430 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = (yyvsp[0].domain); } -#line 1826 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1828 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 48: -#line 429 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 431 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = (yyvsp[0].domain); } -#line 1832 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1834 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 49: -#line 430 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 432 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = (yyvsp[0].domain); } -#line 1838 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1840 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 50: -#line 433 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 435 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.integers) = (yyvsp[-2].integers); (yyval.integers)->emplace_back((yyvsp[0].integer_value)); } -#line 1844 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1846 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 51: -#line 434 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 436 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.integers) = new std::vector(); (yyval.integers)->emplace_back((yyvsp[0].integer_value)); } -#line 1850 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1852 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 52: -#line 437 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 439 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.integer_value) = (yyvsp[0].integer_value); } -#line 1856 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1858 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 53: -#line 438 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 440 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.integer_value) = gtl::FindOrDie(context->integer_map, (yyvsp[0].string_value)); } -#line 1862 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1864 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 54: -#line 439 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 441 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.integer_value) = Lookup( gtl::FindOrDie(context->integer_array_map, (yyvsp[-3].string_value)), (yyvsp[-1].integer_value)); } -#line 1870 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1872 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 55: -#line 444 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 446 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.doubles) = (yyvsp[-2].doubles); (yyval.doubles)->emplace_back((yyvsp[0].double_value)); } -#line 1876 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1878 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 56: -#line 445 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 447 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.doubles) = new std::vector(); (yyval.doubles)->emplace_back((yyvsp[0].double_value)); } -#line 1882 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1884 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 57: -#line 448 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 450 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.double_value) = (yyvsp[0].double_value); } -#line 1888 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1890 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 58: -#line 449 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 451 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.double_value) = gtl::FindOrDie(context->float_map, (yyvsp[0].string_value)); } -#line 1894 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1896 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 59: -#line 450 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 452 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.double_value) = Lookup( gtl::FindOrDie(context->float_array_map, (yyvsp[-3].string_value)), (yyvsp[-1].integer_value)); } -#line 1902 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1904 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 60: -#line 455 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 457 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::IntegerValue((yyvsp[0].integer_value)); } -#line 1908 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1910 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 61: -#line 456 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 458 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::Interval((yyvsp[-2].integer_value), (yyvsp[0].integer_value)); } -#line 1914 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1916 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 62: -#line 457 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 459 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { CHECK((yyvsp[-1].integers) != nullptr); (yyval.domain) = Domain::IntegerList(std::move(*(yyvsp[-1].integers))); delete (yyvsp[-1].integers); } -#line 1924 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1926 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 63: -#line 462 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 464 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::EmptyDomain(); } -#line 1930 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1932 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 64: -#line 463 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 465 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { CHECK_EQ(std::round((yyvsp[0].double_value)), (yyvsp[0].double_value)); (yyval.domain) = Domain::IntegerValue(static_cast((yyvsp[0].double_value))); } -#line 1939 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1941 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 65: -#line 467 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 469 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::IntegerValue( gtl::FindOrDie(context->integer_map, (yyvsp[0].string_value))); } -#line 1945 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1947 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 66: -#line 468 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 470 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domain) = Domain::IntegerValue(Lookup( gtl::FindOrDie(context->integer_array_map, (yyvsp[-3].string_value)), (yyvsp[-1].integer_value))); } -#line 1954 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1956 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 67: -#line 474 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 476 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domains) = (yyvsp[-2].domains); (yyval.domains)->emplace_back((yyvsp[0].domain)); } -#line 1963 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1965 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 68: -#line 478 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 480 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.domains) = new std::vector(); (yyval.domains)->emplace_back((yyvsp[0].domain)); } -#line 1969 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 1971 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 71: -#line 488 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 490 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { const std::string& identifier = (yyvsp[-4].string_value); CHECK((yyvsp[-2].args) != nullptr) << "Missing argument in constraint"; @@ -1957,73 +1959,73 @@ int yyparse(operations_research::fz::ParserContext* context, delete annotations; delete (yyvsp[-2].args); } -#line 1999 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2001 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 72: -#line 515 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 517 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.args) = (yyvsp[-2].args); (yyval.args)->emplace_back((yyvsp[0].arg)); } -#line 2005 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2007 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 73: -#line 516 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 518 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.args) = new std::vector(); (yyval.args)->emplace_back((yyvsp[0].arg)); } -#line 2011 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2013 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 74: -#line 519 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 521 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.arg) = Argument::IntegerValue((yyvsp[0].integer_value)); } -#line 2017 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2019 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 75: -#line 520 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 522 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.arg) = Argument::IntegerValue( ConvertAsIntegerOrDie((yyvsp[0].double_value))); } -#line 2023 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2025 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 76: -#line 521 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 523 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.arg) = Argument::VoidArgument(); } -#line 2029 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2031 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 77: -#line 522 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 524 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.arg) = Argument::Interval((yyvsp[-2].integer_value), (yyvsp[0].integer_value)); } -#line 2035 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2037 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 78: -#line 523 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 525 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { CHECK((yyvsp[-1].integers) != nullptr); (yyval.arg) = Argument::IntegerList(std::move(*(yyvsp[-1].integers))); delete (yyvsp[-1].integers); } -#line 2045 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2047 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 79: -#line 528 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 530 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { const std::string& id = (yyvsp[0].string_value); if (gtl::ContainsKey(context->integer_map, id)) { @@ -2061,11 +2063,11 @@ int yyparse(operations_research::fz::ParserContext* context, (yyval.arg) = Argument::DomainList(d); } } -#line 2081 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2083 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 80: -#line 559 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 561 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { const std::string& id = (yyvsp[-3].string_value); const int64 index = (yyvsp[-1].integer_value); @@ -2083,11 +2085,11 @@ int yyparse(operations_research::fz::ParserContext* context, (yyval.arg) = Argument::FromDomain(d); } } -#line 2103 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2105 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 81: -#line 576 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 578 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { VariableRefOrValueArray* const arguments = (yyvsp[-1].var_or_value_array); CHECK(arguments != nullptr); @@ -2114,81 +2116,81 @@ int yyparse(operations_research::fz::ParserContext* context, } delete arguments; } -#line 2133 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2135 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 82: -#line 601 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 603 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.arg) = Argument::VoidArgument(); } -#line 2141 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2143 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 83: -#line 610 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 612 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.annotations) = (yyvsp[-2].annotations) != nullptr ? (yyvsp[-2].annotations) : new std::vector(); (yyval.annotations)->emplace_back((yyvsp[0].annotation)); } -#line 2150 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2152 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 84: -#line 614 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 616 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.annotations) = nullptr; } -#line 2156 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2158 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 85: -#line 617 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 619 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.annotations) = (yyvsp[-2].annotations); (yyval.annotations)->emplace_back((yyvsp[0].annotation)); } -#line 2162 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2164 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 86: -#line 618 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 620 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.annotations) = new std::vector(); (yyval.annotations)->emplace_back((yyvsp[0].annotation)); } -#line 2168 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2170 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 87: -#line 621 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 623 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.annotation) = Annotation::Interval((yyvsp[-2].integer_value), (yyvsp[0].integer_value)); } -#line 2174 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2176 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 88: -#line 622 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 624 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.annotation) = Annotation::IntegerValue((yyvsp[0].integer_value)); } -#line 2180 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2182 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 89: -#line 623 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 625 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { (yyval.annotation) = Annotation::String((yyvsp[0].string_value)); } -#line 2186 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2188 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 90: -#line 624 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 626 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { const std::string& id = (yyvsp[0].string_value); if (gtl::ContainsKey(context->variable_map, id)) { @@ -2201,11 +2203,11 @@ int yyparse(operations_research::fz::ParserContext* context, (yyval.annotation) = Annotation::Identifier(id); } } -#line 2201 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2203 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 91: -#line 634 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 636 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { std::vector* const annotations = (yyvsp[-1].annotations); if (annotations != nullptr) { @@ -2216,11 +2218,11 @@ int yyparse(operations_research::fz::ParserContext* context, (yyval.annotation) = Annotation::FunctionCall((yyvsp[-3].string_value)); } } -#line 2215 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2217 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 92: -#line 643 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 645 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { CHECK(gtl::ContainsKey(context->variable_array_map, (yyvsp[-3].string_value))) @@ -2229,11 +2231,11 @@ int yyparse(operations_research::fz::ParserContext* context, gtl::FindOrDie(context->variable_array_map, (yyvsp[-3].string_value)), (yyvsp[-1].integer_value))); } -#line 2226 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2228 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 93: -#line 649 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 651 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { std::vector* const annotations = (yyvsp[-1].annotations); if (annotations != nullptr) { @@ -2244,11 +2246,11 @@ int yyparse(operations_research::fz::ParserContext* context, (yyval.annotation) = Annotation::Empty(); } } -#line 2240 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2242 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 94: -#line 664 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 666 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { if ((yyvsp[-1].annotations) != nullptr) { model->Satisfy(std::move(*(yyvsp[-1].annotations))); @@ -2257,11 +2259,11 @@ int yyparse(operations_research::fz::ParserContext* context, model->Satisfy(std::vector()); } } -#line 2253 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2255 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 95: -#line 672 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 674 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { CHECK_EQ(Argument::INT_VAR_REF, (yyvsp[0].arg).type); if ((yyvsp[-2].annotations) != nullptr) { @@ -2272,11 +2274,11 @@ int yyparse(operations_research::fz::ParserContext* context, model->Minimize((yyvsp[0].arg).Var(), std::vector()); } } -#line 2267 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2269 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; case 96: -#line 681 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ +#line 683 "./ortools/flatzinc/parser.yy" /* yacc.c:1646 */ { CHECK_EQ(Argument::INT_VAR_REF, (yyvsp[0].arg).type); if ((yyvsp[-2].annotations) != nullptr) { @@ -2287,10 +2289,10 @@ int yyparse(operations_research::fz::ParserContext* context, model->Maximize((yyvsp[0].arg).Var(), std::vector()); } } -#line 2281 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2283 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ break; -#line 2285 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ +#line 2287 "./ortools/flatzinc/parser.tab.cc" /* yacc.c:1646 */ default: break; } @@ -2488,4 +2490,4 @@ int yyparse(operations_research::fz::ParserContext* context, #endif return yyresult; } -#line 691 "./ortools/flatzinc/parser.yy" /* yacc.c:1906 */ +#line 693 "./ortools/flatzinc/parser.yy" /* yacc.c:1906 */ diff --git a/ortools/flatzinc/parser.tab.hh b/ortools/flatzinc/parser.tab.hh index efee557e5a5..2938343c98a 100644 --- a/ortools/flatzinc/parser.tab.hh +++ b/ortools/flatzinc/parser.tab.hh @@ -44,7 +44,8 @@ extern int orfz_debug; #if !defined(OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_) #define OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_ -#include "ortools/base/strutil.h" +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" #include "ortools/flatzinc/parser_util.h" // Tells flex to use the LexerInfo class to communicate with the bison parser. @@ -55,7 +56,7 @@ typedef operations_research::fz::LexerInfo YYSTYPE; #endif // OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_ -#line 59 "./ortools/flatzinc/parser.tab.hh" /* yacc.c:1909 */ +#line 60 "./ortools/flatzinc/parser.tab.hh" /* yacc.c:1909 */ /* Token type. */ #ifndef YYTOKENTYPE diff --git a/ortools/flatzinc/parser.yy b/ortools/flatzinc/parser.yy index 5c3be520923..4cf0a557cff 100644 --- a/ortools/flatzinc/parser.yy +++ b/ortools/flatzinc/parser.yy @@ -21,7 +21,8 @@ %code requires { #if !defined(OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_) #define OR_TOOLS_FLATZINC_FLATZINC_TAB_HH_ -#include "ortools/base/strutil.h" +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" #include "ortools/flatzinc/parser_util.h" // Tells flex to use the LexerInfo class to communicate with the bison parser. @@ -35,7 +36,8 @@ typedef operations_research::fz::LexerInfo YYSTYPE; // Code in the implementation file. %code { -#include "ortools/base/stringpiece_utils.h" +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" #include "ortools/flatzinc/parser_util.cc" using operations_research::fz::Annotation; @@ -253,7 +255,7 @@ variable_or_constant_declaration: std::vector* const annotations = $5; const VariableRefOrValue& assignment = $6; const bool introduced = ContainsId(annotations, "var_is_introduced") || - strings::StartsWith(identifier, "X_INTRODUCED"); + absl::StartsWith(identifier, "X_INTRODUCED"); IntegerVariable* var = nullptr; if (!assignment.defined) { var = model->AddVariable(identifier, domain, introduced); @@ -290,12 +292,12 @@ variable_or_constant_declaration: CHECK(assignments == nullptr || assignments->variables.size() == num_vars); CHECK(assignments == nullptr || assignments->values.size() == num_vars); const bool introduced = ContainsId(annotations, "var_is_introduced") || - strings::StartsWith(identifier, "X_INTRODUCED"); + absl::StartsWith(identifier, "X_INTRODUCED"); std::vector vars(num_vars, nullptr); for (int i = 0; i < num_vars; ++i) { - const std::string var_name = absl::StrFormat("%s[%d]", identifier.c_str(), i + 1); + const std::string var_name = absl::StrFormat("%s[%d]", identifier, i + 1); if (assignments == nullptr) { vars[i] = model->AddVariable(var_name, domain, introduced); } else if (assignments->variables[i] == nullptr) { diff --git a/ortools/flatzinc/parser_main.cc b/ortools/flatzinc/parser_main.cc index da76a7cb71e..0bc96e25319 100644 --- a/ortools/flatzinc/parser_main.cc +++ b/ortools/flatzinc/parser_main.cc @@ -39,7 +39,7 @@ void ParseFile(const std::string& filename, bool presolve) { std::string problem_name = filename; // Remove the .fzn extension. - CHECK(strings::EndsWith(problem_name, ".fzn")); + CHECK(absl::EndsWith(problem_name, ".fzn")); problem_name.resize(problem_name.size() - 4); // Remove the leading path if present. const size_t found = problem_name.find_last_of("/\\"); @@ -75,8 +75,8 @@ int main(int argc, char** argv) { const char kUsage[] = "Parses a flatzinc .fzn file, optionally presolve it, and prints it in " "human-readable format"; - FLAGS_log_prefix = false; - FLAGS_logtostderr = true; + absl::SetFlag(&FLAGS_log_prefix, false); + absl::SetFlag(&FLAGS_logtostderr, true); gflags::SetUsageMessage(kUsage); gflags::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); diff --git a/ortools/flatzinc/parser_util.h b/ortools/flatzinc/parser_util.h index b198323c7cc..a293f6f3cb4 100644 --- a/ortools/flatzinc/parser_util.h +++ b/ortools/flatzinc/parser_util.h @@ -18,8 +18,8 @@ #define OR_TOOLS_FLATZINC_PARSER_UTIL_H_ #include -#include +#include "absl/container/flat_hash_map.h" #include "ortools/base/map_util.h" #include "ortools/flatzinc/model.h" @@ -27,15 +27,15 @@ namespace operations_research { namespace fz { // This is the context used during parsing. struct ParserContext { - std::unordered_map integer_map; - std::unordered_map> integer_array_map; - std::unordered_map float_map; - std::unordered_map> float_array_map; - std::unordered_map variable_map; - std::unordered_map> + absl::flat_hash_map integer_map; + absl::flat_hash_map> integer_array_map; + absl::flat_hash_map float_map; + absl::flat_hash_map> float_array_map; + absl::flat_hash_map variable_map; + absl::flat_hash_map> variable_array_map; - std::unordered_map domain_map; - std::unordered_map> domain_array_map; + absl::flat_hash_map domain_map; + absl::flat_hash_map> domain_array_map; }; // An optional reference to a variable, or an integer value, used in diff --git a/ortools/flatzinc/presolve.cc b/ortools/flatzinc/presolve.cc index 3799962487c..7a071e94854 100644 --- a/ortools/flatzinc/presolve.cc +++ b/ortools/flatzinc/presolve.cc @@ -15,14 +15,12 @@ #include #include -#include -#include "ortools/base/join.h" +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" #include "ortools/base/map_util.h" -#include "ortools/base/string_view.h" -#include "ortools/base/stringpiece_utils.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/strutil.h" #include "ortools/flatzinc/logging.h" #include "ortools/graph/cliques.h" #include "ortools/util/saturated_arithmetic.h" @@ -69,8 +67,8 @@ bool AtMostOne0OrAtMostOne1(const std::vector& values) { return true; } -std::unordered_set GetValueSet(const Argument& arg) { - std::unordered_set result; +absl::flat_hash_set GetValueSet(const Argument& arg) { + absl::flat_hash_set result; if (arg.HasOneValue()) { result.insert(arg.Value()); } else { @@ -146,7 +144,7 @@ bool OverlapsAt(const Argument& array, int pos, const Argument& other) { } template -void AppendIfNotInSet(T* value, std::unordered_set* s, +void AppendIfNotInSet(T* value, absl::flat_hash_set* s, std::vector* vec) { if (s->insert(value).second) { vec->push_back(value); @@ -170,7 +168,7 @@ void AppendIfNotInSet(T* value, std::unordered_set* s, // ----- Rule helpers ----- void Presolver::ApplyRule( - Constraint* ct, const std::string& rule_name, + int index, Constraint* ct, const std::string& rule_name, const std::function& rule) { const std::string before = HASVLOG ? ct->DebugString() : ""; @@ -195,8 +193,8 @@ void Presolver::ApplyRule( break; } case CONSTRAINT_REWRITTEN: { - AddConstraintToMapping(ct); - AppendIfNotInSet(ct, &changed_constraints_, &changed_constraints_vector_); + AddConstraintToMapping(index, ct); + changed_constraints_.insert(index); if (HASVLOG) { const std::string after = ct->DebugString(); if (after != before) { @@ -207,13 +205,13 @@ void Presolver::ApplyRule( } case CONSTRAINT_ALWAYS_FALSE: { FZVLOG << " - constraint is set to false"; - RemoveConstraintFromMapping(ct); + RemoveConstraintFromMapping(index, ct); ct->SetAsFalse(); break; } case CONSTRAINT_ALWAYS_TRUE: { FZVLOG << " - constraint is set to true"; - RemoveConstraintFromMapping(ct); + RemoveConstraintFromMapping(index, ct); ct->MarkAsInactive(); break; } @@ -224,18 +222,18 @@ void Presolver::MarkChangedVariable(IntegerVariable* var) { AppendIfNotInSet(var, &changed_variables_, &changed_variables_vector_); } -void Presolver::AddConstraintToMapping(Constraint* ct) { +void Presolver::AddConstraintToMapping(int index, Constraint* ct) { for (const Argument& arg : ct->arguments) { for (IntegerVariable* const var : arg.variables) { - var_to_constraints_[var].insert(ct); + var_to_constraint_indices_[var].insert(index); } } } -void Presolver::RemoveConstraintFromMapping(Constraint* ct) { +void Presolver::RemoveConstraintFromMapping(int index, Constraint* ct) { for (const Argument& arg : ct->arguments) { for (IntegerVariable* const var : arg.variables) { - var_to_constraints_[var].erase(ct); + var_to_constraint_indices_[var].erase(index); } } } @@ -528,7 +526,7 @@ Presolver::RuleStatus Presolver::Unreify(Constraint* ct, std::string* log) { if (!last_argument.HasOneValue()) { return NOT_CHANGED; } - DCHECK(strings::EndsWith(ct->type, "_reif")) << ct->DebugString(); + DCHECK(absl::EndsWith(ct->type, "_reif")) << ct->DebugString(); ct->type.resize(ct->type.size() - 5); ct->RemoveTargetVariable(); if (last_argument.Value() == 1) { @@ -1303,7 +1301,7 @@ Presolver::RuleStatus Presolver::PresolveArrayIntElement(Constraint* ct, const std::string after = ct->arguments[2].Var()->DebugString(); if (before != after) { log->append(absl::StrFormat(", reduce target variable from %s to %s", - before.c_str(), after.c_str())); + before, after)); ct->presolve_propagation_done = true; return CONSTRAINT_REWRITTEN; } @@ -1480,14 +1478,14 @@ Presolver::RuleStatus Presolver::RegroupLinear(Constraint* ct, // Only constants, or size == 0. return NOT_CHANGED; } - std::unordered_map coefficients; + absl::flat_hash_map coefficients; const int original_size = ct->arguments[0].values.size(); for (int i = 0; i < original_size; ++i) { coefficients[ct->arguments[1].variables[i]] += ct->arguments[0].values[i]; } if (coefficients.size() != original_size) { // Duplicate variables. log->append("regroup variables"); - std::unordered_set processed; + absl::flat_hash_set processed; int index = 0; int zero = 0; for (int i = 0; i < original_size; ++i) { @@ -1550,7 +1548,7 @@ Presolver::RuleStatus Presolver::PropagatePositiveLinear(Constraint* ct, if (bound < var->domain.Max()) { absl::StrAppendFormat(log, ", intersect %s with [0..%" GG_LL_FORMAT "d]", - var->DebugString().c_str(), bound); + var->DebugString(), bound); IntersectVarWithInterval(var, 0, bound); } } @@ -1564,7 +1562,7 @@ Presolver::RuleStatus Presolver::PropagatePositiveLinear(Constraint* ct, if (bound > var->domain.Min()) { absl::StrAppendFormat( log, ", intersect %s with [%" GG_LL_FORMAT "d .. INT_MAX]", - var->DebugString().c_str(), bound); + var->DebugString(), bound); IntersectVarWithInterval(var, bound, kint64max); return CONSTRAINT_ALWAYS_TRUE; } @@ -1964,7 +1962,7 @@ Presolver::RuleStatus Presolver::PresolveSimplifyElement(Constraint* ct, // Rule 6. if (ct->arguments[0].IsVariable()) { - std::unordered_set all_values = GetValueSet(ct->arguments[2]); + absl::flat_hash_set all_values = GetValueSet(ct->arguments[2]); const std::vector& array = ct->arguments[1].values; const int array_size = array.size(); if (!all_values.empty()) { @@ -2004,7 +2002,7 @@ Presolver::RuleStatus Presolver::PresolveSimplifyElement(Constraint* ct, MarkChangedVariable(ct->arguments[0].Var()); } log->append(absl::StrFormat("reduce index domain to %s", - ct->arguments[0].Var()->DebugString().c_str())); + ct->arguments[0].Var()->DebugString())); } } } @@ -2139,7 +2137,7 @@ Presolver::RuleStatus Presolver::PresolveSimplifyExprElement(Constraint* ct, MarkChangedVariable(ct->arguments[0].Var()); } log->append(absl::StrFormat("reduce index domain to %s", - ct->arguments[0].Var()->DebugString().c_str())); + ct->arguments[0].Var()->DebugString())); } } @@ -2772,7 +2770,7 @@ Presolver::RuleStatus Presolver::PresolveTableInt(Constraint* ct, const int num_tuples = ct->arguments[1].values.size() / num_vars; int ignored_tuples = 0; std::vector new_tuples; - std::vector> next_values(num_vars); + std::vector> next_values(num_vars); for (int t = 0; t < num_tuples; ++t) { std::vector tuple( ct->arguments[1].values.begin() + t * num_vars, @@ -2837,7 +2835,7 @@ Presolver::RuleStatus Presolver::PresolveRegular(Constraint* ct, const int64 initial_state = ct->arguments[4].Value(); - std::unordered_set final_states; + absl::flat_hash_set final_states; switch (ct->arguments[5].type) { case fz::Argument::INT_VALUE: { final_states.insert(ct->arguments[5].values[0]); @@ -2862,7 +2860,7 @@ Presolver::RuleStatus Presolver::PresolveRegular(Constraint* ct, } // Compute the set of reachable state at each time point. - std::vector> reachable_states(num_vars + 1); + std::vector> reachable_states(num_vars + 1); reachable_states[0].insert(initial_state); reachable_states[num_vars] = {final_states.begin(), final_states.end()}; @@ -2878,7 +2876,7 @@ Presolver::RuleStatus Presolver::PresolveRegular(Constraint* ct, // Backward. for (int time = num_vars - 1; time > 0; --time) { - std::unordered_set new_set; + absl::flat_hash_set new_set; const Domain& domain = vars[time]->domain; for (const std::vector& transition : automata) { if (!gtl::ContainsKey(reachable_states[time], transition[0])) continue; @@ -2894,7 +2892,7 @@ Presolver::RuleStatus Presolver::PresolveRegular(Constraint* ct, for (int time = 0; time < num_vars; ++time) { Domain& domain = vars[time]->domain; // Collect valid values. - std::unordered_set reached_values; + absl::flat_hash_set reached_values; for (const std::vector& transition : automata) { if (!gtl::ContainsKey(reachable_states[time], transition[0])) continue; if (!domain.Contains(transition[1])) continue; @@ -2929,7 +2927,7 @@ Presolver::RuleStatus Presolver::PresolveRegular(Constraint* ct, if (HASVLOG) { absl::StrAppendFormat(log, "reduce domain of variable %d from %s to %s; ", - time, before.c_str(), vars[time]->DebugString().c_str()); + time, before, vars[time]->DebugString()); } } } @@ -2968,114 +2966,114 @@ Presolver::RuleStatus Presolver::PresolveDiffN(Constraint* ct, return NOT_CHANGED; } -#define CALL_TYPE(ct, t, method) \ - if (ct->active && ct->type == t) { \ - ApplyRule(ct, #method, [this](Constraint* ct, std::string* log) { \ - return method(ct, log); \ - }); \ +#define CALL_TYPE(index, ct, t, method) \ + if (ct->active && ct->type == t) { \ + ApplyRule(index, ct, #method, [this](Constraint* ct, std::string* log) { \ + return method(ct, log); \ + }); \ } -#define CALL_PREFIX(ct, t, method) \ - if (ct->active && strings::StartsWith(ct->type, t)) { \ - ApplyRule(ct, #method, [this](Constraint* ct, std::string* log) { \ - return method(ct, log); \ - }); \ +#define CALL_PREFIX(index, ct, t, method) \ + if (ct->active && absl::StartsWith(ct->type, t)) { \ + ApplyRule(index, ct, #method, [this](Constraint* ct, std::string* log) { \ + return method(ct, log); \ + }); \ } -#define CALL_SUFFIX(ct, t, method) \ - if (ct->active && strings::EndsWith(ct->type, t)) { \ - ApplyRule(ct, #method, [this](Constraint* ct, std::string* log) { \ - return method(ct, log); \ - }); \ +#define CALL_SUFFIX(index, ct, t, method) \ + if (ct->active && absl::EndsWith(ct->type, t)) { \ + ApplyRule(index, ct, #method, [this](Constraint* ct, std::string* log) { \ + return method(ct, log); \ + }); \ } // Main presolve rule caller. -void Presolver::PresolveOneConstraint(Constraint* ct) { - CALL_SUFFIX(ct, "_reif", Unreify); - CALL_TYPE(ct, "bool2int", PresolveBool2Int); - if (strings::StartsWith(ct->type, "int_")) { - CALL_TYPE(ct, "int_le", PresolveInequalities); - CALL_TYPE(ct, "int_lt", PresolveInequalities); - CALL_TYPE(ct, "int_ge", PresolveInequalities); - CALL_TYPE(ct, "int_gt", PresolveInequalities); - } - if (strings::StartsWith(ct->type, "bool_")) { - CALL_TYPE(ct, "bool_le", PresolveInequalities); - CALL_TYPE(ct, "bool_lt", PresolveInequalities); - CALL_TYPE(ct, "bool_ge", PresolveInequalities); - CALL_TYPE(ct, "bool_gt", PresolveInequalities); - } - - CALL_TYPE(ct, "int_abs", StoreAbs); - CALL_TYPE(ct, "int_eq_reif", StoreIntEqReif); - CALL_TYPE(ct, "int_ne_reif", SimplifyIntNeReif); - CALL_TYPE(ct, "int_eq_reif", RemoveAbsFromIntEqNeReif); - CALL_TYPE(ct, "int_ne", RemoveAbsFromIntEqNeReif); - CALL_TYPE(ct, "int_ne_reif", RemoveAbsFromIntEqNeReif); - CALL_TYPE(ct, "set_in", PresolveSetIn); - CALL_TYPE(ct, "set_not_in", PresolveSetNotIn); - CALL_TYPE(ct, "set_in_reif", PresolveSetInReif); - - if (strings::StartsWith(ct->type, "int_lin_")) { - CALL_TYPE(ct, "int_lin_gt", PresolveIntLinGt); - CALL_TYPE(ct, "int_lin_lt", PresolveIntLinLt); - CALL_PREFIX(ct, "int_lin_", SimplifyLinear); - CALL_PREFIX(ct, "int_lin_", PresolveLinear); - CALL_PREFIX(ct, "int_lin_", RegroupLinear); - CALL_PREFIX(ct, "int_lin_", SimplifyUnaryLinear); - CALL_PREFIX(ct, "int_lin_", SimplifyBinaryLinear); - CALL_TYPE(ct, "int_lin_eq", PropagatePositiveLinear); - CALL_TYPE(ct, "int_lin_le", PropagatePositiveLinear); - CALL_TYPE(ct, "int_lin_ge", PropagatePositiveLinear); - CALL_TYPE(ct, "int_lin_eq", CreateLinearTarget); - CALL_TYPE(ct, "int_lin_eq", PresolveStoreMapping); - CALL_TYPE(ct, "int_lin_eq_reif", CheckIntLinReifBounds); - CALL_TYPE(ct, "int_lin_eq_reif", SimplifyIntLinEqReif); - } - - if (strings::StartsWith(ct->type, "array_")) { - CALL_TYPE(ct, "array_bool_and", PresolveArrayBoolAnd); - CALL_TYPE(ct, "array_bool_or", PresolveArrayBoolOr); - CALL_TYPE(ct, "array_int_element", PresolveSimplifyElement); - CALL_TYPE(ct, "array_bool_element", PresolveSimplifyElement); - CALL_TYPE(ct, "array_int_element", PresolveArrayIntElement); - CALL_TYPE(ct, "array_var_int_element", PresolveSimplifyExprElement); - CALL_TYPE(ct, "array_var_bool_element", PresolveSimplifyExprElement); - } - - if (strings::StartsWith(ct->type, "int_")) { - CALL_TYPE(ct, "int_div", PresolveIntDiv); - CALL_TYPE(ct, "int_times", PresolveIntTimes); - CALL_TYPE(ct, "int_eq", PresolveIntEq); - CALL_TYPE(ct, "int_ne", PresolveIntNe); - CALL_TYPE(ct, "int_eq_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "int_ne_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "int_le_reif", RemoveAbsFromIntLeReif); - CALL_TYPE(ct, "int_le_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "int_lt_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "int_ge_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "int_gt_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "int_mod", PresolveIntMod); - } - - if (strings::StartsWith(ct->type, "bool_")) { - CALL_TYPE(ct, "bool_eq", PresolveIntEq); - CALL_TYPE(ct, "bool_ne", PresolveIntNe); - CALL_TYPE(ct, "bool_not", PresolveIntNe); - CALL_TYPE(ct, "bool_eq_reif", PresolveBoolEqNeReif); - CALL_TYPE(ct, "bool_ne_reif", PresolveBoolEqNeReif); - CALL_TYPE(ct, "bool_xor", PresolveBoolXor); - CALL_TYPE(ct, "bool_ne", PresolveBoolNot); - CALL_TYPE(ct, "bool_not", PresolveBoolNot); - CALL_TYPE(ct, "bool_clause", PresolveBoolClause); - CALL_TYPE(ct, "bool_eq_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "bool_ne_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "bool_le_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "bool_lt_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "bool_ge_reif", PropagateReifiedComparisons); - CALL_TYPE(ct, "bool_gt_reif", PropagateReifiedComparisons); - } - CALL_TYPE(ct, "table_int", PresolveTableInt); - CALL_TYPE(ct, "diffn", PresolveDiffN); - CALL_TYPE(ct, "regular", PresolveRegular); +void Presolver::PresolveOneConstraint(int index, Constraint* ct) { + CALL_SUFFIX(index, ct, "_reif", Unreify); + CALL_TYPE(index, ct, "bool2int", PresolveBool2Int); + if (absl::StartsWith(ct->type, "int_")) { + CALL_TYPE(index, ct, "int_le", PresolveInequalities); + CALL_TYPE(index, ct, "int_lt", PresolveInequalities); + CALL_TYPE(index, ct, "int_ge", PresolveInequalities); + CALL_TYPE(index, ct, "int_gt", PresolveInequalities); + } + if (absl::StartsWith(ct->type, "bool_")) { + CALL_TYPE(index, ct, "bool_le", PresolveInequalities); + CALL_TYPE(index, ct, "bool_lt", PresolveInequalities); + CALL_TYPE(index, ct, "bool_ge", PresolveInequalities); + CALL_TYPE(index, ct, "bool_gt", PresolveInequalities); + } + + CALL_TYPE(index, ct, "int_abs", StoreAbs); + CALL_TYPE(index, ct, "int_eq_reif", StoreIntEqReif); + CALL_TYPE(index, ct, "int_ne_reif", SimplifyIntNeReif); + CALL_TYPE(index, ct, "int_eq_reif", RemoveAbsFromIntEqNeReif); + CALL_TYPE(index, ct, "int_ne", RemoveAbsFromIntEqNeReif); + CALL_TYPE(index, ct, "int_ne_reif", RemoveAbsFromIntEqNeReif); + CALL_TYPE(index, ct, "set_in", PresolveSetIn); + CALL_TYPE(index, ct, "set_not_in", PresolveSetNotIn); + CALL_TYPE(index, ct, "set_in_reif", PresolveSetInReif); + + if (absl::StartsWith(ct->type, "int_lin_")) { + CALL_TYPE(index, ct, "int_lin_gt", PresolveIntLinGt); + CALL_TYPE(index, ct, "int_lin_lt", PresolveIntLinLt); + CALL_PREFIX(index, ct, "int_lin_", SimplifyLinear); + CALL_PREFIX(index, ct, "int_lin_", PresolveLinear); + CALL_PREFIX(index, ct, "int_lin_", RegroupLinear); + CALL_PREFIX(index, ct, "int_lin_", SimplifyUnaryLinear); + CALL_PREFIX(index, ct, "int_lin_", SimplifyBinaryLinear); + CALL_TYPE(index, ct, "int_lin_eq", PropagatePositiveLinear); + CALL_TYPE(index, ct, "int_lin_le", PropagatePositiveLinear); + CALL_TYPE(index, ct, "int_lin_ge", PropagatePositiveLinear); + CALL_TYPE(index, ct, "int_lin_eq", CreateLinearTarget); + CALL_TYPE(index, ct, "int_lin_eq", PresolveStoreMapping); + CALL_TYPE(index, ct, "int_lin_eq_reif", CheckIntLinReifBounds); + CALL_TYPE(index, ct, "int_lin_eq_reif", SimplifyIntLinEqReif); + } + + if (absl::StartsWith(ct->type, "array_")) { + CALL_TYPE(index, ct, "array_bool_and", PresolveArrayBoolAnd); + CALL_TYPE(index, ct, "array_bool_or", PresolveArrayBoolOr); + CALL_TYPE(index, ct, "array_int_element", PresolveSimplifyElement); + CALL_TYPE(index, ct, "array_bool_element", PresolveSimplifyElement); + CALL_TYPE(index, ct, "array_int_element", PresolveArrayIntElement); + CALL_TYPE(index, ct, "array_var_int_element", PresolveSimplifyExprElement); + CALL_TYPE(index, ct, "array_var_bool_element", PresolveSimplifyExprElement); + } + + if (absl::StartsWith(ct->type, "int_")) { + CALL_TYPE(index, ct, "int_div", PresolveIntDiv); + CALL_TYPE(index, ct, "int_times", PresolveIntTimes); + CALL_TYPE(index, ct, "int_eq", PresolveIntEq); + CALL_TYPE(index, ct, "int_ne", PresolveIntNe); + CALL_TYPE(index, ct, "int_eq_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "int_ne_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "int_le_reif", RemoveAbsFromIntLeReif); + CALL_TYPE(index, ct, "int_le_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "int_lt_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "int_ge_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "int_gt_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "int_mod", PresolveIntMod); + } + + if (absl::StartsWith(ct->type, "bool_")) { + CALL_TYPE(index, ct, "bool_eq", PresolveIntEq); + CALL_TYPE(index, ct, "bool_ne", PresolveIntNe); + CALL_TYPE(index, ct, "bool_not", PresolveIntNe); + CALL_TYPE(index, ct, "bool_eq_reif", PresolveBoolEqNeReif); + CALL_TYPE(index, ct, "bool_ne_reif", PresolveBoolEqNeReif); + CALL_TYPE(index, ct, "bool_xor", PresolveBoolXor); + CALL_TYPE(index, ct, "bool_ne", PresolveBoolNot); + CALL_TYPE(index, ct, "bool_not", PresolveBoolNot); + CALL_TYPE(index, ct, "bool_clause", PresolveBoolClause); + CALL_TYPE(index, ct, "bool_eq_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "bool_ne_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "bool_le_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "bool_lt_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "bool_ge_reif", PropagateReifiedComparisons); + CALL_TYPE(index, ct, "bool_gt_reif", PropagateReifiedComparisons); + } + CALL_TYPE(index, ct, "table_int", PresolveTableInt); + CALL_TYPE(index, ct, "diffn", PresolveDiffN); + CALL_TYPE(index, ct, "regular", PresolveRegular); // Last rule: if the target variable of a constraint is fixed, removed it // the target part. @@ -3111,11 +3109,11 @@ void Presolver::StoreDifference(Constraint* ct) { } void Presolver::MergeIntEqNe(Model* model) { - std::unordered_map> + absl::flat_hash_map> int_eq_reif_map; - std::unordered_map> + absl::flat_hash_map> int_ne_reif_map; for (Constraint* const ct : model->constraints()) { if (!ct->active) continue; @@ -3346,10 +3344,12 @@ bool Presolver::RegroupDifferent(Model* model) { } bool Presolver::Run(Model* model) { + auto constraint = [model](int index) { return model->constraints()[index]; }; + // Rebuild var_constraint map if empty. - if (var_to_constraints_.empty()) { - for (Constraint* const ct : model->constraints()) { - AddConstraintToMapping(ct); + if (var_to_constraint_indices_.empty()) { + for (int i = 0; i < model->constraints().size(); ++i) { + AddConstraintToMapping(i, constraint(i)); } } @@ -3360,14 +3360,16 @@ bool Presolver::Run(Model* model) { // Some new substitutions were introduced. Let's process them. SubstituteEverywhere(model); var_representative_map_.clear(); + var_representative_vector_.clear(); } bool changed_since_start = false; // Let's presolve the bool2int predicates first. - for (Constraint* const ct : model->constraints()) { + for (int index = 0; index < model->constraints().size(); ++index) { + Constraint* const ct = constraint(index); if (ct->active && ct->type == "bool2int") { std::string log; - ApplyRule(ct, "PresolveBool2Int", + ApplyRule(index, ct, "PresolveBool2Int", [this](Constraint* ct, std::string* log) { return PresolveBool2Int(ct, log); }); @@ -3377,15 +3379,17 @@ bool Presolver::Run(Model* model) { // Some new substitutions were introduced. Let's process them. SubstituteEverywhere(model); var_representative_map_.clear(); + var_representative_vector_.clear(); } { FZVLOG << " - processing initial model with " << model->constraints().size() << " constraints." << FZENDL; - for (Constraint* const ct : model->constraints()) { + for (int index = 0; index < model->constraints().size(); ++index) { + Constraint* const ct = constraint(index); if (ct->active) { // TODO(user): Optim: remove from postponed queue. - PresolveOneConstraint(ct); + PresolveOneConstraint(index, ct); if (!ct->active || ct->type == "false_constraint") { changed_since_start = true; } @@ -3393,6 +3397,7 @@ bool Presolver::Run(Model* model) { if (!var_representative_map_.empty()) { SubstituteEverywhere(model); var_representative_map_.clear(); + var_representative_vector_.clear(); } } FZVLOG << " - done" << FZENDL; @@ -3404,41 +3409,44 @@ bool Presolver::Run(Model* model) { loops++; FZVLOG << "--- loop " << loops << FZENDL; changed_since_start = true; - std::unordered_set to_scan; - std::vector to_scan_vector; + std::set to_scan; for (IntegerVariable* var : changed_variables_vector_) { - for (Constraint* ct : var_to_constraints_[var]) { + for (const int index : var_to_constraint_indices_[var]) { + Constraint* const ct = constraint(index); if (ct->active) { - AppendIfNotInSet(ct, &to_scan, &to_scan_vector); + to_scan.insert(index); } } } - for (Constraint* ct : changed_constraints_vector_) { + for (const int index : changed_constraints_) { + Constraint* const ct = constraint(index); if (ct->active) { - AppendIfNotInSet(ct, &to_scan, &to_scan_vector); + to_scan.insert(index); } } changed_variables_.clear(); - changed_constraints_.clear(); changed_variables_vector_.clear(); - changed_constraints_vector_.clear(); + changed_constraints_.clear(); var_representative_map_.clear(); + var_representative_vector_.clear(); + FZVLOG << " - processing " << to_scan.size() << " constraints" << FZENDL; - for (Constraint* const ct : to_scan_vector) { + for (const int index : to_scan) { + Constraint* const ct = constraint(index); if (!var_representative_map_.empty()) { - AppendIfNotInSet(ct, &changed_constraints_, - &changed_constraints_vector_); + changed_constraints_.insert(index); } else if (ct->active) { - PresolveOneConstraint(ct); + PresolveOneConstraint(index, ct); } } if (!var_representative_map_.empty()) { // Some new substitutions were introduced. Let's process them. SubstituteEverywhere(model); var_representative_map_.clear(); + var_representative_vector_.clear(); } } @@ -3508,6 +3516,7 @@ void Presolver::AddVariableSubstition(IntegerVariable* from, from->temporary)); from->active = false; var_representative_map_[from] = to; + var_representative_vector_.push_back(from); } } @@ -3531,15 +3540,17 @@ IntegerVariable* Presolver::FindRepresentativeOfVar(IntegerVariable* var) { } void Presolver::SubstituteEverywhere(Model* model) { + auto constraint = [model](int index) { return model->constraints()[index]; }; + // Collected impacted constraints. - std::unordered_set impacted; - for (const auto& p : var_representative_map_) { - const std::unordered_set& contains = - var_to_constraints_[p.first]; + absl::flat_hash_set impacted; + for (const IntegerVariable* var : var_representative_vector_) { + const std::set& contains = var_to_constraint_indices_[var]; impacted.insert(contains.begin(), contains.end()); } // Rewrite the constraints. - for (Constraint* const ct : impacted) { + for (const int index : impacted) { + Constraint* const ct = constraint(index); if (ct != nullptr && ct->active) { for (int i = 0; i < ct->arguments.size(); ++i) { Argument& argument = ct->arguments[i]; @@ -3551,7 +3562,7 @@ void Presolver::SubstituteEverywhere(Model* model) { IntegerVariable* const new_var = FindRepresentativeOfVar(old_var); if (new_var != old_var) { argument.variables[i] = new_var; - var_to_constraints_[new_var].insert(ct); + var_to_constraint_indices_[new_var].insert(index); } } break; @@ -3566,8 +3577,8 @@ void Presolver::SubstituteEverywhere(Model* model) { } } // Cleanup the outdated var_to_constraints sets. - for (const auto& p : var_representative_map_) { - var_to_constraints_[p.first].clear(); + for (IntegerVariable* var : var_representative_vector_) { + var_to_constraint_indices_[var].clear(); } // Rewrite the search. for (Annotation* const ann : model->mutable_search_annotations()) { @@ -3724,7 +3735,7 @@ void CheckRegroupStart(Constraint* ct, Constraint** start, // - *_reif: arity // - otherwise arity + 100. int SortWeight(Constraint* ct) { - int arity = strings::EndsWith(ct->type, "_reif") ? 0 : 100; + int arity = absl::EndsWith(ct->type, "_reif") ? 0 : 100; for (const Argument& arg : ct->arguments) { arity += arg.variables.size(); } @@ -3732,7 +3743,7 @@ int SortWeight(Constraint* ct) { } void CleanUpVariableWithMultipleDefiningConstraints(Model* model) { - std::unordered_map> ct_var_map; + absl::flat_hash_map> ct_var_map; for (Constraint* const ct : model->constraints()) { if (ct->target_variable != nullptr) { ct_var_map[ct->target_variable].push_back(ct); @@ -3785,6 +3796,8 @@ bool IsStrictPrefix(const std::vector& v1, const std::vector& v2) { } // namespace void Presolver::CleanUpModelForTheCpSolver(Model* model, bool use_sat) { + auto constraint = [model](int index) { return model->constraints()[index]; }; + // First pass. for (Constraint* const ct : model->constraints()) { // Treat float variables as int variables, convert constraints to int. @@ -3826,7 +3839,7 @@ void Presolver::CleanUpModelForTheCpSolver(Model* model, bool use_sat) { } if (id == "array_var_int_element") { if (ct->target_variable != nullptr) { - std::unordered_set variables_in_array; + absl::flat_hash_set variables_in_array; for (IntegerVariable* const var : ct->arguments[1].variables) { variables_in_array.insert(var); } @@ -3891,23 +3904,25 @@ void Presolver::CleanUpModelForTheCpSolver(Model* model, bool use_sat) { Constraint* start = nullptr; std::vector chain; std::vector carry_over; - var_to_constraints_.clear(); - for (Constraint* const ct : model->constraints()) { + var_to_constraint_indices_.clear(); + for (int index = 0; index < model->constraints().size(); ++index) { + Constraint* const ct = constraint(index); for (const Argument& arg : ct->arguments) { for (IntegerVariable* const var : arg.variables) { - var_to_constraints_[var].insert(ct); + var_to_constraint_indices_[var].insert(index); } } } // First version. The start is recognized by the double var in the max. // tmp1 = std::max(v1, v1) - for (Constraint* const ct : model->constraints()) { + for (int index = 0; index < model->constraints().size(); ++index) { + Constraint* const ct = constraint(index); if (start == nullptr) { CheckRegroupStart(ct, &start, &chain, &carry_over); } else if (ct->type == start->type && ct->arguments[1].Var() == carry_over.back() && - var_to_constraints_[ct->arguments[0].Var()].size() <= 2) { + var_to_constraint_indices_[ct->arguments[0].Var()].size() <= 2) { chain.push_back(ct->arguments[0].Var()); carry_over.push_back(ct->arguments[2].Var()); ct->active = false; diff --git a/ortools/flatzinc/presolve.h b/ortools/flatzinc/presolve.h index 9edbd389089..283de4fda13 100644 --- a/ortools/flatzinc/presolve.h +++ b/ortools/flatzinc/presolve.h @@ -16,13 +16,13 @@ #include #include -#include -#include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/match.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/stringpiece_utils.h" #include "ortools/flatzinc/model.h" namespace operations_research { @@ -55,7 +55,7 @@ class Presolver { void CleanUpModelForTheCpSolver(Model* model, bool use_sat); // This method is public for tests. - void PresolveOneConstraint(Constraint* ct); + void PresolveOneConstraint(int index, Constraint* ct); private: enum RuleStatus { @@ -178,15 +178,15 @@ class Presolver { bool IntersectVarWithSingleton(IntegerVariable* var, int64 value); bool IntersectVarWithInterval(IntegerVariable* var, int64 imin, int64 imax); bool RemoveValue(IntegerVariable* var, int64 value); - void AddConstraintToMapping(Constraint* ct); - void RemoveConstraintFromMapping(Constraint* ct); + void AddConstraintToMapping(int index, Constraint* ct); + void RemoveConstraintFromMapping(int index, Constraint* ct); // Mark changed variables. void MarkChangedVariable(IntegerVariable* var); // This method wraps each rule, calls it and log its effect. void ApplyRule( - Constraint* ct, const std::string& rule_name, + int index, Constraint* ct, const std::string& rule_name, const std::function& rule); // The presolver will discover some equivalence classes of variables [two @@ -197,45 +197,45 @@ class Presolver { // of 'from' with 'to', rather than the opposite. void AddVariableSubstition(IntegerVariable* from, IntegerVariable* to); IntegerVariable* FindRepresentativeOfVar(IntegerVariable* var); - std::unordered_map + absl::flat_hash_map var_representative_map_; + std::vector var_representative_vector_; // Stores abs_map_[x] = y if x = abs(y). - std::unordered_map abs_map_; + absl::flat_hash_map abs_map_; // Stores affine_map_[x] = a * y + b. - std::unordered_map affine_map_; + absl::flat_hash_map affine_map_; // Stores array2d_index_map_[z] = a * x + y + b. - std::unordered_map + absl::flat_hash_map array2d_index_map_; // Stores x == (y - z). - std::unordered_map> + absl::flat_hash_map> difference_map_; // Stores (x == y) == b - std::unordered_map> + absl::flat_hash_map> int_eq_reif_map_; // Stores all variables defined in the search annotations. - std::unordered_set decision_variables_; + absl::flat_hash_set decision_variables_; - // For all variables, stores all constraints it appears in. - std::unordered_map> - var_to_constraints_; + // For all variables, stores all indices of constraints it appears in. + absl::flat_hash_map> + var_to_constraint_indices_; // Count applications of presolve rules. Use a sorted map for reporting // purposes. std::map successful_rules_; // Store changed objects. - std::unordered_set changed_variables_; + absl::flat_hash_set changed_variables_; std::vector changed_variables_vector_; - std::unordered_set changed_constraints_; - std::vector changed_constraints_vector_; + std::set changed_constraints_; }; } // namespace fz } // namespace operations_research diff --git a/ortools/flatzinc/reporting.cc b/ortools/flatzinc/reporting.cc index a6a5cce53a6..73432cf721c 100644 --- a/ortools/flatzinc/reporting.cc +++ b/ortools/flatzinc/reporting.cc @@ -16,10 +16,10 @@ #include // NOLINT #include +#include "absl/strings/str_format.h" +#include "absl/synchronization/mutex.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/mutex.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/flatzinc/logging.h" diff --git a/ortools/flatzinc/reporting.h b/ortools/flatzinc/reporting.h index 51299c3a493..5326381f137 100644 --- a/ortools/flatzinc/reporting.h +++ b/ortools/flatzinc/reporting.h @@ -15,9 +15,8 @@ #define OR_TOOLS_FLATZINC_REPORTING_H_ #include +#include "absl/synchronization/mutex.h" #include "ortools/base/integral_types.h" -#include "ortools/base/mutex.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" namespace operations_research { diff --git a/ortools/flatzinc/sat_constraint.cc b/ortools/flatzinc/sat_constraint.cc index 44635b33fd6..24a726538f9 100644 --- a/ortools/flatzinc/sat_constraint.cc +++ b/ortools/flatzinc/sat_constraint.cc @@ -15,9 +15,10 @@ #include #include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/int_type.h" @@ -25,7 +26,6 @@ #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/flatzinc/logging.h" @@ -175,7 +175,7 @@ class SatPropagator : public Constraint { private: sat::SatSolver sat_; std::vector vars_; - std::unordered_map indices_; + absl::flat_hash_map indices_; std::vector bound_literals_; NumericalRev sat_decision_level_; std::vector demons_; diff --git a/ortools/flatzinc/solver.cc b/ortools/flatzinc/solver.cc index fcd00822632..973173a937d 100644 --- a/ortools/flatzinc/solver.cc +++ b/ortools/flatzinc/solver.cc @@ -14,14 +14,14 @@ #include "ortools/flatzinc/solver.h" #include -#include +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/flatzinc/checker.h" #include "ortools/flatzinc/constraints.h" @@ -50,7 +50,6 @@ FlatzincParameters::FlatzincParameters() logging(false), statistics(false), verbose_impact(false), - restart_log_size(-1.0), run_all_heuristics(false), heuristic_period(100), log_period(1000000), @@ -93,16 +92,15 @@ std::string Solver::SolutionString(const SolutionOutputSpecs& output) const { if (output.variable != nullptr) { const int64 value = SolutionValue(output.variable); if (output.display_as_boolean) { - return absl::StrFormat("%s = %s;", output.name.c_str(), + return absl::StrFormat("%s = %s;", output.name, value == 1 ? "true" : "false"); } else { - return absl::StrFormat("%s = %" GG_LL_FORMAT "d;", output.name.c_str(), - value); + return absl::StrFormat("%s = %" GG_LL_FORMAT "d;", output.name, value); } } else { const int bound_size = output.bounds.size(); std::string result = - absl::StrFormat("%s = array%dd(", output.name.c_str(), bound_size); + absl::StrFormat("%s = array%dd(", output.name, bound_size); for (int i = 0; i < bound_size; ++i) { if (output.bounds[i].max_value != 0) { result.append(absl::StrFormat( @@ -116,7 +114,7 @@ std::string Solver::SolutionString(const SolutionOutputSpecs& output) const { for (int i = 0; i < output.flat_variables.size(); ++i) { const int64 value = SolutionValue(output.flat_variables[i]); if (output.display_as_boolean) { - result.append(absl::StrFormat(value ? "true" : "false")); + result.append(value ? "true" : "false"); } else { absl::StrAppend(&result, value); } @@ -149,11 +147,11 @@ namespace { struct ConstraintsWithRequiredVariables { Constraint* ct; int index; - std::unordered_set required; + absl::flat_hash_set required; ConstraintsWithRequiredVariables( Constraint* cte, int i, - const std::unordered_set& defined) + const absl::flat_hash_set& defined) : ct(cte), index(i) { // Collect required variables. for (const Argument& arg : ct->arguments) { @@ -166,8 +164,8 @@ struct ConstraintsWithRequiredVariables { } std::string DebugString() const { - return absl::StrFormat("Ctio(%s, %d, deps_size = %lu)", ct->type.c_str(), - index, required.size()); + return absl::StrFormat("Ctio(%s, %d, deps_size = %u)", ct->type, index, + required.size()); } }; @@ -202,7 +200,7 @@ bool Solver::Extract() { int extracted_variables = 0; int extracted_constants = 0; int skipped_variables = 0; - std::unordered_set defined_variables; + absl::flat_hash_set defined_variables; for (IntegerVariable* const var : model_.variables()) { if (var->defining_constraint == nullptr && var->active) { data_.Extract(var); @@ -233,8 +231,8 @@ bool Solver::Extract() { int index = 0; std::vector to_sort; std::vector sorted; - std::unordered_map> + absl::flat_hash_map> dependencies; for (Constraint* ct : model_.constraints()) { if (ct != nullptr && ct->active) { @@ -378,7 +376,7 @@ void Solver::ParseSearchAnnotations(bool ignore_unknown, } FZLOG << " - parsing search annotations" << FZENDL; - std::unordered_set added; + absl::flat_hash_set added; for (const Annotation& ann : flat_annotations) { FZLOG << " - parse " << ann.DebugString() << FZENDL; if (ann.IsFunctionCallWithIdentifier("int_search")) { @@ -532,8 +530,8 @@ void Solver::AddCompletionDecisionBuilders( const std::vector& defined_variables, const std::vector& active_variables, SearchLimit* limit, std::vector* builders) { - std::unordered_set defined_set(defined_variables.begin(), - defined_variables.end()); + absl::flat_hash_set defined_set(defined_variables.begin(), + defined_variables.end()); std::vector output_variables; CollectOutputVariables(&output_variables); std::vector secondary_vars; @@ -635,12 +633,10 @@ DecisionBuilder* Solver::CreateDecisionBuilders(const FlatzincParameters& p, (!p.all_solutions && p.num_solutions == 1) ? p.heuristic_period : -1; - parameters.restart_log_size = p.restart_log_size; parameters.display_level = p.logging ? (p.verbose_impact ? DefaultPhaseParameters::VERBOSE : DefaultPhaseParameters::NORMAL) : DefaultPhaseParameters::NONE; - parameters.use_no_goods = (p.restart_log_size > 0); parameters.var_selection_schema = DefaultPhaseParameters::CHOOSE_MAX_SUM_IMPACT; parameters.value_selection_schema = @@ -717,7 +713,7 @@ void Solver::ReportInconsistentModel(const Model& model, FlatzincParameters p, absl::StrAppendFormat( &solver_status, "%%%% csv: %s, **unsat**, , 0, 0 ms, 0 ms, 0, 0, 0, 0, 0, %s, free", - model.name().c_str(), MemoryUsage().c_str()); + model.name(), MemoryUsage()); report->Print(p.thread_id, solver_status); } } @@ -881,7 +877,7 @@ void Solver::Solve(FlatzincParameters p, SearchReportingInterface* report) { absl::StrFormat("%%%% failures: %" GG_LL_FORMAT "d\n", solver_->failures())); solver_status.append( - absl::StrFormat("%%%% memory: %s\n", MemoryUsage().c_str())); + absl::StrFormat("%%%% memory: %s\n", MemoryUsage())); const int64 best = report->BestSolution(); if (model_.objective() != nullptr) { if (!model_.maximize() && num_solutions > 0) { @@ -900,7 +896,7 @@ void Solver::Solve(FlatzincParameters p, SearchReportingInterface* report) { DefaultPhaseStatString(default_phase_); if (!default_search_stats.empty()) { solver_status.append(absl::StrFormat("%%%% free search stats: %s\n", - default_search_stats.c_str())); + default_search_stats)); } } @@ -920,12 +916,12 @@ void Solver::Solve(FlatzincParameters p, SearchReportingInterface* report) { "%%%% csv: %s, %s, %s, %d, %" GG_LL_FORMAT "d ms, %" GG_LL_FORMAT "d ms, %" GG_LL_FORMAT "d, %" GG_LL_FORMAT "d, %d, %" GG_LL_FORMAT "d, %" GG_LL_FORMAT "d, %s, %s", - model_.name().c_str(), status_string.c_str(), obj_string.c_str(), - num_solutions, solve_time, build_time, solver_->branches(), - solver_->failures(), solver_->constraints(), + model_.name(), status_string, obj_string, num_solutions, solve_time, + build_time, solver_->branches(), solver_->failures(), + solver_->constraints(), solver_->demon_runs(operations_research::Solver::NORMAL_PRIORITY), solver_->demon_runs(operations_research::Solver::DELAYED_PRIORITY), - MemoryUsage().c_str(), search_name_.c_str())); + MemoryUsage(), search_name_)); report->Print(p.thread_id, search_status); if (p.statistics) { report->Print(p.thread_id, solver_status); diff --git a/ortools/flatzinc/solver.h b/ortools/flatzinc/solver.h index e316315155b..768cefb3d77 100644 --- a/ortools/flatzinc/solver.h +++ b/ortools/flatzinc/solver.h @@ -14,9 +14,8 @@ #ifndef OR_TOOLS_FLATZINC_SOLVER_H_ #define OR_TOOLS_FLATZINC_SOLVER_H_ -#include -#include - +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/flatzinc/model.h" #include "ortools/flatzinc/reporting.h" @@ -48,7 +47,6 @@ struct FlatzincParameters { bool logging; bool statistics; bool verbose_impact; - double restart_log_size; bool run_all_heuristics; int heuristic_period; int log_period; @@ -136,15 +134,15 @@ class Solver { ModelStatistics statistics_; SolverData data_; std::vector active_variables_; - std::unordered_map extracted_occurrences_; - std::unordered_set implied_variables_; + absl::flat_hash_map extracted_occurrences_; + absl::flat_hash_set implied_variables_; std::string search_name_; IntVar* objective_var_; OptimizeVar* objective_monitor_; // Default Search Phase (to get stats). DecisionBuilder* default_phase_; // Stored solutions. - std::vector> stored_values_; + std::vector> stored_values_; operations_research::Solver* solver_; }; } // namespace fz diff --git a/ortools/flatzinc/solver_data.h b/ortools/flatzinc/solver_data.h index d29b1fd3ab4..9c94f129b73 100644 --- a/ortools/flatzinc/solver_data.h +++ b/ortools/flatzinc/solver_data.h @@ -15,9 +15,9 @@ #define OR_TOOLS_FLATZINC_SOLVER_DATA_H_ #include -#include #include +#include "absl/container/flat_hash_map.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/flatzinc/model.h" #include "ortools/flatzinc/sat_constraint.h" @@ -36,7 +36,7 @@ class SolverData { std::vector GetOrCreateVariableArray(const Argument& argument); IntExpr* Extract(IntegerVariable* var); void SetExtracted(IntegerVariable* var, IntExpr* expr); - const std::unordered_map& extracted_map() const { + const absl::flat_hash_map& extracted_map() const { return extracted_map_; } @@ -46,7 +46,7 @@ class SolverData { // an AllDifferent constraints. void StoreAllDifferent(std::vector diffs); - // Queries wether the array diffs appears in an AllDifferent constraint. + // Queries whether the array diffs appears in an AllDifferent constraint. // Currently, this performs exact matching, therefore a sub-array of an // array of all-different variables will not match. bool IsAllDifferent(std::vector diffs) const; @@ -65,7 +65,7 @@ class SolverData { private: operations_research::Solver solver_; SatPropagator* sat_; - std::unordered_map extracted_map_; + absl::flat_hash_map extracted_map_; // Stores a set of sorted std::vector. // TODO(user, fdid): If it become too slow, switch to an unordered_set, it diff --git a/ortools/flatzinc/solver_util.cc b/ortools/flatzinc/solver_util.cc index 43d53b0d33b..54eec5130b4 100644 --- a/ortools/flatzinc/solver_util.cc +++ b/ortools/flatzinc/solver_util.cc @@ -13,15 +13,15 @@ #include "ortools/flatzinc/solver_util.h" -#include "ortools/base/join.h" -#include "ortools/base/stringprintf.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "ortools/flatzinc/logging.h" namespace operations_research { namespace fz { void MarkComputedVariables(Constraint* ct, - std::unordered_set* marked) { + absl::flat_hash_set* marked) { const std::string& id = ct->type; if (id == "global_cardinality") { FZVLOG << " - marking " << ct->DebugString() << FZENDL; diff --git a/ortools/flatzinc/solver_util.h b/ortools/flatzinc/solver_util.h index 164b5955adf..d5a5220dd80 100644 --- a/ortools/flatzinc/solver_util.h +++ b/ortools/flatzinc/solver_util.h @@ -14,8 +14,7 @@ #ifndef OR_TOOLS_FLATZINC_SOLVER_UTIL_H_ #define OR_TOOLS_FLATZINC_SOLVER_UTIL_H_ -#include - +#include "absl/container/flat_hash_set.h" #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/flatzinc/model.h" @@ -80,7 +79,7 @@ void FlattenAnnotations(const Annotation& ann, std::vector* out); // for instances will mark x as computed). // If this create cycles, they will be broken later during extraction. void MarkComputedVariables(Constraint* ct, - std::unordered_set* marked); + absl::flat_hash_set* marked); } // namespace fz } // namespace operations_research diff --git a/ortools/glop/CMakeLists.txt b/ortools/glop/CMakeLists.txt index 98fedf1e267..b449dbd1161 100644 --- a/ortools/glop/CMakeLists.txt +++ b/ortools/glop/CMakeLists.txt @@ -4,39 +4,57 @@ set(NAME ${PROJECT_NAME}_glop) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE -# gflags::gflags -# glog::glog +# absl::memory absl::container absl::strings absl::str_format +# gflags::gflags glog::glog # protobuf::libprotobuf # Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + absl::memory absl::container absl::strings absl::str_format + gflags::gflags glog::glog + protobuf::libprotobuf + Cbc::Cbc + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::glop ALIAS ${NAME}) diff --git a/ortools/glop/basis_representation.h b/ortools/glop/basis_representation.h index d4422aef1f9..6080206ea11 100644 --- a/ortools/glop/basis_representation.h +++ b/ortools/glop/basis_representation.h @@ -180,7 +180,7 @@ class BasisFactorization { // matrix_ and basis_. This is fast if IsIdentityBasis() is true, otherwise // it will trigger a refactorization and will return an error if the matrix // could not be factorized. - Status Initialize() MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status Initialize(); // Return the number of rows in the basis. RowIndex GetNumberOfRows() const { return matrix_.num_rows(); } @@ -188,11 +188,11 @@ class BasisFactorization { // Clears eta factorization and refactorizes LU. // Nothing happens if this is called on an already refactorized basis. // Returns an error if the matrix could not be factorized: i.e. not a basis. - Status Refactorize() MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status Refactorize(); // Like Refactorize(), but do it even if IsRefactorized() is true. // Call this if the underlying basis_ changed and Update() wasn't called. - Status ForceRefactorization() MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status ForceRefactorization(); // Returns true if the factorization was just recomputed. bool IsRefactorized() const; @@ -200,8 +200,9 @@ class BasisFactorization { // Updates the factorization. The 'eta' column will be modified with a swap to // avoid a copy (only if the standard eta update is used). Returns an error if // the matrix could not be factorized: i.e. not a basis. - Status Update(ColIndex entering_col, RowIndex leaving_variable_row, - const ScatteredColumn& direction) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status Update(ColIndex entering_col, + RowIndex leaving_variable_row, + const ScatteredColumn& direction); // Left solves the system y.B = rhs, where y initialy contains rhs. void LeftSolve(ScatteredRow* y) const; @@ -279,8 +280,8 @@ class BasisFactorization { // Updates the factorization using the middle product form update. // Qi Huangfu, J. A. Julian Hall, "Novel update techniques for the revised // simplex method", 28 january 2013, Technical Report ERGO-13-0001 - Status MiddleProductFormUpdate(ColIndex entering_col, - RowIndex leaving_variable_row) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status + MiddleProductFormUpdate(ColIndex entering_col, RowIndex leaving_variable_row); // Increases the deterministic time for a solve operation with a vector having // this number of non-zero entries (it can be an approximation). diff --git a/ortools/glop/entering_variable.h b/ortools/glop/entering_variable.h index 2624edc3364..1fa9178ee6a 100644 --- a/ortools/glop/entering_variable.h +++ b/ortools/glop/entering_variable.h @@ -61,27 +61,26 @@ class EnteringVariable { // IsValidPrimalEnteringCandidate() for more details) or kInvalidCol if no // such column exists. This latter case means that the primal algorithm has // terminated: the optimal has been reached. - Status PrimalChooseEnteringColumn(ColIndex* entering_col) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status + PrimalChooseEnteringColumn(ColIndex* entering_col); // Dual optimization phase (i.e. phase II) ratio test. // Returns the index of the entering column given that we want to move along // the "update" row vector in the direction given by the sign of // cost_variation. Computes the smallest step that keeps the dual feasibility // for all the columns. - Status DualChooseEnteringColumn(const UpdateRow& update_row, - Fractional cost_variation, - std::vector* bound_flip_candidates, - ColIndex* entering_col, - Fractional* step) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status DualChooseEnteringColumn( + const UpdateRow& update_row, Fractional cost_variation, + std::vector* bound_flip_candidates, ColIndex* entering_col, + Fractional* step); // Dual feasibility phase (i.e. phase I) ratio test. // Similar to the optimization phase test, but allows a step that increases // the infeasibility of an already infeasible column. The step magnitude is // the one that minimize the sum of infeasibilities when applied. - Status DualPhaseIChooseEnteringColumn(const UpdateRow& update_row, - Fractional cost_variation, - ColIndex* entering_col, - Fractional* step) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status DualPhaseIChooseEnteringColumn( + const UpdateRow& update_row, Fractional cost_variation, + ColIndex* entering_col, Fractional* step); // Sets the pricing parameters. This does not change the pricing rule. void SetParameters(const GlopParameters& parameters); diff --git a/ortools/glop/lp_solver.cc b/ortools/glop/lp_solver.cc index a5efc936478..1a3f5931404 100644 --- a/ortools/glop/lp_solver.cc +++ b/ortools/glop/lp_solver.cc @@ -19,13 +19,11 @@ #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" -#include "ortools/base/join.h" -#include "ortools/base/memory.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/strutil.h" +#include "absl/memory/memory.h" +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" #include "ortools/glop/preprocessor.h" #include "ortools/glop/status.h" #include "ortools/lp_data/lp_types.h" diff --git a/ortools/glop/lp_solver.h b/ortools/glop/lp_solver.h index 947d4859948..313c805006c 100644 --- a/ortools/glop/lp_solver.h +++ b/ortools/glop/lp_solver.h @@ -49,12 +49,12 @@ class LPSolver { // it will just solve the problem from scratch. On the other hand, if the lp // is the same, calling Solve() again should basically resume the solve from // the last position. To disable this behavior, simply call Clear() before. - ProblemStatus Solve(const LinearProgram& lp) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram& lp); // Same as Solve() but use the given time limit rather than constructing a new // one from the current GlopParameters. - ProblemStatus SolveWithTimeLimit(const LinearProgram& lp, - TimeLimit* time_limit) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT ProblemStatus SolveWithTimeLimit(const LinearProgram& lp, + TimeLimit* time_limit); // Puts the solver in a clean state. // diff --git a/ortools/glop/lu_factorization.h b/ortools/glop/lu_factorization.h index 18d7a764a37..ae5f863b782 100644 --- a/ortools/glop/lu_factorization.h +++ b/ortools/glop/lu_factorization.h @@ -48,7 +48,7 @@ class LuFactorization { // it being confused by this revert to identity factorization behavior. The // reason behind it is that this way, calling any public function of this // class will never cause a crash of the program. - Status ComputeFactorization(const MatrixView& matrix) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status ComputeFactorization(const MatrixView& matrix); // Returns the column permutation used by the LU factorization. const ColumnPermutation& GetColumnPermutation() const { return col_perm_; } diff --git a/ortools/glop/markowitz.cc b/ortools/glop/markowitz.cc index e9d8357a466..bbf838a91c4 100644 --- a/ortools/glop/markowitz.cc +++ b/ortools/glop/markowitz.cc @@ -14,7 +14,7 @@ #include "ortools/glop/markowitz.h" #include -#include "ortools/base/stringprintf.h" +#include "absl/strings/str_format.h" #include "ortools/lp_data/lp_utils.h" namespace operations_research { diff --git a/ortools/glop/markowitz.h b/ortools/glop/markowitz.h index 63270c231c0..a193758bb92 100644 --- a/ortools/glop/markowitz.h +++ b/ortools/glop/markowitz.h @@ -75,7 +75,7 @@ #include -#include "ortools/base/inlined_vector.h" +#include "absl/container/inlined_vector.h" #include "ortools/base/logging.h" #include "ortools/base/port.h" #include "ortools/glop/parameters.pb.h" @@ -278,9 +278,11 @@ class Markowitz { // of the matrix. Moreover, by adding singleton columns with a one at the rows // such that 'row_perm[row] == kInvalidRow', then the matrix will be // non-singular. - Status ComputeLU(const MatrixView& basis_matrix, RowPermutation* row_perm, - ColumnPermutation* col_perm, TriangularMatrix* lower, - TriangularMatrix* upper) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status ComputeLU(const MatrixView& basis_matrix, + RowPermutation* row_perm, + ColumnPermutation* col_perm, + TriangularMatrix* lower, + TriangularMatrix* upper); // Only computes P and Q^{-1}, L and U can be computed later from these // permutations using another algorithm (for instance left-looking L.U). This @@ -292,9 +294,9 @@ class Markowitz { // This function also works with a non-square matrix. It will return a set of // independent columns of maximum size. If all the given columns are // independent, the returned Status will be OK. - Status ComputeRowAndColumnPermutation( + ABSL_MUST_USE_RESULT Status ComputeRowAndColumnPermutation( const MatrixView& basis_matrix, RowPermutation* row_perm, - ColumnPermutation* col_perm) MUST_USE_RESULT; + ColumnPermutation* col_perm); // Releases the memory used by this class. void Clear(); diff --git a/ortools/glop/preprocessor.cc b/ortools/glop/preprocessor.cc index 1497b0f2782..dc6f75895b9 100644 --- a/ortools/glop/preprocessor.cc +++ b/ortools/glop/preprocessor.cc @@ -13,7 +13,7 @@ #include "ortools/glop/preprocessor.h" -#include "ortools/base/stringprintf.h" +#include "absl/strings/str_format.h" #include "ortools/glop/revised_simplex.h" #include "ortools/glop/status.h" #include "ortools/lp_data/lp_data_utils.h" @@ -144,7 +144,7 @@ void MainLpPreprocessor::RunAndPushIfRelevant( const EntryIndex new_num_entries = lp->num_entries(); const double preprocess_time = time_limit->GetElapsedTime() - start_time; VLOG(1) << absl::StrFormat( - "%s(%fs): %d(%d) rows, %d(%d) columns, %d(%d) entries.", name.c_str(), + "%s(%fs): %d(%d) rows, %d(%d) columns, %d(%d) entries.", name, preprocess_time, lp->num_constraints().value(), (lp->num_constraints() - initial_num_rows_).value(), lp->num_variables().value(), @@ -3044,14 +3044,14 @@ void DoubletonEqualityRowPreprocessor::RecoverSolution( // When the modified variable is either basic or free, we keep it as is, // and simply make the deleted one basic. case VariableStatus::FREE: - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; case VariableStatus::BASIC: // Several code paths set the deleted column as basic. The code that // sets its value in that case is below, after the switch() block. solution->variable_statuses[r.col[DELETED]] = VariableStatus::BASIC; break; case VariableStatus::AT_LOWER_BOUND: - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; case VariableStatus::AT_UPPER_BOUND: { // The bound was induced by a bound of one of the two original // variables. Put that original variable at its bound, and make @@ -3474,7 +3474,7 @@ void ShiftVariableBoundsPreprocessor::RecoverSolution( } else { switch (solution->variable_statuses[col]) { case VariableStatus::FIXED_VALUE: - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; case VariableStatus::AT_LOWER_BOUND: solution->primal_values[col] = variable_initial_lbs_[col]; break; @@ -3539,7 +3539,7 @@ void ScalingPreprocessor::RecoverSolution(ProblemSolution* solution) const { for (ColIndex col(0); col < num_cols; ++col) { switch (solution->variable_statuses[col]) { case VariableStatus::AT_UPPER_BOUND: - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; case VariableStatus::FIXED_VALUE: solution->primal_values[col] = variable_upper_bounds_[col]; break; @@ -3547,7 +3547,7 @@ void ScalingPreprocessor::RecoverSolution(ProblemSolution* solution) const { solution->primal_values[col] = variable_lower_bounds_[col]; break; case VariableStatus::FREE: - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; case VariableStatus::BASIC: break; } diff --git a/ortools/glop/revised_simplex.cc b/ortools/glop/revised_simplex.cc index 4560d30a0a8..6d8ef4b8337 100644 --- a/ortools/glop/revised_simplex.cc +++ b/ortools/glop/revised_simplex.cc @@ -21,11 +21,11 @@ #include #include +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/glop/initial_basis.h" #include "ortools/glop/parameters.pb.h" #include "ortools/lp_data/lp_data.h" @@ -445,16 +445,15 @@ std::string RevisedSimplex::GetPrettySolverStats() const { return absl::StrFormat( "Problem status : %s\n" "Solving time : %-6.4g\n" - "Number of iterations : %llu\n" + "Number of iterations : %u\n" "Time for solvability (first phase) : %-6.4g\n" - "Number of iterations for solvability : %llu\n" + "Number of iterations for solvability : %u\n" "Time for optimization : %-6.4g\n" - "Number of iterations for optimization : %llu\n" + "Number of iterations for optimization : %u\n" "Stop after first basis : %d\n", - GetProblemStatusString(problem_status_).c_str(), total_time_, - num_iterations_, feasibility_time_, num_feasibility_iterations_, - optimization_time_, num_optimization_iterations_, - FLAGS_simplex_stop_after_first_basis); + GetProblemStatusString(problem_status_), total_time_, num_iterations_, + feasibility_time_, num_feasibility_iterations_, optimization_time_, + num_optimization_iterations_, FLAGS_simplex_stop_after_first_basis); } double RevisedSimplex::DeterministicTime() const { @@ -2946,12 +2945,12 @@ std::string RevisedSimplex::SimpleVariableInfo(ColIndex col) const { VariableType variable_type = variables_info_.GetTypeRow()[col]; VariableStatus variable_status = variables_info_.GetStatusRow()[col]; absl::StrAppendFormat(&output, "%d (%s) = %s, %s, %s, [%s,%s]", col.value(), - variable_name_[col].c_str(), - StringifyWithFlags(variable_values_.Get(col)).c_str(), - GetVariableStatusString(variable_status).c_str(), - GetVariableTypeString(variable_type).c_str(), - StringifyWithFlags(lower_bound_[col]).c_str(), - StringifyWithFlags(upper_bound_[col]).c_str()); + variable_name_[col], + StringifyWithFlags(variable_values_.Get(col)), + GetVariableStatusString(variable_status), + GetVariableTypeString(variable_type), + StringifyWithFlags(lower_bound_[col]), + StringifyWithFlags(upper_bound_[col])); return output; } diff --git a/ortools/glop/revised_simplex.h b/ortools/glop/revised_simplex.h index b05fa282957..565b3f8ef81 100644 --- a/ortools/glop/revised_simplex.h +++ b/ortools/glop/revised_simplex.h @@ -164,7 +164,8 @@ class RevisedSimplex { // and try to use the previously computed solution as a warm-start. To disable // this behavior or give explicit warm-start data, use one of the State*() // functions below. - Status Solve(const LinearProgram& lp, TimeLimit* time_limit) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status Solve(const LinearProgram& lp, + TimeLimit* time_limit); // Do not use the current solution as a warm-start for the next Solve(). The // next Solve() will behave as if the class just got created. @@ -362,15 +363,15 @@ class RevisedSimplex { // Initializes the starting basis. In most cases it starts by the all slack // basis and tries to apply some heuristics to replace fixed variables. - Status CreateInitialBasis() MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status CreateInitialBasis(); // Sets the initial basis to the given columns, try to factorize it and // recompute the basic variable values. - Status InitializeFirstBasis(const RowToColMapping& initial_basis) - MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status + InitializeFirstBasis(const RowToColMapping& initial_basis); // Entry point for the solver initialization. - Status Initialize(const LinearProgram& lp) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status Initialize(const LinearProgram& lp); // Saves the current variable statuses in solution_state_. void SaveState(); @@ -466,9 +467,9 @@ class RevisedSimplex { // along this dual edge. // - target_bound: the bound at which the leaving variable should go when // leaving the basis. - Status DualChooseLeavingVariableRow(RowIndex* leaving_row, - Fractional* cost_variation, - Fractional* target_bound) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status DualChooseLeavingVariableRow( + RowIndex* leaving_row, Fractional* cost_variation, + Fractional* target_bound); // Updates the prices used by DualChooseLeavingVariableRow() after a simplex // iteration by using direction_. The prices are stored in @@ -489,9 +490,9 @@ class RevisedSimplex { // Dual Phase-1 Algorithm for the Simplex Method", Computational Optimization // and Applications, October 2003, Volume 26, Issue 1, pp 63-81. // http://rd.springer.com/article/10.1023%2FA%3A1025102305440 - Status DualPhaseIChooseLeavingVariableRow( + ABSL_MUST_USE_RESULT Status DualPhaseIChooseLeavingVariableRow( RowIndex* leaving_row, Fractional* cost_variation, - Fractional* target_bound) MUST_USE_RESULT; + Fractional* target_bound); // Makes sure the boxed variable are dual-feasible by setting them to the // correct bound according to their reduced costs. This is called @@ -522,8 +523,9 @@ class RevisedSimplex { // Updates the system state according to the given basis pivot. // Returns an error if the update could not be done because of some precision // issue. - Status UpdateAndPivot(ColIndex entering_col, RowIndex leaving_row, - Fractional target_bound) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status UpdateAndPivot(ColIndex entering_col, + RowIndex leaving_row, + Fractional target_bound); // Displays all the timing stats related to the calling object. void DisplayAllStats(); @@ -542,11 +544,11 @@ class RevisedSimplex { // Minimize the objective function, be it for satisfiability or for // optimization. Used by Solve(). - Status Minimize(TimeLimit* time_limit) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status Minimize(TimeLimit* time_limit); // Same as Minimize() for the dual simplex algorithm. // TODO(user): remove duplicate code between the two functions. - Status DualMinimize(TimeLimit* time_limit) MUST_USE_RESULT; + ABSL_MUST_USE_RESULT Status DualMinimize(TimeLimit* time_limit); // Utility functions to return the current ColIndex of the slack column with // given number. Note that currently, such columns are always present in the diff --git a/ortools/graph/CMakeLists.txt b/ortools/graph/CMakeLists.txt index 1fb4b0bc1de..4337a6b1dcb 100644 --- a/ortools/graph/CMakeLists.txt +++ b/ortools/graph/CMakeLists.txt @@ -4,39 +4,60 @@ set(NAME ${PROJECT_NAME}_graph) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE -# gflags::gflags -# glog::glog +# absl::hash absl::meta absl::memory absl::strings absl::str_format +# gflags::gflags glog::glog # protobuf::libprotobuf # Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + absl::hash absl::meta absl::memory absl::strings absl::str_format + gflags::gflags glog::glog + protobuf::libprotobuf + Cbc::Cbc + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::graph ALIAS ${NAME}) diff --git a/ortools/graph/astar.cc b/ortools/graph/astar.cc index 705fff36eaf..4f67c806d40 100644 --- a/ortools/graph/astar.cc +++ b/ortools/graph/astar.cc @@ -12,12 +12,11 @@ // limitations under the License. #include -#include -#include #include +#include +#include #include "ortools/base/adjustable_priority_queue.h" -#include "ortools/base/callback.h" #include "ortools/base/integral_types.h" namespace operations_research { @@ -83,8 +82,8 @@ class AStarSP { std::unique_ptr predecessor_; AdjustablePriorityQueue frontier_; std::vector elements_; - std::unordered_set not_visited_; - std::unordered_set added_to_the_frontier_; + absl::flat_hash_set not_visited_; + absl::flat_hash_set added_to_the_frontier_; }; void AStarSP::Initialize() { @@ -114,7 +113,7 @@ int AStarSP::SelectClosestNode(int64* distance) { } void AStarSP::Update(int node) { - for (std::unordered_set::const_iterator it = not_visited_.begin(); + for (absl::flat_hash_set::const_iterator it = not_visited_.begin(); it != not_visited_.end(); ++it) { const int other_node = *it; const int64 graph_node_i = graph_(node, other_node); diff --git a/ortools/graph/christofides.h b/ortools/graph/christofides.h index 2000982bd1a..ac4975a9863 100644 --- a/ortools/graph/christofides.h +++ b/ortools/graph/christofides.h @@ -20,8 +20,7 @@ #ifndef OR_TOOLS_GRAPH_CHRISTOFIDES_H_ #define OR_TOOLS_GRAPH_CHRISTOFIDES_H_ -#include - +#include "absl/container/flat_hash_map.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/graph/eulerian_path.h" @@ -104,7 +103,7 @@ std::vector ComputeMinimumWeightMatchingWithMIP( // and constraints ensuring that each node appears in exactly one selected // arc. The objective is to minimize the sum of the weights of selected arcs. // It is assumed the graph is symmetrical. - std::unordered_map variable_indices; + absl::flat_hash_map variable_indices; for (NodeIndex node : graph.AllNodes()) { // Creating arc-selection Boolean variable. for (const ArcIndex arc : graph.OutgoingArcs(node)) { diff --git a/ortools/graph/cliques.cc b/ortools/graph/cliques.cc index a562a39520b..9bf67c88fea 100644 --- a/ortools/graph/cliques.cc +++ b/ortools/graph/cliques.cc @@ -15,10 +15,10 @@ #include #include -#include #include #include +#include "absl/container/flat_hash_set.h" #include "ortools/base/hash.h" namespace operations_research { @@ -217,7 +217,7 @@ class FindAndEliminate { std::function graph_; int node_count_; std::function&)> callback_; - std::unordered_set > visited_; + absl::flat_hash_set> visited_; }; } // namespace diff --git a/ortools/graph/cliques.h b/ortools/graph/cliques.h index 937be0f4acf..70ee6f140a5 100644 --- a/ortools/graph/cliques.h +++ b/ortools/graph/cliques.h @@ -26,12 +26,11 @@ #include #include -#include #include +#include "absl/strings/str_cat.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/util/time_limit.h" diff --git a/ortools/graph/connected_components.cc b/ortools/graph/connected_components.cc index ace0a403329..d2ae0abfa97 100644 --- a/ortools/graph/connected_components.cc +++ b/ortools/graph/connected_components.cc @@ -27,7 +27,6 @@ // The following uses disjoint-sets algorithms, see: // https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Disjoint-set_forests -#include #include #include "ortools/graph/connected_components.h" diff --git a/ortools/graph/connected_components.h b/ortools/graph/connected_components.h index 20dfa7fd369..bbe0872b0dc 100644 --- a/ortools/graph/connected_components.h +++ b/ortools/graph/connected_components.h @@ -42,10 +42,13 @@ #include #include #include -#include -#include +#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/hash/hash.h" +#include "absl/meta/type_traits.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" #include "ortools/base/ptr_util.h" @@ -124,35 +127,28 @@ namespace internal { // is a comparator or a hasher (prefer the latter). template struct ConnectedComponentsTypeHelper { - // SFINAE helpers to detect a hash functor. - template - struct hash_by_ref {}; - template - struct hash_by_value {}; + // SFINAE trait to detect hash functors and select unordered containers if so, + // and ordered containers otherwise (= by default). + template + struct SelectContainer { + using Set = std::set; + using Map = std::map; + }; - // SFINAE dispatchers that return the right kind of set depending on the - // functor. + // The expression inside decltype is basically saying that "H(x)" is + // well-formed, where H is an instance of U and x is an instance of T, and is + // a value of integral type. That is, we are "duck-typing" on whether U looks + // like a hash functor. template - static std::unordered_set ReturnSet( - hash_by_ref*); - template - static std::unordered_set ReturnSet( - hash_by_value*); - template - static std::set ReturnSet(...); - using Set = decltype(ReturnSet(nullptr)); + struct SelectContainer< + U, absl::enable_if_t()(std::declval()))>::value>> { + using Set = absl::flat_hash_set; + using Map = absl::flat_hash_map; + }; - // SFINAE dispatchers that return the right kind of map depending on the - // functor. - template - static std::unordered_map ReturnMap( - hash_by_ref*); - template - static std::unordered_map ReturnMap( - hash_by_value*); - template - static std::map ReturnMap(...); - using Map = decltype(ReturnMap(nullptr)); + using Set = typename SelectContainer::Set; + using Map = typename SelectContainer::Map; }; } // namespace internal diff --git a/ortools/graph/connectivity.h b/ortools/graph/connectivity.h index 1d700e27773..5753fb5cad2 100644 --- a/ortools/graph/connectivity.h +++ b/ortools/graph/connectivity.h @@ -56,7 +56,7 @@ namespace operations_research { // } // // Group the nodes in the same connected component together. // // group[class_number][i] contains the i-th node in group class_number. -// std::unordered_map > group(num_connected_components); +// hash_map > group(num_connected_components); // for (int node = 0; node < num_nodes; ++node) { // group[components.GetClassRepresentative(node)].push_back(node); // } diff --git a/ortools/graph/dijkstra.cc b/ortools/graph/dijkstra.cc index 1a510515d19..75fdbe45933 100644 --- a/ortools/graph/dijkstra.cc +++ b/ortools/graph/dijkstra.cc @@ -13,9 +13,9 @@ #include #include -#include #include +#include "absl/container/flat_hash_set.h" #include "ortools/base/adjustable_priority_queue.h" #include "ortools/base/integral_types.h" @@ -70,8 +70,8 @@ class DijkstraSP { std::unique_ptr predecessor_; AdjustablePriorityQueue frontier_; std::vector elements_; - std::unordered_set not_visited_; - std::unordered_set added_to_the_frontier_; + absl::flat_hash_set not_visited_; + absl::flat_hash_set added_to_the_frontier_; }; void DijkstraSP::Initialize() { @@ -99,7 +99,7 @@ int DijkstraSP::SelectClosestNode(int64* distance) { } void DijkstraSP::Update(int node) { - for (std::unordered_set::const_iterator it = not_visited_.begin(); + for (absl::flat_hash_set::const_iterator it = not_visited_.begin(); it != not_visited_.end(); ++it) { const int other_node = *it; const int64 graph_node_i = graph_(node, other_node); diff --git a/ortools/graph/ebert_graph.h b/ortools/graph/ebert_graph.h index 816c22126c4..a23565c73d4 100644 --- a/ortools/graph/ebert_graph.h +++ b/ortools/graph/ebert_graph.h @@ -174,11 +174,10 @@ #include #include +#include "absl/strings/str_cat.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/stringprintf.h" #include "ortools/util/permutation.h" #include "ortools/util/zvector.h" diff --git a/ortools/graph/graph.h b/ortools/graph/graph.h index b171e3606c8..2c2f4115e02 100644 --- a/ortools/graph/graph.h +++ b/ortools/graph/graph.h @@ -165,7 +165,6 @@ #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/port.h" #include "ortools/graph/iterators.h" namespace util { @@ -1120,7 +1119,7 @@ template ArcIndexType ListGraph::OutDegree( NodeIndexType node) const { ArcIndexType degree(0); - for (auto arc ATTRIBUTE_UNUSED : OutgoingArcs(node)) ++degree; + for (auto arc ABSL_ATTRIBUTE_UNUSED : OutgoingArcs(node)) ++degree; return degree; } @@ -1438,7 +1437,7 @@ template ArcIndexType ReverseArcListGraph::OutDegree( NodeIndexType node) const { ArcIndexType degree(0); - for (auto arc ATTRIBUTE_UNUSED : OutgoingArcs(node)) ++degree; + for (auto arc ABSL_ATTRIBUTE_UNUSED : OutgoingArcs(node)) ++degree; return degree; } @@ -1446,7 +1445,7 @@ template ArcIndexType ReverseArcListGraph::InDegree( NodeIndexType node) const { ArcIndexType degree(0); - for (auto arc ATTRIBUTE_UNUSED : OppositeIncomingArcs(node)) ++degree; + for (auto arc ABSL_ATTRIBUTE_UNUSED : OppositeIncomingArcs(node)) ++degree; return degree; } @@ -1947,7 +1946,7 @@ template ArcIndexType ReverseArcMixedGraph::InDegree( NodeIndexType node) const { ArcIndexType degree(0); - for (auto arc ATTRIBUTE_UNUSED : OppositeIncomingArcs(node)) ++degree; + for (auto arc ABSL_ATTRIBUTE_UNUSED : OppositeIncomingArcs(node)) ++degree; return degree; } diff --git a/ortools/graph/io.h b/ortools/graph/io.h index 8595e7b931a..d4e3f1f8d28 100644 --- a/ortools/graph/io.h +++ b/ortools/graph/io.h @@ -22,13 +22,14 @@ #include #include +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" #include "ortools/base/filelineiter.h" -#include "ortools/base/join.h" -#include "ortools/base/numbers.h" -#include "ortools/base/split.h" #include "ortools/base/status.h" #include "ortools/base/statusor.h" -#include "ortools/base/stringprintf.h" #include "ortools/graph/graph.h" namespace util { @@ -139,14 +140,14 @@ util::StatusOr ReadGraphFile( ++num_lines_read; if (num_lines_read == 1) { std::vector header_ints; - if (!SplitStringAndParse(line, " ", &strings::safe_strto64, - &header_ints) || - header_ints.size() < 2 || header_ints[0] < 0 || header_ints[1] < 0) { - return util::Status( - util::error::INVALID_ARGUMENT, - absl::StrCat("First line of '", filename, - "' should be at least two nonnegative integers.")); - } + // if (!SplitStringAndParse(line, " ", &absl::SimpleAtoi, + // &header_ints) || + // header_ints.size() < 2 || header_ints[0] < 0 || header_ints[1] < 0) { + // return util::Status( + // util::error::INVALID_ARGUMENT, + // absl::StrCat("First line of '", filename, + // "' should be at least two nonnegative integers.")); + // } num_nodes = header_ints[0]; num_expected_lines = header_ints[1]; if (num_nodes_with_color_or_null != nullptr) { @@ -244,8 +245,8 @@ util::Status WriteGraphToFile(const Graph& graph, const std::string& filename, " arcs!"); } } - fprintf( - f, "%lld %lld", static_cast(graph.num_nodes()), + absl::FPrintF( + f, "%d %d", static_cast(graph.num_nodes()), static_cast(directed ? graph.num_arcs() : (graph.num_arcs() + num_self_arcs) / 2)); if (!num_nodes_with_color.empty()) { @@ -258,7 +259,7 @@ util::Status WriteGraphToFile(const Graph& graph, const std::string& filename, } fprintf(f, " %lu", num_nodes_with_color.size()); for (int i = 0; i < num_nodes_with_color.size() - 1; ++i) { - fprintf(f, " %lld", static_cast(num_nodes_with_color[i])); + absl::FPrintF(f, " %d", static_cast(num_nodes_with_color[i])); } } fprintf(f, "\n"); @@ -267,8 +268,8 @@ util::Status WriteGraphToFile(const Graph& graph, const std::string& filename, for (const typename Graph::ArcIndex arc : graph.OutgoingArcs(node)) { const typename Graph::NodeIndex head = graph.Head(arc); if (directed || head >= node) { - fprintf(f, "%lld %lld\n", static_cast(node), - static_cast(head)); + absl::FPrintF(f, "%d %d\n", static_cast(node), + static_cast(head)); } } } diff --git a/ortools/graph/linear_assignment.h b/ortools/graph/linear_assignment.h index 4bfd38e897d..b9bfadb923e 100644 --- a/ortools/graph/linear_assignment.h +++ b/ortools/graph/linear_assignment.h @@ -204,11 +204,11 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/stringprintf.h" #include "ortools/graph/ebert_graph.h" #include "ortools/util/permutation.h" #include "ortools/util/zvector.h" @@ -392,8 +392,8 @@ class LinearSumAssignment { } std::string StatsString() const { return absl::StrFormat( - "%lld refinements; %lld relabelings; " - "%lld double pushes; %lld pushes", + "%d refinements; %d relabelings; " + "%d double pushes; %d pushes", refinements_, relabelings_, double_pushes_, pushes_); } int64 pushes_; diff --git a/ortools/graph/max_flow.cc b/ortools/graph/max_flow.cc index 4cf8fc38b0c..9761a0ee9d2 100644 --- a/ortools/graph/max_flow.cc +++ b/ortools/graph/max_flow.cc @@ -15,8 +15,8 @@ #include -#include "ortools/base/memory.h" -#include "ortools/base/stringprintf.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_format.h" #include "ortools/graph/graph.h" #include "ortools/graph/graphs.h" @@ -321,13 +321,13 @@ std::string GenericMaxFlow::DebugString(const std::string& context, const NodeIndex head = Head(arc); return absl::StrFormat( "%s Arc %d, from %d to %d, " - "Capacity = %lld, Residual capacity = %lld, " - "Flow = residual capacity for reverse arc = %lld, " + "Capacity = %d, Residual capacity = %d, " + "Flow = residual capacity for reverse arc = %d, " "Height(tail) = %d, Height(head) = %d, " - "Excess(tail) = %lld, Excess(head) = %lld", - context.c_str(), arc, tail, head, Capacity(arc), - residual_arc_capacity_[arc], Flow(arc), node_potential_[tail], - node_potential_[head], node_excess_[tail], node_excess_[head]); + "Excess(tail) = %d, Excess(head) = %d", + context, arc, tail, head, Capacity(arc), residual_arc_capacity_[arc], + Flow(arc), node_potential_[tail], node_potential_[head], + node_excess_[tail], node_excess_[head]); } template diff --git a/ortools/graph/min_cost_flow.cc b/ortools/graph/min_cost_flow.cc index a0b85bdef58..212308186f6 100644 --- a/ortools/graph/min_cost_flow.cc +++ b/ortools/graph/min_cost_flow.cc @@ -17,9 +17,9 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/mathutil.h" -#include "ortools/base/stringprintf.h" #include "ortools/graph/graph.h" #include "ortools/graph/graphs.h" #include "ortools/graph/max_flow.h" @@ -273,12 +273,12 @@ GenericMinCostFlow::DebugString( node_potential_[tail] - node_potential_[head]; return absl::StrFormat( "%s Arc %d, from %d to %d, " - "Capacity = %lld, Residual capacity = %lld, " - "Flow = residual capacity for reverse arc = %lld, " - "Height(tail) = %lld, Height(head) = %lld, " - "Excess(tail) = %lld, Excess(head) = %lld, " - "Cost = %lld, Reduced cost = %lld, ", - context.c_str(), arc, tail, head, Capacity(arc), + "Capacity = %d, Residual capacity = %d, " + "Flow = residual capacity for reverse arc = %d, " + "Height(tail) = %d, Height(head) = %d, " + "Excess(tail) = %d, Excess(head) = %d, " + "Cost = %d, Reduced cost = %d, ", + context, arc, tail, head, Capacity(arc), static_cast(residual_arc_capacity_[arc]), Flow(arc), node_potential_[tail], node_potential_[head], node_excess_[tail], node_excess_[head], static_cast(scaled_arc_unit_cost_[arc]), @@ -1089,7 +1089,7 @@ SimpleMinCostFlow::Status SimpleMinCostFlow::SolveWithPossibleAdjustment( case MaxFlowStatusClass::OPTIMAL: LOG(ERROR) << "Max flow failed but claimed to have an optimal solution"; - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; default: return BAD_RESULT; } diff --git a/ortools/graph/shortestpaths.cc b/ortools/graph/shortestpaths.cc index 396d111d83c..6b541d7b9fb 100644 --- a/ortools/graph/shortestpaths.cc +++ b/ortools/graph/shortestpaths.cc @@ -17,7 +17,6 @@ #include #include #include -#include #include #include "ortools/base/commandlineflags.h" diff --git a/ortools/graph/strongly_connected_components.h b/ortools/graph/strongly_connected_components.h index 81509fe8628..90d760096c5 100644 --- a/ortools/graph/strongly_connected_components.h +++ b/ortools/graph/strongly_connected_components.h @@ -63,7 +63,7 @@ // It will be called with the connected components of the given graph as they // are found (In the reverse topological order). // -// More pratical details on the algorithm: +// More practical details on the algorithm: // - It deals properly with self-loop and duplicate nodes. // - It is really fast! and work in O(nodes + edges). // - Its memory usage is also bounded by O(nodes + edges) but in practice it diff --git a/ortools/graph/util.h b/ortools/graph/util.h index a047b54b0ef..0d7f74f3690 100644 --- a/ortools/graph/util.h +++ b/ortools/graph/util.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "ortools/base/hash.h" diff --git a/ortools/linear_solver/CMakeLists.txt b/ortools/linear_solver/CMakeLists.txt index e96b2ba30f0..1a03dfe9ed9 100644 --- a/ortools/linear_solver/CMakeLists.txt +++ b/ortools/linear_solver/CMakeLists.txt @@ -4,39 +4,57 @@ set(NAME ${PROJECT_NAME}_linear_solver) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE -# gflags::gflags -# glog::glog +# absl::memory absl::container absl::strings absl::str_format +# gflags::gflags glog::glog # protobuf::libprotobuf # Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + absl::memory absl::container absl::strings absl::str_format + gflags::gflags glog::glog + protobuf::libprotobuf + Cbc::Cbc + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::linear_solver ALIAS ${NAME}) diff --git a/ortools/linear_solver/bop_interface.cc b/ortools/linear_solver/bop_interface.cc index e42d5138699..096eca9836f 100644 --- a/ortools/linear_solver/bop_interface.cc +++ b/ortools/linear_solver/bop_interface.cc @@ -22,8 +22,6 @@ #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/port.h" -#include "ortools/base/stringprintf.h" #include "ortools/bop/bop_parameters.pb.h" #include "ortools/bop/integral_solver.h" #include "ortools/linear_solver/linear_solver.h" diff --git a/ortools/linear_solver/cbc_interface.cc b/ortools/linear_solver/cbc_interface.cc index a227f3a1d98..14d9be2f098 100644 --- a/ortools/linear_solver/cbc_interface.cc +++ b/ortools/linear_solver/cbc_interface.cc @@ -16,16 +16,14 @@ #include #include #include -#include #include #include +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/port.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" #include "ortools/linear_solver/linear_solver.h" diff --git a/ortools/linear_solver/clp_interface.cc b/ortools/linear_solver/clp_interface.cc index 08aa80b9ddd..bedd9d3b0e8 100644 --- a/ortools/linear_solver/clp_interface.cc +++ b/ortools/linear_solver/clp_interface.cc @@ -16,17 +16,15 @@ #include #include #include -#include #include +#include "absl/memory/memory.h" +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/memory.h" -#include "ortools/base/port.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/strutil.h" #include "ortools/base/timer.h" #include "ortools/linear_solver/linear_solver.h" diff --git a/ortools/linear_solver/cplex_interface.cc b/ortools/linear_solver/cplex_interface.cc index 05ef29136a8..56b1f056526 100644 --- a/ortools/linear_solver/cplex_interface.cc +++ b/ortools/linear_solver/cplex_interface.cc @@ -16,9 +16,9 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" #include "ortools/linear_solver/linear_solver.h" diff --git a/ortools/linear_solver/glop_interface.cc b/ortools/linear_solver/glop_interface.cc index 9a9501c34e7..919d033558a 100644 --- a/ortools/linear_solver/glop_interface.cc +++ b/ortools/linear_solver/glop_interface.cc @@ -14,13 +14,10 @@ #include #include #include -#include #include #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/port.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/hash.h" #include "ortools/glop/lp_solver.h" diff --git a/ortools/linear_solver/glpk_interface.cc b/ortools/linear_solver/glpk_interface.cc index 8042ef32910..9aa4ebfd04c 100644 --- a/ortools/linear_solver/glpk_interface.cc +++ b/ortools/linear_solver/glpk_interface.cc @@ -20,17 +20,15 @@ #include #include #include -#include #include #include +#include "absl/memory/memory.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/memory.h" -#include "ortools/base/port.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" #include "ortools/linear_solver/linear_solver.h" diff --git a/ortools/linear_solver/gurobi_interface.cc b/ortools/linear_solver/gurobi_interface.cc index 7f44c331f90..9eceac881f0 100644 --- a/ortools/linear_solver/gurobi_interface.cc +++ b/ortools/linear_solver/gurobi_interface.cc @@ -19,16 +19,14 @@ #include #include #include -#include #include #include +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/port.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" #include "ortools/linear_solver/linear_solver.h" @@ -656,7 +654,8 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) { ExtractModel(); // Sync solver. CheckedGurobiCall(GRBupdatemodel(model_)); - VLOG(1) << absl::StrFormat("Model built in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Model built in %s.", + absl::FormatDuration(timer.GetDuration())); // Set solution hints if any. for (const std::pair& p : solver_->solution_hint_) { @@ -687,7 +686,8 @@ MPSolver::ResultStatus GurobiInterface::Solve(const MPSolverParameters& param) { if (status) { VLOG(1) << "Failed to optimize MIP." << GRBgeterrormsg(env_); } else { - VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Solved in %s.", + absl::FormatDuration(timer.GetDuration())); } // Get the status. diff --git a/ortools/linear_solver/java/linear_solver.i b/ortools/linear_solver/java/linear_solver.i index cbd3da2586f..395406d7f61 100644 --- a/ortools/linear_solver/java/linear_solver.i +++ b/ortools/linear_solver/java/linear_solver.i @@ -176,7 +176,7 @@ import java.lang.reflect.*; %rename (supportsProblemType) operations_research::MPSolver::SupportsProblemType; // no test %rename (setSolverSpecificParametersAsString) operations_research::MPSolver::SetSolverSpecificParametersAsString; // no test -%rename (interruptSolve) operations_research::MPSolver::InterruptSolve; +%rename (interruptSolve) operations_research::MPSolver::InterruptSolve; // no test %rename (wallTime) operations_research::MPSolver::wall_time; %rename (clear) operations_research::MPSolver::Clear; // no test %rename (numVariables) operations_research::MPSolver::NumVariables; diff --git a/ortools/linear_solver/linear_expr.h b/ortools/linear_solver/linear_expr.h index 9d7f64d22d1..196dd0b8200 100644 --- a/ortools/linear_solver/linear_expr.h +++ b/ortools/linear_solver/linear_expr.h @@ -66,7 +66,7 @@ // * LinearExpr e1 = LinearExpr(x) + (y + 5); // * LinearExpr e1 = y + 5 + LinearExpr(x); -#include +#include "absl/container/flat_hash_map.h" namespace operations_research { @@ -118,7 +118,7 @@ class LinearExpr { LinearExpr operator-() const; double offset() const { return offset_; } - const std::unordered_map& terms() const { + const absl::flat_hash_map& terms() const { return terms_; } @@ -128,7 +128,7 @@ class LinearExpr { private: double offset_; - std::unordered_map terms_; + absl::flat_hash_map terms_; }; // NOTE(user): in the ops below, the non-"const LinearExpr&" are intentional. diff --git a/ortools/linear_solver/linear_solver.cc b/ortools/linear_solver/linear_solver.cc index 44c4c70c189..56f7a26722f 100644 --- a/ortools/linear_solver/linear_solver.cc +++ b/ortools/linear_solver/linear_solver.cc @@ -30,13 +30,14 @@ #include "ortools/port/file.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/synchronization/mutex.h" #include "ortools/base/accurate_sum.h" #include "ortools/base/canonical_errors.h" -#include "ortools/base/join.h" #include "ortools/base/map_util.h" -#include "ortools/base/mutex.h" +//#include "ortools/base/status_macros.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/linear_solver/linear_solver.pb.h" #include "ortools/linear_solver/model_exporter.h" #include "ortools/linear_solver/model_validator.h" @@ -406,8 +407,7 @@ MPSolver::MPSolver(const std::string& name, OptimizationProblemType problem_type) : name_(name), problem_type_(DetourProblemType(problem_type)), - time_limit_(0.0) { - timer_.Restart(); + construction_time_(absl::Now()) { interface_.reset(BuildSolverInterface(this)); if (FLAGS_linear_solver_enable_verbose_output) { EnableOutput(); @@ -491,7 +491,7 @@ bool MPSolver::ParseSolverType(absl::string_view solver, MPVariable* MPSolver::LookupVariableOrNull(const std::string& var_name) const { if (!variable_name_to_index_) GenerateVariableNameIndex(); - std::unordered_map::const_iterator it = + absl::flat_hash_map::const_iterator it = variable_name_to_index_->find(var_name); if (it == variable_name_to_index_->end()) return nullptr; return variables_[it->second]; @@ -662,13 +662,8 @@ void MPSolver::SolveWithProto(const MPModelRequest& model_request, return; } if (model_request.has_solver_time_limit_seconds()) { - double time_limit_ms = model_request.solver_time_limit_seconds() * 1000.0; - if (time_limit_ms < - static_cast(std::numeric_limits::max())) { - // static_cast avoids a warning with -Wreal-conversion. This - // helps catching bugs with unwanted conversions from double to ints. - solver.set_time_limit(static_cast(time_limit_ms)); - } + solver.SetTimeLimit( + absl::Seconds(model_request.solver_time_limit_seconds())); } solver.SetSolverSpecificParametersAsString( model_request.solver_specific_parameters()); @@ -703,7 +698,7 @@ void MPSolver::ExportModelToProto(MPModelProto* output_model) const { // This step is needed as long as the variable indices are given by the // underlying solver at the time of model extraction. // TODO(user): remove this step. - std::unordered_map var_to_index; + absl::flat_hash_map var_to_index; for (int j = 0; j < variables_.size(); ++j) { var_to_index[variables_[j]] = j; } @@ -1030,9 +1025,9 @@ std::string PrettyPrintVar(const MPVariable& var) { if (lb > ub) { return prefix + "∅"; } else if (lb == ub) { - return absl::StrFormat("%s{ %lld }", prefix.c_str(), lb); + return absl::StrFormat("%s{ %d }", prefix.c_str(), lb); } else { - return absl::StrFormat("%s{ %lld, %lld }", prefix.c_str(), lb, ub); + return absl::StrFormat("%s{ %d, %d }", prefix.c_str(), lb, ub); } } // Special case: single (non-infinite) real value. @@ -1297,7 +1292,7 @@ void MPSolver::SetHint(std::vector > hint) { void MPSolver::GenerateVariableNameIndex() const { if (variable_name_to_index_) return; - variable_name_to_index_ = std::unordered_map(); + variable_name_to_index_ = absl::flat_hash_map(); for (const MPVariable* const var : variables_) { gtl::InsertOrDie(&*variable_name_to_index_, var->name(), var->index()); } @@ -1305,7 +1300,7 @@ void MPSolver::GenerateVariableNameIndex() const { void MPSolver::GenerateConstraintNameIndex() const { if (constraint_name_to_index_) return; - constraint_name_to_index_ = std::unordered_map(); + constraint_name_to_index_ = absl::flat_hash_map(); for (const MPConstraint* const cst : constraints_) { gtl::InsertOrDie(&*constraint_name_to_index_, cst->name(), cst->index()); } @@ -1513,13 +1508,13 @@ bool MPSolverInterface::SetSolverSpecificParametersAsString( extension.c_str()); bool no_error_so_far = true; if (no_error_so_far) { - no_error_so_far = FileSetContents(filename, parameters).ok(); + no_error_so_far = PortableFileSetContents(filename, parameters).ok(); } if (no_error_so_far) { no_error_so_far = ReadParameterFile(filename); // We need to clean up the file even if ReadParameterFile() returned // false. In production we can continue even if the deletion failed. - if (!DeleteFile(filename).ok()) { + if (!PortableDeleteFile(filename).ok()) { LOG(DFATAL) << "Couldn't delete temporary parameters file: " << filename; } } diff --git a/ortools/linear_solver/linear_solver.h b/ortools/linear_solver/linear_solver.h index 81de958dd2b..aa97266a0d6 100644 --- a/ortools/linear_solver/linear_solver.h +++ b/ortools/linear_solver/linear_solver.h @@ -146,15 +146,14 @@ #include "ortools/base/commandlineflags.h" -#include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" +#include "absl/types/optional.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/optional.h" -#include "ortools/base/port.h" #include "ortools/base/status.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/strutil.h" #include "ortools/base/timer.h" #include "ortools/glop/parameters.pb.h" #include "ortools/linear_solver/linear_expr.h" @@ -534,29 +533,21 @@ class MPSolver { void EnableOutput(); void SuppressOutput(); - void set_time_limit(int64 time_limit_milliseconds) { - DCHECK_GE(time_limit_milliseconds, 0); - time_limit_ = time_limit_milliseconds; + absl::Duration TimeLimit() const { return time_limit_; } + void SetTimeLimit(absl::Duration time_limit) { + DCHECK_GE(time_limit, absl::ZeroDuration()); + time_limit_ = time_limit; } - // In milliseconds. - int64 time_limit() const { return time_limit_; } - - // In seconds. Note that this returns a double. - double time_limit_in_secs() const { - // static_cast avoids a warning with -Wreal-conversion. This - // helps catching bugs with unwanted conversions from double to ints. - return static_cast(time_limit_) / 1000.0; + absl::Duration DurationSinceConstruction() const { + return absl::Now() - construction_time_; } - // Returns wall_time() in milliseconds since the creation of the solver. - int64 wall_time() const { return timer_.GetInMs(); } - // Returns the number of simplex iterations. int64 iterations() const; - // Returns the number of branch-and-bound nodes. Only available for - // discrete problems. + // Returns the number of branch-and-bound nodes evaluated during the solve. + // Only available for discrete problems. int64 nodes() const; // Returns a std::string describing the underlying solver and its version. @@ -610,7 +601,29 @@ class MPSolver { // As of 2018-08-09, only Gurobi supports NextSolution(), see // linear_solver_underlying_gurobi_test for an example of how to configure // Gurobi for this purpose. The other solvers return false unconditionally. - bool NextSolution() MUST_USE_RESULT; + ABSL_MUST_USE_RESULT bool NextSolution(); + + // DEPRECATED: Use TimeLimit() and SetTimeLimit(absl::Duration) instead. + // NOTE: These deprecated functions used the convention time_limit = 0 to mean + // "no limit", which now corresponds to time_limit_ = InfiniteDuration(). + int64 time_limit() const { + return time_limit_ == absl::InfiniteDuration() + ? 0 + : absl::ToInt64Milliseconds(time_limit_); + } + void set_time_limit(int64 time_limit_milliseconds) { + SetTimeLimit(time_limit_milliseconds == 0 + ? absl::InfiniteDuration() + : absl::Milliseconds(time_limit_milliseconds)); + } + double time_limit_in_secs() const { + return static_cast(time_limit()) / 1000.0; + } + + // DEPRECATED: Use DurationSinceConstruction() instead. + int64 wall_time() const { + return absl::ToInt64Milliseconds(DurationSinceConstruction()); + } friend class GLPKInterface; friend class CLPInterface; @@ -659,7 +672,7 @@ class MPSolver { // The vector of variables in the problem. std::vector variables_; // A map from a variable's name to its index in variables_. - mutable absl::optional > + mutable absl::optional > variable_name_to_index_; // Whether variables have been extracted to the underlying interface. std::vector variable_is_extracted_; @@ -667,7 +680,7 @@ class MPSolver { // The vector of constraints in the problem. std::vector constraints_; // A map from a constraint's name to its index in constraints_. - mutable absl::optional > + mutable absl::optional > constraint_name_to_index_; // Whether constraints have been extracted to the underlying interface. std::vector constraint_is_extracted_; @@ -684,10 +697,9 @@ class MPSolver { // hint is provided and a std::vector for the hint value. std::vector > solution_hint_; - // Time limit in milliseconds (0 = no limit). - int64 time_limit_; + absl::Duration time_limit_ = absl::InfiniteDuration(); // Default = No limit. - WallTimer timer_; + const absl::Time construction_time_; // Permanent storage for SetSolverSpecificParametersAsString(). std::string solver_specific_parameter_string_; @@ -722,7 +734,7 @@ class MPObjective { // Returns a map from variables to their coefficients in the objective. If a // variable is not present in the map, then its coefficient is zero. - const std::unordered_map& terms() const { + const absl::flat_hash_map& terms() const { return coefficients_; } @@ -790,13 +802,13 @@ class MPObjective { // to several models. // At construction, an MPObjective has no terms (which is equivalent // on having a coefficient of 0 for all variables), and an offset of 0. - explicit MPObjective(MPSolverInterface* const interface) - : interface_(interface), coefficients_(1), offset_(0.0) {} + explicit MPObjective(MPSolverInterface* const interface_in) + : interface_(interface_in), offset_(0.0) {} MPSolverInterface* const interface_; // Mapping var -> coefficient. - std::unordered_map coefficients_; + absl::flat_hash_map coefficients_; // Constant term. double offset_; @@ -865,7 +877,7 @@ class MPVariable { // is specified in the constructor. A variable cannot belong to // several models. MPVariable(int index, double lb, double ub, bool integer, - const std::string& name, MPSolverInterface* const interface) + const std::string& name, MPSolverInterface* const interface_in) : index_(index), lb_(lb), ub_(ub), @@ -873,7 +885,7 @@ class MPVariable { name_(name.empty() ? absl::StrFormat("auto_v_%09d", index) : name), solution_value_(0.0), reduced_cost_(0.0), - interface_(interface) {} + interface_(interface_in) {} void set_solution_value(double value) { solution_value_ = value; } void set_reduced_cost(double reduced_cost) { reduced_cost_ = reduced_cost; } @@ -910,7 +922,7 @@ class MPConstraint { // Returns a map from variables to their coefficients in the constraint. If a // variable is not present in the map, then its coefficient is zero. - const std::unordered_map& terms() const { + const absl::flat_hash_map& terms() const { return coefficients_; } @@ -972,15 +984,14 @@ class MPConstraint { // that is specified in the constructor. A constraint cannot belong // to several models. MPConstraint(int index, double lb, double ub, const std::string& name, - MPSolverInterface* const interface) - : coefficients_(1), - index_(index), + MPSolverInterface* const interface_in) + : index_(index), lb_(lb), ub_(ub), name_(name.empty() ? absl::StrFormat("auto_c_%09d", index) : name), is_lazy_(false), dual_value_(0.0), - interface_(interface) {} + interface_(interface_in) {} void set_dual_value(double dual_value) { dual_value_ = dual_value; } @@ -990,7 +1001,7 @@ class MPConstraint { bool ContainsNewVariables(); // Mapping var -> coefficient. - std::unordered_map coefficients_; + absl::flat_hash_map coefficients_; const int index_; // See index(). diff --git a/ortools/linear_solver/model_exporter.cc b/ortools/linear_solver/model_exporter.cc index abc8b3ca054..644df20b96e 100644 --- a/ortools/linear_solver/model_exporter.cc +++ b/ortools/linear_solver/model_exporter.cc @@ -15,15 +15,14 @@ #include #include -#include +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/strutil.h" #include "ortools/linear_solver/linear_solver.pb.h" #include "ortools/util/fp_utils.h" @@ -56,7 +55,7 @@ class NameManager { std::string MakeUniqueName(const std::string& name); private: - std::unordered_set names_set_; + absl::flat_hash_set names_set_; int last_n_; }; @@ -106,7 +105,7 @@ std::vector MPModelProtoExporter::ExtractAndProcessNames( int i = 0; for (const auto& item : proto) { const std::string obfuscated_name = - absl::StrFormat("%s%0*d", prefix.c_str(), num_digits, i); + absl::StrFormat("%s%0*d", prefix, num_digits, i); if (obfuscate || !item.has_name()) { result[i] = namer.MakeUniqueName(obfuscated_name); LOG_IF(WARNING, FLAGS_lp_log_invalid_name && !item.has_name()) @@ -208,12 +207,10 @@ void LineBreaker::Append(const std::string& s) { } std::string DoubleToStringWithForcedSign(double d) { - return absl::StrCat((d < 0 ? "" : "+"), absl::LegacyPrecision(d)); + return absl::StrCat((d < 0 ? "" : "+"), (d)); } -std::string DoubleToString(double d) { - return absl::StrCat(absl::LegacyPrecision(d)); -} +std::string DoubleToString(double d) { return absl::StrCat((d)); } } // namespace @@ -351,7 +348,7 @@ bool MPModelProtoExporter::ExportModelAsLpFormat(bool obfuscated, const double ub = var_proto.upper_bound(); if (var_proto.is_integer() && lb == round(lb) && ub == round(ub)) { absl::StrAppendFormat(output, " %.0f <= %s <= %.0f\n", lb, - exported_variable_names_[var_index].c_str(), ub); + exported_variable_names_[var_index], ub); } else { absl::StrAppend(output, " "); if (lb == -std::numeric_limits::infinity() && @@ -378,7 +375,7 @@ bool MPModelProtoExporter::ExportModelAsLpFormat(bool obfuscated, const MPVariableProto& var_proto = proto_.variable(var_index); if (IsBoolean(var_proto)) { absl::StrAppendFormat(output, " %s\n", - exported_variable_names_[var_index].c_str()); + exported_variable_names_[var_index]); } } } @@ -409,19 +406,22 @@ void MPModelProtoExporter::AppendMpsPair(const std::string& name, double value, --precision; value_str = absl::StrFormat("%.*g", precision, value); } - absl::StrAppendFormat(output, " %-8s %*s ", name.c_str(), kFixedMpsDoubleWidth, - value_str.c_str()); + absl::StrAppendFormat(output, " %-8s %*s ", name, kFixedMpsDoubleWidth, + value_str); } else { - absl::StrAppendFormat(output, " %-16s %21s ", name.c_str(), - DoubleToString(value).c_str()); + absl::StrAppendFormat(output, " %-16s %21s ", name, + DoubleToString(value)); } } void MPModelProtoExporter::AppendMpsLineHeader(const std::string& id, const std::string& name, std::string* output) const { - absl::StrAppendFormat(output, use_fixed_mps_format_ ? " %-2s %-8s" : " %-2s %-16s", - id.c_str(), name.c_str()); + if (use_fixed_mps_format_) { + absl::StrAppendFormat(output, " %-2s %-8s", id, name); + } else { + absl::StrAppendFormat(output, " %-2s %-16s", id, name); + } } void MPModelProtoExporter::AppendMpsLineHeaderWithNewLine( @@ -503,7 +503,7 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, AppendComments("*", output); // NAME section. - absl::StrAppendFormat(output, "%-14s%s\n", "NAME", proto_.name().c_str()); + absl::StrAppendFormat(output, "%-14s%s\n", "NAME", proto_.name()); // ROWS section. current_mps_column_ = 0; @@ -554,7 +554,7 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, std::string columns_section; AppendMpsColumns(/*integrality=*/true, transpose, &columns_section); if (!columns_section.empty()) { - const char* const kIntMarkerFormat = " %-10s%-36s%-10s\n"; + constexpr const char kIntMarkerFormat[] = " %-10s%-36s%-10s\n"; columns_section = absl::StrFormat(kIntMarkerFormat, "INTSTART", "'MARKER'", "'INTORG'") + columns_section; @@ -612,7 +612,7 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, if (var_proto.is_integer()) { if (IsBoolean(var_proto)) { AppendMpsLineHeader("BV", "BOUND", &bounds_section); - absl::StrAppendFormat(&bounds_section, " %s\n", var_name.c_str()); + absl::StrAppendFormat(&bounds_section, " %s\n", var_name); } else { if (lb != 0.0) { AppendMpsBound("LI", var_name, lb, &bounds_section); @@ -625,7 +625,7 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, if (lb == -std::numeric_limits::infinity() && ub == +std::numeric_limits::infinity()) { AppendMpsLineHeader("FR", "BOUND", &bounds_section); - absl::StrAppendFormat(&bounds_section, " %s\n", var_name.c_str()); + absl::StrAppendFormat(&bounds_section, " %s\n", var_name); } else if (lb == ub) { AppendMpsBound("FX", var_name, lb, &bounds_section); } else { @@ -633,7 +633,7 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, AppendMpsBound("LO", var_name, lb, &bounds_section); } else if (ub == +std::numeric_limits::infinity()) { AppendMpsLineHeader("PL", "BOUND", &bounds_section); - absl::StrAppendFormat(&bounds_section, " %s\n", var_name.c_str()); + absl::StrAppendFormat(&bounds_section, " %s\n", var_name); } if (ub != +std::numeric_limits::infinity()) { AppendMpsBound("UP", var_name, ub, &bounds_section); diff --git a/ortools/linear_solver/model_exporter.h b/ortools/linear_solver/model_exporter.h index 03ba0ced32f..ec85b498565 100644 --- a/ortools/linear_solver/model_exporter.h +++ b/ortools/linear_solver/model_exporter.h @@ -15,7 +15,6 @@ #define OR_TOOLS_LINEAR_SOLVER_MODEL_EXPORTER_H_ #include -#include #include #include "ortools/base/hash.h" diff --git a/ortools/linear_solver/model_validator.cc b/ortools/linear_solver/model_validator.cc index bf2841708f6..aeb6a8e0be2 100644 --- a/ortools/linear_solver/model_validator.cc +++ b/ortools/linear_solver/model_validator.cc @@ -15,8 +15,8 @@ #include #include +#include "absl/strings/str_cat.h" #include "ortools/base/accurate_sum.h" -#include "ortools/base/join.h" #include "ortools/port/proto_utils.h" #include "ortools/util/fp_utils.h" @@ -32,21 +32,18 @@ std::string FindErrorInMPVariable(const MPVariableProto& variable) { variable.lower_bound() == kInfinity || variable.upper_bound() == -kInfinity || variable.lower_bound() > variable.upper_bound()) { - return absl::StrCat("Infeasible bounds: [", - absl::LegacyPrecision(variable.lower_bound()), ", ", - absl::LegacyPrecision(variable.upper_bound()), "]"); + return absl::StrCat("Infeasible bounds: [", (variable.lower_bound()), ", ", + (variable.upper_bound()), "]"); } if (variable.is_integer() && ceil(variable.lower_bound()) > floor(variable.upper_bound())) { - return absl::StrCat("Infeasible bounds for integer variable: [", - absl::LegacyPrecision(variable.lower_bound()), ", ", - absl::LegacyPrecision(variable.upper_bound()), "]", - " translate to the empty set"); + return absl::StrCat( + "Infeasible bounds for integer variable: [", (variable.lower_bound()), + ", ", (variable.upper_bound()), "]", " translate to the empty set"); } if (!std::isfinite(variable.objective_coefficient())) { - return absl::StrCat( - "Invalid objective_coefficient: ", - absl::LegacyPrecision(variable.objective_coefficient())); + return absl::StrCat("Invalid objective_coefficient: ", + (variable.objective_coefficient())); } return std::string(); } @@ -61,9 +58,8 @@ std::string FindErrorInMPConstraint(const MPConstraintProto& constraint, constraint.lower_bound() == kInfinity || constraint.upper_bound() == -kInfinity || constraint.lower_bound() > constraint.upper_bound()) { - return absl::StrCat("Infeasible bounds: [", - absl::LegacyPrecision(constraint.lower_bound()), ", ", - absl::LegacyPrecision(constraint.upper_bound()), "]"); + return absl::StrCat("Infeasible bounds: [", (constraint.lower_bound()), + ", ", (constraint.upper_bound()), "]"); } // TODO(user): clarify explicitly, at least in a comment, whether we want @@ -84,8 +80,7 @@ std::string FindErrorInMPConstraint(const MPConstraintProto& constraint, } const double coeff = constraint.coefficient(i); if (!std::isfinite(coeff)) { - return absl::StrCat("coefficient(", i, ")=", absl::LegacyPrecision(coeff), - " is invalid"); + return absl::StrCat("coefficient(", i, ")=", (coeff), " is invalid"); } } @@ -127,8 +122,7 @@ std::string FindErrorInSolutionHint( } var_in_hint[var_index] = true; if (!std::isfinite(solution_hint.var_value(i))) { - return absl::StrCat("var_value(", i, ")=", - absl::LegacyPrecision(solution_hint.var_value(i)), + return absl::StrCat("var_value(", i, ")=", (solution_hint.var_value(i)), " is not a finite number"); } } @@ -146,7 +140,7 @@ std::string FindErrorInMPModelProto(const MPModelProto& model) { if (!std::isfinite(model.objective_offset())) { return absl::StrCat("Invalid objective_offset: ", - absl::LegacyPrecision(model.objective_offset())); + (model.objective_offset())); } const int num_vars = model.variable_size(); const int num_cts = model.constraint_size(); @@ -232,11 +226,9 @@ std::string FindFeasibilityErrorInSolutionHint(const MPModelProto& model, if (!IsSmallerWithinTolerance(value, ub, tolerance) || !IsSmallerWithinTolerance(lb, value, tolerance)) { return absl::StrCat("Variable '", model.variable(var_index).name(), - "' is set to ", absl::LegacyPrecision(value), - " which is not in the variable bounds [", - absl::LegacyPrecision(lb), ", ", - absl::LegacyPrecision(ub), "] modulo a tolerance of ", - absl::LegacyPrecision(tolerance), "."); + "' is set to ", (value), + " which is not in the variable bounds [", (lb), ", ", + (ub), "] modulo a tolerance of ", (tolerance), "."); } } @@ -254,10 +246,8 @@ std::string FindFeasibilityErrorInSolutionHint(const MPModelProto& model, !IsSmallerWithinTolerance(lb, activity.Value(), tolerance)) { return absl::StrCat( "Constraint '", model.constraint(cst_index).name(), "' has activity ", - absl::LegacyPrecision(activity.Value()), - " which is not in the constraint bounds [", absl::LegacyPrecision(lb), - ", ", absl::LegacyPrecision(ub), "] modulo a tolerance of ", - absl::LegacyPrecision(tolerance), "."); + (activity.Value()), " which is not in the constraint bounds [", (lb), + ", ", (ub), "] modulo a tolerance of ", (tolerance), "."); } } diff --git a/ortools/linear_solver/scip_interface.cc b/ortools/linear_solver/scip_interface.cc index c1674d57f78..dec4a45fb8d 100644 --- a/ortools/linear_solver/scip_interface.cc +++ b/ortools/linear_solver/scip_interface.cc @@ -17,15 +17,12 @@ #include #include #include -#include #include #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/port.h" -#include "ortools/base/stringprintf.h" #include "ortools/base/timer.h" #include "ortools/linear_solver/linear_solver.h" #include "scip/scip.h" @@ -440,7 +437,8 @@ MPSolver::ResultStatus SCIPInterface::Solve(const MPSolverParameters& param) { } ExtractModel(); - VLOG(1) << absl::StrFormat("Model built in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Model built in %s.", + absl::FormatDuration(timer.GetDuration())); // Time limit. if (solver_->time_limit() != 0) { @@ -516,7 +514,8 @@ MPSolver::ResultStatus SCIPInterface::Solve(const MPSolverParameters& param) { result_status_ = MPSolver::ABNORMAL; return result_status_; } - VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get()); + VLOG(1) << absl::StrFormat("Solved in %s.", + absl::FormatDuration(timer.GetDuration())); // Get the results. SCIP_SOL* const solution = SCIPgetBestSol(scip_); @@ -581,8 +580,10 @@ int64 SCIPInterface::iterations() const { int64 SCIPInterface::nodes() const { if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfNodes; - // TODO(user): or is it SCIPgetNTotalNodes? - return SCIPgetNNodes(scip_); + // This is the total number of nodes used in the solve, potentially across + // multiple branch-and-bound trees. Use limits/totalnodes (rather than + // limits/nodes) to control this value. + return SCIPgetNTotalNodes(scip_); } double SCIPInterface::best_objective_bound() const { diff --git a/ortools/lp_data/CMakeLists.txt b/ortools/lp_data/CMakeLists.txt index 0225651ab7e..14b3db0058e 100644 --- a/ortools/lp_data/CMakeLists.txt +++ b/ortools/lp_data/CMakeLists.txt @@ -4,39 +4,54 @@ set(NAME ${PROJECT_NAME}_lp_data) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE -# gflags::gflags -# glog::glog +# absl::synchronization absl::strings absl::str_format +# gflags::gflags glog::glog # protobuf::libprotobuf # Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + absl::synchronization absl::strings absl::str_format + gflags::gflags glog::glog + protobuf::libprotobuf + Cbc::Cbc + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::lp_data ALIAS ${NAME}) diff --git a/ortools/lp_data/lp_data.cc b/ortools/lp_data/lp_data.cc index a15d18e0630..9031db95791 100644 --- a/ortools/lp_data/lp_data.cc +++ b/ortools/lp_data/lp_data.cc @@ -16,12 +16,12 @@ #include #include #include -#include #include +#include "absl/container/flat_hash_map.h" -#include "ortools/base/join.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/lp_data/lp_print_utils.h" #include "ortools/lp_data/lp_utils.h" #include "ortools/lp_data/matrix_utils.h" @@ -201,7 +201,7 @@ RowIndex LinearProgram::CreateNewConstraint() { } ColIndex LinearProgram::FindOrCreateVariable(const std::string& variable_id) { - const std::unordered_map::iterator it = + const absl::flat_hash_map::iterator it = variable_table_.find(variable_id); if (it != variable_table_.end()) { return it->second; @@ -215,7 +215,7 @@ ColIndex LinearProgram::FindOrCreateVariable(const std::string& variable_id) { RowIndex LinearProgram::FindOrCreateConstraint( const std::string& constraint_id) { - const std::unordered_map::iterator it = + const absl::flat_hash_map::iterator it = constraint_table_.find(constraint_id); if (it != constraint_table_.end()) { return it->second; @@ -416,7 +416,7 @@ Fractional LinearProgram::GetObjectiveCoefficientForMinimizationVersion( std::string LinearProgram::GetDimensionString() const { return absl::StrFormat( - "%d rows, %d columns, %lld entries", num_constraints().value(), + "%d rows, %d columns, %d entries", num_constraints().value(), num_variables().value(), // static_cast is needed because the Android port uses int32. static_cast(num_entries().value())); @@ -436,7 +436,7 @@ std::string LinearProgram::GetObjectiveStatsString() const { if (num_non_zeros == 0) { return "No objective term. This is a pure feasibility problem."; } else { - return absl::StrFormat("%lld non-zeros, range [%e, %e]", num_non_zeros, + return absl::StrFormat("%d non-zeros, range [%e, %e]", num_non_zeros, min_value, max_value); } } @@ -613,14 +613,14 @@ std::string LinearProgram::DumpSolution(const DenseRow& variable_values) const { for (ColIndex col(0); col < variable_values.size(); ++col) { if (!output.empty()) absl::StrAppend(&output, ", "); absl::StrAppend(&output, GetVariableName(col), " = ", - absl::LegacyPrecision(variable_values[col])); + (variable_values[col])); } return output; } std::string LinearProgram::GetProblemStats() const { return ProblemStatFormatter( - "%d,%d,%lld,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d," + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d," "%d,%d,%d,%d"); } @@ -628,7 +628,7 @@ std::string LinearProgram::GetPrettyProblemStats() const { return ProblemStatFormatter( "Number of rows : %d\n" "Number of variables in file : %d\n" - "Number of entries (non-zeros) : %lld\n" + "Number of entries (non-zeros) : %d\n" "Number of entries in the objective : %d\n" "Number of entries in the right-hand side : %d\n" "Number of <= constraints : %d\n" @@ -1053,7 +1053,7 @@ void LinearProgram::DeleteColumns(const DenseBooleanRow& columns_to_delete) { variable_names_.resize(new_index, ""); // Remove the id of the deleted columns and adjust the index of the other. - std::unordered_map::iterator it = + absl::flat_hash_map::iterator it = variable_table_.begin(); while (it != variable_table_.end()) { const ColIndex col = it->second; @@ -1208,7 +1208,7 @@ void LinearProgram::DeleteRows(const DenseBooleanColumn& rows_to_delete) { matrix_.DeleteRows(new_index, permutation); // Remove the id of the deleted rows and adjust the index of the other. - std::unordered_map::iterator it = + absl::flat_hash_map::iterator it = constraint_table_.begin(); while (it != constraint_table_.end()) { const RowIndex row = it->second; @@ -1257,7 +1257,8 @@ bool LinearProgram::IsValid() const { return true; } -std::string LinearProgram::ProblemStatFormatter(const char* format) const { +std::string LinearProgram::ProblemStatFormatter( + const absl::string_view format) const { int num_objective_non_zeros = 0; int num_non_negative_variables = 0; int num_boxed_variables = 0; @@ -1334,17 +1335,23 @@ std::string LinearProgram::ProblemStatFormatter(const char* format) const { const int num_non_binary_variables = NonBinaryVariablesList().size(); const int num_continuous_variables = ColToIntIndex(num_variables()) - num_integer_variables; + auto format_runtime = + absl::ParsedFormat<'d', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', + 'd', 'd', 'd', 'd', 'd', 'd', 'd'>::New(format); + CHECK(format_runtime); return absl::StrFormat( - format, RowToIntIndex(num_constraints()), ColToIntIndex(num_variables()), - matrix_.num_entries().value(), num_objective_non_zeros, num_rhs_non_zeros, - num_less_than_constraints, num_greater_than_constraints, - num_equal_constraints, num_range_constraints, num_non_negative_variables, - num_boxed_variables, num_free_variables, num_fixed_variables, - num_other_variables, num_integer_variables, num_binary_variables, - num_non_binary_variables, num_continuous_variables); -} - -std::string LinearProgram::NonZeroStatFormatter(const char* format) const { + *format_runtime, RowToIntIndex(num_constraints()), + ColToIntIndex(num_variables()), matrix_.num_entries().value(), + num_objective_non_zeros, num_rhs_non_zeros, num_less_than_constraints, + num_greater_than_constraints, num_equal_constraints, + num_range_constraints, num_non_negative_variables, num_boxed_variables, + num_free_variables, num_fixed_variables, num_other_variables, + num_integer_variables, num_binary_variables, num_non_binary_variables, + num_continuous_variables); +} + +std::string LinearProgram::NonZeroStatFormatter( + const absl::string_view format) const { StrictITIVector num_entries_in_row(num_constraints(), EntryIndex(0)); StrictITIVector num_entries_in_column(num_variables(), @@ -1367,8 +1374,10 @@ std::string LinearProgram::NonZeroStatFormatter(const char* format) const { const double fill_rate = 100.0 * static_cast(num_entries.value()) / static_cast(height * width); + auto format_runtime = + absl::ParsedFormat<'f', 'd', 'f', 'f', 'd', 'f', 'f'>::New(format); return absl::StrFormat( - format, fill_rate, GetMaxElement(num_entries_in_row).value(), + *format_runtime, fill_rate, GetMaxElement(num_entries_in_row).value(), Average(num_entries_in_row), StandardDeviation(num_entries_in_row), GetMaxElement(num_entries_in_column).value(), Average(num_entries_in_column), StandardDeviation(num_entries_in_column)); @@ -1456,14 +1465,14 @@ std::string ProblemSolution::DebugString() const { std::string s = "Problem status: " + GetProblemStatusString(status); for (ColIndex col(0); col < primal_values.size(); ++col) { absl::StrAppendFormat(&s, "\n Var #%d: %s %g", col.value(), - GetVariableStatusString(variable_statuses[col]).c_str(), - primal_values[col]); + GetVariableStatusString(variable_statuses[col]), + primal_values[col]); } s += "\n------------------------------"; for (RowIndex row(0); row < dual_values.size(); ++row) { absl::StrAppendFormat(&s, "\n Constraint #%d: %s %g", row.value(), - GetConstraintStatusString(constraint_statuses[row]).c_str(), - dual_values[row]); + GetConstraintStatusString(constraint_statuses[row]), + dual_values[row]); } return s; } diff --git a/ortools/lp_data/lp_data.h b/ortools/lp_data/lp_data.h index 034cc551c57..c9bd6a88886 100644 --- a/ortools/lp_data/lp_data.h +++ b/ortools/lp_data/lp_data.h @@ -27,9 +27,9 @@ #include // for max #include #include // for std::string -#include -#include #include // for vector +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "ortools/base/hash.h" #include "ortools/base/int_type.h" @@ -548,11 +548,11 @@ class LinearProgram { // A helper function to format problem statistics. Used by GetProblemStats() // and GetPrettyProblemStats(). - std::string ProblemStatFormatter(const char* format) const; + std::string ProblemStatFormatter(const absl::string_view format) const; // A helper function to format non-zero statistics. Used by GetNonZeroStats() // and GetPrettyNonZeroStats(). - std::string NonZeroStatFormatter(const char* format) const; + std::string NonZeroStatFormatter(const absl::string_view format) const; // Resizes all row vectors to include index 'row'. void ResizeRowsIfNeeded(RowIndex row); @@ -596,10 +596,10 @@ class LinearProgram { mutable std::vector non_binary_variables_list_; // Map used to find the index of a variable based on its id. - std::unordered_map variable_table_; + absl::flat_hash_map variable_table_; // Map used to find the index of a constraint based on its id. - std::unordered_map constraint_table_; + absl::flat_hash_map constraint_table_; // Offset of the objective, i.e. value of the objective when all variables // are set to zero. diff --git a/ortools/lp_data/lp_decomposer.cc b/ortools/lp_data/lp_decomposer.cc index ed4fe18ca29..c756e5ed066 100644 --- a/ortools/lp_data/lp_decomposer.cc +++ b/ortools/lp_data/lp_decomposer.cc @@ -15,8 +15,8 @@ #include +#include "absl/synchronization/mutex.h" #include "ortools/algorithms/dynamic_partition.h" -#include "ortools/base/mutex.h" #include "ortools/lp_data/lp_data.h" #include "ortools/lp_data/lp_utils.h" diff --git a/ortools/lp_data/lp_decomposer.h b/ortools/lp_data/lp_decomposer.h index ba26dab46a1..876e57db5aa 100644 --- a/ortools/lp_data/lp_decomposer.h +++ b/ortools/lp_data/lp_decomposer.h @@ -17,7 +17,7 @@ #include #include -#include "ortools/base/mutex.h" +#include "absl/synchronization/mutex.h" #include "ortools/lp_data/lp_data.h" #include "ortools/lp_data/lp_types.h" diff --git a/ortools/lp_data/lp_print_utils.cc b/ortools/lp_data/lp_print_utils.cc index b18527f2e16..5340fc861a4 100644 --- a/ortools/lp_data/lp_print_utils.cc +++ b/ortools/lp_data/lp_print_utils.cc @@ -16,8 +16,8 @@ #include #include +#include "absl/strings/str_cat.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/lp_data/lp_types.h" #include "ortools/util/rational_approximation.h" diff --git a/ortools/lp_data/lp_print_utils.h b/ortools/lp_data/lp_print_utils.h index 4b2a5684140..22987898d0a 100644 --- a/ortools/lp_data/lp_print_utils.h +++ b/ortools/lp_data/lp_print_utils.h @@ -18,8 +18,8 @@ #include +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" -#include "ortools/base/stringprintf.h" #include "ortools/lp_data/lp_types.h" namespace operations_research { diff --git a/ortools/lp_data/matrix_scaler.cc b/ortools/lp_data/matrix_scaler.cc index 1b7789f4b89..3b4b4396122 100644 --- a/ortools/lp_data/matrix_scaler.cc +++ b/ortools/lp_data/matrix_scaler.cc @@ -17,9 +17,8 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/logging.h" -#include "ortools/base/memory.h" -#include "ortools/base/stringprintf.h" #include "ortools/glop/revised_simplex.h" #include "ortools/lp_data/lp_utils.h" #include "ortools/lp_data/sparse.h" diff --git a/ortools/lp_data/mps_reader.cc b/ortools/lp_data/mps_reader.cc index bd5b32cba54..c6294187def 100644 --- a/ortools/lp_data/mps_reader.cc +++ b/ortools/lp_data/mps_reader.cc @@ -19,22 +19,20 @@ #include #include -#include "ortools/base/callback.h" +#include "absl/strings/match.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/file.h" #include "ortools/base/filelineiter.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" // for FindOrNull, FindWithDefault -#include "ortools/base/match.h" -#include "ortools/base/numbers.h" // for safe_strtod -#include "ortools/base/split.h" #include "ortools/base/status.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/strutil.h" #include "ortools/lp_data/lp_print_utils.h" -DEFINE_bool(mps_free_form, false, "Read MPS files in free form."); -DEFINE_bool(mps_stop_after_first_error, true, "Stop after the first error."); +ABSL_FLAG(bool, mps_free_form, false, "Read MPS files in free form."); +ABSL_FLAG(bool, mps_stop_after_first_error, true, + "Stop after the first error."); namespace operations_research { namespace glop { @@ -44,7 +42,7 @@ const int MPSReader::kFieldStartPos[kNumFields] = {1, 4, 14, 24, 39, 49}; const int MPSReader::kFieldLength[kNumFields] = {2, 8, 8, 12, 8, 12}; MPSReader::MPSReader() - : free_form_(FLAGS_mps_free_form), + : free_form_(absl::GetFlag(FLAGS_mps_free_form)), data_(nullptr), problem_name_(""), parse_success_(true), @@ -108,9 +106,8 @@ void MPSReader::DisplaySummary() { void MPSReader::SplitLineIntoFields() { if (free_form_) { - fields_ = - absl::StrSplit(line_, absl::delimiter::AnyOf(" \t"), absl::SkipEmpty()); - CHECK_GE(kNumFields, fields_.size()); + fields_ = absl::StrSplit(line_, absl::ByAnyChar(" \t"), absl::SkipEmpty()); + DCHECK_GE(kNumFields, fields_.size()); } else { int length = line_.length(); for (int i = 0; i < kNumFields; ++i) { @@ -160,10 +157,10 @@ bool MPSReader::LoadFileWithMode(const std::string& file_name, bool free_form, LinearProgram* data) { free_form_ = free_form; if (LoadFile(file_name, data)) { - free_form_ = FLAGS_mps_free_form; + free_form_ = absl::GetFlag(FLAGS_mps_free_form); return true; } - free_form_ = FLAGS_mps_free_form; + free_form_ = absl::GetFlag(FLAGS_mps_free_form); return false; } @@ -193,7 +190,8 @@ bool MPSReader::IsCommentOrBlank() const { void MPSReader::ProcessLine(const std::string& line) { ++line_num_; - if (!parse_success_ && FLAGS_mps_stop_after_first_error) return; + if (!parse_success_ && absl::GetFlag(FLAGS_mps_stop_after_first_error)) + return; line_ = line; if (IsCommentOrBlank()) { return; // Skip blank lines and comments. @@ -293,7 +291,7 @@ void MPSReader::ProcessLine(const std::string& line) { double MPSReader::GetDoubleFromString(const std::string& param) { double result; - if (!strings::safe_strtod(param, &result)) { + if (!absl::SimpleAtod(param, &result)) { if (log_errors_) { LOG(ERROR) << "At line " << line_num_ << ": Failed to convert std::string to double. String = " diff --git a/ortools/lp_data/mps_reader.h b/ortools/lp_data/mps_reader.h index 53a3ace3f7d..aa5aed22341 100644 --- a/ortools/lp_data/mps_reader.h +++ b/ortools/lp_data/mps_reader.h @@ -26,8 +26,8 @@ #include // for max #include // for std::string -#include -#include // for vector +#include // for vector +#include "absl/container/flat_hash_map.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/hash.h" @@ -35,12 +35,11 @@ #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/macros.h" // for DISALLOW_COPY_AND_ASSIGN, NULL #include "ortools/base/map_util.h" // for FindOrNull, FindWithDefault -#include "ortools/base/stringprintf.h" #include "ortools/lp_data/lp_data.h" #include "ortools/lp_data/lp_types.h" -DECLARE_bool(mps_free_form); -DECLARE_bool(mps_stop_after_first_error); +ABSL_DECLARE_FLAG(bool, mps_free_form); +ABSL_DECLARE_FLAG(bool, mps_stop_after_first_error); namespace operations_research { namespace glop { @@ -212,16 +211,16 @@ class MPSReader { SectionId section_; // Maps section mnemonic --> section id. - std::unordered_map section_name_to_id_map_; + absl::flat_hash_map section_name_to_id_map_; // Maps row type mnemonic --> row type id. - std::unordered_map row_name_to_id_map_; + absl::flat_hash_map row_name_to_id_map_; // Maps bound type mnemonic --> bound type id. - std::unordered_map bound_name_to_id_map_; + absl::flat_hash_map bound_name_to_id_map_; // Set of bound type mnemonics that constrain variables to be integer. - std::unordered_set integer_type_names_set_; + absl::flat_hash_set integer_type_names_set_; // The current line number in the file being parsed. int64 line_num_; diff --git a/ortools/lp_data/sparse.cc b/ortools/lp_data/sparse.cc index 0c8bc43de14..b7e5e9021be 100644 --- a/ortools/lp_data/sparse.cc +++ b/ortools/lp_data/sparse.cc @@ -14,8 +14,7 @@ #include "ortools/lp_data/sparse.h" #include -#include "ortools/base/join.h" -#include "ortools/base/stringprintf.h" +#include "absl/strings/str_format.h" namespace operations_research { namespace glop { diff --git a/ortools/lp_data/sparse_column.cc b/ortools/lp_data/sparse_column.cc index 7972f992001..2812f84b1d0 100644 --- a/ortools/lp_data/sparse_column.cc +++ b/ortools/lp_data/sparse_column.cc @@ -14,7 +14,6 @@ #include "ortools/lp_data/sparse_column.h" #include -#include "ortools/base/stringprintf.h" #include "ortools/lp_data/lp_types.h" namespace operations_research { diff --git a/ortools/lp_data/sparse_vector.h b/ortools/lp_data/sparse_vector.h index 8dde8d84dc3..5dda12520ee 100644 --- a/ortools/lp_data/sparse_vector.h +++ b/ortools/lp_data/sparse_vector.h @@ -35,9 +35,9 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" // for CHECK* -#include "ortools/base/stringprintf.h" #include "ortools/graph/iterators.h" #include "ortools/lp_data/lp_types.h" #include "ortools/lp_data/permutation.h" diff --git a/ortools/port/CMakeLists.txt b/ortools/port/CMakeLists.txt index 15de565ad5d..b41a5a4abe8 100644 --- a/ortools/port/CMakeLists.txt +++ b/ortools/port/CMakeLists.txt @@ -4,39 +4,48 @@ set(NAME ${PROJECT_NAME}_port) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE +# absl::strings +# gflags::gflags glog::glog # protobuf::libprotobuf -# glog::glog # Cbc::Cbc -# gflags::gflags # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + absl::strings + gflags::gflags glog::glog + protobuf::libprotobuf + Cbc::Cbc + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::port ALIAS ${NAME}) diff --git a/ortools/port/file.h b/ortools/port/file.h index a13a5c2579c..bfbc3303799 100644 --- a/ortools/port/file.h +++ b/ortools/port/file.h @@ -14,19 +14,19 @@ #ifndef OR_TOOLS_PORT_FILE_H_ #define OR_TOOLS_PORT_FILE_H_ +#include "absl/strings/string_view.h" #include "ortools/base/status.h" -#include "ortools/base/string_view.h" namespace operations_research { // See ortools/base/file.h -::util::Status FileSetContents(absl::string_view file_name, - absl::string_view content); +::util::Status PortableFileSetContents(absl::string_view file_name, + absl::string_view content); -::util::Status FileGetContents(absl::string_view file_name, - std::string* output); +::util::Status PortableFileGetContents(absl::string_view file_name, + std::string* output); -::util::Status DeleteFile(absl::string_view file_name); +::util::Status PortableDeleteFile(absl::string_view file_name); // Returns true if successful. Outputs temp file to filename. bool PortableTemporaryFile(const char* directory_prefix, diff --git a/ortools/port/file_nonport.cc b/ortools/port/file_nonport.cc index 98730b21282..1b6579df626 100644 --- a/ortools/port/file_nonport.cc +++ b/ortools/port/file_nonport.cc @@ -17,13 +17,13 @@ namespace operations_research { -::util::Status FileSetContents(absl::string_view file_name, - absl::string_view content) { +::util::Status PortableFileSetContents(absl::string_view file_name, + absl::string_view content) { return file::SetContents(file_name, content, file::Defaults()); } -::util::Status FileGetContents(absl::string_view file_name, - std::string* output) { +::util::Status PortableFileGetContents(absl::string_view file_name, + std::string* output) { return file::GetContents(file_name, output, file::Defaults()); } @@ -32,7 +32,7 @@ bool PortableTemporaryFile(const char* directory_prefix, return false; } -::util::Status DeleteFile(absl::string_view file_name) { +::util::Status PortableDeleteFile(absl::string_view file_name) { return file::Delete(file_name, file::Defaults()); } diff --git a/ortools/port/proto_utils.h b/ortools/port/proto_utils.h index 2d8c3340e1c..a1e646d4177 100644 --- a/ortools/port/proto_utils.h +++ b/ortools/port/proto_utils.h @@ -21,8 +21,7 @@ #include "google/protobuf/text_format.h" #endif -#include "ortools/base/join.h" -#include "ortools/base/port.h" +#include "absl/strings/str_cat.h" namespace operations_research { #if defined(__PORTABLE_PLATFORM__) @@ -43,8 +42,8 @@ std::string ProtoEnumToString(ProtoEnumType enum_value) { template bool ProtobufTextFormatMergeFromString(const std::string& proto_text_string - ATTRIBUTE_UNUSED, - ProtoType* proto ATTRIBUTE_UNUSED) { + ABSL_ATTRIBUTE_UNUSED, + ProtoType* proto ABSL_ATTRIBUTE_UNUSED) { return false; } diff --git a/ortools/sat/CMakeLists.txt b/ortools/sat/CMakeLists.txt index 35f98ab485c..44e18cbbb18 100644 --- a/ortools/sat/CMakeLists.txt +++ b/ortools/sat/CMakeLists.txt @@ -4,39 +4,63 @@ set(NAME ${PROJECT_NAME}_sat) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE -# gflags::gflags -# glog::glog +# absl::types absl::memory absl::synchronization absl::container absl::strings absl::str_format +# gflags::gflags glog::glog # protobuf::libprotobuf # Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + absl::types absl::memory absl::synchronization absl::container absl::strings absl::str_format + gflags::gflags glog::glog + protobuf::libprotobuf + Cbc::Cbc + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::sat ALIAS ${NAME}) diff --git a/ortools/sat/all_different.cc b/ortools/sat/all_different.cc index 6010d797574..7c642837d6d 100644 --- a/ortools/sat/all_different.cc +++ b/ortools/sat/all_different.cc @@ -16,8 +16,8 @@ #include #include #include -#include +#include "absl/container/flat_hash_set.h" #include "ortools/base/int_type.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" diff --git a/ortools/sat/all_different.h b/ortools/sat/all_different.h index 5cdec24dd58..25118abf528 100644 --- a/ortools/sat/all_different.h +++ b/ortools/sat/all_different.h @@ -15,10 +15,10 @@ #define OR_TOOLS_SAT_ALL_DIFFERENT_H_ #include -#include #include #include +#include "absl/container/flat_hash_map.h" #include "ortools/base/integral_types.h" #include "ortools/base/macros.h" #include "ortools/sat/integer.h" @@ -186,7 +186,7 @@ class AllDifferentBoundsPropagator : public PropagatorInterface { // is needed. int64 num_calls_; std::vector> to_insert_; - std::unordered_map value_to_variable_; + absl::flat_hash_map value_to_variable_; std::vector integer_reason_; DISALLOW_COPY_AND_ASSIGN(AllDifferentBoundsPropagator); diff --git a/ortools/sat/boolean_problem.cc b/ortools/sat/boolean_problem.cc index 09f3f3b47ce..9f970a70281 100644 --- a/ortools/sat/boolean_problem.cc +++ b/ortools/sat/boolean_problem.cc @@ -17,13 +17,13 @@ #include #include #include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_format.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #if !defined(__PORTABLE_PLATFORM__) #include "ortools/graph/io.h" #endif // __PORTABLE_PLATFORM__ @@ -259,7 +259,7 @@ bool LoadAndConsumeBooleanProblem(LinearBooleanProblem* problem, SatSolver* solver) { const util::Status status = ValidateBooleanProblem(*problem); if (!status.ok()) { - LOG(WARNING) << "The given problem is invalid! " << status.error_message(); + LOG(WARNING) << "The given problem is invalid! " << status.message(); } if (solver->parameters().log_search_progress()) { #if !defined(__PORTABLE_PLATFORM__) @@ -399,7 +399,7 @@ std::string LinearBooleanProblemToCnfString( const int first_slack_variable = problem.original_num_variables(); // This will contains the objective. - std::unordered_map literal_to_weight; + absl::flat_hash_map literal_to_weight; std::vector> non_slack_objective; // This will be the weight of the "hard" clauses in the wcnf format. It must @@ -429,7 +429,7 @@ std::string LinearBooleanProblemToCnfString( hard_weight += weight; ++i; } - output += absl::StrFormat("p wcnf %d %d %lld\n", first_slack_variable, + output += absl::StrFormat("p wcnf %d %d %d\n", first_slack_variable, static_cast(problem.constraints_size() + non_slack_objective.size()), hard_weight); @@ -453,7 +453,7 @@ std::string LinearBooleanProblemToCnfString( } } if (is_wcnf) { - output += absl::StrFormat("%lld ", weight); + output += absl::StrFormat("%d ", weight); } output += constraint_output + " 0\n"; } @@ -464,8 +464,7 @@ std::string LinearBooleanProblemToCnfString( // Since it is falsifying this clause that cost "weigtht", we need to take // its negation. const Literal literal(-p.first); - output += absl::StrFormat("%lld %s 0\n", p.second, - literal.DebugString().c_str()); + output += absl::StrFormat("%d %s 0\n", p.second, literal.DebugString()); } } @@ -510,7 +509,7 @@ class IdGenerator { } private: - std::unordered_map, int> id_map_; + absl::flat_hash_map, int> id_map_; }; } // namespace. diff --git a/ortools/sat/circuit.cc b/ortools/sat/circuit.cc index 737cddef321..2fd8b5cac1c 100644 --- a/ortools/sat/circuit.cc +++ b/ortools/sat/circuit.cc @@ -15,7 +15,7 @@ #include -#include +#include "absl/container/flat_hash_map.h" #include "ortools/base/map_util.h" #include "ortools/sat/sat_solver.h" @@ -36,7 +36,7 @@ CircuitPropagator::CircuitPropagator(const int num_nodes, prev_.resize(num_nodes_, -1); next_literal_.resize(num_nodes_); must_be_in_cycle_.resize(num_nodes_); - std::unordered_map literal_to_watch_index; + absl::flat_hash_map literal_to_watch_index; const int num_arcs = tails.size(); graph_.reserve(num_arcs); diff --git a/ortools/sat/circuit.h b/ortools/sat/circuit.h index 1c5b69ca0c9..5db1755d1a5 100644 --- a/ortools/sat/circuit.h +++ b/ortools/sat/circuit.h @@ -18,7 +18,7 @@ #include #include -#include +#include "absl/container/flat_hash_map.h" #include "ortools/base/int_type.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" @@ -82,7 +82,7 @@ class CircuitPropagator : PropagatorInterface, ReversibleInterface { // TODO(user): for large dense graph, using a matrix is faster and uses less // memory. If the need arise we can have the two implementations. std::vector self_arcs_; - std::unordered_map, Literal> graph_; + absl::flat_hash_map, Literal> graph_; // Data used to interpret the watch indices passed to IncrementalPropagate(). struct Arc { diff --git a/ortools/sat/clause.cc b/ortools/sat/clause.cc index 67589b42647..a65f53314c3 100644 --- a/ortools/sat/clause.cc +++ b/ortools/sat/clause.cc @@ -170,8 +170,8 @@ bool LiteralWatchers::Propagate(Trail* trail) { return true; } -absl::Span LiteralWatchers::Reason(const Trail& trail, - int trail_index) const { +absl::Span LiteralWatchers::Reason(const Trail& trail, + int trail_index) const { return reasons_[trail_index]->PropagationReason(); } @@ -352,7 +352,7 @@ void BinaryImplicationGraph::AddBinaryClauseDuringSearch(Literal a, Literal b, } void BinaryImplicationGraph::AddAtMostOne( - absl::Span at_most_one) { + absl::Span at_most_one) { if (at_most_one.empty()) return; for (const Literal a : at_most_one) { for (const Literal b : at_most_one) { @@ -410,8 +410,8 @@ bool BinaryImplicationGraph::Propagate(Trail* trail) { return true; } -absl::Span BinaryImplicationGraph::Reason(const Trail& trail, - int trail_index) const { +absl::Span BinaryImplicationGraph::Reason( + const Trail& trail, int trail_index) const { return {&reasons_[trail_index], 1}; } @@ -805,7 +805,7 @@ bool BinaryImplicationGraph::ComputeTransitiveReduction() { } const int diff = direct_implications.size() - new_size; direct_implications.resize(new_size); - //direct_implications.shrink_to_fit(); + direct_implications.shrink_to_fit(); num_redundant_implications_ += diff; num_implications_ -= diff; @@ -863,7 +863,7 @@ void BinaryImplicationGraph::TransformIntoMaxCliques( int num_removed = 0; int num_added = 0; - std::unordered_set, VectorHash> max_cliques; + absl::flat_hash_set, VectorHash> max_cliques; gtl::ITIVector> max_cliques_containing( implications_.size()); @@ -943,7 +943,7 @@ void BinaryImplicationGraph::MarkDescendants(Literal root) { } std::vector BinaryImplicationGraph::ExpandAtMostOne( - const absl::Span at_most_one) { + const absl::Span at_most_one) { std::vector clique(at_most_one.begin(), at_most_one.end()); // Optim. diff --git a/ortools/sat/clause.h b/ortools/sat/clause.h index f0fc07c4479..e516d4b9696 100644 --- a/ortools/sat/clause.h +++ b/ortools/sat/clause.h @@ -19,18 +19,18 @@ #include #include -#include -#include #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/container/inlined_vector.h" +#include "absl/types/span.h" #include "ortools/base/hash.h" -#include "ortools/base/inlined_vector.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/integral_types.h" #include "ortools/base/macros.h" -#include "ortools/base/span.h" #include "ortools/sat/drat_proof_handler.h" #include "ortools/sat/model.h" #include "ortools/sat/sat_base.h" @@ -79,13 +79,13 @@ class SatClause { // Returns the reason for the last unit propagation of this clause. The // preconditions are the same as for PropagatedLiteral(). Note that we don't // need to include the propagated literal. - absl::Span PropagationReason() const { - return absl::Span(&(literals_[1]), size_ - 1); + absl::Span PropagationReason() const { + return absl::Span(&(literals_[1]), size_ - 1); } // Returns a Span<> representation of the clause. - absl::Span AsSpan() const { - return absl::Span(&(literals_[0]), size_); + absl::Span AsSpan() const { + return absl::Span(&(literals_[0]), size_); } // Removes literals that are fixed. This should only be called at level 0 @@ -98,7 +98,7 @@ class SatClause { // Rewrites a clause with another shorter one. Note that the clause shouldn't // be attached when this is called. - void Rewrite(absl::Span new_clause) { + void Rewrite(absl::Span new_clause) { size_ = 0; for (const Literal l : new_clause) literals_[size_++] = l; } @@ -159,7 +159,8 @@ class LiteralWatchers : public SatPropagator { // SatPropagator API. bool Propagate(Trail* trail) final; - absl::Span Reason(const Trail& trail, int trail_index) const final; + absl::Span Reason(const Trail& trail, + int trail_index) const final; // Returns the reason of the variable at given trail_index. This only works // for variable propagated by this class and is almost the same as Reason() @@ -205,7 +206,7 @@ class LiteralWatchers : public SatPropagator { return gtl::ContainsKey(clauses_info_, clause); } int64 num_removable_clauses() const { return clauses_info_.size(); } - std::unordered_map* mutable_clauses_info() { + absl::flat_hash_map* mutable_clauses_info() { return &clauses_info_; } @@ -291,7 +292,7 @@ class LiteralWatchers : public SatPropagator { int to_minimize_index_ = 0; // Only contains removable clause. - std::unordered_map clauses_info_; + absl::flat_hash_map clauses_info_; DratProofHandler* drat_proof_handler_ = nullptr; @@ -331,7 +332,7 @@ class BinaryClauseManager { void ClearNewlyAdded() { newly_added_.clear(); } private: - std::unordered_set> set_; + absl::flat_hash_set> set_; std::vector newly_added_; DISALLOW_COPY_AND_ASSIGN(BinaryClauseManager); }; @@ -386,7 +387,8 @@ class BinaryImplicationGraph : public SatPropagator { } bool Propagate(Trail* trail) final; - absl::Span Reason(const Trail& trail, int trail_index) const final; + absl::Span Reason(const Trail& trail, + int trail_index) const final; // Resizes the data structure. void Resize(int num_variables); @@ -405,7 +407,7 @@ class BinaryImplicationGraph : public SatPropagator { // // TODO(user): For large constraint, handle them natively instead of // converting them into implications? - void AddAtMostOne(absl::Span at_most_one); + void AddAtMostOne(absl::Span at_most_one); // Uses the binary implication graph to minimize the given conflict by // removing literals that implies others. The idea is that if a and b are two @@ -523,7 +525,7 @@ class BinaryImplicationGraph : public SatPropagator { // if this is called with any sub-clique of the result we will get the same // maximal clique. std::vector ExpandAtMostOne( - const absl::Span at_most_one); + const absl::Span at_most_one); // Binary reasons by trail_index. We need a deque because we kept pointers to // elements of this array and this can dynamically change size. diff --git a/ortools/sat/cp_constraints.cc b/ortools/sat/cp_constraints.cc index ffca38a80e6..24b3df2e74e 100644 --- a/ortools/sat/cp_constraints.cc +++ b/ortools/sat/cp_constraints.cc @@ -14,8 +14,8 @@ #include "ortools/sat/cp_constraints.h" #include -#include +#include "absl/container/flat_hash_map.h" #include "ortools/base/map_util.h" #include "ortools/sat/sat_solver.h" #include "ortools/util/sort.h" diff --git a/ortools/sat/cp_model.cc b/ortools/sat/cp_model.cc index 0845e06f8b4..ad981b96761 100644 --- a/ortools/sat/cp_model.cc +++ b/ortools/sat/cp_model.cc @@ -11,9 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "ortools/base/join.h" #include "ortools/sat/cp_model.h" -#include "ortools/base/stringprintf.h" +#include "absl/strings/str_format.h" #include "ortools/base/map_util.h" #include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_utils.h" @@ -33,7 +32,7 @@ BoolVar BoolVar::WithName(const std::string& name) { std::string BoolVar::DebugString() const { if (index_ < 0) { - return absl::StrFormat("Not(%s)", Not().DebugString().c_str()); + return absl::StrFormat("Not(%s)", Not().DebugString()); } else { std::string output; const IntegerVariableProto& var_proto = cp_model_->variables(index_); @@ -45,7 +44,7 @@ std::string BoolVar::DebugString() const { if (var_proto.name().empty()) { absl::StrAppendFormat(&output, "BoolVar%i(", index_); } else { - absl::StrAppendFormat(&output, "%s(", var_proto.name().c_str()); + absl::StrAppendFormat(&output, "%s(", var_proto.name()); } if (var_proto.domain(0) == var_proto.domain(1)) { output.append(var_proto.domain(0) == 0 ? "false)" : "true)"); @@ -85,7 +84,7 @@ IntVar::IntVar(const BoolVar& var) { std::string IntVar::DebugString() const { if (index_ < 0) { return absl::StrFormat("Not(%s)", - IntVar(NegatedRef(index_), cp_model_).DebugString().c_str()); + IntVar(NegatedRef(index_), cp_model_).DebugString()); } const IntegerVariableProto& var_proto = cp_model_->variables(index_); // Special case for constant variables without names. @@ -124,7 +123,7 @@ LinearExpr::LinearExpr(IntVar var) { AddVar(var); } LinearExpr::LinearExpr(int64 constant) { constant_ = constant; } -LinearExpr LinearExpr::Sum(absl::Span vars) { +LinearExpr LinearExpr::Sum(absl::Span vars) { LinearExpr result; for (const IntVar& var : vars) { result.AddVar(var); @@ -132,8 +131,8 @@ LinearExpr LinearExpr::Sum(absl::Span vars) { return result; } -LinearExpr LinearExpr::ScalProd(absl::Span vars, - absl::Span coeffs) { +LinearExpr LinearExpr::ScalProd(absl::Span vars, + absl::Span coeffs) { CHECK_EQ(vars.size(), coeffs.size()); LinearExpr result; for (int i = 0; i < vars.size(); ++i) { @@ -142,7 +141,7 @@ LinearExpr LinearExpr::ScalProd(absl::Span vars, return result; } -LinearExpr LinearExpr::BooleanSum(absl::Span vars) { +LinearExpr LinearExpr::BooleanSum(absl::Span vars) { LinearExpr result; for (const IntVar& var : vars) { result.AddVar(var); @@ -150,8 +149,8 @@ LinearExpr LinearExpr::BooleanSum(absl::Span vars) { return result; } -LinearExpr LinearExpr::BooleanScalProd(absl::Span vars, - absl::Span coeffs) { +LinearExpr LinearExpr::BooleanScalProd(absl::Span vars, + absl::Span coeffs) { CHECK_EQ(vars.size(), coeffs.size()); LinearExpr result; for (int i = 0; i < vars.size(); ++i) { @@ -188,7 +187,7 @@ Constraint Constraint::WithName(const std::string& name) { const std::string& Constraint::Name() const { return proto_->name(); } -Constraint Constraint::OnlyEnforceIf(absl::Span literals) { +Constraint Constraint::OnlyEnforceIf(absl::Span literals) { for (const BoolVar& var : literals) { proto_->add_enforcement_literal(var.index_); } @@ -206,7 +205,7 @@ void CircuitConstraint::AddArc(int tail, int head, BoolVar literal) { proto_->mutable_circuit()->add_literals(literal.index_); } -void TableConstraint::AddTuple(absl::Span tuple) { +void TableConstraint::AddTuple(absl::Span tuple) { CHECK_EQ(tuple.size(), proto_->table().vars_size()); for (const int64 t : tuple) { proto_->mutable_table()->add_values(t); @@ -339,7 +338,7 @@ int CpModelBuilder::GetOrCreateIntegerIndex(int index) { IntVar CpModelBuilder::NewIntVar(const Domain& domain) { const int index = cp_model_.variables_size(); IntegerVariableProto* const var_proto = cp_model_.add_variables(); - for (const auto& interval : domain.intervals()) { + for (const auto& interval : domain) { var_proto->add_domain(interval.start); var_proto->add_domain(interval.end); } @@ -384,7 +383,7 @@ IntervalVar CpModelBuilder::NewOptionalIntervalVar(IntVar start, IntVar size, return IntervalVar(index, &cp_model_); } -Constraint CpModelBuilder::AddBoolOr(absl::Span literals) { +Constraint CpModelBuilder::AddBoolOr(absl::Span literals) { ConstraintProto* const proto = cp_model_.add_constraints(); for (const BoolVar& lit : literals) { proto->mutable_bool_or()->add_literals(lit.index_); @@ -392,7 +391,7 @@ Constraint CpModelBuilder::AddBoolOr(absl::Span literals) { return Constraint(proto); } -Constraint CpModelBuilder::AddBoolAnd(absl::Span literals) { +Constraint CpModelBuilder::AddBoolAnd(absl::Span literals) { ConstraintProto* const proto = cp_model_.add_constraints(); for (const BoolVar& lit : literals) { proto->mutable_bool_and()->add_literals(lit.index_); @@ -400,7 +399,7 @@ Constraint CpModelBuilder::AddBoolAnd(absl::Span literals) { return Constraint(proto); } -Constraint CpModelBuilder::AddBoolXor(absl::Span literals) { +Constraint CpModelBuilder::AddBoolXor(absl::Span literals) { ConstraintProto* const proto = cp_model_.add_constraints(); for (const BoolVar& lit : literals) { proto->mutable_bool_xor()->add_literals(lit.index_); @@ -485,7 +484,7 @@ Constraint CpModelBuilder::AddLinearConstraint(const LinearExpr& expr, proto->mutable_linear()->add_coeffs(coeff); } const int64 cst = expr.constant(); - for (const auto& i : domain.intervals()) { + for (const auto& i : domain) { proto->mutable_linear()->add_domain(i.start - cst); proto->mutable_linear()->add_domain(i.end - cst); } @@ -504,7 +503,7 @@ Constraint CpModelBuilder::AddNotEqual(const LinearExpr& left, return Constraint(proto); } -Constraint CpModelBuilder::AddAllDifferent(absl::Span vars) { +Constraint CpModelBuilder::AddAllDifferent(absl::Span vars) { ConstraintProto* const proto = cp_model_.add_constraints(); for (const IntVar& var : vars) { proto->mutable_all_diff()->add_vars(GetOrCreateIntegerIndex(var.index_)); @@ -513,7 +512,7 @@ Constraint CpModelBuilder::AddAllDifferent(absl::Span vars) { } Constraint CpModelBuilder::AddVariableElement( - IntVar index, absl::Span variables, IntVar target) { + IntVar index, absl::Span variables, IntVar target) { ConstraintProto* const proto = cp_model_.add_constraints(); proto->mutable_element()->set_index(GetOrCreateIntegerIndex(index.index_)); proto->mutable_element()->set_target(GetOrCreateIntegerIndex(target.index_)); @@ -524,7 +523,7 @@ Constraint CpModelBuilder::AddVariableElement( } Constraint CpModelBuilder::AddElement(IntVar index, - absl::Span values, + absl::Span values, IntVar target) { ConstraintProto* const proto = cp_model_.add_constraints(); proto->mutable_element()->set_index(GetOrCreateIntegerIndex(index.index_)); @@ -540,7 +539,7 @@ CircuitConstraint CpModelBuilder::AddCircuitConstraint() { } TableConstraint CpModelBuilder::AddAllowedAssignments( - absl::Span vars) { + absl::Span vars) { ConstraintProto* const proto = cp_model_.add_constraints(); for (const IntVar& var : vars) { proto->mutable_table()->add_vars(GetOrCreateIntegerIndex(var.index_)); @@ -549,7 +548,7 @@ TableConstraint CpModelBuilder::AddAllowedAssignments( } TableConstraint CpModelBuilder::AddForbiddenAssignments( - absl::Span vars) { + absl::Span vars) { ConstraintProto* const proto = cp_model_.add_constraints(); for (const IntVar& var : vars) { proto->mutable_table()->add_vars(GetOrCreateIntegerIndex(var.index_)); @@ -559,8 +558,8 @@ TableConstraint CpModelBuilder::AddForbiddenAssignments( } Constraint CpModelBuilder::AddInverseConstraint( - absl::Span variables, - absl::Span inverse_variables) { + absl::Span variables, + absl::Span inverse_variables) { ConstraintProto* const proto = cp_model_.add_constraints(); for (const IntVar& var : variables) { proto->mutable_inverse()->add_f_direct(GetOrCreateIntegerIndex(var.index_)); @@ -581,8 +580,8 @@ ReservoirConstraint CpModelBuilder::AddReservoirConstraint(int64 min_level, } AutomatonConstraint CpModelBuilder::AddAutomaton( - absl::Span transition_variables, int starting_state, - absl::Span final_states) { + absl::Span transition_variables, int starting_state, + absl::Span final_states) { ConstraintProto* const proto = cp_model_.add_constraints(); for (const IntVar& var : transition_variables) { proto->mutable_automata()->add_vars(GetOrCreateIntegerIndex(var.index_)); @@ -595,7 +594,7 @@ AutomatonConstraint CpModelBuilder::AddAutomaton( } Constraint CpModelBuilder::AddMinEquality(IntVar target, - absl::Span vars) { + absl::Span vars) { ConstraintProto* const proto = cp_model_.add_constraints(); proto->mutable_int_min()->set_target(GetOrCreateIntegerIndex(target.index_)); for (const IntVar& var : vars) { @@ -605,7 +604,7 @@ Constraint CpModelBuilder::AddMinEquality(IntVar target, } Constraint CpModelBuilder::AddMaxEquality(IntVar target, - absl::Span vars) { + absl::Span vars) { ConstraintProto* const proto = cp_model_.add_constraints(); proto->mutable_int_max()->set_target(GetOrCreateIntegerIndex(target.index_)); for (const IntVar& var : vars) { @@ -643,7 +642,7 @@ Constraint CpModelBuilder::AddModuloEquality(IntVar target, IntVar var, } Constraint CpModelBuilder::AddProductEquality(IntVar target, - absl::Span vars) { + absl::Span vars) { ConstraintProto* const proto = cp_model_.add_constraints(); proto->mutable_int_prod()->set_target(GetOrCreateIntegerIndex(target.index_)); for (const IntVar& var : vars) { @@ -652,7 +651,7 @@ Constraint CpModelBuilder::AddProductEquality(IntVar target, return Constraint(proto); } -Constraint CpModelBuilder::AddNoOverlap(absl::Span vars) { +Constraint CpModelBuilder::AddNoOverlap(absl::Span vars) { ConstraintProto* const proto = cp_model_.add_constraints(); for (const IntervalVar& var : vars) { proto->mutable_no_overlap()->add_intervals( @@ -702,7 +701,7 @@ void CpModelBuilder::ScaleObjectiveBy(double scaling) { } void CpModelBuilder::AddDecisionStrategy( - absl::Span variables, + absl::Span variables, DecisionStrategyProto::VariableSelectionStrategy var_strategy, DecisionStrategyProto::DomainReductionStrategy domain_strategy) { DecisionStrategyProto* const proto = cp_model_.add_search_strategy(); @@ -714,7 +713,7 @@ void CpModelBuilder::AddDecisionStrategy( } void CpModelBuilder::AddDecisionStrategy( - absl::Span variables, + absl::Span variables, DecisionStrategyProto::VariableSelectionStrategy var_strategy, DecisionStrategyProto::DomainReductionStrategy domain_strategy) { DecisionStrategyProto* const proto = cp_model_.add_search_strategy(); diff --git a/ortools/sat/cp_model.h b/ortools/sat/cp_model.h index d7bf50db48a..6bfab9e7795 100644 --- a/ortools/sat/cp_model.h +++ b/ortools/sat/cp_model.h @@ -35,8 +35,8 @@ #define OR_TOOLS_SAT_CP_MODEL_H_ #include -#include -#include "ortools/base/span.h" +#include "absl/container/flat_hash_map.h" +#include "absl/types/span.h" #include "ortools/sat/cp_model.pb.h" #include "ortools/sat/cp_model_solver.h" #include "ortools/sat/cp_model_utils.h" @@ -210,18 +210,18 @@ class LinearExpr { void AddTerm(IntVar var, int64 coeff); // Constructs the sum of a list of variables. - static LinearExpr Sum(absl::Span vars); + static LinearExpr Sum(absl::Span vars); // Constructs the scalar product of variables and coefficients. - static LinearExpr ScalProd(absl::Span vars, - absl::Span coeffs); + static LinearExpr ScalProd(absl::Span vars, + absl::Span coeffs); // Constructs the sum of a list of Booleans. - static LinearExpr BooleanSum(absl::Span vars); + static LinearExpr BooleanSum(absl::Span vars); // Constructs the scalar product of Booleans and coefficients. - static LinearExpr BooleanScalProd(absl::Span vars, - absl::Span coeffs); + static LinearExpr BooleanScalProd(absl::Span vars, + absl::Span coeffs); // Useful for testing. const std::vector& variables() const { return variables_; } @@ -326,9 +326,9 @@ class Constraint { // - bool_or, bool_and, linear: fully supported. // - interval: only support a single enforcement literal. // - other: no support (but can be added on a per-demand basis). - Constraint OnlyEnforceIf(absl::Span literals); + Constraint OnlyEnforceIf(absl::Span literals); - // See OnlyEnforceIf(absl::Span literals). + // See OnlyEnforceIf(absl::Span literals). Constraint OnlyEnforceIf(BoolVar literal); // Sets the name of the constraint. @@ -366,7 +366,7 @@ class CircuitConstraint : public Constraint { // constraint incrementally. class TableConstraint : public Constraint { public: - void AddTuple(absl::Span tuple); + void AddTuple(absl::Span tuple); private: friend class CpModelBuilder; @@ -465,13 +465,13 @@ class CpModelBuilder { BoolVar presence); // Adds the constraint that at least one of the literals must be true. - Constraint AddBoolOr(absl::Span literals); + Constraint AddBoolOr(absl::Span literals); // Adds the constraint that all literals must be true. - Constraint AddBoolAnd(absl::Span literals); + Constraint AddBoolAnd(absl::Span literals); /// Adds the constraint that a odd number of literal is true. - Constraint AddBoolXor(absl::Span literals); + Constraint AddBoolXor(absl::Span literals); // Adds a => b. Constraint AddImplication(BoolVar a, BoolVar b) { @@ -500,15 +500,15 @@ class CpModelBuilder { Constraint AddNotEqual(const LinearExpr& left, const LinearExpr& right); // this constraint forces all variables to have different values. - Constraint AddAllDifferent(absl::Span vars); + Constraint AddAllDifferent(absl::Span vars); // Adds the element constraint: variables[index] == target Constraint AddVariableElement(IntVar index, - absl::Span variables, + absl::Span variables, IntVar target); // Adds the element constraint: values[index] == target - Constraint AddElement(IntVar index, absl::Span values, + Constraint AddElement(IntVar index, absl::Span values, IntVar target); // Adds a circuit constraint. @@ -535,7 +535,7 @@ class CpModelBuilder { // // It returns a table constraint that allows adding tuples incrementally after // construction, - TableConstraint AddAllowedAssignments(absl::Span vars); + TableConstraint AddAllowedAssignments(absl::Span vars); // Adds an forbidden assignments constraint. // @@ -545,12 +545,12 @@ class CpModelBuilder { // // It returns a table constraint that allows adding tuples incrementally after // construction, - TableConstraint AddForbiddenAssignments(absl::Span vars); + TableConstraint AddForbiddenAssignments(absl::Span vars); // An inverse constraint enforces that if 'variables[i]' is assigned a value // 'j', then inverse_variables[j] is assigned a value 'i'. And vice versa. - Constraint AddInverseConstraint(absl::Span variables, - absl::Span inverse_variables); + Constraint AddInverseConstraint(absl::Span variables, + absl::Span inverse_variables); // Adds a reservoir constraint with optional refill/emptying events. // @@ -593,14 +593,14 @@ class CpModelBuilder { // It returns an AutomatonConstraint that allows adding transition // incrementally after construction. AutomatonConstraint AddAutomaton( - absl::Span transition_variables, int starting_state, - absl::Span final_states); + absl::Span transition_variables, int starting_state, + absl::Span final_states); // Adds target == min(vars). - Constraint AddMinEquality(IntVar target, absl::Span vars); + Constraint AddMinEquality(IntVar target, absl::Span vars); // Adds target == max(vars). - Constraint AddMaxEquality(IntVar target, absl::Span vars); + Constraint AddMaxEquality(IntVar target, absl::Span vars); // Adds target = num / denom (integer division rounded towards 0). Constraint AddDivisionEquality(IntVar target, IntVar numerator, @@ -613,11 +613,11 @@ class CpModelBuilder { Constraint AddModuloEquality(IntVar target, IntVar var, IntVar mod); // Adds target == prod(vars). - Constraint AddProductEquality(IntVar target, absl::Span vars); + Constraint AddProductEquality(IntVar target, absl::Span vars); // Adds a constraint than ensures that all present intervals do not overlap // in time. - Constraint AddNoOverlap(absl::Span vars); + Constraint AddNoOverlap(absl::Span vars); // The no_overlap_2d constraint prevents a set of boxes from overlapping. NoOverlap2DConstraint AddNoOverlap2D(); @@ -639,13 +639,13 @@ class CpModelBuilder { // Adds a decision strategy on a list of integer variables. void AddDecisionStrategy( - absl::Span variables, + absl::Span variables, DecisionStrategyProto::VariableSelectionStrategy var_strategy, DecisionStrategyProto::DomainReductionStrategy domain_strategy); // Adds a decision strategy on a list of boolean variables. void AddDecisionStrategy( - absl::Span variables, + absl::Span variables, DecisionStrategyProto::VariableSelectionStrategy var_strategy, DecisionStrategyProto::DomainReductionStrategy domain_strategy); @@ -672,8 +672,8 @@ class CpModelBuilder { LinearConstraintProto* proto); CpModelProto cp_model_; - std::unordered_map constant_to_index_map_; - std::unordered_map bool_to_integer_index_map_; + absl::flat_hash_map constant_to_index_map_; + absl::flat_hash_map bool_to_integer_index_map_; }; // Solves the current cp_model and returns an instance of CpSolverResponse. diff --git a/ortools/sat/cp_model_checker.cc b/ortools/sat/cp_model_checker.cc index 1db0e4c5c76..ecf2bd45543 100644 --- a/ortools/sat/cp_model_checker.cc +++ b/ortools/sat/cp_model_checker.cc @@ -15,12 +15,12 @@ #include #include -#include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_cat.h" #include "ortools/base/hash.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" #include "ortools/port/proto_utils.h" @@ -49,7 +49,7 @@ bool DomainInProtoIsValid(const ProtoWithDomain& proto) { for (int i = 0; i < proto.domain_size(); i += 2) { domain.push_back({proto.domain(i), proto.domain(i + 1)}); } - return IntervalsAreSortedAndDisjoint(domain); + return IntervalsAreSortedAndNonAdjacent(domain); } bool VariableReferenceIsValid(const CpModelProto& model, int reference) { @@ -379,7 +379,7 @@ class ConstraintChecker { } bool AllDiffConstraintIsFeasible(const ConstraintProto& ct) { - std::unordered_set values; + absl::flat_hash_set values; for (const int v : ct.all_diff().vars()) { if (gtl::ContainsKey(values, Value(v))) return false; values.insert(Value(v)); @@ -459,7 +459,7 @@ class ConstraintChecker { // TODO(user, fdid): Improve complexity for large durations. const int64 capacity = Value(ct.cumulative().capacity()); const int num_intervals = ct.cumulative().intervals_size(); - std::unordered_map usage; + absl::flat_hash_map usage; for (int i = 0; i < num_intervals; ++i) { const ConstraintProto interval_constraint = model.constraints(ct.cumulative().intervals(i)); @@ -499,7 +499,7 @@ class ConstraintChecker { bool AutomataConstraintIsFeasible(const ConstraintProto& ct) { // Build the transition table {tail, label} -> head. - std::unordered_map, int64> transition_map; + absl::flat_hash_map, int64> transition_map; const int num_transitions = ct.automata().transition_tail().size(); for (int i = 0; i < num_transitions; ++i) { transition_map[{ct.automata().transition_tail(i), diff --git a/ortools/sat/cp_model_expand.cc b/ortools/sat/cp_model_expand.cc index 470d1b7c8e4..4cdfd40c1b3 100644 --- a/ortools/sat/cp_model_expand.cc +++ b/ortools/sat/cp_model_expand.cc @@ -15,7 +15,7 @@ #include -#include +#include "absl/container/flat_hash_map.h" #include "ortools/base/hash.h" #include "ortools/base/map_util.h" #include "ortools/sat/cp_model.pb.h" @@ -28,7 +28,7 @@ namespace { struct ExpansionHelper { CpModelProto expanded_proto; - std::unordered_map, int> precedence_cache; + absl::flat_hash_map, int> precedence_cache; std::map statistics; static const int kAlwaysTrue = kint32min; diff --git a/ortools/sat/cp_model_lns.cc b/ortools/sat/cp_model_lns.cc index 01bee95d109..50ccce5d8ce 100644 --- a/ortools/sat/cp_model_lns.cc +++ b/ortools/sat/cp_model_lns.cc @@ -15,7 +15,6 @@ #include -#include #include "ortools/sat/cp_model_utils.h" #include "ortools/util/random_engine.h" diff --git a/ortools/sat/cp_model_loader.cc b/ortools/sat/cp_model_loader.cc index 9334205e131..d336ae832b5 100644 --- a/ortools/sat/cp_model_loader.cc +++ b/ortools/sat/cp_model_loader.cc @@ -20,8 +20,8 @@ #include #include -#include -#include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/logging.h" @@ -58,7 +58,7 @@ std::vector ValuesFromProto(const Values& values) { // Returns the size of the given domain capped to int64max. int64 DomainSize(const Domain& domain) { int64 size = 0; - for (const ClosedInterval interval : domain.intervals()) { + for (const ClosedInterval interval : domain) { size += operations_research::CapAdd( 1, operations_research::CapSub(interval.end, interval.start)); } @@ -67,36 +67,29 @@ int64 DomainSize(const Domain& domain) { } // namespace -ModelWithMapping::ModelWithMapping(const CpModelProto& model_proto, - Model* model) - : model_(model) { +void CpModelMapping::CreateVariables(const CpModelProto& model_proto, + bool view_all_booleans_as_integers, + Model* m) { const int num_proto_variables = model_proto.variables_size(); - // Fills lower_bounds_, this is only used in ExtractFullAssignment(). - lower_bounds_.resize(num_proto_variables, 0); - for (int i = 0; i < num_proto_variables; ++i) { - lower_bounds_[i] = model_proto.variables(i).domain(0); - } - // All [0, 1] variables always have a corresponding Boolean, even if it is // fixed to 0 (domain == [0,0]) or fixed to 1 (domain == [1,1]). booleans_.resize(num_proto_variables, kNoBooleanVariable); for (int i = 0; i < num_proto_variables; ++i) { - const auto domain = ReadDomain(model_proto.variables(i)); - if (domain.size() != 1) continue; - if (domain[0].start >= 0 && domain[0].end <= 1) { - booleans_[i] = Add(NewBooleanVariable()); + const auto domain = ReadDomainFromProto(model_proto.variables(i)); + if (domain.Min() >= 0 && domain.Max() <= 1) { + booleans_[i] = m->Add(NewBooleanVariable()); if (booleans_[i] >= reverse_boolean_map_.size()) { reverse_boolean_map_.resize(booleans_[i].value() + 1, -1); } reverse_boolean_map_[booleans_[i]] = i; - if (domain[0].start == 0 && domain[0].end == 0) { + if (domain.Max() == 0) { // Fix to false. - Add(ClauseConstraint({sat::Literal(booleans_[i], false)})); - } else if (domain[0].start == 1 && domain[0].end == 1) { + m->Add(ClauseConstraint({sat::Literal(booleans_[i], false)})); + } else if (domain.Min() == 1) { // Fix to true. - Add(ClauseConstraint({sat::Literal(booleans_[i], true)})); + m->Add(ClauseConstraint({sat::Literal(booleans_[i], true)})); } } } @@ -104,11 +97,6 @@ ModelWithMapping::ModelWithMapping(const CpModelProto& model_proto, // Compute the list of positive variable reference for which we need to // create an IntegerVariable. std::vector var_to_instantiate_as_integer; - const SatParameters& parameters = *(model->GetOrCreate()); - const bool view_all_booleans_as_integers = - (parameters.linearization_level() >= 2) || - (parameters.search_branching() == SatParameters::FIXED_SEARCH && - model_proto.search_strategy().empty()); if (view_all_booleans_as_integers) { var_to_instantiate_as_integer.resize(num_proto_variables); for (int i = 0; i < num_proto_variables; ++i) { @@ -156,7 +144,7 @@ ModelWithMapping::ModelWithMapping(const CpModelProto& model_proto, integers_.resize(num_proto_variables, kNoIntegerVariable); for (const int i : var_to_instantiate_as_integer) { const auto& var_proto = model_proto.variables(i); - integers_[i] = Add(NewIntegerVariable(ReadDomainFromProto(var_proto))); + integers_[i] = m->Add(NewIntegerVariable(ReadDomainFromProto(var_proto))); if (integers_[i] >= reverse_integer_map_.size()) { reverse_integer_map_.resize(integers_[i].value() + 1, -1); } @@ -169,11 +157,11 @@ ModelWithMapping::ModelWithMapping(const CpModelProto& model_proto, if (booleans_[i] == kNoBooleanVariable) continue; // Associate with corresponding integer variable. - model_->GetOrCreate()->AssociateToIntegerEqualValue( + m->GetOrCreate()->AssociateToIntegerEqualValue( sat::Literal(booleans_[i], true), integers_[i], IntegerValue(1)); // This is needed so that IsFullyEncoded() returns true. - model_->GetOrCreate()->FullyEncodeVariable(integers_[i]); + m->GetOrCreate()->FullyEncodeVariable(integers_[i]); } // Create the interval variables. @@ -189,13 +177,13 @@ ModelWithMapping::ModelWithMapping(const CpModelProto& model_proto, // TODO(user): Fix the constant variable situation. An optional interval // with constant start/end or size cannot share the same constant // variable if it is used in non-optional situation. - intervals_[c] = Add(NewOptionalInterval( + intervals_[c] = m->Add(NewOptionalInterval( Integer(ct.interval().start()), Integer(ct.interval().end()), Integer(ct.interval().size()), enforcement_literal)); } else { - intervals_[c] = Add(NewInterval(Integer(ct.interval().start()), - Integer(ct.interval().end()), - Integer(ct.interval().size()))); + intervals_[c] = m->Add(NewInterval(Integer(ct.interval().start()), + Integer(ct.interval().end()), + Integer(ct.interval().size()))); } already_loaded_ct_.insert(&ct); } @@ -204,9 +192,10 @@ ModelWithMapping::ModelWithMapping(const CpModelProto& model_proto, // The logic assumes that the linear constraints have been presolved, so that // equality with a domain bound have been converted to <= or >= and so that we // never have any trivial inequalities. -void ModelWithMapping::ExtractEncoding(const CpModelProto& model_proto) { - IntegerEncoder* encoder = GetOrCreate(); - IntegerTrail* integer_trail = GetOrCreate(); +void CpModelMapping::ExtractEncoding(const CpModelProto& model_proto, + Model* m) { + IntegerEncoder* encoder = m->GetOrCreate(); + IntegerTrail* integer_trail = m->GetOrCreate(); // Detection of literal equivalent to (i_var == value). We collect all the // half-reified constraint lit => equality or lit => inequality for a given @@ -266,7 +255,7 @@ void ModelWithMapping::ExtractEncoding(const CpModelProto& model_proto) { (RefIsPositive(ref) ? 1 : -1)); // Detect enforcement_literal => (var >= value or var <= value). - if (domain_if_enforced.intervals().size() == 1) { + if (domain_if_enforced.NumIntervals() == 1) { if (domain_if_enforced.Max() >= domain.Max() && domain_if_enforced.Min() > domain.Min()) { inequalities.push_back( @@ -339,7 +328,7 @@ void ModelWithMapping::ExtractEncoding(const CpModelProto& model_proto) { int num_half_inequalities = 0; for (const auto inequality : inequalities) { if (ConstraintIsAlreadyLoaded(inequality.ct)) continue; - model_->Add( + m->Add( Implication(inequality.literal, encoder->GetOrCreateAssociatedLiteral(inequality.i_lit))); @@ -364,7 +353,7 @@ void ModelWithMapping::ExtractEncoding(const CpModelProto& model_proto) { if (encoding.empty()) continue; num_constraints += encoding.size(); - std::unordered_set values; + absl::flat_hash_set values; for (int j = 0; j + 1 < encoding.size(); j++) { if ((encoding[j].value != encoding[j + 1].value) || (encoding[j].literal != encoding[j + 1].literal.Negated()) || @@ -387,9 +376,9 @@ void ModelWithMapping::ExtractEncoding(const CpModelProto& model_proto) { const class Literal eq = encoder->GetOrCreateLiteralAssociatedToEquality( integers_[i], IntegerValue(equality.value)); if (equality.is_equality) { - model_->Add(Implication(equality.literal, eq)); + m->Add(Implication(equality.literal, eq)); } else { - model_->Add(Implication(equality.literal, eq.Negated())); + m->Add(Implication(equality.literal, eq.Negated())); } ++num_half_equalities; @@ -422,9 +411,9 @@ void ModelWithMapping::ExtractEncoding(const CpModelProto& model_proto) { } } -void ModelWithMapping::DetectOptionalVariables( - const CpModelProto& model_proto) { - const SatParameters& parameters = *(model_->GetOrCreate()); +void CpModelMapping::DetectOptionalVariables(const CpModelProto& model_proto, + Model* m) { + const SatParameters& parameters = *(m->GetOrCreate()); if (!parameters.use_optional_variables()) return; if (parameters.enumerate_all_solutions()) return; @@ -470,7 +459,7 @@ void ModelWithMapping::DetectOptionalVariables( // Auto-detect optional variables. int num_optionals = 0; - auto* integer_trail = model_->GetOrCreate(); + auto* integer_trail = m->GetOrCreate(); for (int var = 0; var < num_proto_variables; ++var) { const IntegerVariableProto& var_proto = model_proto.variables(var); const int64 min = var_proto.domain(0); @@ -486,72 +475,102 @@ void ModelWithMapping::DetectOptionalVariables( VLOG(2) << "Auto-detected " << num_optionals << " optional variables."; } -std::vector ModelWithMapping::ExtractFullAssignment() const { - std::vector result; - const int num_variables = integers_.size(); - Trail* trail = model_->GetOrCreate(); - IntegerTrail* integer_trail = model_->GetOrCreate(); - for (int i = 0; i < num_variables; ++i) { - if (integers_[i] != kNoIntegerVariable) { - if (integer_trail->IsCurrentlyIgnored(integers_[i])) { - // This variable is "ignored" so it may not be fixed, simply use - // the current lower bound. Any value in its domain should lead to - // a feasible solution. - result.push_back(model_->Get(LowerBound(integers_[i]))); - } else { - if (model_->Get(LowerBound(integers_[i])) != - model_->Get(UpperBound(integers_[i]))) { - // Notify that everything is not fixed. - return {}; - } - result.push_back(model_->Get(Value(integers_[i]))); - } - } else { - DCHECK(IsBoolean(i)); - if (trail->Assignment().VariableIsAssigned(booleans_[i])) { - result.push_back(model_->Get(Value(booleans_[i]))); - } else { - // Notify that everything is not fixed. - return {}; - } - } - } - return result; -} - -void LoadBoolOrConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - std::vector literals = m->Literals(ct.bool_or().literals()); +void LoadBoolOrConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + std::vector literals = mapping->Literals(ct.bool_or().literals()); for (const int ref : ct.enforcement_literal()) { - literals.push_back(m->Literal(ref).Negated()); + literals.push_back(mapping->Literal(ref).Negated()); } m->Add(ClauseConstraint(literals)); } -void LoadBoolAndConstraint(const ConstraintProto& ct, ModelWithMapping* m) { +void LoadBoolAndConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); std::vector literals; for (const int ref : ct.enforcement_literal()) { - literals.push_back(m->Literal(ref).Negated()); + literals.push_back(mapping->Literal(ref).Negated()); } - for (const Literal literal : m->Literals(ct.bool_and().literals())) { + for (const Literal literal : mapping->Literals(ct.bool_and().literals())) { literals.push_back(literal); m->Add(ClauseConstraint(literals)); literals.pop_back(); } } -void LoadAtMostOneConstraint(const ConstraintProto& ct, ModelWithMapping* m) { +void LoadAtMostOneConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); CHECK(!HasEnforcementLiteral(ct)) << "Not supported."; - m->Add(AtMostOneConstraint(m->Literals(ct.at_most_one().literals()))); + m->Add(AtMostOneConstraint(mapping->Literals(ct.at_most_one().literals()))); } -void LoadBoolXorConstraint(const ConstraintProto& ct, ModelWithMapping* m) { +void LoadBoolXorConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); CHECK(!HasEnforcementLiteral(ct)) << "Not supported."; - m->Add(LiteralXorIs(m->Literals(ct.bool_xor().literals()), true)); + m->Add(LiteralXorIs(mapping->Literals(ct.bool_xor().literals()), true)); +} + +namespace { + +// Boolean encoding of: +// enforcement_literal => coeff1 * var1 + coeff2 * var2 == rhs; +void LoadEquivalenceAC(const std::vector enforcement_literal, + IntegerValue coeff1, IntegerVariable var1, + IntegerValue coeff2, IntegerVariable var2, + const IntegerValue rhs, Model* m) { + auto* encoder = m->GetOrCreate(); + CHECK(encoder->VariableIsFullyEncoded(var1)); + CHECK(encoder->VariableIsFullyEncoded(var2)); + absl::flat_hash_map term1_value_to_literal; + for (const auto value_literal : encoder->FullDomainEncoding(var1)) { + term1_value_to_literal[coeff1 * value_literal.value] = + value_literal.literal; + } + for (const auto value_literal : encoder->FullDomainEncoding(var2)) { + const IntegerValue target = rhs - value_literal.value * coeff2; + if (!gtl::ContainsKey(term1_value_to_literal, target)) { + m->Add(EnforcedClause(enforcement_literal, + {value_literal.literal.Negated()})); + } else { + const Literal target_literal = term1_value_to_literal[target]; + m->Add(EnforcedClause(enforcement_literal, + {value_literal.literal.Negated(), target_literal})); + m->Add(EnforcedClause(enforcement_literal, + {value_literal.literal, target_literal.Negated()})); + + // This "target" can never be reached again, so it is safe to remove it. + // We do that so we know the term1 values that are never reached. + term1_value_to_literal.erase(target); + } + } + + // Exclude the values that can never be "matched" by coeff2 * var2. + for (const auto entry : term1_value_to_literal) { + m->Add(EnforcedClause(enforcement_literal, {entry.second.Negated()})); + } } -void LoadLinearConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - const std::vector vars = m->Integers(ct.linear().vars()); +} // namespace + +void LoadLinearConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const std::vector vars = + mapping->Integers(ct.linear().vars()); const std::vector coeffs = ValuesFromProto(ct.linear().coeffs()); + + const SatParameters& params = *m->GetOrCreate(); + if (params.boolean_encoding_level() > 0 && vars.size() == 2 && + ct.linear().domain_size() == 2 && + ct.linear().domain(0) == ct.linear().domain(1)) { + auto* encoder = m->GetOrCreate(); + if (encoder->VariableIsFullyEncoded(vars[0]) && + encoder->VariableIsFullyEncoded(vars[1])) { + return LoadEquivalenceAC(mapping->Literals(ct.enforcement_literal()), + IntegerValue(coeffs[0]), vars[0], + IntegerValue(coeffs[1]), vars[1], + IntegerValue(ct.linear().domain(0)), m); + } + } + if (ct.linear().domain_size() == 2) { const int64 lb = ct.linear().domain(0); const int64 ub = ct.linear().domain(1); @@ -563,11 +582,11 @@ void LoadLinearConstraint(const ConstraintProto& ct, ModelWithMapping* m) { std::vector cst; for (int i = 0; i < vars.size(); ++i) { const int ref = ct.linear().vars(i); - if (!m->IsBoolean(ref)) { + if (!mapping->IsBoolean(ref)) { all_booleans = false; continue; } - cst.push_back({m->Literal(ref), coeffs[i]}); + cst.push_back({mapping->Literal(ref), coeffs[i]}); } if (all_booleans) { m->Add(BooleanLinearConstraint(lb, ub, &cst)); @@ -581,7 +600,7 @@ void LoadLinearConstraint(const ConstraintProto& ct, ModelWithMapping* m) { } } else { const std::vector enforcement_literals = - m->Literals(ct.enforcement_literal()); + mapping->Literals(ct.enforcement_literal()); if (lb != kint64min) { m->Add(ConditionalWeightedSumGreaterOrEqual(enforcement_literals, vars, coeffs, lb)); @@ -608,7 +627,7 @@ void LoadLinearConstraint(const ConstraintProto& ct, ModelWithMapping* m) { } } for (const int ref : ct.enforcement_literal()) { - clause.push_back(m->Literal(ref).Negated()); + clause.push_back(mapping->Literal(ref).Negated()); } // TODO(user): In the cases where this clause only contains two literals, @@ -617,8 +636,10 @@ void LoadLinearConstraint(const ConstraintProto& ct, ModelWithMapping* m) { } } -void LoadAllDiffConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - const std::vector vars = m->Integers(ct.all_diff().vars()); +void LoadAllDiffConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const std::vector vars = + mapping->Integers(ct.all_diff().vars()); // If all variables are fully encoded and domains are not too large, use // arc-consistent reasoning. Otherwise, use bounds-consistent reasoning. IntegerTrail* integer_trail = m->GetOrCreate(); @@ -642,43 +663,52 @@ void LoadAllDiffConstraint(const ConstraintProto& ct, ModelWithMapping* m) { } } -void LoadIntProdConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - const IntegerVariable prod = m->Integer(ct.int_prod().target()); - const std::vector vars = m->Integers(ct.int_prod().vars()); +void LoadIntProdConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const IntegerVariable prod = mapping->Integer(ct.int_prod().target()); + const std::vector vars = + mapping->Integers(ct.int_prod().vars()); CHECK_EQ(vars.size(), 2) << "General int_prod not supported yet."; m->Add(ProductConstraint(vars[0], vars[1], prod)); } -void LoadIntDivConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - const IntegerVariable div = m->Integer(ct.int_div().target()); - const std::vector vars = m->Integers(ct.int_div().vars()); +void LoadIntDivConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const IntegerVariable div = mapping->Integer(ct.int_div().target()); + const std::vector vars = + mapping->Integers(ct.int_div().vars()); m->Add(DivisionConstraint(vars[0], vars[1], div)); } -void LoadIntMinConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - const IntegerVariable min = m->Integer(ct.int_min().target()); - const std::vector vars = m->Integers(ct.int_min().vars()); +void LoadIntMinConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const IntegerVariable min = mapping->Integer(ct.int_min().target()); + const std::vector vars = + mapping->Integers(ct.int_min().vars()); m->Add(IsEqualToMinOf(min, vars)); } -void LoadIntMaxConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - const IntegerVariable max = m->Integer(ct.int_max().target()); - const std::vector vars = m->Integers(ct.int_max().vars()); +void LoadIntMaxConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const IntegerVariable max = mapping->Integer(ct.int_max().target()); + const std::vector vars = + mapping->Integers(ct.int_max().vars()); m->Add(IsEqualToMaxOf(max, vars)); } -void LoadNoOverlapConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - m->Add(Disjunctive(m->Intervals(ct.no_overlap().intervals()))); +void LoadNoOverlapConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + m->Add(Disjunctive(mapping->Intervals(ct.no_overlap().intervals()))); } -void LoadNoOverlap2dConstraint(const ConstraintProto& ct, ModelWithMapping* m) { +void LoadNoOverlap2dConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); const std::vector x_intervals = - m->Intervals(ct.no_overlap_2d().x_intervals()); + mapping->Intervals(ct.no_overlap_2d().x_intervals()); const std::vector y_intervals = - m->Intervals(ct.no_overlap_2d().y_intervals()); + mapping->Intervals(ct.no_overlap_2d().y_intervals()); - const IntervalsRepository& repository = - *(m->model()->Get()); + const IntervalsRepository& repository = *(m->Get()); std::vector x; std::vector y; std::vector dx; @@ -692,28 +722,31 @@ void LoadNoOverlap2dConstraint(const ConstraintProto& ct, ModelWithMapping* m) { m->Add(StrictNonOverlappingRectangles(x, y, dx, dy)); } -void LoadCumulativeConstraint(const ConstraintProto& ct, ModelWithMapping* m) { +void LoadCumulativeConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); const std::vector intervals = - m->Intervals(ct.cumulative().intervals()); - const IntegerVariable capacity = m->Integer(ct.cumulative().capacity()); + mapping->Intervals(ct.cumulative().intervals()); + const IntegerVariable capacity = mapping->Integer(ct.cumulative().capacity()); const std::vector demands = - m->Integers(ct.cumulative().demands()); + mapping->Integers(ct.cumulative().demands()); m->Add(Cumulative(intervals, demands, capacity)); } // If a variable is constant and its value appear in no other variable domains, // then the literal encoding the index and the one encoding the target at this // value are equivalent. -void DetectEquivalencesInElementConstraint(const ConstraintProto& ct, - ModelWithMapping* m) { +bool DetectEquivalencesInElementConstraint(const ConstraintProto& ct, + Model* m) { + auto* mapping = m->GetOrCreate(); IntegerEncoder* encoder = m->GetOrCreate(); IntegerTrail* integer_trail = m->GetOrCreate(); - const IntegerVariable index = m->Integer(ct.element().index()); - const IntegerVariable target = m->Integer(ct.element().target()); - const std::vector vars = m->Integers(ct.element().vars()); - - if (m->Get(IsFixed(index))) return; + const IntegerVariable index = mapping->Integer(ct.element().index()); + const IntegerVariable target = mapping->Integer(ct.element().target()); + const std::vector vars = + mapping->Integers(ct.element().vars()); + CHECK(!m->Get(IsFixed(index))); + CHECK(!m->Get(IsFixed(target))); Domain union_of_non_constant_domains; std::map constant_to_num; @@ -737,33 +770,36 @@ void DetectEquivalencesInElementConstraint(const ConstraintProto& ct, // Use the literal from the index encoding to encode the target at the // "unique" values. + bool is_one_to_one_mapping = true; for (const auto literal_value : m->Add(FullyEncodeVariable(index))) { const int i = literal_value.value.value(); - if (!m->Get(IsFixed(vars[i]))) continue; + if (!m->Get(IsFixed(vars[i]))) { + is_one_to_one_mapping = false; + continue; + } const IntegerValue value(m->Get(Value(vars[i]))); if (constant_to_num[value] == 1) { const Literal r = literal_value.literal; encoder->AssociateToIntegerEqualValue(r, target, value); + } else { + is_one_to_one_mapping = false; } } + + return is_one_to_one_mapping; } // TODO(user): Be more efficient when the element().vars() are constants. // Ideally we should avoid creating them as integer variable since we don't // use them. -void LoadElementConstraintBounds(const ConstraintProto& ct, - ModelWithMapping* m) { - const IntegerVariable index = m->Integer(ct.element().index()); - const IntegerVariable target = m->Integer(ct.element().target()); - const std::vector vars = m->Integers(ct.element().vars()); - - IntegerTrail* integer_trail = m->GetOrCreate(); - if (m->Get(IsFixed(index))) { - const int64 value = integer_trail->LowerBound(index).value(); - m->Add(Equality(target, vars[value])); - return; - } +void LoadElementConstraintBounds(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const IntegerVariable index = mapping->Integer(ct.element().index()); + const IntegerVariable target = mapping->Integer(ct.element().target()); + const std::vector vars = + mapping->Integers(ct.element().vars()); + CHECK(!m->Get(IsFixed(index))); // We always fully encode the index on an element constraint. const auto encoding = m->Add(FullyEncodeVariable((index))); @@ -771,8 +807,8 @@ void LoadElementConstraintBounds(const ConstraintProto& ct, std::vector possible_vars; for (const auto literal_value : encoding) { const int i = literal_value.value.value(); - CHECK_GE(i, 0) << "Should be presolved."; - CHECK_LT(i, vars.size()) << "Should be presolved."; + CHECK_GE(i, 0); + CHECK_LT(i, vars.size()); possible_vars.push_back(vars[i]); selectors.push_back(literal_value.literal); const Literal r = literal_value.literal; @@ -789,7 +825,10 @@ void LoadElementConstraintBounds(const ConstraintProto& ct, m->Add(ConditionalLowerOrEqualWithOffset(target, vars[i], 0, r)); } } - m->Add(PartialIsOneOfVar(target, possible_vars, selectors)); + + if (!m->Get(IsFixed(target))) { + m->Add(PartialIsOneOfVar(target, possible_vars, selectors)); + } } // Arc-Consistent encoding of the element constraint as SAT clauses. @@ -806,23 +845,16 @@ void LoadElementConstraintBounds(const ConstraintProto& ct, // value). Rules 1 and 2 are enforced by target == value <=> \Or_{i} // selected[i][value]. Rule 3 is enforced by index == i <=> \Or_{value} // selected[i][value]. -void LoadElementConstraintAC(const ConstraintProto& ct, ModelWithMapping* m) { - const IntegerVariable index = m->Integer(ct.element().index()); - const IntegerVariable target = m->Integer(ct.element().target()); - const std::vector vars = m->Integers(ct.element().vars()); - - IntegerTrail* integer_trail = m->GetOrCreate(); - if (m->Get(IsFixed(index))) { - const int64 value = integer_trail->LowerBound(index).value(); - m->Add(Equality(target, vars[value])); - return; - } - - // Make map target_value -> literal. - if (m->Get(IsFixed(target))) { - return LoadElementConstraintBounds(ct, m); - } - std::unordered_map target_map; +void LoadElementConstraintAC(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const IntegerVariable index = mapping->Integer(ct.element().index()); + const IntegerVariable target = mapping->Integer(ct.element().target()); + const std::vector vars = + mapping->Integers(ct.element().vars()); + CHECK(!m->Get(IsFixed(index))); + CHECK(!m->Get(IsFixed(target))); + + absl::flat_hash_map target_map; const auto target_encoding = m->Add(FullyEncodeVariable(target)); for (const auto literal_value : target_encoding) { target_map[literal_value.value] = literal_value.literal; @@ -830,8 +862,9 @@ void LoadElementConstraintAC(const ConstraintProto& ct, ModelWithMapping* m) { // For i \in index and value in vars[i], make (index == i /\ vars[i] == value) // literals and store them by value in vectors. - std::unordered_map> value_to_literals; + absl::flat_hash_map> value_to_literals; const auto index_encoding = m->Add(FullyEncodeVariable(index)); + IntegerTrail* integer_trail = m->GetOrCreate(); for (const auto literal_value : index_encoding) { const int i = literal_value.value.value(); const Literal i_lit = literal_value.literal; @@ -878,35 +911,94 @@ void LoadElementConstraintAC(const ConstraintProto& ct, ModelWithMapping* m) { } } -void LoadElementConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - IntegerEncoder* encoder = m->GetOrCreate(); +namespace { + +// This Boolean encoding is enough for consistency, but does not propagate as +// much as LoadElementConstraintAC(). However, setting any of the non-propagated +// Booleans to its "wrong" value will result directly in a conflict, so the +// solver will easily learn an AC encoding... +// +// The advantage is that this does not introduce extra BooleanVariables. +void LoadElementConstraintHalfAC(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const IntegerVariable index = mapping->Integer(ct.element().index()); + const IntegerVariable target = mapping->Integer(ct.element().target()); + const std::vector vars = + mapping->Integers(ct.element().vars()); + CHECK(!m->Get(IsFixed(index))); + CHECK(!m->Get(IsFixed(target))); + + m->Add(FullyEncodeVariable(target)); + for (const auto value_literal : m->Add(FullyEncodeVariable(index))) { + const int i = value_literal.value.value(); + m->Add(FullyEncodeVariable(vars[i])); + LoadEquivalenceAC({value_literal.literal}, IntegerValue(1), vars[i], + IntegerValue(-1), target, IntegerValue(0), m); + } +} + +} // namespace + +void LoadElementConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const IntegerVariable index = mapping->Integer(ct.element().index()); + const IntegerVariable target = mapping->Integer(ct.element().target()); + const std::vector vars = + mapping->Integers(ct.element().vars()); + + // Retrict the domain of index in case there was no presolve. + if (!m->GetOrCreate()->UpdateInitialDomain( + index, Domain(0, vars.size() - 1))) { + return; + } + + // This returns true if there is nothing else to do after the equivalences + // of the form (index literal <=> target_literal) have been added. + if (!m->Get(IsFixed(index)) && !m->Get(IsFixed(target)) && + DetectEquivalencesInElementConstraint(ct, m)) { + return; + } + + // Special case when index is fixed. + if (m->Get(IsFixed(index))) { + m->Add(Equality(target, vars[m->Get(Value(index))])); + return; + } + + // Special case when target is fixed. + if (m->Get(IsFixed(target))) { + return LoadElementConstraintBounds(ct, m); + } - const int target = ct.element().target(); - const IntegerVariable target_var = m->Integer(target); - const bool target_is_AC = m->Get(IsFixed(target_var)) || - encoder->VariableIsFullyEncoded(target_var); + IntegerEncoder* encoder = m->GetOrCreate(); + const bool target_is_AC = encoder->VariableIsFullyEncoded(target); int num_AC_variables = 0; const int num_vars = ct.element().vars().size(); for (const int v : ct.element().vars()) { - IntegerVariable variable = m->Integer(v); + IntegerVariable variable = mapping->Integer(v); const bool is_full = m->Get(IsFixed(variable)) || encoder->VariableIsFullyEncoded(variable); if (is_full) num_AC_variables++; } - DetectEquivalencesInElementConstraint(ct, m); - const SatParameters& params = *m->model()->GetOrCreate(); + const SatParameters& params = *m->GetOrCreate(); if (params.boolean_encoding_level() > 0 && (target_is_AC || num_AC_variables >= num_vars - 1)) { - LoadElementConstraintAC(ct, m); + if (params.boolean_encoding_level() > 1) { + LoadElementConstraintAC(ct, m); + } else { + LoadElementConstraintHalfAC(ct, m); + } } else { LoadElementConstraintBounds(ct, m); } } -void LoadTableConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - const std::vector vars = m->Integers(ct.table().vars()); +void LoadTableConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const std::vector vars = + mapping->Integers(ct.table().vars()); const std::vector values = ValuesFromProto(ct.table().values()); const int num_vars = vars.size(); const int num_tuples = values.size() / num_vars; @@ -924,8 +1016,10 @@ void LoadTableConstraint(const ConstraintProto& ct, ModelWithMapping* m) { } } -void LoadAutomataConstraint(const ConstraintProto& ct, ModelWithMapping* m) { - const std::vector vars = m->Integers(ct.automata().vars()); +void LoadAutomataConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + const std::vector vars = + mapping->Integers(ct.automata().vars()); const int num_transitions = ct.automata().transition_tail_size(); std::vector> transitions; @@ -945,7 +1039,7 @@ void LoadAutomataConstraint(const ConstraintProto& ct, ModelWithMapping* m) { // From vector of n IntegerVariables, returns an n x n matrix of Literal // such that matrix[i][j] is the Literal corresponding to vars[i] == j. std::vector> GetSquareMatrixFromIntegerVariables( - const std::vector& vars, ModelWithMapping* m) { + const std::vector& vars, Model* m) { const int n = vars.size(); const Literal kTrueLiteral = m->GetOrCreate()->GetTrueLiteral(); @@ -974,33 +1068,35 @@ std::vector> GetSquareMatrixFromIntegerVariables( return matrix; } -void LoadCircuitConstraint(const ConstraintProto& ct, ModelWithMapping* m) { +void LoadCircuitConstraint(const ConstraintProto& ct, Model* m) { const auto& circuit = ct.circuit(); if (circuit.tails().empty()) return; std::vector tails(circuit.tails().begin(), circuit.tails().end()); std::vector heads(circuit.heads().begin(), circuit.heads().end()); - std::vector literals = m->Literals(circuit.literals()); + std::vector literals = + m->GetOrCreate()->Literals(circuit.literals()); const int num_nodes = ReindexArcs(&tails, &heads, &literals); m->Add(SubcircuitConstraint(num_nodes, tails, heads, literals)); } -void LoadRoutesConstraint(const ConstraintProto& ct, ModelWithMapping* m) { +void LoadRoutesConstraint(const ConstraintProto& ct, Model* m) { const auto& routes = ct.routes(); if (routes.tails().empty()) return; std::vector tails(routes.tails().begin(), routes.tails().end()); std::vector heads(routes.heads().begin(), routes.heads().end()); - std::vector literals = m->Literals(routes.literals()); + std::vector literals = + m->GetOrCreate()->Literals(routes.literals()); const int num_nodes = ReindexArcs(&tails, &heads, &literals); m->Add(SubcircuitConstraint(num_nodes, tails, heads, literals, /*multiple_subcircuit_through_zero=*/true)); } -void LoadCircuitCoveringConstraint(const ConstraintProto& ct, - ModelWithMapping* m) { +void LoadCircuitCoveringConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); const std::vector nexts = - m->Integers(ct.circuit_covering().nexts()); + mapping->Integers(ct.circuit_covering().nexts()); const std::vector> graph = GetSquareMatrixFromIntegerVariables(nexts, m); const std::vector distinguished( @@ -1010,15 +1106,17 @@ void LoadCircuitCoveringConstraint(const ConstraintProto& ct, m->Add(CircuitCovering(graph, distinguished)); } -void LoadInverseConstraint(const ConstraintProto& ct, ModelWithMapping* m) { +void LoadInverseConstraint(const ConstraintProto& ct, Model* m) { + auto* mapping = m->GetOrCreate(); + // Fully encode both arrays of variables, encode the constraint using Boolean // equalities: f_direct[i] == j <=> f_inverse[j] == i. const int num_variables = ct.inverse().f_direct_size(); CHECK_EQ(num_variables, ct.inverse().f_inverse_size()); const std::vector direct = - m->Integers(ct.inverse().f_direct()); + mapping->Integers(ct.inverse().f_direct()); const std::vector inverse = - m->Integers(ct.inverse().f_inverse()); + mapping->Integers(ct.inverse().f_inverse()); // Fill LiteralIndex matrices. std::vector> matrix_direct( @@ -1074,7 +1172,7 @@ void LoadInverseConstraint(const ConstraintProto& ct, ModelWithMapping* m) { } } -bool LoadConstraint(const ConstraintProto& ct, ModelWithMapping* m) { +bool LoadConstraint(const ConstraintProto& ct, Model* m) { switch (ct.constraint_case()) { case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET: return true; diff --git a/ortools/sat/cp_model_loader.h b/ortools/sat/cp_model_loader.h index d665327bba9..0fd6b464a14 100644 --- a/ortools/sat/cp_model_loader.h +++ b/ortools/sat/cp_model_loader.h @@ -18,7 +18,7 @@ #include #include "ortools/base/integral_types.h" -#include +#include "absl/container/flat_hash_set.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/logging.h" @@ -33,47 +33,55 @@ namespace operations_research { namespace sat { -// Holds the sat::model and the mapping between the proto indices and the -// sat::model ones. -class ModelWithMapping { +// Holds the mapping between CpModel proto indices and the sat::model ones. +// +// This also holds some information used when loading a CpModel proto. +class CpModelMapping { public: // Extracts all the used variables in the CpModelProto and creates a // sat::Model representation for them. More precisely // - All Boolean variables will be mapped. // - All Interval variables will be mapped. // - All non-Boolean variable will have a corresponding IntegerVariable, and - // depending on the options, some or all of the BooleanVariable will also - // have an IntegerVariable corresponding to its "integer view". + // depending on the view_all_booleans_as_integers, some or all of the + // BooleanVariable will also have an IntegerVariable corresponding to its + // "integer view". // // Note(user): We could create IntegerVariable on the fly as they are needed, // but that loose the original variable order which might be useful in // heuristics later. - ModelWithMapping(const CpModelProto& model_proto, Model* model); + void CreateVariables(const CpModelProto& model_proto, + bool view_all_booleans_as_integers, Model* m); // Automatically detect optional variables. - void DetectOptionalVariables(const CpModelProto& model_proto); + void DetectOptionalVariables(const CpModelProto& model_proto, Model* m); // Extract the encodings (IntegerVariable <-> Booleans) present in the model. // This effectively load some linear constraints of size 1 that will be marked // as already loaded. - void ExtractEncoding(const CpModelProto& model_proto); + void ExtractEncoding(const CpModelProto& model_proto, Model* m); // Returns true if the given CpModelProto variable reference refers to a // Boolean varaible. Such variable will always have an associated Literal(), // but not always an associated Integer(). bool IsBoolean(int ref) const { - CHECK_LT(PositiveRef(ref), booleans_.size()); + DCHECK_LT(PositiveRef(ref), booleans_.size()); return booleans_[PositiveRef(ref)] != kNoBooleanVariable; } + bool IsInteger(int ref) const { + DCHECK_LT(PositiveRef(ref), integers_.size()); + return integers_[PositiveRef(ref)] != kNoIntegerVariable; + } + sat::Literal Literal(int ref) const { DCHECK(IsBoolean(ref)); return sat::Literal(booleans_[PositiveRef(ref)], RefIsPositive(ref)); } IntegerVariable Integer(int ref) const { + DCHECK(IsInteger(ref)); const IntegerVariable var = integers_[PositiveRef(ref)]; - DCHECK_NE(var, kNoIntegerVariable); return RefIsPositive(ref) ? var : NegationOf(var); } @@ -94,7 +102,7 @@ class ModelWithMapping { template std::vector Literals(const ProtoIndices& indices) const { std::vector result; - for (const int i : indices) result.push_back(ModelWithMapping::Literal(i)); + for (const int i : indices) result.push_back(CpModelMapping::Literal(i)); return result; } @@ -105,10 +113,6 @@ class ModelWithMapping { return result; } - // This assumes that all the variable are currently fixed in the underlying - // solver. Returns their current value. - std::vector ExtractFullAssignment() const; - // Depending on the option, we will load constraints in stages. This is used // to detect constraints that are already loaded. For instance the interval // constraints and the linear constraint of size 1 (encodings) are usually @@ -147,29 +151,7 @@ class ModelWithMapping { return result; } - Model* model() const { return model_; } - - // Shortcuts for the underlying model_ functions. - template - T Add(std::function f) { - return f(model_); - } - template - T Get(std::function f) const { - return f(*model_); - } - template - T* GetOrCreate() { - return model_->GetOrCreate(); - } - template - void TakeOwnership(T* t) { - return model_->TakeOwnership(t); - } - private: - Model* model_; - // Note that only the variables used by at least one constraint will be // created, the other will have a kNo[Integer,Interval,Boolean]VariableValue. std::vector integers_; @@ -182,42 +164,37 @@ class ModelWithMapping { gtl::ITIVector reverse_boolean_map_; gtl::ITIVector reverse_integer_map_; - // Used to return a feasible solution for the unused variables. - std::vector lower_bounds_; - // Set of constraints to ignore because they were already dealt with by // ExtractEncoding(). - std::unordered_set already_loaded_ct_; + absl::flat_hash_set already_loaded_ct_; }; // Calls one of the functions below. -// Returns false if we do not know how to load the given cosntraints. -bool LoadConstraint(const ConstraintProto& ct, ModelWithMapping* m); - -void LoadBoolOrConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadBoolAndConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadAtMostOneConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadBoolXorConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadLinearConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadAllDiffConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadIntProdConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadIntDivConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadIntMinConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadIntMaxConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadNoOverlapConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadNoOverlap2dConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadCumulativeConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadElementConstraintBounds(const ConstraintProto& ct, - ModelWithMapping* m); -void LoadElementConstraintAC(const ConstraintProto& ct, ModelWithMapping* m); -void LoadElementConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadTableConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadAutomataConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadCircuitConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadRoutesConstraint(const ConstraintProto& ct, ModelWithMapping* m); -void LoadCircuitCoveringConstraint(const ConstraintProto& ct, - ModelWithMapping* m); -void LoadInverseConstraint(const ConstraintProto& ct, ModelWithMapping* m); +// Returns false if we do not know how to load the given constraints. +bool LoadConstraint(const ConstraintProto& ct, Model* m); + +void LoadBoolOrConstraint(const ConstraintProto& ct, Model* m); +void LoadBoolAndConstraint(const ConstraintProto& ct, Model* m); +void LoadAtMostOneConstraint(const ConstraintProto& ct, Model* m); +void LoadBoolXorConstraint(const ConstraintProto& ct, Model* m); +void LoadLinearConstraint(const ConstraintProto& ct, Model* m); +void LoadAllDiffConstraint(const ConstraintProto& ct, Model* m); +void LoadIntProdConstraint(const ConstraintProto& ct, Model* m); +void LoadIntDivConstraint(const ConstraintProto& ct, Model* m); +void LoadIntMinConstraint(const ConstraintProto& ct, Model* m); +void LoadIntMaxConstraint(const ConstraintProto& ct, Model* m); +void LoadNoOverlapConstraint(const ConstraintProto& ct, Model* m); +void LoadNoOverlap2dConstraint(const ConstraintProto& ct, Model* m); +void LoadCumulativeConstraint(const ConstraintProto& ct, Model* m); +void LoadElementConstraintBounds(const ConstraintProto& ct, Model* m); +void LoadElementConstraintAC(const ConstraintProto& ct, Model* m); +void LoadElementConstraint(const ConstraintProto& ct, Model* m); +void LoadTableConstraint(const ConstraintProto& ct, Model* m); +void LoadAutomataConstraint(const ConstraintProto& ct, Model* m); +void LoadCircuitConstraint(const ConstraintProto& ct, Model* m); +void LoadRoutesConstraint(const ConstraintProto& ct, Model* m); +void LoadCircuitCoveringConstraint(const ConstraintProto& ct, Model* m); +void LoadInverseConstraint(const ConstraintProto& ct, Model* m); } // namespace sat } // namespace operations_research diff --git a/ortools/sat/cp_model_presolve.cc b/ortools/sat/cp_model_presolve.cc index 631ec399071..d8f6063a9d3 100644 --- a/ortools/sat/cp_model_presolve.cc +++ b/ortools/sat/cp_model_presolve.cc @@ -21,18 +21,16 @@ #include #include #include -#include -#include #include #include -#include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_join.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/port.h" #include "ortools/base/stl_util.h" #include "ortools/port/proto_utils.h" #include "ortools/sat/cp_model_checker.h" @@ -319,13 +317,13 @@ struct PresolveContext { // // WARNING: This assumes the ConstraintProto* to stay valid during the full // presolve even if we add new constraint to the CpModelProto. - std::unordered_set affine_constraints; + absl::flat_hash_set affine_constraints; // For each constant variable appearing in the model, we maintain a reference // variable with the same constant value. If two variables end up having the // same fixed value, then we can detect it using this and add a new // equivalence relation. See ExploitFixedDomain(). - std::unordered_map constant_to_ref; + absl::flat_hash_map constant_to_ref; // Variable <-> constraint graph. // The vector list is sorted and contains unique elements. @@ -336,7 +334,7 @@ struct PresolveContext { // // TODO(user): Make this private? std::vector> constraint_to_vars; - std::vector> var_to_constraints; + std::vector> var_to_constraints; CpModelProto* working_model; CpModelProto* mapping_model; @@ -349,7 +347,7 @@ struct PresolveContext { bool enumerate_all_solutions = false; // Just used to display statistics on the presolve rules that were used. - std::unordered_map stats_by_rule_name; + absl::flat_hash_map stats_by_rule_name; // Temporary storage. std::vector tmp_literals; @@ -374,8 +372,8 @@ struct PresolveContext { // PresolveContext class. // ============================================================================= -MUST_USE_RESULT bool RemoveConstraint(ConstraintProto* ct, - PresolveContext* context) { +ABSL_MUST_USE_RESULT bool RemoveConstraint(ConstraintProto* ct, + PresolveContext* context) { ct->Clear(); return true; } @@ -475,8 +473,8 @@ bool PresolveBoolOr(ConstraintProto* ct, PresolveContext* context) { return changed; } -MUST_USE_RESULT bool MarkConstraintAsFalse(ConstraintProto* ct, - PresolveContext* context) { +ABSL_MUST_USE_RESULT bool MarkConstraintAsFalse(ConstraintProto* ct, + PresolveContext* context) { if (HasEnforcementLiteral(*ct)) { // Change the constraint to a bool_or. ct->mutable_bool_or()->clear_literals(); @@ -973,7 +971,7 @@ bool PresolveLinear(ConstraintProto* ct, PresolveContext* context) { // the size is reasonable. term_domains[i] = context->DomainOf(var).ContinuousMultiplicationBy(coeff); left_domains[i + 1] = left_domains[i].AdditionWith(term_domains[i]); - if (left_domains[i + 1].intervals().size() > kDomainComplexityLimit) { + if (left_domains[i + 1].NumIntervals() > kDomainComplexityLimit) { // We take a super-set, otherwise it will be too slow. // // TODO(user): We could be smarter in how we compute this if we allow for @@ -995,7 +993,7 @@ bool PresolveLinear(ConstraintProto* ct, PresolveContext* context) { // TODO(user): add an IntersectionIsEmpty() function. std::vector rhs_intervals; for (const ClosedInterval i : - restricted_rhs.UnionWith(implied_rhs.Complement()).intervals()) { + restricted_rhs.UnionWith(implied_rhs.Complement())) { if (!Domain::FromIntervals({i}) .IntersectionWith(restricted_rhs) .IsEmpty()) { @@ -1020,7 +1018,7 @@ bool PresolveLinear(ConstraintProto* ct, PresolveContext* context) { term_domains[num_vars] = rhs.Negation(); for (int i = num_vars - 1; i >= 0; --i) { right_domain = right_domain.AdditionWith(term_domains[i + 1]); - if (right_domain.intervals().size() > kDomainComplexityLimit) { + if (right_domain.NumIntervals() > kDomainComplexityLimit) { // We take a super-set, otherwise it will be too slow. right_domain = Domain(right_domain.Min(), right_domain.Max()); } @@ -1063,6 +1061,130 @@ bool PresolveLinear(ConstraintProto* ct, PresolveContext* context) { return var_constraint_graph_changed; } +// Fixes the variable at 'var_index' to 'fixed_value' in the constraint and +// returns the modified RHS Domain. +Domain FixVariableInLinearConstraint(const int var_index, + const int64 fixed_value, + ConstraintProto* ct, + PresolveContext* context) { + auto* arg = ct->mutable_linear(); + const int num_vars = arg->vars_size(); + CHECK_LT(var_index, num_vars); + const int ref = arg->vars(var_index); + CHECK(context->DomainOf(ref).Contains(fixed_value)); + const int64 coeff = arg->coeffs(var_index); + // Subtract the fixed term from the domain. + const Domain term_domain(coeff * fixed_value); + const Domain rhs_domain = ReadDomainFromProto(ct->linear()); + const Domain new_rhs_domain = rhs_domain.AdditionWith(term_domain.Negation()); + std::vector> constraint_entries; + // Copy coefficients of all variables except the fixed one. + for (int i = 0; i < num_vars; ++i) { + if (i == var_index) continue; + constraint_entries.push_back({arg->vars(i), arg->coeffs(i)}); + } + arg->clear_coeffs(); + arg->clear_vars(); + for (int i = 0; i < constraint_entries.size(); ++i) { + arg->add_vars(constraint_entries[i].first); + arg->add_coeffs(constraint_entries[i].second); + } + FillDomainInProto(new_rhs_domain, arg); + return new_rhs_domain; +} + +// Identify Boolean variable that makes the constraint always true when set to +// true or false. Moves such literal to the constraint enforcement literals +// list. +// +// This operation is similar to coefficient strengthening in the MIP world. +void ExtractEnforcementLiteralFromLinearConstraint(ConstraintProto* ct, + PresolveContext* context) { + Domain rhs_domain = ReadDomainFromProto(ct->linear()); + + if (rhs_domain.NumIntervals() != 1) return; + + // Return early if the constraint has both bounds. This is because in + // PresolveLinear() we relax the rhs domain, and after this operation, if + // we have two finite bounds, then there can be no literal that will make + // the constraint always true. + if (rhs_domain.Min() != kint64min && rhs_domain.Max() != kint64max) return; + + const LinearConstraintProto& arg = ct->linear(); + const int num_vars = arg.vars_size(); + int64 min_sum = 0; + int64 max_sum = 0; + for (int i = 0; i < num_vars; ++i) { + const int ref = arg.vars(i); + const int64 coeff = arg.coeffs(i); + const int64 term_a = coeff * context->MinOf(ref); + const int64 term_b = coeff * context->MaxOf(ref); + min_sum += std::min(term_a, term_b); + max_sum += std::max(term_a, term_b); + } + for (int i = 0; i < arg.vars_size(); ++i) { + // Only work with binary variables. + // + // TODO(user, krunalp): This could be generalized to non-binary variable + // but that would require introducing the encoding "literal <=> integer + // variable at is min/max" and using this literal in the enforcement list. + // It is thus a bit more involved, and might not be as useful. + const int ref = arg.vars(i); + if (context->MinOf(ref) != 0) continue; + if (context->MaxOf(ref) != 1) continue; + const int64 coeff = arg.coeffs(i); + if (rhs_domain.Max() != kint64max) { + DCHECK_EQ(rhs_domain.Min(), kint64min); + if (max_sum - std::abs(coeff) <= rhs_domain.Max()) { + if (coeff > 0) { + // Fix the variable to 1 in the constraint and add it as enforcement + // literal. + rhs_domain = FixVariableInLinearConstraint(i, 1, ct, context); + ct->add_enforcement_literal(ref); + // 'min_sum' remains unaffected. + max_sum -= coeff; + } else { + // Fix the variable to 0 in the constraint and add its negation as + // enforcement literal. + rhs_domain = FixVariableInLinearConstraint(i, 0, ct, context); + ct->add_enforcement_literal(NegatedRef(ref)); + // 'max_sum' remains unaffected. + min_sum -= coeff; + } + context->UpdateRuleStats( + "linear: extracted enforcement literal from constraint"); + --i; + continue; + } + } else { + DCHECK_NE(rhs_domain.Min(), kint64min); + DCHECK_EQ(rhs_domain.Max(), kint64max); + + if (min_sum + std::abs(coeff) >= rhs_domain.Min()) { + if (coeff > 0) { + // Fix the variable to 0 in the constraint and add its negation as + // enforcement literal. + rhs_domain = FixVariableInLinearConstraint(i, 0, ct, context); + ct->add_enforcement_literal(NegatedRef(ref)); + // 'min_sum' remains unaffected. + max_sum -= coeff; + } else { + // Fix the variable to 1 in the constraint and add it as enforcement + // literal. + rhs_domain = FixVariableInLinearConstraint(i, 1, ct, context); + ct->add_enforcement_literal(ref); + // 'max_sum' remains unaffected. + min_sum -= coeff; + } + context->UpdateRuleStats( + "linear: extracted enforcement literal from constraint"); + --i; + continue; + } + } + } +} + void ExtractAtMostOneFromLinear(ConstraintProto* ct, PresolveContext* context) { if (HasEnforcementLiteral(*ct)) return; const Domain domain = ReadDomainFromProto(ct->linear()); @@ -1174,7 +1296,7 @@ bool PresolveLinearOnBooleans(ConstraintProto* ct, PresolveContext* context) { } return PresolveBoolAnd(ct, context); } else if (min_sum + min_coeff >= domain.Min() && - domain.intervals().front().end == kint64max) { + domain.front().end == kint64max) { // At least one Boolean is true. context->UpdateRuleStats("linear: positive clause"); const auto copy = arg; @@ -1185,7 +1307,7 @@ bool PresolveLinearOnBooleans(ConstraintProto* ct, PresolveContext* context) { } return PresolveBoolOr(ct, context); } else if (max_sum - min_coeff <= domain.Max() && - domain.intervals().back().start == kint64min) { + domain.back().start == kint64min) { // At least one Boolean is false. context->UpdateRuleStats("linear: negative clause"); const auto copy = arg; @@ -1198,7 +1320,7 @@ bool PresolveLinearOnBooleans(ConstraintProto* ct, PresolveContext* context) { } else if (!HasEnforcementLiteral(*ct) && min_sum + max_coeff <= domain.Max() && min_sum + 2 * min_coeff > domain.Max() && - domain.intervals().back().start == kint64min) { + domain.back().start == kint64min) { // At most one Boolean is true. context->UpdateRuleStats("linear: positive at most one"); const auto copy = arg; @@ -1211,7 +1333,7 @@ bool PresolveLinearOnBooleans(ConstraintProto* ct, PresolveContext* context) { } else if (!HasEnforcementLiteral(*ct) && max_sum - max_coeff >= domain.Min() && max_sum - 2 * min_coeff < domain.Min() && - domain.intervals().front().end == kint64max) { + domain.front().end == kint64max) { // At most one Boolean is false. context->UpdateRuleStats("linear: negative at most one"); const auto copy = arg; @@ -1244,7 +1366,7 @@ bool PresolveLinearOnBooleans(ConstraintProto* ct, PresolveContext* context) { ConstraintProto* new_ct = context->working_model->add_constraints(); auto* new_arg = new_ct->mutable_bool_or(); if (HasEnforcementLiteral(*ct)) { - new_ct->add_enforcement_literal(ct->enforcement_literal(0)); + *new_ct->mutable_enforcement_literal() = ct->enforcement_literal(); } for (int i = 0; i < num_vars; ++i) { new_arg->add_literals(((mask >> i) & 1) ? NegatedRef(arg.vars(i)) @@ -1296,7 +1418,7 @@ bool PresolveElement(ConstraintProto* ct, PresolveContext* context) { int num_vars = 0; bool all_constants = true; - std::unordered_set constant_set; + absl::flat_hash_set constant_set; bool all_included_in_target_domain = true; bool reduced_index_domain = false; @@ -1307,8 +1429,7 @@ bool PresolveElement(ConstraintProto* ct, PresolveContext* context) { Domain infered_domain; const Domain target_domain = context->DomainOf(target_ref); - for (const ClosedInterval interval : - context->DomainOf(index_ref).intervals()) { + for (const ClosedInterval interval : context->DomainOf(index_ref)) { for (int value = interval.start; value <= interval.end; ++value) { CHECK_GE(value, 0); CHECK_LT(value, ct->element().vars_size()); @@ -1391,7 +1512,7 @@ bool PresolveTable(ConstraintProto* ct, PresolveContext* context) { std::vector tuple(num_vars); std::vector> new_tuples; new_tuples.reserve(num_tuples); - std::vector> new_domains(num_vars); + std::vector> new_domains(num_vars); for (int i = 0; i < num_tuples; ++i) { bool delete_row = false; std::string tmp; @@ -1808,7 +1929,7 @@ template void ExtractClauses(const ClauseContainer& container, CpModelProto* proto) { // We regroup the "implication" into bool_and to have a more consise proto and // also for nicer information about the number of binary clauses. - std::unordered_map ref_to_bool_and; + absl::flat_hash_map ref_to_bool_and; for (int i = 0; i < container.NumClauses(); ++i) { const std::vector& clause = container.Clause(i); if (clause.empty()) continue; @@ -1850,7 +1971,7 @@ void ExtractClauses(const ClauseContainer& container, CpModelProto* proto) { } } -void Probe(PresolveContext* context) { +void Probe(TimeLimit* global_time_limit, PresolveContext* context) { if (context->is_unsat) return; // Update the domain in the current CpModelProto. @@ -1868,14 +1989,16 @@ void Probe(PresolveContext* context) { // TODO(user): Maybe do not load slow to propagate constraints? for instance // we do not use any linear relaxation here. Model model; + model.GetOrCreate()->MergeWithGlobalTimeLimit(global_time_limit); auto* encoder = model.GetOrCreate(); encoder->DisableImplicationBetweenLiteral(); - ModelWithMapping m(model_proto, &model); - m.DetectOptionalVariables(model_proto); - m.ExtractEncoding(model_proto); + auto* mapping = model.GetOrCreate(); + mapping->CreateVariables(model_proto, false, &model); + mapping->DetectOptionalVariables(model_proto, &model); + mapping->ExtractEncoding(model_proto, &model); for (const ConstraintProto& ct : model_proto.constraints()) { - if (m.ConstraintIsAlreadyLoaded(&ct)) continue; - CHECK(LoadConstraint(ct, &m)); + if (mapping->ConstraintIsAlreadyLoaded(&ct)) continue; + CHECK(LoadConstraint(ct, &model)); } encoder->AddAllImplicationsBetweenAssociatedLiterals(); auto* sat_solver = model.GetOrCreate(); @@ -1895,7 +2018,7 @@ void Probe(PresolveContext* context) { // Update the presolve context with fixed Boolean variables. for (int i = 0; i < sat_solver->LiteralTrail().Index(); ++i) { const Literal l = sat_solver->LiteralTrail()[i]; - const int var = m.GetProtoVariableFromBooleanVariable(l.Variable()); + const int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable()); if (var >= 0) { const int ref = l.IsPositive() ? var : NegatedRef(var); context->SetLiteralToTrue(ref); @@ -1907,18 +2030,19 @@ void Probe(PresolveContext* context) { for (int var = 0; var < num_variables; ++var) { // Restrict IntegerVariable domain. // Note that Boolean are already dealt with above. - if (!m.IsBoolean(var)) { + if (!mapping->IsBoolean(var)) { const Domain new_domain = - integer_trail->InitialVariableDomain(m.Integer(var)); + integer_trail->InitialVariableDomain(mapping->Integer(var)); context->IntersectDomainWith(var, new_domain); continue; } // Add Boolean equivalence relations. - const Literal l = m.Literal(var); + const Literal l = mapping->Literal(var); const Literal r = implication_graph->RepresentativeOf(l); if (r != l) { - const int r_var = m.GetProtoVariableFromBooleanVariable(r.Variable()); + const int r_var = + mapping->GetProtoVariableFromBooleanVariable(r.Variable()); CHECK_GE(r_var, 0); context->AddBooleanEqualityRelation( var, r.IsPositive() ? r_var : NegatedRef(r_var)); @@ -2105,7 +2229,7 @@ void ExpandObjective(PresolveContext* context) { // constraints for each variables. const int num_variables = context->working_model->variables_size(); const int num_constraints = context->working_model->constraints_size(); - std::unordered_set relevant_constraints; + absl::flat_hash_set relevant_constraints; std::vector var_to_num_relevant_constraints(num_variables, 0); for (int ct_index = 0; ct_index < num_constraints; ++ct_index) { const ConstraintProto& ct = context->working_model->constraints(ct_index); @@ -2126,7 +2250,7 @@ void ExpandObjective(PresolveContext* context) { // We currently never expand a variable more than once. int num_expansions = 0; - std::unordered_set processed_vars; + absl::flat_hash_set processed_vars; while (!relevant_constraints.empty()) { // Find a not yet expanded var. int objective_var = -1; @@ -2372,14 +2496,22 @@ bool PresolveOneConstraint(int c, PresolveContext* context) { case ConstraintProto::ConstraintCase::kIntDiv: return PresolveIntDiv(ct, context); case ConstraintProto::ConstraintCase::kLinear: { - bool changed = PresolveLinear(ct, context); - if (changed) context->UpdateConstraintVariableUsage(c); + if (PresolveLinear(ct, context)) { + context->UpdateConstraintVariableUsage(c); + } if (ct->constraint_case() == ConstraintProto::ConstraintCase::kLinear) { - // Tricky: This is needed in case the variables have been mapped to - // their representative by PresolveLinear() above. - changed |= PresolveLinearOnBooleans(ct, context); + const int old_num_enforcement_literals = ct->enforcement_literal_size(); + ExtractEnforcementLiteralFromLinearConstraint(ct, context); + if (ct->enforcement_literal_size() > old_num_enforcement_literals) { + PresolveLinear(ct, context); + context->UpdateConstraintVariableUsage(c); + } } - return changed; + + if (ct->constraint_case() == ConstraintProto::ConstraintCase::kLinear) { + return PresolveLinearOnBooleans(ct, context); + } + return false; } case ConstraintProto::ConstraintCase::kInterval: return PresolveInterval(ct, context); @@ -2406,7 +2538,7 @@ void PresolveToFixPoint(PresolveContext* context) { // This is used for constraint having unique variables in them (i.e. not // appearing anywhere else) to not call the presolve more than once for this // reason. - std::unordered_set> var_constraint_pair_already_called; + absl::flat_hash_set> var_constraint_pair_already_called; // The queue of "active" constraints, initialized to all of them. std::vector in_queue(context->working_model->constraints_size(), true); @@ -2605,10 +2737,8 @@ void PresolveCpModel(const PresolveOptions& options, PresolveContext context; context.working_model = presolved_model; context.mapping_model = mapping_model; - if (options.parameters != nullptr) { - context.enumerate_all_solutions = - options.parameters->enumerate_all_solutions(); - } + context.enumerate_all_solutions = + options.parameters.enumerate_all_solutions(); // We copy the search strategy to the mapping_model. for (const auto& decision_strategy : presolved_model->search_strategy()) { @@ -2639,9 +2769,11 @@ void PresolveCpModel(const PresolveOptions& options, // Runs the probing. // TODO(user): do that and the pure-SAT part below more than once. - // TODO(user): Expose parameters to control this. - Probe(&context); - PresolveToFixPoint(&context); + if (options.parameters.cp_model_probing_level() > 0) { + Probe(options.time_limit, &context); + PresolveToFixPoint(&context); + } + RemoveUnusedEquivalentVariables(&context); // Run SAT specific presolve on the pure-SAT part of the problem. @@ -2727,7 +2859,7 @@ void PresolveCpModel(const PresolveOptions& options, // will result in the same domain reduction strategy. Moreover, if the // variable order is not CHOOSE_FIRST, then we also encode the associated // affine transformation in order to preserve the order. - std::unordered_set used_variables; + absl::flat_hash_set used_variables; for (DecisionStrategyProto& strategy : *presolved_model->mutable_search_strategy()) { DecisionStrategyProto copy = strategy; @@ -2792,7 +2924,7 @@ void PresolveCpModel(const PresolveOptions& options, ApplyVariableMapping(mapping, presolved_model); // Stats and checks. - if (VLOG_IS_ON(1)) { + if (options.log_info) { LOG(INFO) << "- " << context.affine_relations.NumRelations() << " affine relations were detected."; LOG(INFO) << "- " << context.var_equiv_relations.NumRelations() diff --git a/ortools/sat/cp_model_presolve.h b/ortools/sat/cp_model_presolve.h index cdfb952fcee..54c9c3fb1a8 100644 --- a/ortools/sat/cp_model_presolve.h +++ b/ortools/sat/cp_model_presolve.h @@ -25,7 +25,7 @@ namespace sat { struct PresolveOptions { bool log_info = true; - SatParameters* parameters = nullptr; + SatParameters parameters; TimeLimit* time_limit = nullptr; }; diff --git a/ortools/sat/cp_model_search.cc b/ortools/sat/cp_model_search.cc index 3b246c169eb..67627ad609b 100644 --- a/ortools/sat/cp_model_search.cc +++ b/ortools/sat/cp_model_search.cc @@ -14,10 +14,10 @@ #include "ortools/sat/cp_model_search.h" #include -#include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_format.h" #include "ortools/base/cleanup.h" -#include "ortools/base/stringprintf.h" #include "ortools/sat/cp_model_utils.h" #include "ortools/sat/util.h" @@ -41,7 +41,7 @@ struct VarValue { }; const std::function ConstructSearchStrategyInternal( - const std::unordered_map>& + const absl::flat_hash_map>& var_to_coeff_offset_pair, const std::vector& strategies, Model* model) { IntegerEncoder* const integer_encoder = model->GetOrCreate(); @@ -190,7 +190,7 @@ std::function ConstructSearchStrategy( } std::vector strategies; - std::unordered_map> var_to_coeff_offset_pair; + absl::flat_hash_map> var_to_coeff_offset_pair; for (const DecisionStrategyProto& proto : cp_model_proto.search_strategy()) { strategies.push_back(Strategy()); Strategy& strategy = strategies.back(); @@ -242,6 +242,10 @@ std::function InstrumentSearchStrategy( const LiteralIndex decision = instrumented_strategy(); if (decision == kNoLiteralIndex) return decision; + for (const IntegerLiteral i_lit : + model->Get()->GetIntegerLiterals(Literal(decision))) { + LOG(INFO) << "decision " << i_lit; + } const int level = model->Get()->CurrentDecisionLevel(); std::string to_display = absl::StrCat("Diff since last call, level=", level, "\n"); @@ -252,9 +256,11 @@ std::function InstrumentSearchStrategy( integer_trail->LowerBound(var).value(), integer_trail->UpperBound(var).value()); if (new_domain != old_domains[ref]) { - old_domains[ref] = new_domain; absl::StrAppend(&to_display, cp_model_proto.variables(ref).name(), " [", - new_domain.first, ",", new_domain.second, "]\n"); + old_domains[ref].first, ",", old_domains[ref].second, + "] -> [", new_domain.first, ",", new_domain.second, + "]\n"); + old_domains[ref] = new_domain; } } LOG(INFO) << to_display; diff --git a/ortools/sat/cp_model_solver.cc b/ortools/sat/cp_model_solver.cc index 1140997c856..086703fd678 100644 --- a/ortools/sat/cp_model_solver.cc +++ b/ortools/sat/cp_model_solver.cc @@ -14,34 +14,35 @@ #include "ortools/sat/cp_model_solver.h" #include +#include #include #include #include #include #include -#include -#include -#include #include #include -#include "ortools/base/commandlineflags.h" -#include "ortools/base/logging.h" -#include "ortools/base/mutex.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/timer.h" #if !defined(__PORTABLE_PLATFORM__) +#include "absl/synchronization/notification.h" #include "google/protobuf/text_format.h" -#include "ortools/base/notification.h" +#include "ortools/base/file.h" #endif // __PORTABLE_PLATFORM__ -#include "ortools/base/cleanup.h" + +#include "ortools/base/integral_types.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "absl/synchronization/mutex.h" +#include "ortools/base/commandlineflags.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" -#include "ortools/base/iterator_adaptors.h" -#include "ortools/base/join.h" +#include "ortools/base/logging.h" #include "ortools/base/map_util.h" -#include "ortools/base/memory.h" -#include "ortools/base/stl_util.h" +#include "ortools/base/status.h" +#include "ortools/base/threadpool.h" +#include "ortools/base/timer.h" #include "ortools/graph/connectivity.h" #include "ortools/port/proto_utils.h" #include "ortools/sat/circuit.h" @@ -103,16 +104,17 @@ namespace { // fixed point. // ============================================================================= -// This class is designed to be used over a ModelWithMapping, it will ask the -// underlying Model to fully encode IntegerVariables of the model using -// constraint processors PropagateConstraintXXX(), until no such processor wants -// to fully encode a variable. The workflow is to call PropagateFullEncoding() -// on a set of constraints, then ComputeFixedPoint() to launch the fixed point -// computation. +// This class is will ask the underlying Model to fully encode IntegerVariables +// of the model using constraint processors PropagateConstraintXXX(), until no +// such processor wants to fully encode a variable. The workflow is to call +// PropagateFullEncoding() on a set of constraints, then ComputeFixedPoint() to +// launch the fixed point computation. class FullEncodingFixedPointComputer { public: - explicit FullEncodingFixedPointComputer(ModelWithMapping* model) - : model_(model), integer_encoder_(model->GetOrCreate()) {} + explicit FullEncodingFixedPointComputer(Model* model) + : model_(model), + mapping_(model->GetOrCreate()), + integer_encoder_(model->GetOrCreate()) {} // We only add to the propagation queue variable that are fully encoded. // Note that if a variable was already added once, we never add it again. @@ -181,14 +183,14 @@ class FullEncodingFixedPointComputer { // Note that we always consider a fixed variable to be fully encoded here. const bool IsFullyEncoded(int v) { - const IntegerVariable variable = model_->Integer(v); + const IntegerVariable variable = mapping_->Integer(v); return model_->Get(IsFixed(variable)) || integer_encoder_->VariableIsFullyEncoded(variable); } void FullyEncode(int v) { v = PositiveRef(v); - const IntegerVariable variable = model_->Integer(v); + const IntegerVariable variable = mapping_->Integer(v); if (!model_->Get(IsFixed(variable))) { model_->Add(FullyEncodeVariable(variable)); } @@ -201,15 +203,16 @@ class FullEncodingFixedPointComputer { bool PropagateInverse(const ConstraintProto* ct); bool PropagateLinear(const ConstraintProto* ct); - ModelWithMapping* model_; + Model* model_; + CpModelMapping* mapping_; IntegerEncoder* integer_encoder_; std::vector variable_was_added_in_to_propagate_; std::vector variables_to_propagate_; std::vector> variable_watchers_; - std::unordered_set constraint_is_finished_; - std::unordered_set constraint_is_registered_; + absl::flat_hash_set constraint_is_finished_; + absl::flat_hash_set constraint_is_registered_; }; bool FullEncodingFixedPointComputer::PropagateElement( @@ -282,7 +285,7 @@ bool FullEncodingFixedPointComputer::PropagateLinear( // If some domain is too large, abort; if (!gtl::ContainsKey(constraint_is_registered_, ct)) { for (const int v : ct->linear().vars()) { - const IntegerVariable var = model_->Integer(v); + const IntegerVariable var = mapping_->Integer(v); IntegerTrail* integer_trail = model_->GetOrCreate(); const IntegerValue lb = integer_trail->LowerBound(var); const IntegerValue ub = integer_trail->UpperBound(var); @@ -400,11 +403,11 @@ std::string CpModelStats(const CpModelProto& model_proto) { " in objective)") : ""; absl::StrAppend(&result, "#Variables: ", model_proto.variables_size(), - objective_string.c_str(), "\n"); + objective_string, "\n"); if (num_vars_per_domains.size() < 50) { for (const auto& entry : num_vars_per_domains) { const std::string temp = absl::StrCat(" - ", entry.second, " in ", - entry.first.ToString().c_str(), "\n"); + entry.first.ToString(), "\n"); absl::StrAppend(&result, Summarize(temp)); } } else { @@ -414,8 +417,8 @@ std::string CpModelStats(const CpModelProto& model_proto) { for (const auto& entry : num_vars_per_domains) { min = std::min(min, entry.first.Min()); max = std::max(max, entry.first.Max()); - max_complexity = std::max( - max_complexity, static_cast(entry.first.intervals().size())); + max_complexity = std::max(max_complexity, + static_cast(entry.first.NumIntervals())); } absl::StrAppend(&result, " - ", num_vars_per_domains.size(), " different domains in [", min, ",", max, @@ -492,26 +495,57 @@ double ScaleObjectiveValue(const CpObjectiveProto& proto, int64 value) { return proto.scaling_factor() * result; } -void FillSolutionInResponse(const CpModelProto& model_proto, - const ModelWithMapping& m, +void FillSolutionInResponse(const CpModelProto& model_proto, const Model& model, IntegerVariable objective_var, CpSolverResponse* response) { - const std::vector solution = m.ExtractFullAssignment(); response->set_status(CpSolverStatus::FEASIBLE); response->clear_solution(); response->clear_solution_lower_bounds(); response->clear_solution_upper_bounds(); + + auto* mapping = model.Get(); + auto* trail = model.Get(); + auto* integer_trail = model.Get(); + + std::vector solution; + for (int i = 0; i < model_proto.variables_size(); ++i) { + if (mapping->IsInteger(i)) { + const IntegerVariable var = mapping->Integer(i); + if (integer_trail->IsCurrentlyIgnored(var)) { + // This variable is "ignored" so it may not be fixed, simply use + // the current lower bound. Any value in its domain should lead to + // a feasible solution. + solution.push_back(model.Get(LowerBound(var))); + } else { + if (model.Get(LowerBound(var)) != model.Get(UpperBound(var))) { + solution.clear(); + break; + } + solution.push_back(model.Get(Value(var))); + } + } else { + DCHECK(mapping->IsBoolean(i)); + const Literal literal = mapping->Literal(i); + if (trail->Assignment().LiteralIsAssigned(literal)) { + solution.push_back(model.Get(Value(literal))); + } else { + solution.clear(); + break; + } + } + } + if (!solution.empty()) { DCHECK(SolutionIsFeasible(model_proto, solution)); for (const int64 value : solution) response->add_solution(value); } else { // Not all variables are fixed. // We fill instead the lb/ub of each variables. - const auto& assignment = m.model()->Get()->Assignment(); + const auto& assignment = trail->Assignment(); for (int i = 0; i < model_proto.variables_size(); ++i) { - if (m.IsBoolean(i)) { - if (assignment.VariableIsAssigned(m.Literal(i).Variable())) { - const int value = m.Get(Value(m.Literal(i))); + if (mapping->IsBoolean(i)) { + if (assignment.VariableIsAssigned(mapping->Literal(i).Variable())) { + const int value = model.Get(Value(mapping->Literal(i))); response->add_solution_lower_bounds(value); response->add_solution_upper_bounds(value); } else { @@ -519,8 +553,10 @@ void FillSolutionInResponse(const CpModelProto& model_proto, response->add_solution_upper_bounds(1); } } else { - response->add_solution_lower_bounds(m.Get(LowerBound(m.Integer(i)))); - response->add_solution_upper_bounds(m.Get(UpperBound(m.Integer(i)))); + response->add_solution_lower_bounds( + model.Get(LowerBound(mapping->Integer(i)))); + response->add_solution_upper_bounds( + model.Get(UpperBound(mapping->Integer(i)))); } } } @@ -530,12 +566,12 @@ void FillSolutionInResponse(const CpModelProto& model_proto, const CpObjectiveProto& obj = model_proto.objective(); int64 objective_value = 0; for (int i = 0; i < model_proto.objective().vars_size(); ++i) { - objective_value += model_proto.objective().coeffs(i) * - m.model()->Get(LowerBound( - m.Integer(model_proto.objective().vars(i)))); + objective_value += + model_proto.objective().coeffs(i) * + model.Get( + LowerBound(mapping->Integer(model_proto.objective().vars(i)))); } response->set_objective_value(ScaleObjectiveValue(obj, objective_value)); - const IntegerTrail* integer_trail = m.model()->Get(); response->set_best_objective_bound(ScaleObjectiveValue( obj, integer_trail->LevelZeroBound(objective_var).value())); } else { @@ -601,40 +637,44 @@ IntegerVariable GetOrCreateVariableGreaterOrEqualToSumOf( // // TODO(user): In full generality, we could encode all the constraint as an LP. void TryToLinearizeConstraint(const CpModelProto& model_proto, - const ConstraintProto& ct, ModelWithMapping* m, + const ConstraintProto& ct, Model* m, int linearization_level, LinearRelaxation* relaxation) { + auto* mapping = m->GetOrCreate(); if (ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) { if (linearization_level < 2) return; - LinearConstraintBuilder lc(m->model(), IntegerValue(1), kMaxIntegerValue); + LinearConstraintBuilder lc(m, IntegerValue(1), kMaxIntegerValue); for (const int enforcement_ref : ct.enforcement_literal()) { - CHECK(lc.AddLiteralTerm(m->Literal(NegatedRef(enforcement_ref)), + CHECK(lc.AddLiteralTerm(mapping->Literal(NegatedRef(enforcement_ref)), IntegerValue(1))); } for (const int ref : ct.bool_or().literals()) { - CHECK(lc.AddLiteralTerm(m->Literal(ref), IntegerValue(1))); + CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1))); } relaxation->linear_constraints.push_back(lc.Build()); } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolAnd) { + // TODO(user): These constraints can be many, and if they are not regrouped + // in big at most ones, then they should probably only added lazily as cuts. + // Regroup this with future clique-cut separation logic. if (linearization_level < 2) return; if (!HasEnforcementLiteral(ct)) return; if (ct.enforcement_literal().size() == 1) { - const Literal enforcement = m->Literal(ct.enforcement_literal(0)); + const Literal enforcement = mapping->Literal(ct.enforcement_literal(0)); for (const int ref : ct.bool_and().literals()) { relaxation->at_most_ones.push_back( - {enforcement, m->Literal(ref).Negated()}); + {enforcement, mapping->Literal(ref).Negated()}); } return; } for (const int ref : ct.bool_and().literals()) { // Same as the clause linearization above. - LinearConstraintBuilder lc(m->model(), IntegerValue(1), kMaxIntegerValue); + LinearConstraintBuilder lc(m, IntegerValue(1), kMaxIntegerValue); for (const int enforcement_ref : ct.enforcement_literal()) { - CHECK(lc.AddLiteralTerm(m->Literal(NegatedRef(enforcement_ref)), + CHECK(lc.AddLiteralTerm(mapping->Literal(NegatedRef(enforcement_ref)), IntegerValue(1))); } - CHECK(lc.AddLiteralTerm(m->Literal(ref), IntegerValue(1))); + CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1))); relaxation->linear_constraints.push_back(lc.Build()); } } else if (ct.constraint_case() == @@ -642,7 +682,7 @@ void TryToLinearizeConstraint(const CpModelProto& model_proto, if (HasEnforcementLiteral(ct)) return; std::vector at_most_one; for (const int ref : ct.at_most_one().literals()) { - at_most_one.push_back(m->Literal(ref)); + at_most_one.push_back(mapping->Literal(ref)); } relaxation->at_most_ones.push_back(at_most_one); } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kIntMax) { @@ -652,9 +692,9 @@ void TryToLinearizeConstraint(const CpModelProto& model_proto, // This deal with the corner case X = max(X, Y, Z, ..) ! // Note that this can be presolved into X >= Y, X >= Z, ... if (target == var) continue; - LinearConstraintBuilder lc(m->model(), kMinIntegerValue, IntegerValue(0)); - lc.AddTerm(m->Integer(var), IntegerValue(1)); - lc.AddTerm(m->Integer(target), IntegerValue(-1)); + LinearConstraintBuilder lc(m, kMinIntegerValue, IntegerValue(0)); + lc.AddTerm(mapping->Integer(var), IntegerValue(1)); + lc.AddTerm(mapping->Integer(target), IntegerValue(-1)); relaxation->linear_constraints.push_back(lc.Build()); } } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kIntMin) { @@ -662,9 +702,9 @@ void TryToLinearizeConstraint(const CpModelProto& model_proto, const int target = ct.int_min().target(); for (const int var : ct.int_min().vars()) { if (target == var) continue; - LinearConstraintBuilder lc(m->model(), kMinIntegerValue, IntegerValue(0)); - lc.AddTerm(m->Integer(target), IntegerValue(1)); - lc.AddTerm(m->Integer(var), IntegerValue(-1)); + LinearConstraintBuilder lc(m, kMinIntegerValue, IntegerValue(0)); + lc.AddTerm(mapping->Integer(target), IntegerValue(1)); + lc.AddTerm(mapping->Integer(var), IntegerValue(-1)); relaxation->linear_constraints.push_back(lc.Build()); } } else if (ct.constraint_case() == @@ -678,80 +718,13 @@ void TryToLinearizeConstraint(const CpModelProto& model_proto, // we should probably also add x >= -y, but then this do not happen in // our test set. if (size == 2 && ct.int_prod().vars(0) == ct.int_prod().vars(1)) { - LinearConstraintBuilder lc(m->model(), kMinIntegerValue, IntegerValue(0)); - lc.AddTerm(m->Integer(ct.int_prod().vars(0)), IntegerValue(1)); - lc.AddTerm(m->Integer(target), IntegerValue(-1)); + LinearConstraintBuilder lc(m, kMinIntegerValue, IntegerValue(0)); + lc.AddTerm(mapping->Integer(ct.int_prod().vars(0)), IntegerValue(1)); + lc.AddTerm(mapping->Integer(target), IntegerValue(-1)); relaxation->linear_constraints.push_back(lc.Build()); } } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) { - // Note that we ignore the holes in the domain. - // - // TODO(user): In LoadLinearConstraint() we already created intermediate - // Booleans for each disjoint interval, we should reuse them here if - // possible. - // - // TODO(user): process the "at most one" part of a == 1 separately? - const IntegerValue min = IntegerValue(ct.linear().domain(0)); - const IntegerValue max = - IntegerValue(ct.linear().domain(ct.linear().domain_size() - 1)); - if (min == kint64min && max == kint64max) return; - - if (!HasEnforcementLiteral(ct)) { - LinearConstraintBuilder lc(m->model(), min, max); - for (int i = 0; i < ct.linear().vars_size(); i++) { - const int ref = ct.linear().vars(i); - const int64 coeff = ct.linear().coeffs(i); - lc.AddTerm(m->Integer(ref), IntegerValue(coeff)); - } - relaxation->linear_constraints.push_back(lc.Build()); - return; - } - - // Reified version. - // TODO(user): support any number of enforcement literal. - if (ct.enforcement_literal().size() != 1) return; - if (linearization_level < 3) return; - - // Compute the implied bounds on the linear expression. - IntegerValue implied_lb(0); - IntegerValue implied_ub(0); - for (int i = 0; i < ct.linear().vars_size(); i++) { - int ref = ct.linear().vars(i); - IntegerValue coeff(ct.linear().coeffs(i)); - if (!RefIsPositive(ref)) { - ref = PositiveRef(ref); - coeff = -coeff; - } - const IntegerVariableProto& p = model_proto.variables(ref); - if (coeff > 0.0) { - implied_lb += coeff * IntegerValue(p.domain(0)); - implied_ub += coeff * IntegerValue(p.domain(p.domain_size() - 1)); - } else { - implied_lb += coeff * IntegerValue(p.domain(p.domain_size() - 1)); - implied_ub += coeff * IntegerValue(p.domain(0)); - } - } - const int e = ct.enforcement_literal(0); - if (min != kint64min) { - // (e => terms >= min) <=> terms >= implied_lb + e * (min - implied_lb); - LinearConstraintBuilder lc(m->model(), implied_lb, kMaxIntegerValue); - for (int i = 0; i < ct.linear().vars_size(); i++) { - const int ref = ct.linear().vars(i); - lc.AddTerm(m->Integer(ref), IntegerValue(ct.linear().coeffs(i))); - } - CHECK(lc.AddLiteralTerm(m->Literal(e), implied_lb - min)); - relaxation->linear_constraints.push_back(lc.Build()); - } - if (max != kint64max) { - // (e => terms <= max) <=> terms <= implied_ub + e * (max - implied_ub) - LinearConstraintBuilder lc(m->model(), kMinIntegerValue, implied_ub); - for (int i = 0; i < ct.linear().vars_size(); i++) { - const int ref = ct.linear().vars(i); - lc.AddTerm(m->Integer(ref), IntegerValue(ct.linear().coeffs(i))); - } - CHECK(lc.AddLiteralTerm(m->Literal(e), implied_ub - max)); - relaxation->linear_constraints.push_back(lc.Build()); - } + AppendLinearConstraintRelaxation(ct, linearization_level, *m, relaxation); } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit) { if (HasEnforcementLiteral(ct)) return; @@ -764,7 +737,7 @@ void TryToLinearizeConstraint(const CpModelProto& model_proto, std::map> incoming_arc_constraints; std::map> outgoing_arc_constraints; for (int i = 0; i < num_arcs; i++) { - const Literal arc = m->Literal(ct.circuit().literals(i)); + const Literal arc = mapping->Literal(ct.circuit().literals(i)); const int tail = ct.circuit().tails(i); const int head = ct.circuit().heads(i); @@ -778,7 +751,7 @@ void TryToLinearizeConstraint(const CpModelProto& model_proto, for (const auto& entry : *node_map) { const std::vector& exactly_one = entry.second; if (exactly_one.size() > 1) { - LinearConstraintBuilder at_least_one_lc(m->model(), IntegerValue(1), + LinearConstraintBuilder at_least_one_lc(m, IntegerValue(1), kMaxIntegerValue); for (const Literal l : exactly_one) { CHECK(at_least_one_lc.AddLiteralTerm(l, IntegerValue(1))); @@ -792,14 +765,14 @@ void TryToLinearizeConstraint(const CpModelProto& model_proto, } } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kElement) { - const IntegerVariable index = m->Integer(ct.element().index()); - const IntegerVariable target = m->Integer(ct.element().target()); - const std::vector vars = m->Integers(ct.element().vars()); + const IntegerVariable index = mapping->Integer(ct.element().index()); + const IntegerVariable target = mapping->Integer(ct.element().target()); + const std::vector vars = + mapping->Integers(ct.element().vars()); // We only relax the case where all the vars are constant. // target = sum (index == i) * fixed_vars[i]. - LinearConstraintBuilder constraint(m->model(), IntegerValue(0), - IntegerValue(0)); + LinearConstraintBuilder constraint(m, IntegerValue(0), IntegerValue(0)); constraint.AddTerm(target, IntegerValue(-1)); IntegerTrail* integer_trail = m->GetOrCreate(); for (const auto literal_value : m->Add(FullyEncodeVariable((index)))) { @@ -817,14 +790,15 @@ void TryToLinearizeConstraint(const CpModelProto& model_proto, } void TryToAddCutGenerators(const CpModelProto& model_proto, - const ConstraintProto& ct, ModelWithMapping* m, + const ConstraintProto& ct, Model* m, LinearRelaxation* relaxation) { + auto* mapping = m->GetOrCreate(); if (ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit) { std::vector tails(ct.circuit().tails().begin(), ct.circuit().tails().end()); std::vector heads(ct.circuit().heads().begin(), ct.circuit().heads().end()); - std::vector literals = m->Literals(ct.circuit().literals()); + std::vector literals = mapping->Literals(ct.circuit().literals()); const int num_nodes = ReindexArcs(&tails, &heads, &literals); std::vector vars; @@ -845,7 +819,7 @@ void TryToAddCutGenerators(const CpModelProto& model_proto, auto* encoder = m->GetOrCreate(); for (int i = 0; i < ct.routes().tails_size(); ++i) { const IntegerVariable var = - encoder->GetLiteralView(m->Literal(ct.routes().literals(i))); + encoder->GetLiteralView(mapping->Literal(ct.routes().literals(i))); if (var == kNoIntegerVariable) return; vars.push_back(var); @@ -871,21 +845,22 @@ void TryToAddCutGenerators(const CpModelProto& model_proto, // Adds one LinearProgrammingConstraint per connected component of the model. IntegerVariable AddLPConstraints(const CpModelProto& model_proto, - int linearization_level, ModelWithMapping* m) { + int linearization_level, Model* m) { LinearRelaxation relaxation; // Linearize the constraints. IndexReferences refs; + auto* mapping = m->GetOrCreate(); auto* encoder = m->GetOrCreate(); for (const auto& ct : model_proto.constraints()) { // We linearize fully/partially encoded variable differently, so we just // skip all these constraint that corresponds to these encoding. - if (m->ConstraintIsAlreadyLoaded(&ct)) continue; + if (mapping->ConstraintIsAlreadyLoaded(&ct)) continue; // Make sure the literal from a circuit constraint always have a view. if (ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit) { for (const int ref : ct.circuit().literals()) { - m->Add(NewIntegerVariableFromLiteral(m->Literal(ref))); + m->Add(NewIntegerVariableFromLiteral(mapping->Literal(ref))); } } @@ -900,7 +875,7 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, AddReferencesUsedByConstraint(ct, &refs); bool ok = true; for (const int literal_ref : refs.literals) { - const Literal literal = m->Literal(literal_ref); + const Literal literal = mapping->Literal(literal_ref); if (encoder->GetLiteralView(literal) == kNoIntegerVariable && encoder->GetLiteralView(literal.Negated()) == kNoIntegerVariable) { ok = false; @@ -923,27 +898,26 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, int num_full_encoding_relaxations = 0; int num_partial_encoding_relaxations = 0; for (int i = 0; i < model_proto.variables_size(); ++i) { - if (m->IsBoolean(i)) continue; + if (mapping->IsBoolean(i)) continue; - const IntegerVariable var = m->Integer(i); + const IntegerVariable var = mapping->Integer(i); if (m->Get(IsFixed(var))) continue; // TODO(user): This different encoding for the partial variable might be // better (less LP constraints), but we do need more investigation to // decide. if (/* DISABLES CODE */ (false)) { - AppendPartialEncodingRelaxation(var, *(m->model()), &relaxation); + AppendPartialEncodingRelaxation(var, *m, &relaxation); continue; } if (encoder->VariableIsFullyEncoded(var)) { - if (AppendFullEncodingRelaxation(var, *(m->model()), &relaxation)) { + if (AppendFullEncodingRelaxation(var, *m, &relaxation)) { ++num_full_encoding_relaxations; } } else { const int old = relaxation.linear_constraints.size(); - AppendPartialGreaterThanEncodingRelaxation(var, *(m->model()), - &relaxation); + AppendPartialGreaterThanEncodingRelaxation(var, *m, &relaxation); if (relaxation.linear_constraints.size() > old) { ++num_partial_encoding_relaxations; } @@ -956,11 +930,11 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, &relaxation.at_most_ones); for (const std::vector& at_most_one : relaxation.at_most_ones) { if (at_most_one.empty()) continue; - LinearConstraintBuilder lc(m->model(), kMinIntegerValue, IntegerValue(1)); + LinearConstraintBuilder lc(m, kMinIntegerValue, IntegerValue(1)); for (const Literal literal : at_most_one) { // Note that it is okay to simply ignore the literal if it has no // integer view. - const bool unused = + const bool unused ABSL_ATTRIBUTE_UNUSED = lc.AddLiteralTerm(literal, IntegerValue(1)); } relaxation.linear_constraints.push_back(lc.Build()); @@ -1033,7 +1007,8 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, // us on one of its components. This is critical on the zephyrus problems for // instance. for (int i = 0; i < model_proto.objective().coeffs_size(); ++i) { - const IntegerVariable var = m->Integer(model_proto.objective().vars(i)); + const IntegerVariable var = + mapping->Integer(model_proto.objective().vars(i)); const int id = components.GetClassRepresentative(get_var_index(var)); components_to_size[id] += 1; } @@ -1045,7 +1020,7 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, const int id = components.GetClassRepresentative(get_constraint_index(i)); if (components_to_size[id] <= 1) continue; if (!gtl::ContainsKey(representative_to_lp_constraint, id)) { - auto* lp = m->model()->Create(); + auto* lp = m->Create(); representative_to_lp_constraint[id] = lp; lp_constraints.push_back(lp); } @@ -1067,7 +1042,7 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, const int id = components.GetClassRepresentative(get_cut_generator_index(i)); if (!gtl::ContainsKey(representative_to_lp_constraint, id)) { - auto* lp = m->model()->Create(); + auto* lp = m->Create(); representative_to_lp_constraint[id] = lp; lp_constraints.push_back(lp); } @@ -1084,7 +1059,8 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, // First pass: set objective coefficients on the lp constraints, and store // the cp terms in one vector per component. for (int i = 0; i < model_proto.objective().coeffs_size(); ++i) { - const IntegerVariable var = m->Integer(model_proto.objective().vars(i)); + const IntegerVariable var = + mapping->Integer(model_proto.objective().vars(i)); const int64 coeff = model_proto.objective().coeffs(i); const int id = components.GetClassRepresentative(get_var_index(var)); if (gtl::ContainsKey(representative_to_lp_constraint, id)) { @@ -1103,7 +1079,7 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, gtl::FindOrDie(representative_to_lp_constraint, id); const std::vector>& terms = it.second; const IntegerVariable sub_obj_var = - GetOrCreateVariableGreaterOrEqualToSumOf(terms, m->model()); + GetOrCreateVariableGreaterOrEqualToSumOf(terms, m); top_level_cp_terms.push_back(std::make_pair(sub_obj_var, 1)); lp->SetMainObjectiveVariable(sub_obj_var); num_components_containing_objective++; @@ -1111,12 +1087,12 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, } const IntegerVariable main_objective_var = - GetOrCreateVariableGreaterOrEqualToSumOf(top_level_cp_terms, m->model()); + GetOrCreateVariableGreaterOrEqualToSumOf(top_level_cp_terms, m); // Register LP constraints. Note that this needs to be done after all the // constraints have been added. for (auto* lp_constraint : lp_constraints) { - lp_constraint->RegisterWith(m->model()); + lp_constraint->RegisterWith(m); VLOG(2) << "LP constraint: " << lp_constraint->DimensionString() << "."; } @@ -1127,7 +1103,7 @@ IntegerVariable AddLPConstraints(const CpModelProto& model_proto, } void ExtractLinearObjective(const CpModelProto& model_proto, - ModelWithMapping* m, + const CpModelMapping& mapping, std::vector* linear_vars, std::vector* linear_coeffs) { CHECK(model_proto.has_objective()); @@ -1135,7 +1111,7 @@ void ExtractLinearObjective(const CpModelProto& model_proto, linear_vars->reserve(obj.vars_size()); linear_coeffs->reserve(obj.vars_size()); for (int i = 0; i < obj.vars_size(); ++i) { - linear_vars->push_back(m->Integer(obj.vars(i))); + linear_vars->push_back(mapping.Integer(obj.vars(i))); linear_coeffs->push_back(IntegerValue(obj.coeffs(i))); } } @@ -1234,12 +1210,18 @@ CpSolverResponse SolveCpModelInternal( // We will add them all at once after model_proto is loaded. model->GetOrCreate()->DisableImplicationBetweenLiteral(); - ModelWithMapping m(model_proto, model); - m.DetectOptionalVariables(model_proto); - m.ExtractEncoding(model_proto); + auto* mapping = model->GetOrCreate(); + const SatParameters& parameters = *(model->GetOrCreate()); + const bool view_all_booleans_as_integers = + (parameters.linearization_level() >= 2) || + (parameters.search_branching() == SatParameters::FIXED_SEARCH && + model_proto.search_strategy().empty()); + mapping->CreateVariables(model_proto, view_all_booleans_as_integers, model); + mapping->DetectOptionalVariables(model_proto, model); + mapping->ExtractEncoding(model_proto, model); // Force some variables to be fully encoded. - FullEncodingFixedPointComputer fixpoint(&m); + FullEncodingFixedPointComputer fixpoint(model); for (const ConstraintProto& ct : model_proto.constraints()) { fixpoint.PropagateFullEncoding(&ct); } @@ -1249,12 +1231,12 @@ CpSolverResponse SolveCpModelInternal( std::set unsupported_types; int num_ignored_constraints = 0; for (const ConstraintProto& ct : model_proto.constraints()) { - if (m.ConstraintIsAlreadyLoaded(&ct)) { + if (mapping->ConstraintIsAlreadyLoaded(&ct)) { ++num_ignored_constraints; continue; } - if (!LoadConstraint(ct, &m)) { + if (!LoadConstraint(ct, model)) { unsupported_types.insert(ConstraintCaseName(ct.constraint_case())); continue; } @@ -1301,7 +1283,6 @@ CpSolverResponse SolveCpModelInternal( // Note that we do that before we finish loading the problem (objective and LP // relaxation), because propagation will be faster at this point and it should // be enough for the purpose of this auto-detection. - const SatParameters& parameters = *(model->GetOrCreate()); if (model->Mutable() != nullptr && parameters.auto_detect_greater_than_at_least_one_of()) { model->Mutable() @@ -1312,8 +1293,8 @@ CpSolverResponse SolveCpModelInternal( // TODO(user): This should be done in the presolve instead. // TODO(user): We don't have a good deterministic time on all constraints, // so this might take more time than wanted. - if (is_real_solve) { - ProbeBooleanVariables(/*deterministic_time_limit=*/1.0, m.model()); + if (is_real_solve && parameters.cp_model_probing_level() > 1) { + ProbeBooleanVariables(/*deterministic_time_limit=*/1.0, model); if (!model->GetOrCreate() ->ComputeTransitiveReduction()) { model->GetOrCreate()->NotifyThatModelIsUnsat(); @@ -1326,19 +1307,19 @@ CpSolverResponse SolveCpModelInternal( if (is_real_solve && parameters.linearization_level() > 0) { // Linearize some part of the problem and register LP constraint(s). objective_var = - AddLPConstraints(model_proto, parameters.linearization_level(), &m); + AddLPConstraints(model_proto, parameters.linearization_level(), model); } else if (model_proto.has_objective()) { const CpObjectiveProto& obj = model_proto.objective(); std::vector> terms; terms.reserve(obj.vars_size()); for (int i = 0; i < obj.vars_size(); ++i) { - terms.push_back(std::make_pair(m.Integer(obj.vars(i)), obj.coeffs(i))); + terms.push_back( + std::make_pair(mapping->Integer(obj.vars(i)), obj.coeffs(i))); } if (parameters.optimize_with_core()) { objective_var = GetOrCreateVariableWithTightBound(terms, model); } else { - objective_var = - GetOrCreateVariableGreaterOrEqualToSumOf(terms, m.model()); + objective_var = GetOrCreateVariableGreaterOrEqualToSumOf(terms, model); } } @@ -1381,7 +1362,7 @@ CpSolverResponse SolveCpModelInternal( std::vector coeffs; const CpObjectiveProto& obj = model_proto.objective(); for (int i = 0; i < obj.vars_size(); ++i) { - vars.push_back(m.Integer(obj.vars(i))); + vars.push_back(mapping->Integer(obj.vars(i))); coeffs.push_back(obj.coeffs(i)); } vars.push_back(objective_var); @@ -1396,10 +1377,10 @@ CpSolverResponse SolveCpModelInternal( // Initialize the search strategy function. std::function next_decision = ConstructSearchStrategy( - model_proto, m.GetVariableMapping(), objective_var, model); + model_proto, mapping->GetVariableMapping(), objective_var, model); if (VLOG_IS_ON(3)) { next_decision = InstrumentSearchStrategy( - model_proto, m.GetVariableMapping(), next_decision, model); + model_proto, mapping->GetVariableMapping(), next_decision, model); } // Solve. @@ -1408,16 +1389,16 @@ CpSolverResponse SolveCpModelInternal( // TODO(user): remove argument as it isn't used. Pass solution info instead? std::string solution_info; - const auto solution_observer = [&model_proto, &response, &num_solutions, &m, - &solution_info, &external_solution_observer, - objective_var, + const auto solution_observer = [&model_proto, &response, &num_solutions, + &model, &solution_info, + &external_solution_observer, objective_var, &fill_response_statistics](const Model&) { num_solutions++; - FillSolutionInResponse(model_proto, m, objective_var, &response); + FillSolutionInResponse(model_proto, *model, objective_var, &response); fill_response_statistics(); response.set_solution_info( - absl::StrCat("num_bool:", m.model()->Get()->NumVariables(), - " ", solution_info)); + absl::StrCat("num_bool:", model->Get()->NumVariables(), " ", + solution_info)); external_solution_observer(response); }; @@ -1437,10 +1418,10 @@ CpSolverResponse SolveCpModelInternal( const int ref = model_proto.solution_hint().vars(i); CHECK(RefIsPositive(ref)); BooleanOrIntegerVariable var; - if (m.IsBoolean(ref)) { - var.bool_var = m.Literal(ref).Variable(); + if (mapping->IsBoolean(ref)) { + var.bool_var = mapping->Literal(ref).Variable(); } else { - var.int_var = m.Integer(ref); + var.int_var = mapping->Integer(ref); } vars.push_back(var); values.push_back(IntegerValue(model_proto.solution_hint().values(i))); @@ -1487,7 +1468,8 @@ CpSolverResponse SolveCpModelInternal( if (parameters.optimize_with_core()) { std::vector linear_vars; std::vector linear_coeffs; - ExtractLinearObjective(model_proto, &m, &linear_vars, &linear_coeffs); + ExtractLinearObjective(model_proto, *model->GetOrCreate(), + &linear_vars, &linear_coeffs); if (parameters.optimize_with_max_hs()) { status = MinimizeWithHittingSetAndLazyEncoding( VLOG_IS_ON(2), objective_var, linear_vars, linear_coeffs, @@ -1682,11 +1664,10 @@ CpSolverResponse SolvePureSatModel(const CpModelProto& model_proto, // Deal with fixed variables. for (int ref = 0; ref < num_variables; ++ref) { - const auto domain = ReadDomain(model_proto.variables(ref)); - CHECK_EQ(domain.size(), 1); - if (domain[0].start == domain[0].end) { + const Domain domain = ReadDomainFromProto(model_proto.variables(ref)); + if (domain.Min() == domain.Max()) { const Literal ref_literal = - domain[0].start == 0 ? get_literal(ref).Negated() : get_literal(ref); + domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref); solver->AddUnitClause(ref_literal); if (drat_proof_handler != nullptr) { drat_proof_handler->AddProblemClause({ref_literal}); @@ -1851,7 +1832,7 @@ CpSolverResponse SolveCpModelWithLNS( CpModelProto local_problem = generators[selected_generator]->Generate( response, num_workers * seed + worker_id, saved_difficulty); const std::string solution_info = absl::StrFormat( - "%s(d=%0.2f s=%i t=%0.2f)", generators[selected_generator]->name().c_str(), + "%s(d=%0.2f s=%i t=%0.2f)", generators[selected_generator]->name(), saved_difficulty, seed, deterministic_time); Model local_model; @@ -1862,11 +1843,7 @@ CpSolverResponse SolveCpModelWithLNS( local_parameters.set_stop_after_first_solution(false); local_model.Add(NewSatParameters(local_parameters)); } - if (limit->ExternalBooleanAsLimit() != nullptr) { - TimeLimit* local_limit = local_model.GetOrCreate(); - local_limit->RegisterExternalBooleanAsLimit( - limit->ExternalBooleanAsLimit()); - } + local_model.GetOrCreate()->MergeWithGlobalTimeLimit(limit); // Presolve and solve the LNS fragment. CpSolverResponse local_response; @@ -1875,7 +1852,7 @@ CpSolverResponse SolveCpModelWithLNS( std::vector postsolve_mapping; PresolveOptions options; options.log_info = VLOG_IS_ON(2); - options.parameters = local_model.GetOrCreate(); + options.parameters = *local_model.GetOrCreate(); options.time_limit = local_model.GetOrCreate(); PresolveCpModel(options, &local_problem, &mapping_proto, &postsolve_mapping); @@ -2191,7 +2168,7 @@ CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model) { std::vector postsolve_mapping; PresolveOptions options; options.log_info = VLOG_IS_ON(1); - options.parameters = model->GetOrCreate(); + options.parameters = *model->GetOrCreate(); options.time_limit = model->GetOrCreate(); PresolveCpModel(options, &new_model, &mapping_proto, &postsolve_mapping); VLOG(1) << CpModelStats(new_model); @@ -2230,7 +2207,7 @@ CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model) { : response.best_objective_bound(), maximize ? response.best_objective_bound() : response.objective_value(), - response.solution_info().c_str()); + response.solution_info()); if (observers.empty()) return; CpSolverResponse copy = response; diff --git a/ortools/sat/cp_model_symmetries.cc b/ortools/sat/cp_model_symmetries.cc index 0f4555e5d88..7c53e2a21ee 100644 --- a/ortools/sat/cp_model_symmetries.cc +++ b/ortools/sat/cp_model_symmetries.cc @@ -15,12 +15,12 @@ #include -#include +#include "absl/container/flat_hash_map.h" +#include "absl/memory/memory.h" #include "google/protobuf/repeated_field.h" #include "ortools/algorithms/find_graph_symmetries.h" #include "ortools/base/hash.h" #include "ortools/base/map_util.h" -#include "ortools/base/memory.h" #include "ortools/sat/cp_model_utils.h" namespace operations_research { @@ -50,7 +50,7 @@ class IdGenerator { } private: - std::unordered_map, int, VectorHash> id_map_; + absl::flat_hash_map, int, VectorHash> id_map_; }; // Appends values in `repeated_field` to `vector`. diff --git a/ortools/sat/cp_model_utils.cc b/ortools/sat/cp_model_utils.cc index 4debd86db65..a28340028ec 100644 --- a/ortools/sat/cp_model_utils.cc +++ b/ortools/sat/cp_model_utils.cc @@ -13,7 +13,7 @@ #include "ortools/sat/cp_model_utils.h" -#include +#include "absl/container/flat_hash_set.h" #include "ortools/base/stl_util.h" namespace operations_research { @@ -22,7 +22,7 @@ namespace sat { namespace { template -void AddIndices(const IntList& indices, std::unordered_set* output) { +void AddIndices(const IntList& indices, absl::flat_hash_set* output) { output->insert(indices.begin(), indices.end()); } diff --git a/ortools/sat/cp_model_utils.h b/ortools/sat/cp_model_utils.h index 9094841316b..f6efadb05ca 100644 --- a/ortools/sat/cp_model_utils.h +++ b/ortools/sat/cp_model_utils.h @@ -19,7 +19,7 @@ #include #include -#include +#include "absl/container/flat_hash_set.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/sat/cp_model.pb.h" @@ -47,9 +47,9 @@ inline int EnforcementLiteral(const ConstraintProto& ct) { // // TODO(user): replace this by constant version of the Apply...() functions? struct IndexReferences { - std::unordered_set variables; - std::unordered_set literals; - std::unordered_set intervals; + absl::flat_hash_set variables; + absl::flat_hash_set literals; + absl::flat_hash_set intervals; }; void AddReferencesUsedByConstraint(const ConstraintProto& ct, IndexReferences* output); @@ -81,39 +81,30 @@ bool DomainInProtoContains(const ProtoWithDomain& proto, int64 value) { return false; } -// Sets the domain field of a proto from a sorted interval list. +// Serializes a Domain into the domain field of a proto. template -void FillDomain(const std::vector& domain, - ProtoWithDomain* proto) { +void FillDomainInProto(const Domain& domain, ProtoWithDomain* proto) { proto->clear_domain(); - CHECK(IntervalsAreSortedAndDisjoint(domain)); for (const ClosedInterval& interval : domain) { proto->add_domain(interval.start); proto->add_domain(interval.end); } } -template -void FillDomainInProto(const Domain& domain, ProtoWithDomain* proto) { - FillDomain(domain.intervals(), proto); -} -// Extract a sorted interval list from the domain field of a proto. +// Reads a Domain from the domain field of a proto. template -std::vector ReadDomain(const ProtoWithDomain& proto) { - std::vector result; +Domain ReadDomainFromProto(const ProtoWithDomain& proto) { + std::vector intervals; for (int i = 0; i < proto.domain_size(); i += 2) { - result.push_back({proto.domain(i), proto.domain(i + 1)}); + intervals.push_back({proto.domain(i), proto.domain(i + 1)}); } - CHECK(IntervalsAreSortedAndDisjoint(result)); - return result; -} -template -Domain ReadDomainFromProto(const ProtoWithDomain& proto) { - return Domain::FromIntervals(ReadDomain(proto)); + return Domain::FromIntervals(intervals); } // Returns the list of values in a given domain. // This will fail if the domain contains more than one millions values. +// +// TODO(user): work directly on the Domain class instead. template std::vector AllValuesInDomain(const ProtoWithDomain& proto) { std::vector result; diff --git a/ortools/sat/csharp/CpModel.cs b/ortools/sat/csharp/CpModel.cs index 0f0b993337b..5be2a55a775 100644 --- a/ortools/sat/csharp/CpModel.cs +++ b/ortools/sat/csharp/CpModel.cs @@ -823,9 +823,9 @@ public String ModelStats() { return SatHelper.ModelStats(model_); } - public String Validate() { + public String Validate() { return SatHelper.ValidateModel(model_); - } + } private int ConvertConstant(long value) { diff --git a/ortools/sat/disjunctive.cc b/ortools/sat/disjunctive.cc index 82bfe3b19da..aea56348428 100644 --- a/ortools/sat/disjunctive.cc +++ b/ortools/sat/disjunctive.cc @@ -273,7 +273,7 @@ bool DisjunctiveOverloadChecker::Propagate() { event_to_task_.clear(); event_time_.clear(); int num_events = 0; - for (const auto task_time : helper_->TaskByIncreasingStartMin()) { + for (const auto task_time : helper_->TaskByIncreasingShiftedStartMin()) { const int task = task_time.task_index; // TODO(user): We need to take into account task with zero duration because @@ -322,11 +322,11 @@ bool DisjunctiveOverloadChecker::Propagate() { const IntegerValue window_end = theta_tree_.GetEnvelopeOf(critical_event) - 1; for (int event = critical_event; event < num_events; event++) { - if (theta_tree_.EnergyMin(event) > 0) { + const IntegerValue energy_min = theta_tree_.EnergyMin(event); + if (energy_min > 0) { const int task = event_to_task_[event]; helper_->AddPresenceReason(task); - helper_->AddDurationMinReason(task); - helper_->AddStartMinReason(task, window_start); + helper_->AddEnergyAfterReason(task, energy_min, window_start); helper_->AddEndMaxReason(task, window_end); } } @@ -346,22 +346,23 @@ bool DisjunctiveOverloadChecker::Propagate() { current_end, &critical_event, &optional_event, &available_energy); const int optional_task = event_to_task_[optional_event]; + const IntegerValue optional_duration_min = + helper_->DurationMin(optional_task); const IntegerValue window_start = event_time_[critical_event]; - const IntegerValue window_end = current_end + - helper_->DurationMin(optional_task) - - available_energy - 1; + const IntegerValue window_end = + current_end + optional_duration_min - available_energy - 1; for (int event = critical_event; event < num_events; event++) { - if (theta_tree_.EnergyMin(event) > 0) { + const IntegerValue energy_min = theta_tree_.EnergyMin(event); + if (energy_min > 0) { const int task = event_to_task_[event]; helper_->AddPresenceReason(task); - helper_->AddDurationMinReason(task); - helper_->AddStartMinReason(task, window_start); + helper_->AddEnergyAfterReason(task, energy_min, window_start); helper_->AddEndMaxReason(task, window_end); } } - helper_->AddDurationMinReason(optional_task); - helper_->AddStartMinReason(optional_task, window_start); + helper_->AddEnergyAfterReason(optional_task, optional_duration_min, + window_start); helper_->AddEndMaxReason(optional_task, window_end); // If tasks shares the same presence literal, it is possible that we @@ -403,7 +404,7 @@ bool DisjunctiveDetectablePrecedences::Propagate() { const IntegerValue start_max = to_insert.time; if (end_min <= start_max) break; if (helper_->IsPresent(task_index)) { - task_set_.AddEntry({task_index, helper_->StartMin(task_index), + task_set_.AddEntry({task_index, helper_->ShiftedStartMin(task_index), helper_->DurationMin(task_index)}); } --queue_index; @@ -433,11 +434,9 @@ bool DisjunctiveDetectablePrecedences::Propagate() { const int ct = sorted_tasks[i].task; if (ct == t) continue; DCHECK(helper_->IsPresent(ct)); - DCHECK_GE(helper_->StartMin(ct), window_start); - DCHECK_LT(helper_->StartMax(ct), end_min); helper_->AddPresenceReason(ct); - helper_->AddDurationMinReason(ct); - helper_->AddStartMinReason(ct, window_start); + helper_->AddEnergyAfterReason(ct, sorted_tasks[i].duration_min, + window_start); helper_->AddStartMaxReason(ct, end_min - 1); } @@ -455,7 +454,7 @@ bool DisjunctiveDetectablePrecedences::Propagate() { // its start (by opposition to an optional interval where the push might // not happen if its start is not optional). task_set_.NotifyEntryIsNowLastIfPresent( - {t, helper_->StartMin(t), helper_->DurationMin(t)}); + {t, helper_->ShiftedStartMin(t), helper_->DurationMin(t)}); } } return true; @@ -493,7 +492,7 @@ bool DisjunctivePrecedences::Propagate() { const int task = before_[i].index; min_offset = std::min(min_offset, before_[i].offset); task_set_.AddUnsortedEntry( - {task, helper_->StartMin(task), helper_->DurationMin(task)}); + {task, helper_->ShiftedStartMin(task), helper_->DurationMin(task)}); } DCHECK_GE(task_set_.SortedTasks().size(), 2); if (integer_trail_->IsCurrentlyIgnored(var)) continue; @@ -517,10 +516,9 @@ bool DisjunctivePrecedences::Propagate() { for (int i = critical_index; i < sorted_tasks.size(); ++i) { const int ct = sorted_tasks[i].task; DCHECK(helper_->IsPresent(ct)); - DCHECK_GE(helper_->StartMin(ct), window_start); helper_->AddPresenceReason(ct); - helper_->AddDurationMinReason(ct); - helper_->AddStartMinReason(ct, window_start); + helper_->AddEnergyAfterReason(ct, sorted_tasks[i].duration_min, + window_start); precedences_->AddPrecedenceReason(task_to_arc_index_[ct], min_offset, helper_->MutableLiteralReason(), helper_->MutableIntegerReason()); @@ -570,7 +568,7 @@ bool DisjunctiveNotLast::Propagate() { const IntegerValue start_max = to_insert.time; if (end_max <= start_max) break; if (helper_->IsPresent(task_index)) { - task_set_.AddEntry({task_index, helper_->StartMin(task_index), + task_set_.AddEntry({task_index, helper_->ShiftedStartMin(task_index), helper_->DurationMin(task_index)}); } --queue_index; @@ -617,8 +615,8 @@ bool DisjunctiveNotLast::Propagate() { const int ct = sorted_tasks[i].task; if (ct == t) continue; helper_->AddPresenceReason(ct); - helper_->AddDurationMinReason(ct); - helper_->AddStartMinReason(ct, window_start); + helper_->AddEnergyAfterReason(ct, sorted_tasks[i].duration_min, + window_start); helper_->AddStartMaxReason(ct, largest_ct_start_max); } @@ -658,9 +656,10 @@ bool DisjunctiveEdgeFinding::Propagate() { non_gray_task_to_event_.resize(num_tasks); event_to_task_.clear(); event_time_.clear(); + event_size_.clear(); int num_events = 0; int num_not_gray = 0; - for (const auto task_time : helper_->TaskByIncreasingStartMin()) { + for (const auto task_time : helper_->TaskByIncreasingShiftedStartMin()) { const int task = task_time.task_index; if (helper_->IsAbsent(task)) { // Even though we completely ignore absent tasks, we still mark them as @@ -673,6 +672,7 @@ bool DisjunctiveEdgeFinding::Propagate() { // We already mark all the non-present task as gray. const IntegerValue energy_min = helper_->DurationMin(task); + event_size_.push_back(energy_min); if (helper_->IsPresent(task)) { ++num_not_gray; is_gray_[task] = false; @@ -717,8 +717,7 @@ bool DisjunctiveEdgeFinding::Propagate() { const int task = event_to_task_[event]; if (is_gray_[task]) continue; helper_->AddPresenceReason(task); - helper_->AddDurationMinReason(task); - helper_->AddStartMinReason(task, window_start); + helper_->AddEnergyAfterReason(task, event_size_[event], window_start); helper_->AddEndMaxReason(task, window_end); } return helper_->ReportConflict(); @@ -741,65 +740,44 @@ bool DisjunctiveEdgeFinding::Propagate() { non_gray_end_max, &critical_event_with_gray, &gray_event, &available_energy); const int gray_task = event_to_task_[gray_event]; - const IntegerValue gray_start_min = helper_->StartMin(gray_task); // Since the gray task is after all the other, we have a new lower bound. CHECK(is_gray_[gray_task]); - if (gray_start_min < non_gray_end_min) { - helper_->ClearReason(); - + if (helper_->StartMin(gray_task) < non_gray_end_min) { // The API is not ideal here. We just want the start of the critical // tasks that explain the non_gray_end_min computed above. const int critical_event = theta_tree_.GetMaxEventWithEnvelopeGreaterThan(non_gray_end_min - 1); - const IntegerValue critical_event_start = event_time_[critical_event]; - - // The explanation is a bit tricky, we consider two cases. - int first_event; - IntegerValue window_start; - IntegerValue window_end; - if (gray_start_min >= critical_event_start) { - // In this case, if the gray task is not last amongst the tasks - // contributing to the max envelope, we have an overload in the - // window: - first_event = critical_event; - window_start = critical_event_start; - window_end = non_gray_end_min + helper_->DurationMin(gray_task) - 1; - - // Note that the explanation above also explain the max envelope, so - // we don't need anything else. - } else { - // In this case, to explain the overload we need the window below. - // Note that window_end is chosen to be has big as possible and still - // have an overload if the gray task is not last. - CHECK_LT(critical_event_with_gray, critical_event); - first_event = critical_event_with_gray; - window_start = event_time_[critical_event_with_gray]; - window_end = non_gray_end_max + helper_->DurationMin(gray_task) - - available_energy - 1; - - // Moreover, to push the gray task as much as possible, we also need - // the subset of non-gray task after critical_event to start after - // critical_event_start. - } + const int first_event = + std::min(critical_event, critical_event_with_gray); + const int second_event = + std::max(critical_event, critical_event_with_gray); + const IntegerValue first_start = event_time_[first_event]; + const IntegerValue second_start = event_time_[second_event]; + + // window_end is chosen to be has big as possible and still have an + // overload if the gray task is not last. + const IntegerValue window_end = + non_gray_end_max + event_size_[gray_event] - available_energy - 1; + CHECK_GE(window_end, non_gray_end_max); // The non-gray part of the explanation as detailed above. + helper_->ClearReason(); for (int event = first_event; event < num_events; event++) { const int task = event_to_task_[event]; if (is_gray_[task]) continue; helper_->AddPresenceReason(task); - helper_->AddDurationMinReason(task); - helper_->AddStartMinReason(task, event >= critical_event - ? critical_event_start - : window_start); + helper_->AddEnergyAfterReason( + task, event_size_[event], + event >= second_event ? second_start : first_start); helper_->AddEndMaxReason(task, window_end); } // Add the reason for the gray_task (we don't need the end-max or // presence reason). - helper_->AddDurationMinReason(gray_task); - helper_->AddStartMinReason(gray_task, window_start); + helper_->AddEnergyAfterReason(gray_task, event_size_[gray_event], + event_time_[critical_event_with_gray]); // Enqueue the new start-min for gray_task. // @@ -825,7 +803,7 @@ bool DisjunctiveEdgeFinding::Propagate() { is_gray_[new_gray_task] = true; theta_tree_.AddOrUpdateOptionalEvent(new_gray_event, event_time_[new_gray_event], - helper_->DurationMin(new_gray_task)); + event_size_[new_gray_event]); } ++end_max_index; --num_not_gray; diff --git a/ortools/sat/disjunctive.h b/ortools/sat/disjunctive.h index 8cb910a23c7..7766154d61c 100644 --- a/ortools/sat/disjunctive.h +++ b/ortools/sat/disjunctive.h @@ -188,6 +188,7 @@ class DisjunctiveEdgeFinding : public PropagatorInterface { std::vector non_gray_task_to_event_; std::vector event_to_task_; std::vector event_time_; + std::vector event_size_; std::vector is_gray_; }; diff --git a/ortools/sat/doc/boolean_logic.md b/ortools/sat/doc/boolean_logic.md index e81921d551c..62e9f68dcc2 100644 --- a/ortools/sat/doc/boolean_logic.md +++ b/ortools/sat/doc/boolean_logic.md @@ -27,7 +27,7 @@ from __future__ import print_function from ortools.sat.python import cp_model -def LiteralSampleSat(): +def LiteralSample(): model = cp_model.CpModel() x = model.NewBoolVar('x') not_x = x.Not() @@ -35,7 +35,7 @@ def LiteralSampleSat(): print(not_x) -LiteralSampleSat() +LiteralSample() ``` ### C++ code @@ -46,7 +46,7 @@ LiteralSampleSat() namespace operations_research { namespace sat { -void LiteralSampleSat() { +void LiteralSample() { CpModelBuilder cp_model; const BoolVar x = cp_model.NewBoolVar().WithName("x"); @@ -58,7 +58,7 @@ void LiteralSampleSat() { } // namespace operations_research int main() { - operations_research::sat::LiteralSampleSat(); + operations_research::sat::LiteralSample(); return EXIT_SUCCESS; } @@ -71,8 +71,7 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.Literal; -/** Code sample to demonstrate Boolean variable and literals. */ -public class LiteralSampleSat { +public class LiteralSample { static { System.loadLibrary("jniortools"); } @@ -91,14 +90,18 @@ public class LiteralSampleSat { using System; using Google.OrTools.Sat; -public class LiteralSampleSat +public class CodeSamplesSat { - static void Main() + static void LiteralSample() { CpModel model = new CpModel(); IntVar x = model.NewBoolVar("x"); ILiteral not_x = x.Not(); } + + static void Main() { + LiteralSample(); + } } ``` @@ -126,7 +129,7 @@ from __future__ import print_function from ortools.sat.python import cp_model -def BoolOrSampleSat(): +def BoolOrSample(): model = cp_model.CpModel() x = model.NewBoolVar('x') @@ -135,7 +138,7 @@ def BoolOrSampleSat(): model.AddBoolOr([x, y.Not()]) -BoolOrSampleSat() +BoolOrSample() ``` ### C++ code @@ -146,7 +149,7 @@ BoolOrSampleSat() namespace operations_research { namespace sat { -void BoolOrSampleSat() { +void BoolOrSample() { CpModelBuilder cp_model; const BoolVar x = cp_model.NewBoolVar(); @@ -158,7 +161,7 @@ void BoolOrSampleSat() { } // namespace operations_research int main() { - operations_research::sat::BoolOrSampleSat(); + operations_research::sat::BoolOrSample(); return EXIT_SUCCESS; } @@ -171,8 +174,7 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.Literal; -/** Code sample to demonstrates a simple Boolean constraint. */ -public class BoolOrSampleSat { +public class BoolOrSample { static { System.loadLibrary("jniortools"); } @@ -191,17 +193,20 @@ public class BoolOrSampleSat { using System; using Google.OrTools.Sat; -public class BoolOrSampleSat +public class CodeSamplesSat { - static void Main() + static void BoolOrSample() { CpModel model = new CpModel(); - IntVar x = model.NewBoolVar("x"); IntVar y = model.NewBoolVar("y"); - model.AddBoolOr(new ILiteral[] { x, y.Not() }); } + + static void Main() + { + BoolOrSample(); + } } ``` @@ -234,7 +239,7 @@ from __future__ import print_function from ortools.sat.python import cp_model -def ReifiedSampleSat(): +def ReifiedSample(): """Showcase creating a reified constraint.""" model = cp_model.CpModel() @@ -254,7 +259,7 @@ def ReifiedSampleSat(): model.AddBoolOr([b.Not(), y.Not()]) -ReifiedSampleSat() +ReifiedSample() ``` ### C++ code @@ -265,7 +270,7 @@ ReifiedSampleSat() namespace operations_research { namespace sat { -void ReifiedSampleSat() { +void ReifiedSample() { CpModelBuilder cp_model; const BoolVar x = cp_model.NewBoolVar(); @@ -288,7 +293,7 @@ void ReifiedSampleSat() { } // namespace operations_research int main() { - operations_research::sat::ReifiedSampleSat(); + operations_research::sat::ReifiedSample(); return EXIT_SUCCESS; } @@ -311,7 +316,7 @@ import com.google.ortools.sat.Literal; *

The SAT solver offers half-reification. To implement full reification, two half-reified * constraints must be used. */ -public class ReifiedSampleSat { +public class ReifiedSample { static { System.loadLibrary("jniortools"); } @@ -342,9 +347,9 @@ public class ReifiedSampleSat { using System; using Google.OrTools.Sat; -public class ReifiedSampleSat +public class CodeSamplesSat { - static void Main() + static void ReifiedSample() { CpModel model = new CpModel(); @@ -363,5 +368,9 @@ public class ReifiedSampleSat model.AddBoolOr(new ILiteral[] {b.Not(), x}); model.AddBoolOr(new ILiteral[] {b.Not(), y.Not()}); } + + static void Main() { + ReifiedSample(); + } } ``` diff --git a/ortools/sat/doc/index.md b/ortools/sat/doc/index.md index 9d1a25538d6..b0517648941 100644 --- a/ortools/sat/doc/index.md +++ b/ortools/sat/doc/index.md @@ -2,8 +2,6 @@ -## Documentation structure - This document presents modeling recipes for the CP-SAT solver. These are grouped by type: @@ -17,13 +15,7 @@ by type: Code samples are given in C++ and python. Each language have different requirements for the code samples. -## Searching for one (optimal) solution of a CP-SAT model - -By default, searching for one solution will return the first solution found if -the model has no objective, or the optimal solution if the model has an -objective. - -### Python code samples +## Python code samples The Python interface to the CP-SAT solver is implemented using two classes. @@ -34,7 +26,7 @@ The Python interface to the CP-SAT solver is implemented using two classes. access the solution found by the solve. ```python -"""Simple solve.""" +"""Creates a single Boolean variable.""" from __future__ import absolute_import from __future__ import division @@ -43,125 +35,67 @@ from __future__ import print_function from ortools.sat.python import cp_model -def SimpleSatProgram(): - """Minimal CP-SAT example to showcase calling the solver.""" - # Creates the model. +def CodeSample(): 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 constraints. - model.Add(x != y) - - # Creates a solver and solves the model. - solver = cp_model.CpSolver() - status = solver.Solve(model) - - if status == cp_model.FEASIBLE: - print('x = %i' % solver.Value(x)) - print('y = %i' % solver.Value(y)) - print('z = %i' % solver.Value(z)) + x = model.NewBoolVar('x') + print(x) -SimpleSatProgram() +CodeSample() ``` -### C++ code samples +## C++ code samples The interface to the C++ CP-SAT solver is implemented through the **CpModelBuilder** class described in *ortools/sat/cp_model.h*. This class is just a helper to fill in the cp_model protobuf. -Calling Solve() method will return a CpSolverResponse protobuf that contains the -solve status, the values for each variable in the model if solve was successful, -and some metrics. - ```cpp #include "ortools/sat/cp_model.h" namespace operations_research { namespace sat { -void SimpleSatProgram() { +void CodeSample() { CpModelBuilder cp_model; - const Domain domain(0, 2); - const IntVar x = cp_model.NewIntVar(domain).WithName("x"); - const IntVar y = cp_model.NewIntVar(domain).WithName("y"); - const IntVar z = cp_model.NewIntVar(domain).WithName("z"); - - cp_model.AddNotEqual(x, y); - - // Solving part. - const CpSolverResponse response = Solve(cp_model); - LOG(INFO) << CpSolverResponseStats(response); - - if (response.status() == CpSolverStatus::FEASIBLE) { - // Get the value of x in the solution. - LOG(INFO) << "x = " << SolutionIntegerValue(response, x); - LOG(INFO) << "y = " << SolutionIntegerValue(response, y); - LOG(INFO) << "z = " << SolutionIntegerValue(response, z); - } + const IntVar x = cp_model.NewBoolVar().WithName("x"); + LOG(INFO) << x; } - } // namespace sat } // namespace operations_research int main() { - operations_research::sat::SimpleSatProgram(); + operations_research::sat::CodeSample(); return EXIT_SUCCESS; } ``` -### Java code samples +## Java code samples The Java code implements the same interface as the Python code, with a **CpModel** and a **CpSolver** class. ```java -import com.google.ortools.sat.CpSolverStatus; import com.google.ortools.sat.CpModel; -import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.IntVar; -/** Minimal CP-SAT example to showcase calling the solver. */ -public class SimpleSatProgram { +public class CodeSample { static { System.loadLibrary("jniortools"); } public static void main(String[] args) throws Exception { - // Create the model. + // Creates the model. CpModel model = new CpModel(); - - // Create the variables. - int numVals = 3; - - IntVar x = model.newIntVar(0, numVals - 1, "x"); - IntVar y = model.newIntVar(0, numVals - 1, "y"); - IntVar z = model.newIntVar(0, numVals - 1, "z"); - - // Create the constraints. - model.addDifferent(x, y); - - // Create a solver and solve the model. - CpSolver solver = new CpSolver(); - CpSolverStatus status = solver.solve(model); - - if (status == CpSolverStatus.FEASIBLE) { - System.out.println("x = " + solver.value(x)); - System.out.println("y = " + solver.value(y)); - System.out.println("z = " + solver.value(z)); - } + // Creates the Boolean variable. + IntVar x = model.newBoolVar("x"); + System.out.println(x); } } ``` -### C\# code samples +## C\# code samples The C\# code implements the same interface as the Python code, with a **CpModel** and a **CpSolver** class. @@ -171,33 +105,19 @@ The C\# code implements the same interface as the Python code, with a using System; using Google.OrTools.Sat; -public class SimpleSatProgram +public class CodeSamplesSat { - static void Main() + static void CodeSample() { // Creates the model. CpModel model = new CpModel(); + // Creates the Boolean variable. + IntVar x = model.NewBoolVar("x"); + } - // Creates the variables. - int num_vals = 3; - - IntVar x = model.NewIntVar(0, num_vals - 1, "x"); - IntVar y = model.NewIntVar(0, num_vals - 1, "y"); - IntVar z = model.NewIntVar(0, num_vals - 1, "z"); - - // Creates the constraints. - model.Add(x != y); - - // Creates a solver and solves the model. - CpSolver solver = new CpSolver(); - CpSolverStatus status = solver.Solve(model); - - if (status == CpSolverStatus.Feasible) - { - Console.WriteLine("x = " + solver.Value(x)); - Console.WriteLine("y = " + solver.Value(y)); - Console.WriteLine("z = " + solver.Value(z)); - } + static void Main() + { + CodeSample(); } } ``` diff --git a/ortools/sat/doc/integer_arithmetic.md b/ortools/sat/doc/integer_arithmetic.md index d23cf16b2a8..0ae14f83a37 100644 --- a/ortools/sat/doc/integer_arithmetic.md +++ b/ortools/sat/doc/integer_arithmetic.md @@ -53,7 +53,7 @@ from __future__ import print_function from ortools.sat.python import cp_model -def RabbitsAndPheasantsSat(): +def RabbitsAndPheasants(): """Solves the rabbits + pheasants problem.""" model = cp_model.CpModel() @@ -73,7 +73,7 @@ def RabbitsAndPheasantsSat(): print('%i rabbits and %i pheasants' % (solver.Value(r), solver.Value(p))) -RabbitsAndPheasantsSat() +RabbitsAndPheasants() ``` ### C++ code @@ -84,7 +84,7 @@ RabbitsAndPheasantsSat() namespace operations_research { namespace sat { -void RabbitsAndPheasantsSat() { +void RabbitsAndPheasants() { CpModelBuilder cp_model; const Domain all_animals(0, 20); @@ -108,7 +108,7 @@ void RabbitsAndPheasantsSat() { } // namespace operations_research int main() { - operations_research::sat::RabbitsAndPheasantsSat(); + operations_research::sat::RabbitsAndPheasants(); return EXIT_SUCCESS; } @@ -126,7 +126,7 @@ import com.google.ortools.sat.IntVar; * In a field of rabbits and pheasants, there are 20 heads and 56 legs. How many rabbits and * pheasants are there? */ -public class RabbitsAndPheasantsSat { +public class RabbitsAndPheasants { static { System.loadLibrary("jniortools"); } @@ -158,9 +158,9 @@ public class RabbitsAndPheasantsSat { using System; using Google.OrTools.Sat; -public class RabbitsAndPheasantsSat +public class CodeSamplesSat { - static void Main() + static void RabbitsAndPheasants() { // Creates the model. CpModel model = new CpModel(); @@ -182,5 +182,10 @@ public class RabbitsAndPheasantsSat solver.Value(p) + " pheasants"); } } + + static void Main() + { + RabbitsAndPheasants(); + } } ``` diff --git a/ortools/sat/doc/scheduling.md b/ortools/sat/doc/scheduling.md index d2a2ba19e74..1ef5635ac1c 100644 --- a/ortools/sat/doc/scheduling.md +++ b/ortools/sat/doc/scheduling.md @@ -32,20 +32,18 @@ from __future__ import print_function from ortools.sat.python import cp_model -def IntervalSampleSat(): +def IntervalSample(): model = cp_model.CpModel() - horizon = 100 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, 'interval') - print('start = %s, duration = %i, end = %s, interval = %s' % (start_var, duration, end_var, interval_var)) -IntervalSampleSat() +IntervalSample() ``` ### C++ code @@ -56,7 +54,7 @@ IntervalSampleSat() namespace operations_research { namespace sat { -void IntervalSampleSat() { +void IntervalSample() { CpModelBuilder cp_model; const int kHorizon = 100; @@ -77,7 +75,7 @@ void IntervalSampleSat() { } // namespace operations_research int main() { - operations_research::sat::IntervalSampleSat(); + operations_research::sat::IntervalSample(); return EXIT_SUCCESS; } @@ -90,8 +88,7 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.IntVar; import com.google.ortools.sat.IntervalVar; -/** Code sample to demonstrates how to build an interval. */ -public class IntervalSampleSat { +public class IntervalSample { static { System.loadLibrary("jniortools"); } @@ -115,9 +112,9 @@ public class IntervalSampleSat { using System; using Google.OrTools.Sat; -public class IntervalSampleSat +public class CodeSamplesSat { - static void Main() + static void IntervalSample() { CpModel model = new CpModel(); int horizon = 100; @@ -128,6 +125,11 @@ public class IntervalSampleSat IntervalVar interval = model.NewIntervalVar(start_var, duration, end_var, "interval"); } + + static void Main() + { + IntervalSample(); + } } ``` @@ -149,10 +151,8 @@ from __future__ import print_function from ortools.sat.python import cp_model -def OptionalIntervalSampleSat(): - """Build an optional interval.""" +def OptionalIntervalSample(): model = cp_model.CpModel() - horizon = 100 start_var = model.NewIntVar(0, horizon, 'start') duration = 10 # Python cp/sat code accept integer variables or constants. @@ -160,12 +160,11 @@ def OptionalIntervalSampleSat(): presence_var = model.NewBoolVar('presence') interval_var = model.NewOptionalIntervalVar(start_var, duration, end_var, presence_var, 'interval') - print('start = %s, duration = %i, end = %s, presence = %s, interval = %s' % (start_var, duration, end_var, presence_var, interval_var)) -OptionalIntervalSampleSat() +OptionalIntervalSample() ``` ### C++ code @@ -176,7 +175,7 @@ OptionalIntervalSampleSat() namespace operations_research { namespace sat { -void OptionalIntervalSampleSat() { +void OptionalIntervalSample() { CpModelBuilder cp_model; const int kHorizon = 100; @@ -202,7 +201,7 @@ void OptionalIntervalSampleSat() { } // namespace operations_research int main() { - operations_research::sat::OptionalIntervalSampleSat(); + operations_research::sat::OptionalIntervalSample(); return EXIT_SUCCESS; } @@ -216,8 +215,7 @@ import com.google.ortools.sat.IntVar; import com.google.ortools.sat.IntervalVar; import com.google.ortools.sat.Literal; -/** Code sample to demonstrates how to build an optional interval. */ -public class OptionalIntervalSampleSat { +public class OptionalIntervalSample { static { System.loadLibrary("jniortools"); } @@ -243,9 +241,9 @@ public class OptionalIntervalSampleSat { using System; using Google.OrTools.Sat; -public class OptionalIntervalSampleSat +public class CodeSamplesSat { - static void Main() + static void OptionalIntervalSample() { CpModel model = new CpModel(); int horizon = 100; @@ -257,6 +255,11 @@ public class OptionalIntervalSampleSat IntervalVar interval = model.NewOptionalIntervalVar( start_var, duration, end_var, presence_var, "interval"); } + + static void Main() + { + OptionalIntervalSample(); + } } ``` @@ -281,7 +284,7 @@ from __future__ import print_function from ortools.sat.python import cp_model -def NoOverlapSampleSat(): +def NoOverlapSample(): """No overlap sample with fixed activities.""" model = cp_model.CpModel() horizon = 21 # 3 weeks. @@ -330,7 +333,7 @@ def NoOverlapSampleSat(): print('Solver exited with nonoptimal status: %i' % status) -NoOverlapSampleSat() +NoOverlapSample() ``` ### C++ code @@ -341,7 +344,7 @@ NoOverlapSampleSat() namespace operations_research { namespace sat { -void NoOverlapSampleSat() { +void NoOverlapSample() { CpModelBuilder cp_model; const int64 kHorizon = 21; // 3 weeks. @@ -407,7 +410,7 @@ void NoOverlapSampleSat() { } // namespace operations_research int main() { - operations_research::sat::NoOverlapSampleSat(); + operations_research::sat::NoOverlapSample(); return EXIT_SUCCESS; } @@ -426,7 +429,7 @@ import com.google.ortools.sat.IntervalVar; * We want to schedule 3 tasks on 3 weeks excluding weekends, making the final day as early as * possible. */ -public class NoOverlapSampleSat { +public class NoOverlapSample { static { System.loadLibrary("jniortools"); } @@ -488,9 +491,9 @@ public class NoOverlapSampleSat { using System; using Google.OrTools.Sat; -public class NoOverlapSampleSat +public class CodeSamplesSat { - static void Main() + static void NoOverlapSample() { CpModel model = new CpModel(); // Three weeks. @@ -543,6 +546,11 @@ public class NoOverlapSampleSat Console.WriteLine("Task 2 starts at " + solver.Value(start_2)); } } + + static void Main() + { + NoOverlapSample(); + } } ``` @@ -638,7 +646,7 @@ def RankTasks(model, starts, presences, ranks): model.Add(ranks[i] == sum(precedences[(j, i)] for j in all_tasks) - 1) -def RankingSampleSat(): +def RankingSample(): """Ranks tasks in a NoOverlap constraint.""" model = cp_model.CpModel() @@ -713,7 +721,7 @@ def RankingSampleSat(): print('Solver exited with nonoptimal status: %i' % status) -RankingSampleSat() +RankingSample() ``` ### C++ code @@ -724,7 +732,7 @@ RankingSampleSat() namespace operations_research { namespace sat { -void RankingSampleSat() { +void RankingSample() { CpModelBuilder cp_model; const int kHorizon = 100; const int kNumTasks = 4; @@ -855,7 +863,7 @@ void RankingSampleSat() { } // namespace operations_research int main() { - operations_research::sat::RankingSampleSat(); + operations_research::sat::RankingSample(); return EXIT_SUCCESS; } @@ -873,18 +881,14 @@ import com.google.ortools.sat.Literal; import java.util.ArrayList; import java.util.List; -/** Code sample to demonstrates how to rank intervals. */ -public class RankingSampleSat { +// This code takes a list of interval variables in a noOverlap constraint, and a parallel list of +// integer variables and enforces the following constraint: +// - rank[i] == -1 iff interval[i] is not active. +// - rank[i] == number of active intervals that precede interval[i]. +public class RankingSample { static { System.loadLibrary("jniortools"); } - /** - * This code takes a list of interval variables in a noOverlap constraint, and a parallel list of - * integer variables and enforces the following constraint - * - * - rank[i] == -1 iff interval[i] is not active. - * - rank[i] == number of active intervals that precede interval[i]. - */ static void rankTasks(CpModel model, IntVar[] starts, Literal[] presences, IntVar[] ranks) { int numTasks = starts.length; @@ -1038,7 +1042,7 @@ using System; using System.Collections.Generic; using Google.OrTools.Sat; -public class RankingSampleSat +public class CodeSamplesSat { static void RankTasks(CpModel model, IntVar[] starts, @@ -1095,7 +1099,7 @@ public class RankingSampleSat } } - static void Main() + static void RankingSample() { CpModel model = new CpModel(); // Three weeks. @@ -1178,6 +1182,11 @@ public class RankingSampleSat String.Format("Solver exited with nonoptimal status: {0}", status)); } } + + static void Main() + { + RankingSample(); + } } ``` diff --git a/ortools/sat/doc/solver.md b/ortools/sat/doc/solver.md index 5fdc062fee1..97b6d8aa6da 100644 --- a/ortools/sat/doc/solver.md +++ b/ortools/sat/doc/solver.md @@ -2,6 +2,178 @@ +## Searching for one (optimal) solution + +By default, searching for one solution will return the first solution found if +the model has no objective, or the optimal solution if the model has an +objective. + +### Python solver code + +The CpSolver class encapsulates searching for a solution of a model. + +```python +"""Simple solve.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from ortools.sat.python import cp_model + + +def SimpleSolve(): + """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 constraints. + model.Add(x != y) + + # Creates a solver and solves the model. + solver = cp_model.CpSolver() + status = solver.Solve(model) + + if status == cp_model.FEASIBLE: + print('x = %i' % solver.Value(x)) + print('y = %i' % solver.Value(y)) + print('z = %i' % solver.Value(z)) + + +SimpleSolve() +``` + +### C++ solver code + +Calling Solve() method will return a CpSolverResponse protobuf that contains the +solve status, the values for each variable in the model if solve was successful, +and some metrics. + +```cpp +#include "ortools/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void SimpleSolve() { + CpModelBuilder cp_model; + + const Domain domain(0, 2); + const IntVar x = cp_model.NewIntVar(domain).WithName("x"); + const IntVar y = cp_model.NewIntVar(domain).WithName("y"); + const IntVar z = cp_model.NewIntVar(domain).WithName("z"); + + cp_model.AddNotEqual(x, y); + + // Solving part. + const CpSolverResponse response = Solve(cp_model); + LOG(INFO) << CpSolverResponseStats(response); + + if (response.status() == CpSolverStatus::FEASIBLE) { + // Get the value of x in the solution. + LOG(INFO) << "x = " << SolutionIntegerValue(response, x); + LOG(INFO) << "y = " << SolutionIntegerValue(response, y); + LOG(INFO) << "z = " << SolutionIntegerValue(response, z); + } +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::SimpleSolve(); + + return EXIT_SUCCESS; +} +``` + +### Java code + +As in Python, the CpSolver class encapsulates searching for a solution. + +```java +import com.google.ortools.sat.CpSolverStatus; +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.IntVar; + +/** Solve a simple problem with three variables and one different constraint. */ +public class SimpleSolve { + + static { System.loadLibrary("jniortools"); } + + public static void main(String[] args) throws Exception { + // Create the model. + CpModel model = new CpModel(); + // Create the variables. + int numVals = 3; + + IntVar x = model.newIntVar(0, numVals - 1, "x"); + IntVar y = model.newIntVar(0, numVals - 1, "y"); + IntVar z = model.newIntVar(0, numVals - 1, "z"); + // Create the constraints. + model.addDifferent(x, y); + + // Create a solver and solve the model. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.solve(model); + + if (status == CpSolverStatus.FEASIBLE) { + System.out.println("x = " + solver.value(x)); + System.out.println("y = " + solver.value(y)); + System.out.println("z = " + solver.value(z)); + } + } +} +``` + +### C\# code + +As in Python, the CpSolver class encapsulates searching for a solution of a +model. + +```cs +using System; +using Google.OrTools.Sat; + +public class CodeSamplesSat +{ + static void SimpleSolve() + { + // Creates the model. + CpModel model = new CpModel(); + // Creates the variables. + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + // Creates the constraints. + model.Add(x != y); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Feasible) + { + Console.WriteLine("x = " + solver.Value(x)); + Console.WriteLine("y = " + solver.Value(y)); + Console.WriteLine("z = " + solver.Value(z)); + } + } + + static void Main() + { + SimpleSolve(); + } +} +``` + ## Changing the parameters of the solver The SatParameters protobuf encapsulates the set of parameters of a CP-SAT @@ -19,7 +191,7 @@ from __future__ import print_function from ortools.sat.python import cp_model -def SolveWithTimeLimitSampleSat(): +def MinimalCpSatWithTimeLimit(): """Minimal CP-SAT example to showcase calling the solver.""" # Creates the model. model = cp_model.CpModel() @@ -45,7 +217,7 @@ def SolveWithTimeLimitSampleSat(): print('z = %i' % solver.Value(z)) -SolveWithTimeLimitSampleSat() +MinimalCpSatWithTimeLimit() ``` ### Specifying the time limit in C++ @@ -58,7 +230,7 @@ SolveWithTimeLimitSampleSat() namespace operations_research { namespace sat { -void SolveWithTimeLimitSampleSat() { +void SolveWithTimeLimit() { CpModelBuilder cp_model; const Domain domain(0, 2); @@ -91,7 +263,7 @@ void SolveWithTimeLimitSampleSat() { } // namespace operations_research int main() { - operations_research::sat::SolveWithTimeLimitSampleSat(); + operations_research::sat::SolveWithTimeLimit(); return EXIT_SUCCESS; } @@ -105,8 +277,7 @@ import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.IntVar; -/** Solves a problem with a time limit. */ -public class SolveWithTimeLimitSampleSat { +public class SolveWithTimeLimit { static { System.loadLibrary("jniortools"); } @@ -144,9 +315,9 @@ Parameters must be passed as string to the solver. using System; using Google.OrTools.Sat; -public class SolveWithTimeLimitSampleSat +public class CodeSamplesSat { - static void Main() + static void MinimalCpSatWithTimeLimit() { // Creates the model. CpModel model = new CpModel(); @@ -174,6 +345,11 @@ public class SolveWithTimeLimitSampleSat Console.WriteLine("z = " + solver.Value(z)); } } + + static void Main() + { + MinimalCpSatWithTimeLimit(); + } } ``` @@ -217,20 +393,17 @@ class VarArrayAndObjectiveSolutionPrinter(cp_model.CpSolverSolutionCallback): return self.__solution_count -def SolveAndPrintIntermediateSolutionsSampleSat(): +def MinimalCpSatPrintIntermediateSolutions(): """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 constraints. model.Add(x != y) - model.Maximize(x + 2 * y + 3 * z) # Creates a solver and solves. @@ -242,7 +415,7 @@ def SolveAndPrintIntermediateSolutionsSampleSat(): print('Number of solutions found: %i' % solution_printer.SolutionCount()) -SolveAndPrintIntermediateSolutionsSampleSat() +MinimalCpSatPrintIntermediateSolutions() ``` ### C++ code @@ -254,7 +427,7 @@ SolveAndPrintIntermediateSolutionsSampleSat() namespace operations_research { namespace sat { -void SolveAndPrintIntermediateSolutionsSampleSat() { +void SolveWithIntermediateSolutions() { CpModelBuilder cp_model; const Domain domain(0, 2); @@ -276,9 +449,7 @@ void SolveAndPrintIntermediateSolutionsSampleSat() { LOG(INFO) << " z = " << SolutionIntegerValue(r, z); num_solutions++; })); - const CpSolverResponse response = SolveWithModel(cp_model, &model); - LOG(INFO) << "Number of solutions found: " << num_solutions; } @@ -286,7 +457,7 @@ void SolveAndPrintIntermediateSolutionsSampleSat() { } // namespace operations_research int main() { - operations_research::sat::SolveAndPrintIntermediateSolutionsSampleSat(); + operations_research::sat::SolveWithIntermediateSolutions(); return EXIT_SUCCESS; } @@ -300,8 +471,7 @@ import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolverSolutionCallback; import com.google.ortools.sat.IntVar; -/** Solves an optimization problem and displays all intermediate solutions. */ -public class SolveAndPrintIntermediateSolutionsSampleSat { +public class SolveWithIntermediateSolutions { static { System.loadLibrary("jniortools"); } @@ -331,14 +501,12 @@ public class SolveAndPrintIntermediateSolutionsSampleSat { public static void main(String[] args) throws Exception { // Create the model. CpModel model = new CpModel(); - // Create the variables. int numVals = 3; IntVar x = model.newIntVar(0, numVals - 1, "x"); IntVar y = model.newIntVar(0, numVals - 1, "y"); IntVar z = model.newIntVar(0, numVals - 1, "z"); - // Create the constraint. model.addDifferent(x, y); @@ -392,13 +560,12 @@ public class VarArraySolutionPrinterWithObjective : CpSolverSolutionCallback private IntVar[] variables_; } -public class SolveAndPrintIntermediateSolutionsSampleSat +public class CodeSamplesSat { - static void Main() + static void MinimalCpSatPrintIntermediateSolutions() { // Creates the model. CpModel model = new CpModel(); - // Creates the variables. int num_vals = 3; @@ -417,10 +584,14 @@ public class SolveAndPrintIntermediateSolutionsSampleSat VarArraySolutionPrinterWithObjective cb = new VarArraySolutionPrinterWithObjective(new IntVar[] { x, y, z }); solver.SolveWithSolutionCallback(model, cb); - Console.WriteLine(String.Format("Number of solutions found: {0}", cb.SolutionCount())); } + + static void Main() + { + MinimalCpSatPrintIntermediateSolutions(); + } } ``` @@ -463,17 +634,15 @@ class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback): return self.__solution_count -def SearchForAllSolutionsSampleSat(): +def SolveAllSolutions(): """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') - # Create the constraints. model.Add(x != y) @@ -481,12 +650,11 @@ def SearchForAllSolutionsSampleSat(): solver = cp_model.CpSolver() solution_printer = VarArraySolutionPrinter([x, y, z]) status = solver.SearchForAllSolutions(model, solution_printer) - print('Status = %s' % solver.StatusName(status)) print('Number of solutions found: %i' % solution_printer.SolutionCount()) -SearchForAllSolutionsSampleSat() +SolveAllSolutions() ``` ### C++ code @@ -501,7 +669,7 @@ To search for all solutions, a parameter of the SAT solver must be changed. namespace operations_research { namespace sat { -void SearchAllSolutionsSampleSat() { +void SearchAllSolutions() { CpModelBuilder cp_model; const Domain domain(0, 2); @@ -513,6 +681,11 @@ void SearchAllSolutionsSampleSat() { Model model; + // Tell the solver to enumerate all solutions. + SatParameters parameters; + parameters.set_enumerate_all_solutions(true); + model.Add(NewSatParameters(parameters)); + int num_solutions = 0; model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { LOG(INFO) << "Solution " << num_solutions; @@ -521,13 +694,7 @@ void SearchAllSolutionsSampleSat() { LOG(INFO) << " z = " << SolutionIntegerValue(r, z); num_solutions++; })); - - // Tell the solver to enumerate all solutions. - SatParameters parameters; - parameters.set_enumerate_all_solutions(true); - model.Add(NewSatParameters(parameters)); const CpSolverResponse response = SolveWithModel(cp_model, &model); - LOG(INFO) << "Number of solutions found: " << num_solutions; } @@ -535,7 +702,7 @@ void SearchAllSolutionsSampleSat() { } // namespace operations_research int main() { - operations_research::sat::SearchAllSolutionsSampleSat(); + operations_research::sat::SearchAllSolutions(); return EXIT_SUCCESS; } @@ -549,8 +716,7 @@ import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolverSolutionCallback; import com.google.ortools.sat.IntVar; -/** Code sample that solves a model and displays all solutions. */ -public class SearchForAllSolutionsSampleSat { +public class SolveAllSolutions { static { System.loadLibrary("jniortools"); } @@ -579,14 +745,12 @@ public class SearchForAllSolutionsSampleSat { public static void main(String[] args) throws Exception { // Create the model. CpModel model = new CpModel(); - // Create the variables. int numVals = 3; IntVar x = model.newIntVar(0, numVals - 1, "x"); IntVar y = model.newIntVar(0, numVals - 1, "y"); IntVar z = model.newIntVar(0, numVals - 1, "z"); - // Create the constraints. model.addDifferent(x, y); @@ -638,13 +802,13 @@ public class VarArraySolutionPrinter : CpSolverSolutionCallback private IntVar[] variables_; } -public class SearchForAllSolutionsSampleSat + +public class CodeSamplesSat { - static void Main() + static void MinimalCpSatAllSolutions() { // Creates the model. CpModel model = new CpModel(); - // Creates the variables. int num_vals = 3; @@ -660,10 +824,14 @@ public class SearchForAllSolutionsSampleSat VarArraySolutionPrinter cb = new VarArraySolutionPrinter(new IntVar[] { x, y, z }); solver.SearchAllSolutions(model, cb); - Console.WriteLine(String.Format("Number of solutions found: {0}", cb.SolutionCount())); } + + static void Main() + { + MinimalCpSatAllSolutions(); + } } ``` @@ -710,7 +878,7 @@ class VarArraySolutionPrinterWithLimit(cp_model.CpSolverSolutionCallback): return self.__solution_count -def StopAfterNSolutionsSampleSat(): +def StopAfterNSolutions(): """Showcases calling the solver to search for small number of solutions.""" # Creates the model. model = cp_model.CpModel() @@ -729,7 +897,7 @@ def StopAfterNSolutionsSampleSat(): assert solution_printer.SolutionCount() == 5 -StopAfterNSolutionsSampleSat() +StopAfterNSolutions() ``` ### C++ code @@ -748,7 +916,7 @@ limit, and setting that bool to true. namespace operations_research { namespace sat { -void StopAfterNSolutionsSampleSat() { +void StopAfterNSolutions() { CpModelBuilder cp_model; const Domain domain(0, 2); @@ -789,7 +957,7 @@ void StopAfterNSolutionsSampleSat() { } // namespace operations_research int main() { - operations_research::sat::StopAfterNSolutionsSampleSat(); + operations_research::sat::StopAfterNSolutions(); return EXIT_SUCCESS; } @@ -806,8 +974,7 @@ import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolverSolutionCallback; import com.google.ortools.sat.IntVar; -/** Code sample that solves a model and displays a small number of solutions. */ -public class StopAfterNSolutionsSampleSat { +public class StopAfterNSolutions { static { System.loadLibrary("jniortools"); } @@ -911,9 +1078,9 @@ public class VarArraySolutionPrinterWithLimit : CpSolverSolutionCallback private int solution_limit_; } -public class StopAfterNSolutionsSampleSat +public class CodeSamplesSat { - static void Main() + static void StopAfterNSolutions() { // Creates the model. CpModel model = new CpModel(); @@ -932,5 +1099,10 @@ public class StopAfterNSolutionsSampleSat Console.WriteLine(String.Format("Number of solutions found: {0}", cb.SolutionCount())); } + + static void Main() + { + StopAfterNSolutions(); + } } ``` diff --git a/ortools/sat/drat_checker.cc b/ortools/sat/drat_checker.cc index e79cf38d824..528f12058d2 100644 --- a/ortools/sat/drat_checker.cc +++ b/ortools/sat/drat_checker.cc @@ -15,11 +15,11 @@ #include #include +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "absl/time/clock.h" #include "ortools/base/hash.h" -#include "ortools/base/numbers.h" -#include "ortools/base/split.h" #include "ortools/base/stl_util.h" -#include "ortools/base/time_support.h" #include "ortools/util/time_limit.h" namespace operations_research { @@ -52,7 +52,7 @@ bool DratChecker::Clause::IsDeleted(ClauseIndex clause_index) const { return deleted_index <= clause_index; } -void DratChecker::AddProblemClause(absl::Span clause) { +void DratChecker::AddProblemClause(absl::Span clause) { DCHECK_EQ(first_infered_clause_index_, kNoClauseIndex); const ClauseIndex clause_index = AddClause(clause); @@ -65,7 +65,7 @@ void DratChecker::AddProblemClause(absl::Span clause) { } } -void DratChecker::AddInferedClause(absl::Span clause) { +void DratChecker::AddInferedClause(absl::Span clause) { const ClauseIndex infered_clause_index = AddClause(clause); if (first_infered_clause_index_ == kNoClauseIndex) { first_infered_clause_index_ = infered_clause_index; @@ -85,7 +85,7 @@ void DratChecker::AddInferedClause(absl::Span clause) { } } -ClauseIndex DratChecker::AddClause(absl::Span clause) { +ClauseIndex DratChecker::AddClause(absl::Span clause) { const int first_literal_index = literals_.size(); literals_.insert(literals_.end(), clause.begin(), clause.end()); // Sort the input clause in strictly increasing order (by sorting and then @@ -107,7 +107,7 @@ ClauseIndex DratChecker::AddClause(absl::Span clause) { return ClauseIndex(clauses_.size() - 1); } -void DratChecker::DeleteClause(absl::Span clause) { +void DratChecker::DeleteClause(absl::Span clause) { // Temporarily add 'clause' to find if it has been previously added. const auto it = clause_set_.find(AddClause(clause)); if (it != clause_set_.end()) { @@ -219,7 +219,7 @@ std::vector> DratChecker::GetClausesNeededForProof( for (ClauseIndex i = begin; i < end; ++i) { const Clause& clause = clauses_[i]; if (clause.is_needed_for_proof) { - const absl::Span& literals = Literals(clause); + const absl::Span& literals = Literals(clause); result.emplace_back(literals.begin(), literals.end()); if (clause.rat_literal_index != kNoLiteralIndex) { const int rat_literal_clause_index = @@ -233,9 +233,9 @@ std::vector> DratChecker::GetClausesNeededForProof( return result; } -absl::Span DratChecker::Literals(const Clause& clause) const { - return absl::Span(literals_.data() + clause.first_literal_index, - clause.num_literals); +absl::Span DratChecker::Literals(const Clause& clause) const { + return absl::Span( + literals_.data() + clause.first_literal_index, clause.num_literals); } void DratChecker::Init() { @@ -274,7 +274,7 @@ void DratChecker::WatchClause(ClauseIndex clause_index) { } bool DratChecker::HasRupProperty(ClauseIndex num_clauses, - absl::Span clause) { + absl::Span clause) { ClauseIndex conflict = kNoClauseIndex; for (const Literal literal : clause) { conflict = @@ -456,11 +456,12 @@ void DratChecker::LogStatistics(int64 duration_nanos) const { LOG(INFO) << "verification time: " << 1e-9 * duration_nanos << " s"; } -bool ContainsLiteral(absl::Span clause, Literal literal) { +bool ContainsLiteral(absl::Span clause, Literal literal) { return std::find(clause.begin(), clause.end(), literal) != clause.end(); } -bool Resolve(absl::Span clause, absl::Span other_clause, +bool Resolve(absl::Span clause, + absl::Span other_clause, Literal complementary_literal, VariablesAssignment* assignment, std::vector* resolvent) { DCHECK(ContainsLiteral(clause, complementary_literal)); @@ -507,17 +508,16 @@ bool AddProblemClauses(const std::string& file_path, bool result = true; while (std::getline(file, line)) { line_number++; - std::vector words = - absl::StrSplit(line, absl::delimiter::AnyOf(" \t"), absl::SkipEmpty()); + std::vector words = + absl::StrSplit(line, absl::ByAnyChar(" \t"), absl::SkipWhitespace()); if (words.empty() || words[0] == "c") { // Ignore empty and comment lines. continue; } if (words[0] == "p") { if (num_clauses > 0 || words.size() != 4 || words[1] != "cnf" || - !strings::safe_strto32(words[2], &num_variables) || - num_variables <= 0 || - !strings::safe_strto32(words[3], &num_clauses) || num_clauses <= 0) { + !absl::SimpleAtoi(words[2], &num_variables) || num_variables <= 0 || + !absl::SimpleAtoi(words[3], &num_clauses) || num_clauses <= 0) { LOG(ERROR) << "Invalid content '" << line << "' at line " << line_number << " of " << file_path; result = false; @@ -528,7 +528,7 @@ bool AddProblemClauses(const std::string& file_path, literals.clear(); for (int i = 0; i < words.size(); ++i) { int signed_value; - if (!strings::safe_strto32(words[i], &signed_value) || + if (!absl::SimpleAtoi(words[i], &signed_value) || std::abs(signed_value) > num_variables || (signed_value == 0 && i != words.size() - 1)) { LOG(ERROR) << "Invalid content '" << line << "' at line " << line_number @@ -556,13 +556,13 @@ bool AddInferedAndDeletedClauses(const std::string& file_path, bool result = true; while (std::getline(file, line)) { line_number++; - std::vector words = - absl::StrSplit(line, absl::delimiter::AnyOf(" \t"), absl::SkipEmpty()); + std::vector words = + absl::StrSplit(line, absl::ByAnyChar(" \t"), absl::SkipWhitespace()); bool delete_clause = !words.empty() && words[0] == "d"; literals.clear(); for (int i = (delete_clause ? 1 : 0); i < words.size(); ++i) { int signed_value; - if (!strings::safe_strto32(words[i], &signed_value) || + if (!absl::SimpleAtoi(words[i], &signed_value) || (signed_value == 0 && i != words.size() - 1)) { LOG(ERROR) << "Invalid content '" << line << "' at line " << line_number << " of " << file_path; diff --git a/ortools/sat/drat_checker.h b/ortools/sat/drat_checker.h index 34c24e22434..b266dffa6f3 100644 --- a/ortools/sat/drat_checker.h +++ b/ortools/sat/drat_checker.h @@ -17,10 +17,10 @@ #include #include -#include +#include "absl/container/flat_hash_set.h" +#include "absl/types/span.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" -#include "ortools/base/span.h" #include "ortools/sat/sat_base.h" namespace operations_research { @@ -49,7 +49,7 @@ class DratChecker { // Adds a clause of the problem that must be checked. The problem clauses must // be added first, before any infered clause. The given clause must not // contain a literal and its negation. Must not be called after Check(). - void AddProblemClause(absl::Span clause); + void AddProblemClause(absl::Span clause); // Adds a clause which is infered from the problem clauses and the previously // infered clauses (that are have not been deleted). Infered clauses must be @@ -57,12 +57,12 @@ class DratChecker { // Tautology (RAT) property for literal l must start with this literal. The // given clause must not contain a literal and its negation. Must not be // called after Check(). - void AddInferedClause(absl::Span clause); + void AddInferedClause(absl::Span clause); // Deletes a problem or infered clause. The order of the literals does not // matter. In particular, it can be different from the order that was used // when the clause was added. Must not be called after Check(). - void DeleteClause(absl::Span clause); + void DeleteClause(absl::Span clause); // Checks that the infered clauses form a DRAT proof that the problem clauses // are UNSAT. For this the last added infered clause must be the empty clause @@ -170,13 +170,13 @@ class DratChecker { }; // Adds a clause and returns its index. - ClauseIndex AddClause(absl::Span clause); + ClauseIndex AddClause(absl::Span clause); // Removes the last clause added to 'clauses_'. void RemoveLastClause(); // Returns the literals of the given clause in increasing order. - absl::Span Literals(const Clause& clause) const; + absl::Span Literals(const Clause& clause) const; // Initializes the data structures used to check the DRAT proof. void Init(); @@ -189,7 +189,8 @@ class DratChecker { // deleted clauses whose index is strictly less than 'num_clauses'. If so, // marks the clauses actually used in this process as needed to check to DRAT // proof. - bool HasRupProperty(ClauseIndex num_clauses, absl::Span clause); + bool HasRupProperty(ClauseIndex num_clauses, + absl::Span clause); // Assigns 'literal' to true in 'assignment_' (and pushes it to 'assigned_'), // records its source clause 'source_clause_index' in 'assignment_source_', @@ -228,7 +229,7 @@ class DratChecker { // adding a clause to clauses_, this set can be used to find if the same // clause was previously added (i.e if a find using the new clause index // returns a previous index) and not yet deleted. - std::unordered_set clause_set_; + absl::flat_hash_set clause_set_; // All the literals used in 'clauses_'. std::vector literals_; @@ -296,7 +297,7 @@ class DratChecker { // Returns true if the given clause contains the given literal. This works in // O(clause.size()). -bool ContainsLiteral(absl::Span clause, Literal literal); +bool ContainsLiteral(absl::Span clause, Literal literal); // Returns true if 'complementary_literal' is the unique complementary literal // in the two given clauses. If so the resolvent of these clauses (i.e. their @@ -305,7 +306,8 @@ bool ContainsLiteral(absl::Span clause, Literal literal); // 'other_clause' must contain its negation. 'assignment' must have at least as // many variables as each clause, and they must all be unassigned. They are // still unassigned upon return. -bool Resolve(absl::Span clause, absl::Span other_clause, +bool Resolve(absl::Span clause, + absl::Span other_clause, Literal complementary_literal, VariablesAssignment* assignment, std::vector* resolvent); diff --git a/ortools/sat/drat_proof_handler.cc b/ortools/sat/drat_proof_handler.cc index 7706ac06b61..08b5ba70750 100644 --- a/ortools/sat/drat_proof_handler.cc +++ b/ortools/sat/drat_proof_handler.cc @@ -15,9 +15,9 @@ #include +#include "absl/memory/memory.h" #include "ortools/base/int_type.h" #include "ortools/base/logging.h" -#include "ortools/base/memory.h" namespace operations_research { namespace sat { @@ -62,13 +62,13 @@ void DratProofHandler::AddOneVariable() { reverse_mapping_.push_back(BooleanVariable(variable_index_++)); } -void DratProofHandler::AddProblemClause(absl::Span clause) { +void DratProofHandler::AddProblemClause(absl::Span clause) { if (drat_checker_ != nullptr) { drat_checker_->AddProblemClause(clause); } } -void DratProofHandler::AddClause(absl::Span clause) { +void DratProofHandler::AddClause(absl::Span clause) { MapClause(clause); if (drat_checker_ != nullptr) { drat_checker_->AddInferedClause(values_); @@ -78,7 +78,7 @@ void DratProofHandler::AddClause(absl::Span clause) { } } -void DratProofHandler::DeleteClause(absl::Span clause) { +void DratProofHandler::DeleteClause(absl::Span clause) { MapClause(clause); if (drat_checker_ != nullptr) { drat_checker_->DeleteClause(values_); @@ -97,7 +97,7 @@ DratChecker::Status DratProofHandler::Check(double max_time_in_seconds) { return DratChecker::Status::UNKNOWN; } -void DratProofHandler::MapClause(absl::Span clause) { +void DratProofHandler::MapClause(absl::Span clause) { values_.clear(); for (const Literal l : clause) { CHECK_LT(l.Variable(), reverse_mapping_.size()); diff --git a/ortools/sat/drat_proof_handler.h b/ortools/sat/drat_proof_handler.h index b066ff070a7..970cca619eb 100644 --- a/ortools/sat/drat_proof_handler.h +++ b/ortools/sat/drat_proof_handler.h @@ -18,8 +18,8 @@ #include #include +#include "absl/types/span.h" #include "ortools/base/int_type_indexed_vector.h" -#include "ortools/base/span.h" #include "ortools/sat/drat_checker.h" #include "ortools/sat/drat_writer.h" #include "ortools/sat/sat_base.h" @@ -65,13 +65,13 @@ class DratProofHandler { // Adds a clause of the UNSAT problem. This must be called before any call to // AddClause() or DeleteClause(), in order to be able to check the DRAT proof // with the Check() method when it is complete. - void AddProblemClause(absl::Span clause); + void AddProblemClause(absl::Span clause); // Writes a new clause to the DRAT output. The output clause is sorted so that // newer variables always comes first. This is needed because in the DRAT // format, the clause is checked for the RAT property with only its first // literal. Must not be called after Check(). - void AddClause(absl::Span clause); + void AddClause(absl::Span clause); // Writes a "deletion" information about a clause that has been added before // to the DRAT output. Note that it is also possible to delete a clause from @@ -80,7 +80,7 @@ class DratProofHandler { // Because of a limitation a the DRAT-trim tool, it seems the order of the // literals during addition and deletion should be EXACTLY the same. Because // of this we get warnings for problem clauses. - void DeleteClause(absl::Span clause); + void DeleteClause(absl::Span clause); // Returns VALID if the DRAT proof is correct, INVALID if it is not correct, // or UNKNOWN if proof checking was not enabled (by choosing the right @@ -92,7 +92,7 @@ class DratProofHandler { DratChecker::Status Check(double max_time_in_seconds); private: - void MapClause(absl::Span clause); + void MapClause(absl::Span clause); // We need to keep track of the variable newly created. int variable_index_; diff --git a/ortools/sat/drat_writer.cc b/ortools/sat/drat_writer.cc index 4bbb6b4b233..9b8c82afe22 100644 --- a/ortools/sat/drat_writer.cc +++ b/ortools/sat/drat_writer.cc @@ -15,8 +15,8 @@ #include +#include "absl/strings/str_format.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #if !defined(__PORTABLE_PLATFORM__) #endif // !__PORTABLE_PLATFORM__ #include "ortools/base/status.h" @@ -33,14 +33,16 @@ DratWriter::~DratWriter() { } } -void DratWriter::AddClause(absl::Span clause) { WriteClause(clause); } +void DratWriter::AddClause(absl::Span clause) { + WriteClause(clause); +} -void DratWriter::DeleteClause(absl::Span clause) { +void DratWriter::DeleteClause(absl::Span clause) { buffer_ += "d "; WriteClause(clause); } -void DratWriter::WriteClause(absl::Span clause) { +void DratWriter::WriteClause(absl::Span clause) { for (const Literal literal : clause) { absl::StrAppendFormat(&buffer_, "%d ", literal.SignedValue()); } diff --git a/ortools/sat/drat_writer.h b/ortools/sat/drat_writer.h index 0071adf80d9..6b9a0d81f63 100644 --- a/ortools/sat/drat_writer.h +++ b/ortools/sat/drat_writer.h @@ -21,7 +21,7 @@ #else class File {}; #endif // !__PORTABLE_PLATFORM__ -#include "ortools/base/span.h" +#include "absl/types/span.h" #include "ortools/sat/sat_base.h" namespace operations_research { @@ -41,15 +41,15 @@ class DratWriter { // Writes a new clause to the DRAT output. Note that the RAT property is only // checked on the first literal. - void AddClause(absl::Span clause); + void AddClause(absl::Span clause); // Writes a "deletion" information about a clause that has been added before // to the DRAT output. Note that it is also possible to delete a clause from // the problem. - void DeleteClause(absl::Span clause); + void DeleteClause(absl::Span clause); private: - void WriteClause(absl::Span clause); + void WriteClause(absl::Span clause); // TODO(user): Support binary format as proof in text format can be large. bool in_binary_format_; diff --git a/ortools/sat/integer.cc b/ortools/sat/integer.cc index c23e7ab6dfd..3edd99678ab 100644 --- a/ortools/sat/integer.cc +++ b/ortools/sat/integer.cc @@ -36,7 +36,7 @@ std::vector NegationOf( void IntegerEncoder::FullyEncodeVariable(IntegerVariable var) { CHECK(!VariableIsFullyEncoded(var)); CHECK_EQ(0, sat_solver_->CurrentDecisionLevel()); - CHECK(!(*domains_)[var].empty()); // UNSAT. We don't deal with that here. + CHECK(!(*domains_)[var].IsEmpty()); // UNSAT. We don't deal with that here. std::vector values; for (const ClosedInterval interval : (*domains_)[var]) { @@ -136,12 +136,21 @@ IntegerEncoder::PartialDomainEncoding(IntegerVariable var) const { // too. Except for the min/max of the initial domain. if (var >= encoding_by_var_.size()) return encoding; - std::set possible_values; - possible_values.insert(IntegerValue((*domains_)[var].front().start)); - possible_values.insert(IntegerValue((*domains_)[var].back().end)); - for (const auto entry : encoding_by_var_[var]) { - possible_values.insert(entry.first); + std::vector possible_values; + { + const IntegerValue min_value((*domains_)[var].Min()); + const IntegerValue max_value((*domains_)[var].Max()); + possible_values.push_back(min_value); + for (const auto entry : encoding_by_var_[var]) { + if (entry.first >= max_value) break; + if (entry.first > min_value) { + possible_values.push_back(entry.first); + } + } + possible_values.push_back(max_value); + DCHECK(std::is_sorted(possible_values.begin(), possible_values.end())); } + for (const IntegerValue value : possible_values) { const std::pair key{var, value}; const auto it = equality_to_associated_literal_.find(key); @@ -218,8 +227,8 @@ std::pair IntegerEncoder::Canonicalize( const IntegerVariable var(i_lit.var); IntegerValue after(i_lit.bound); IntegerValue before(i_lit.bound - 1); - CHECK_GE(before, (*domains_)[var].front().start); - CHECK_LE(after, (*domains_)[var].back().end); + CHECK_GE(before, (*domains_)[var].Min()); + CHECK_LE(after, (*domains_)[var].Max()); int64 previous = kint64min; for (const ClosedInterval& interval : (*domains_)[var]) { if (before > previous && before < interval.start) before = previous; @@ -232,10 +241,10 @@ std::pair IntegerEncoder::Canonicalize( } Literal IntegerEncoder::GetOrCreateAssociatedLiteral(IntegerLiteral i_lit) { - if (i_lit.bound <= (*domains_)[i_lit.var].front().start) { + if (i_lit.bound <= (*domains_)[i_lit.var].Min()) { return GetTrueLiteral(); } - if (i_lit.bound > (*domains_)[i_lit.var].back().end) { + if (i_lit.bound > (*domains_)[i_lit.var].Max()) { return GetFalseLiteral(); } @@ -273,8 +282,8 @@ Literal IntegerEncoder::GetOrCreateLiteralAssociatedToEquality( void IntegerEncoder::AssociateToIntegerLiteral(Literal literal, IntegerLiteral i_lit) { const auto& domain = (*domains_)[i_lit.var]; - const IntegerValue min(domain.front().start); - const IntegerValue max(domain.back().end); + const IntegerValue min(domain.Min()); + const IntegerValue max(domain.Max()); if (i_lit.bound <= min) { sat_solver_->AddUnitClause(literal); } else if (i_lit.bound > max) { @@ -302,7 +311,7 @@ void IntegerEncoder::AssociateToIntegerEqualValue(Literal literal, // Detect literal view. Note that the same literal can be associated to more // than one variable, and thus already have a view. We don't change it in // this case. - const Domain domain = Domain::FromIntervals((*domains_)[var]); + const Domain& domain = (*domains_)[var]; if (value == 1 && domain.Min() >= 0 && domain.Max() <= 1) { if (literal.Index() >= literal_view_.size()) { literal_view_.resize(literal.Index().value() + 1, kNoIntegerVariable); @@ -522,7 +531,7 @@ IntegerVariable IntegerTrail::AddIntegerVariable(IntegerValue lower_bound, vars_.push_back({lower_bound, static_cast(integer_trail_.size())}); var_trail_index_cache_.push_back(integer_trail_.size()); integer_trail_.push_back({lower_bound, i}); - domains_->push_back({{lower_bound.value(), upper_bound.value()}}); + domains_->push_back(Domain(lower_bound.value(), upper_bound.value())); // TODO(user): the is_ignored_literals_ Booleans are currently always the same // for a variable and its negation. So it may be better not to store it twice @@ -532,7 +541,7 @@ IntegerVariable IntegerTrail::AddIntegerVariable(IntegerValue lower_bound, vars_.push_back({-upper_bound, static_cast(integer_trail_.size())}); var_trail_index_cache_.push_back(integer_trail_.size()); integer_trail_.push_back({-upper_bound, NegationOf(i)}); - domains_->push_back({{-upper_bound.value(), -lower_bound.value()}}); + domains_->push_back(Domain(-upper_bound.value(), -lower_bound.value())); for (SparseBitset* w : watchers_) { w->Resize(NumIntegerVariables()); @@ -548,12 +557,8 @@ IntegerVariable IntegerTrail::AddIntegerVariable(const Domain& domain) { return var; } -// Because we use InlinedVectors internally, we need the conversion. -// -// TODO(user): we could return a reference, but this is likely not in any -// critical code path. -Domain IntegerTrail::InitialVariableDomain(IntegerVariable var) const { - return Domain::FromIntervals((*domains_)[var]); +const Domain& IntegerTrail::InitialVariableDomain(IntegerVariable var) const { + return (*domains_)[var]; } bool IntegerTrail::UpdateInitialDomain(IntegerVariable var, Domain domain) { @@ -561,21 +566,15 @@ bool IntegerTrail::UpdateInitialDomain(IntegerVariable var, Domain domain) { // TODO(user): A bit inefficient as this recreate a vector for no reason. // The IntersectionOfSortedDisjointIntervals() should take a Span<> instead. - const Domain old_domain = InitialVariableDomain(var); + const Domain& old_domain = InitialVariableDomain(var); domain = domain.IntersectionWith(old_domain); if (old_domain == domain) return true; if (domain.IsEmpty()) return false; - { - const std::vector temp = domain.intervals(); - (*domains_)[var].assign(temp.begin(), temp.end()); - } - { - const std::vector temp = domain.Negation().intervals(); - (*domains_)[NegationOf(var)].assign(temp.begin(), temp.end()); - } + (*domains_)[var] = domain; + (*domains_)[NegationOf(var)] = domain.Negation(); - if (domain.intervals().size() > 1) { + if (domain.NumIntervals() > 1) { var_to_current_lb_interval_index_.Set(var, 0); var_to_current_lb_interval_index_.Set(NegationOf(var), 0); } @@ -590,13 +589,14 @@ bool IntegerTrail::UpdateInitialDomain(IntegerVariable var, Domain domain) { {}, {})); // Set to false excluded literals. + // TODO(user): This is only needed to propagate holes and is a bit slow, I am + // not sure it is worthwhile. int i = 0; int num_fixed = 0; const auto encoding = encoder_->PartialDomainEncoding(var); - const auto intervals = domain.intervals(); for (const auto pair : encoding) { - while (i < intervals.size() && pair.value > intervals[i].end) ++i; - if (i == intervals.size() || pair.value < intervals[i].start) { + while (i < domain.NumIntervals() && pair.value > domain[i].end) ++i; + if (i == domain.NumIntervals() || pair.value < domain[i].start) { // Set the literal to false; ++num_fixed; if (trail_->Assignment().LiteralIsTrue(pair.literal)) return false; @@ -676,7 +676,7 @@ int IntegerTrail::FindLowestTrailIndexThatExplainBound( // // TODO(user): use priority queue instead of O(n^2) algo. void IntegerTrail::RelaxLinearReason( - IntegerValue slack, absl::Span coeffs, + IntegerValue slack, absl::Span coeffs, std::vector* reason) const { CHECK_GE(slack, 0); if (slack == 0) return; @@ -726,8 +726,8 @@ void IntegerTrail::RemoveLevelZeroBounds( bool IntegerTrail::EnqueueAssociatedLiteral( Literal literal, int trail_index_with_same_reason, - absl::Span literal_reason, - absl::Span integer_reason, + absl::Span literal_reason, + absl::Span integer_reason, BooleanVariable* variable_with_same_reason) { if (!trail_->Assignment().VariableIsAssigned(literal.Variable())) { if (integer_search_levels_.empty()) { @@ -776,8 +776,8 @@ bool IntegerTrail::EnqueueAssociatedLiteral( namespace { -std::string ReasonDebugString(absl::Span literal_reason, - absl::Span integer_reason) { +std::string ReasonDebugString(absl::Span literal_reason, + absl::Span integer_reason) { std::string result = "literals:{"; for (const Literal l : literal_reason) { if (result.back() != '{') result += ","; @@ -814,13 +814,14 @@ std::string IntegerTrail::DebugString() { } bool IntegerTrail::Enqueue(IntegerLiteral i_lit, - absl::Span literal_reason, - absl::Span integer_reason) { + absl::Span literal_reason, + absl::Span integer_reason) { return Enqueue(i_lit, literal_reason, integer_reason, integer_trail_.size()); } -bool IntegerTrail::ReasonIsValid(absl::Span literal_reason, - absl::Span integer_reason) { +bool IntegerTrail::ReasonIsValid( + absl::Span literal_reason, + absl::Span integer_reason) { const VariablesAssignment& assignment = trail_->Assignment(); for (const Literal lit : literal_reason) { if (!assignment.LiteralIsFalse(lit)) return false; @@ -842,12 +843,34 @@ bool IntegerTrail::ReasonIsValid(absl::Span literal_reason, return false; } } + + // This may not indicate an incorectness, but just some propagators that + // didn't reach a fixed-point at level zero. + if (!integer_search_levels_.empty()) { + int num_literal_assigned_after_root_node = 0; + for (const Literal lit : literal_reason) { + if (trail_->Info(lit.Variable()).level > 0) { + num_literal_assigned_after_root_node++; + } + } + for (const IntegerLiteral i_lit : integer_reason) { + if (LevelZeroBound(i_lit.var) < i_lit.bound) { + num_literal_assigned_after_root_node++; + } + } + LOG_IF(WARNING, num_literal_assigned_after_root_node == 0) + << "Propagating a literal with no reason at a positive level!\n" + << "level:" << integer_search_levels_.size() << " " + << ReasonDebugString(literal_reason, integer_reason) << "\n" + << DebugString(); + } + return true; } bool IntegerTrail::Enqueue(IntegerLiteral i_lit, - absl::Span literal_reason, - absl::Span integer_reason, + absl::Span literal_reason, + absl::Span integer_reason, int trail_index_with_same_reason) { DCHECK(ReasonIsValid(literal_reason, integer_reason)); @@ -860,18 +883,6 @@ bool IntegerTrail::Enqueue(IntegerLiteral i_lit, if (i_lit.bound <= vars_[i_lit.var].current_bound) return true; ++num_enqueues_; - // This may not indicate an incorectness, but just some propagators that - // didn't reach a fixed-point at level zero. - if (DEBUG_MODE && !integer_search_levels_.empty()) { - std::vector l(literal_reason.begin(), literal_reason.end()); - MergeReasonInto(integer_reason, &l); - LOG_IF(WARNING, l.empty()) - << "Propagating a literal with no reason at a positive level!\n" - << "level:" << integer_search_levels_.size() << " i_lit:" << i_lit - << " " << ReasonDebugString(literal_reason, integer_reason) << "\n" - << DebugString(); - } - const IntegerVariable var(i_lit.var); // If the domain of var is not a single intervals and i_lit.bound fall into a @@ -880,10 +891,10 @@ bool IntegerTrail::Enqueue(IntegerLiteral i_lit, // // Note: The literals in the reason are not necessarily canonical, but then // we always map these to enqueued literals during conflict resolution. - if ((*domains_)[var].size() > 1) { + if ((*domains_)[var].NumIntervals() > 1) { const auto& domain = (*domains_)[var]; int index = var_to_current_lb_interval_index_.FindOrDie(var); - const int size = domain.size(); + const int size = domain.NumIntervals(); while (index < size && i_lit.bound > domain[index].end) { ++index; } @@ -1069,7 +1080,7 @@ std::vector IntegerTrail::ReasonFor(IntegerLiteral literal) const { // TODO(user): If this is called many time on the same variables, it could be // made faster by using some caching mecanism. -void IntegerTrail::MergeReasonInto(absl::Span literals, +void IntegerTrail::MergeReasonInto(absl::Span literals, std::vector* output) const { DCHECK(tmp_queue_.empty()); const int size = vars_.size(); @@ -1194,8 +1205,8 @@ void IntegerTrail::MergeReasonIntoInternal(std::vector* output) const { } } -absl::Span IntegerTrail::Reason(const Trail& trail, - int trail_index) const { +absl::Span IntegerTrail::Reason(const Trail& trail, + int trail_index) const { const int index = boolean_trail_index_to_integer_one_[trail_index]; std::vector* reason = trail.GetEmptyVectorToStoreReason(trail_index); added_variables_.ClearAndResize(BooleanVariable(trail_->NumVariables())); @@ -1211,9 +1222,9 @@ absl::Span IntegerTrail::Reason(const Trail& trail, return *reason; } -void IntegerTrail::EnqueueLiteral(Literal literal, - absl::Span literal_reason, - absl::Span integer_reason) { +void IntegerTrail::EnqueueLiteral( + Literal literal, absl::Span literal_reason, + absl::Span integer_reason) { DCHECK(!trail_->Assignment().LiteralIsAssigned(literal)); DCHECK(ReasonIsValid(literal_reason, integer_reason)); if (integer_search_levels_.empty()) { @@ -1222,18 +1233,6 @@ void IntegerTrail::EnqueueLiteral(Literal literal, return; } - // This may not indicate an incorectness, but just some propagators that - // didn't reach a fixed-point at level zero. - if (DEBUG_MODE && !integer_search_levels_.empty()) { - std::vector l(literal_reason.begin(), literal_reason.end()); - MergeReasonInto(integer_reason, &l); - LOG_IF(WARNING, l.empty()) - << "Propagating a literal with no reason at a positive level!\n" - << "level:" << integer_search_levels_.size() << " lit:" << literal - << " " << ReasonDebugString(literal_reason, integer_reason) << "\n" - << DebugString(); - } - const int trail_index = trail_->Index(); if (trail_index >= boolean_trail_index_to_integer_one_.size()) { boolean_trail_index_to_integer_one_.resize(trail_index + 1); diff --git a/ortools/sat/integer.h b/ortools/sat/integer.h index 9555787d679..323cadeee9e 100644 --- a/ortools/sat/integer.h +++ b/ortools/sat/integer.h @@ -20,22 +20,20 @@ #include #include #include -#include #include #include -#include +#include "absl/container/flat_hash_map.h" +#include "absl/container/inlined_vector.h" +#include "absl/strings/str_cat.h" +#include "absl/types/span.h" #include "ortools/base/hash.h" -#include "ortools/base/inlined_vector.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/map_util.h" -#include "ortools/base/port.h" -#include "ortools/base/span.h" #include "ortools/graph/iterators.h" #include "ortools/sat/model.h" #include "ortools/sat/sat_base.h" @@ -142,9 +140,7 @@ inline std::ostream& operator<<(std::ostream& os, IntegerLiteral i_lit) { using InlinedIntegerLiteralVector = absl::InlinedVector; // A singleton that holds the INITIAL integer variable domains. -struct IntegerDomains - : public gtl::ITIVector> { +struct IntegerDomains : public gtl::ITIVector { explicit IntegerDomains(Model* model) {} }; @@ -366,6 +362,9 @@ class IntegerEncoder { // We keep all the literals associated to an Integer variable in a map ordered // by bound (so we can properly add implications between the literals // corresponding to the same variable). + // + // TODO(user): Remove the entry no longer needed because of level zero + // propagations. gtl::ITIVector> encoding_by_var_; @@ -381,7 +380,7 @@ class IntegerEncoder { // Mapping (variable == value) -> associated literal. Note that even if // there is more than one literal associated to the same fact, we just keep // the first one that was added. - std::unordered_map, Literal> + absl::flat_hash_map, Literal> equality_to_associated_literal_; // Variables that are fully encoded. @@ -415,7 +414,8 @@ class IntegerTrail : public SatPropagator { // correct state before calling any of its functions. bool Propagate(Trail* trail) final; void Untrail(const Trail& trail, int literal_trail_index) final; - absl::Span Reason(const Trail& trail, int trail_index) const final; + absl::Span Reason(const Trail& trail, + int trail_index) const final; // Returns the number of created integer variables. // @@ -436,7 +436,7 @@ class IntegerTrail : public SatPropagator { // Returns the initial domain of the given variable. Note that the min/max // are updated with level zero propagation, but not holes. - Domain InitialVariableDomain(IntegerVariable var) const; + const Domain& InitialVariableDomain(IntegerVariable var) const; // Takes the intersection with the current initial variable domain. // @@ -531,7 +531,7 @@ class IntegerTrail : public SatPropagator { // // TODO(user): Test that the code work in the presence of integer overflow. void RelaxLinearReason(IntegerValue slack, - absl::Span coeffs, + absl::Span coeffs, std::vector* reason) const; // Removes from the reasons the literal that are always true. @@ -557,23 +557,23 @@ class IntegerTrail : public SatPropagator { // TODO(user): If the given bound is equal to the current bound, maybe the new // reason is better? how to decide and what to do in this case? to think about // it. Currently we simply don't do anything. - MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, - absl::Span literal_reason, - absl::Span integer_reason); + ABSL_MUST_USE_RESULT bool Enqueue( + IntegerLiteral i_lit, absl::Span literal_reason, + absl::Span integer_reason); // Same as Enqueue(), but takes an extra argument which if smaller than // integer_trail_.size() is interpreted as the trail index of an old Enqueue() // that had the same reason as this one. Note that the given Span must still // be valid as they are used in case of conflict. - MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, - absl::Span literal_reason, - absl::Span integer_reason, - int trail_index_with_same_reason); + ABSL_MUST_USE_RESULT bool Enqueue( + IntegerLiteral i_lit, absl::Span literal_reason, + absl::Span integer_reason, + int trail_index_with_same_reason); // Enqueues the given literal on the trail. // See the comment of Enqueue() for the reason format. - void EnqueueLiteral(Literal literal, absl::Span literal_reason, - absl::Span integer_reason); + void EnqueueLiteral(Literal literal, absl::Span literal_reason, + absl::Span integer_reason); // Returns the reason (as set of Literal currently false) for a given integer // literal. Note that the bound must be less restrictive than the current @@ -582,7 +582,7 @@ class IntegerTrail : public SatPropagator { // Appends the reason for the given integer literals to the output and call // STLSortAndRemoveDuplicates() on it. - void MergeReasonInto(absl::Span literals, + void MergeReasonInto(absl::Span literals, std::vector* output) const; // Returns the number of enqueues that changed a variable bounds. We don't @@ -603,15 +603,15 @@ class IntegerTrail : public SatPropagator { // Helper functions to report a conflict. Always return false so a client can // simply do: return integer_trail_->ReportConflict(...); - bool ReportConflict(absl::Span literal_reason, - absl::Span integer_reason) { + bool ReportConflict(absl::Span literal_reason, + absl::Span integer_reason) { DCHECK(ReasonIsValid(literal_reason, integer_reason)); std::vector* conflict = trail_->MutableConflict(); conflict->assign(literal_reason.begin(), literal_reason.end()); MergeReasonInto(integer_reason, conflict); return false; } - bool ReportConflict(absl::Span integer_reason) { + bool ReportConflict(absl::Span integer_reason) { DCHECK(ReasonIsValid({}, integer_reason)); std::vector* conflict = trail_->MutableConflict(); conflict->clear(); @@ -647,8 +647,8 @@ class IntegerTrail : public SatPropagator { private: // Used for DHECKs to validate the reason given to the public functions above. // Tests that all Literal are false. Tests that all IntegerLiteral are true. - bool ReasonIsValid(absl::Span literal_reason, - absl::Span integer_reason); + bool ReasonIsValid(absl::Span literal_reason, + absl::Span integer_reason); // Does the work of MergeReasonInto() when queue_ is already initialized. void MergeReasonIntoInternal(std::vector* output) const; @@ -657,8 +657,8 @@ class IntegerTrail : public SatPropagator { // an integer literal and maintained by encoder_. bool EnqueueAssociatedLiteral(Literal literal, int trail_index_with_same_reason, - absl::Span literal_reason, - absl::Span integer_reason, + absl::Span literal_reason, + absl::Span integer_reason, BooleanVariable* variable_with_same_reason); // Returns the lowest trail index of a TrailEntry that can be used to explain @@ -700,7 +700,7 @@ class IntegerTrail : public SatPropagator { // Used by GetOrCreateConstantIntegerVariable() to return already created // constant variables that share the same value. - std::unordered_map constant_map_; + absl::flat_hash_map constant_map_; // The integer trail. It always start by num_vars sentinel values with the // level 0 bounds (in one to one correspondence with vars_). @@ -738,7 +738,7 @@ class IntegerTrail : public SatPropagator { // // TODO(user): Avoid using hash_map here, a simple vector should be more // efficient, but we need the "rev" aspect. - RevMap> + RevMap> var_to_current_lb_interval_index_; // Temporary data used by MergeReasonInto(). diff --git a/ortools/sat/integer_expr.cc b/ortools/sat/integer_expr.cc index 25292e7de46..d10a1c8f443 100644 --- a/ortools/sat/integer_expr.cc +++ b/ortools/sat/integer_expr.cc @@ -15,8 +15,8 @@ #include #include -#include +#include "absl/container/flat_hash_map.h" #include "ortools/base/stl_util.h" #include "ortools/util/sorted_interval_list.h" @@ -531,7 +531,7 @@ std::function IsOneOf(IntegerVariable var, CHECK(!values.empty()); CHECK_EQ(values.size(), selectors.size()); std::vector unique_values; - std::unordered_map> value_to_selector; + absl::flat_hash_map> value_to_selector; for (int i = 0; i < values.size(); ++i) { unique_values.push_back(values[i].value()); value_to_selector[values[i].value()].push_back(selectors[i]); diff --git a/ortools/sat/intervals.cc b/ortools/sat/intervals.cc index 4573fe3824f..293f3747095 100644 --- a/ortools/sat/intervals.cc +++ b/ortools/sat/intervals.cc @@ -87,11 +87,15 @@ SchedulingConstraintHelper::SchedulingConstraintHelper( task_by_increasing_min_end_.resize(num_tasks); task_by_decreasing_max_start_.resize(num_tasks); task_by_decreasing_max_end_.resize(num_tasks); + task_by_increasing_shifted_start_min_.resize(num_tasks); + task_by_decreasing_shifted_end_max_.resize(num_tasks); for (int t = 0; t < num_tasks; ++t) { task_by_increasing_min_start_[t].task_index = t; task_by_increasing_min_end_[t].task_index = t; task_by_decreasing_max_start_[t].task_index = t; task_by_decreasing_max_end_[t].task_index = t; + task_by_increasing_shifted_start_min_[t].task_index = t; + task_by_decreasing_shifted_end_max_[t].task_index = t; } } @@ -103,6 +107,8 @@ void SchedulingConstraintHelper::SetTimeDirection(bool is_forward) { std::swap(end_vars_, minus_start_vars_); std::swap(task_by_increasing_min_start_, task_by_decreasing_max_end_); std::swap(task_by_increasing_min_end_, task_by_decreasing_max_start_); + std::swap(task_by_increasing_shifted_start_min_, + task_by_decreasing_shifted_end_max_); } const std::vector& @@ -154,6 +160,18 @@ SchedulingConstraintHelper::TaskByDecreasingEndMax() { return task_by_decreasing_max_end_; } +const std::vector& +SchedulingConstraintHelper::TaskByIncreasingShiftedStartMin() { + const int num_tasks = NumTasks(); + for (int i = 0; i < num_tasks; ++i) { + TaskTime& ref = task_by_increasing_shifted_start_min_[i]; + ref.time = ShiftedStartMin(ref.task_index); + } + IncrementalSort(task_by_increasing_shifted_start_min_.begin(), + task_by_increasing_shifted_start_min_.end()); + return task_by_increasing_shifted_start_min_; +} + // Produces a relaxed reason for StartMax(before) < EndMin(after). void SchedulingConstraintHelper::AddReasonForBeingBefore(int before, int after) { diff --git a/ortools/sat/intervals.h b/ortools/sat/intervals.h index 984666b4384..5371395c97f 100644 --- a/ortools/sat/intervals.h +++ b/ortools/sat/intervals.h @@ -142,6 +142,22 @@ class SchedulingConstraintHelper { IntegerValue EndMin(int t) const; IntegerValue EndMax(int t) const; + // In the presense of tasks with a variable duration, we do not necessarily + // have start_min + duration_min = end_min, we can instead have a situation + // like: + // | |<- duration-min ->| + // ^ ^ ^ + // start-min | end-min + // | + // We define the "shifted start min" to be the right most time such that + // we known that we must have min-duration "energy" to the right of it if the + // task is present. Using it in our scheduling propagators allows to propagate + // more in the presence of tasks with variable duration (or optional task + // where we also do not necessarily have start_min + duration_min = end_min. + // + // To explain this shifted start min, one must use the AddEnergyAfterReason(). + IntegerValue ShiftedStartMin(int t) const; + bool StartIsFixed(int t) const; bool EndIsFixed(int t) const; @@ -168,6 +184,7 @@ class SchedulingConstraintHelper { const std::vector& TaskByIncreasingEndMin(); const std::vector& TaskByDecreasingStartMax(); const std::vector& TaskByDecreasingEndMax(); + const std::vector& TaskByIncreasingShiftedStartMin(); // Functions to clear and then set the current reason. void ClearReason(); @@ -178,6 +195,7 @@ class SchedulingConstraintHelper { void AddStartMaxReason(int t, IntegerValue upper_bound); void AddEndMinReason(int t, IntegerValue lower_bound); void AddEndMaxReason(int t, IntegerValue upper_bound); + void AddEnergyAfterReason(int t, IntegerValue energy_min, IntegerValue time); // Adds the reason why task "before" must be before task "after". // That is StartMax(before) < EndMin(after). @@ -199,11 +217,11 @@ class SchedulingConstraintHelper { // conditionned on its presence. The functions will do the correct thing // depending on whether or not the start_min/end_max are optional variables // whose presence implies the interval presence. - MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_min_start); - MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue new_max_end); - MUST_USE_RESULT bool PushTaskAbsence(int t); - MUST_USE_RESULT bool PushIntegerLiteral(IntegerLiteral bound); - MUST_USE_RESULT bool ReportConflict(); + ABSL_MUST_USE_RESULT bool IncreaseStartMin(int t, IntegerValue new_min_start); + ABSL_MUST_USE_RESULT bool DecreaseEndMax(int t, IntegerValue new_max_end); + ABSL_MUST_USE_RESULT bool PushTaskAbsence(int t); + ABSL_MUST_USE_RESULT bool PushIntegerLiteral(IntegerLiteral bound); + ABSL_MUST_USE_RESULT bool ReportConflict(); // Returns the underlying integer variables. const std::vector& StartVars() const { return start_vars_; } @@ -243,6 +261,9 @@ class SchedulingConstraintHelper { std::vector task_by_decreasing_max_start_; std::vector task_by_decreasing_max_end_; + std::vector task_by_increasing_shifted_start_min_; + std::vector task_by_decreasing_shifted_end_max_; + // Reason vectors. std::vector literal_reason_; std::vector integer_reason_; @@ -280,6 +301,11 @@ inline IntegerValue SchedulingConstraintHelper::EndMax(int t) const { return integer_trail_->UpperBound(end_vars_[t]); } +// for optional interval, we don't necessarily have start + duration = end. +inline IntegerValue SchedulingConstraintHelper::ShiftedStartMin(int t) const { + return std::max(StartMin(t), EndMin(t) - DurationMin(t)); +} + inline bool SchedulingConstraintHelper::StartIsFixed(int t) const { return StartMin(t) == StartMax(t); } @@ -323,6 +349,7 @@ inline void SchedulingConstraintHelper::AddDurationMinReason(int t) { inline void SchedulingConstraintHelper::AddDurationMinReason( int t, IntegerValue lower_bound) { if (duration_vars_[t] != kNoIntegerVariable) { + DCHECK_GE(DurationMin(t), lower_bound); integer_reason_.push_back( IntegerLiteral::GreaterOrEqual(duration_vars_[t], lower_bound)); } @@ -330,28 +357,42 @@ inline void SchedulingConstraintHelper::AddDurationMinReason( inline void SchedulingConstraintHelper::AddStartMinReason( int t, IntegerValue lower_bound) { + DCHECK_GE(StartMin(t), lower_bound); integer_reason_.push_back( IntegerLiteral::GreaterOrEqual(start_vars_[t], lower_bound)); } inline void SchedulingConstraintHelper::AddStartMaxReason( int t, IntegerValue upper_bound) { + DCHECK_LE(StartMax(t), upper_bound); integer_reason_.push_back( IntegerLiteral::LowerOrEqual(start_vars_[t], upper_bound)); } inline void SchedulingConstraintHelper::AddEndMinReason( int t, IntegerValue lower_bound) { + DCHECK_GE(EndMin(t), lower_bound); integer_reason_.push_back( IntegerLiteral::GreaterOrEqual(end_vars_[t], lower_bound)); } inline void SchedulingConstraintHelper::AddEndMaxReason( int t, IntegerValue upper_bound) { + DCHECK_LE(EndMax(t), upper_bound); integer_reason_.push_back( IntegerLiteral::LowerOrEqual(end_vars_[t], upper_bound)); } +inline void SchedulingConstraintHelper::AddEnergyAfterReason( + int t, IntegerValue energy_min, IntegerValue time) { + if (StartMin(t) >= time) { + AddStartMinReason(t, time); + } else { + AddEndMinReason(t, time + energy_min); + } + AddDurationMinReason(t, energy_min); +} + // ============================================================================= // Model based functions. // ============================================================================= diff --git a/ortools/sat/linear_programming_constraint.cc b/ortools/sat/linear_programming_constraint.cc index e71c3433073..9d31bb5cd79 100644 --- a/ortools/sat/linear_programming_constraint.cc +++ b/ortools/sat/linear_programming_constraint.cc @@ -17,14 +17,17 @@ #include #include +#include "absl/container/flat_hash_map.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" #include "ortools/glop/parameters.pb.h" +#include "ortools/glop/preprocessor.h" #include "ortools/glop/status.h" #include "ortools/graph/strongly_connected_components.h" +#include "ortools/util/saturated_arithmetic.h" namespace operations_research { namespace sat { @@ -137,7 +140,7 @@ void LinearProgrammingConstraint::RegisterWith(Model* model) { // sense to detect duplicate constraints and merge bounds. { int new_size = 0; - std::unordered_map + absl::flat_hash_map equiv_constraint; for (LinearConstraintInternal& constraint : integer_lp_) { std::sort(constraint.terms.begin(), constraint.terms.end()); @@ -368,8 +371,12 @@ bool LinearProgrammingConstraint::Propagate() { lp_data_.NotifyThatColumnsAreClean(); lp_data_.AddSlackVariablesWhereNecessary(false); const auto status = simplex_.Solve(lp_data_, time_limit_); - CHECK(status.ok()) << "LinearProgrammingConstraint encountered an error: " - << status.error_message(); + if (!status.ok()) { + LOG(WARNING) << "The LP solver encountered an error: " + << status.error_message(); + simplex_.ClearStateForNextSolve(); + return true; + } } } diff --git a/ortools/sat/linear_programming_constraint.h b/ortools/sat/linear_programming_constraint.h index 92e2a8447f9..b4f45cbc72b 100644 --- a/ortools/sat/linear_programming_constraint.h +++ b/ortools/sat/linear_programming_constraint.h @@ -17,8 +17,9 @@ #include #include -#include +#include "absl/container/flat_hash_map.h" #include "ortools/base/int_type.h" +#include "ortools/base/hash.h" #include "ortools/glop/revised_simplex.h" #include "ortools/lp_data/lp_data.h" #include "ortools/lp_data/lp_types.h" @@ -57,7 +58,9 @@ struct LinearConstraint { absl::StrAppend(&result, lb.value(), " <= "); } for (int i = 0; i < vars.size(); ++i) { - absl::StrAppend(&result, coeffs[i].value(), "*[", vars[i].value(), "] "); + const IntegerValue coeff = + VariableIsPositive(vars[i]) ? coeffs[i] : -coeffs[i]; + absl::StrAppend(&result, coeff.value(), "*X", vars[i].value() / 2, " "); } if (ub.value() < kMaxIntegerValue) { absl::StrAppend(&result, "<= ", ub.value()); @@ -100,7 +103,7 @@ class LinearConstraintBuilder { // Add literal * coeff to the constaint. Returns false and do nothing if the // given literal didn't have an integer view. - MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff) { + ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff) { if (assignment_.LiteralIsTrue(lit)) { if (lb_ > kMinIntegerValue) lb_ -= coeff; if (ub_ < kMaxIntegerValue) ub_ -= coeff; @@ -411,7 +414,7 @@ class LinearProgrammingConstraint : public PropagatorInterface, // Note that these indices are dense in [0, mirror_lp_variable_.size()] so // they can be used as vector indices. std::vector integer_variables_; - std::unordered_map mirror_lp_variable_; + absl::flat_hash_map mirror_lp_variable_; // We need to remember what to optimize if an objective is given, because // then we will switch the objective between feasibility and optimization. @@ -465,7 +468,8 @@ class LinearProgrammingConstraint : public PropagatorInterface, // // Important: only positive variable do appear here. class LinearProgrammingDispatcher - : public std::unordered_map { + : public absl::flat_hash_map { public: explicit LinearProgrammingDispatcher(Model* model) {} }; diff --git a/ortools/sat/linear_relaxation.cc b/ortools/sat/linear_relaxation.cc index c8f7797301e..cdc518d9abc 100644 --- a/ortools/sat/linear_relaxation.cc +++ b/ortools/sat/linear_relaxation.cc @@ -13,8 +13,11 @@ #include "ortools/sat/linear_relaxation.h" -#include +#include "absl/container/flat_hash_set.h" #include "ortools/base/iterator_adaptors.h" +#include "ortools/sat/cp_model_loader.h" +#include "ortools/sat/integer.h" +#include "ortools/sat/linear_programming_constraint.h" namespace operations_research { namespace sat { @@ -57,7 +60,7 @@ namespace { // TODO(user): Not super efficient. std::pair GetMinAndMaxNotEncoded( IntegerVariable var, - const std::unordered_set& encoded_values, + const absl::flat_hash_set& encoded_values, const Model& model) { const auto* domains = model.Get(); if (domains == nullptr || var >= domains->size()) { @@ -78,7 +81,9 @@ std::pair GetMinAndMaxNotEncoded( } IntegerValue max = kMinIntegerValue; - for (const ClosedInterval interval : gtl::reversed_view((*domains)[var])) { + const auto& domain = (*domains)[var]; + for (int i = domain.NumIntervals() - 1; i >= 0; --i) { + const ClosedInterval interval = domain[i]; for (IntegerValue v(interval.end); v >= interval.start; --v) { if (!gtl::ContainsKey(encoded_values, v)) { max = v; @@ -104,7 +109,7 @@ void AppendPartialEncodingRelaxation(IntegerVariable var, const Model& model, if (encoding.empty()) return; std::vector at_most_one_ct; - std::unordered_set encoded_values; + absl::flat_hash_set encoded_values; for (const auto value_literal : encoding) { const Literal literal = value_literal.literal; @@ -239,5 +244,96 @@ void AppendPartialGreaterThanEncodingRelaxation(IntegerVariable var, } } +void AppendLinearConstraintRelaxation(const ConstraintProto& constraint_proto, + const int linearization_level, + const Model& model, + LinearRelaxation* relaxation) { + auto* mapping = model.Get(); + + // Note that we ignore the holes in the domain. + // + // TODO(user): In LoadLinearConstraint() we already created intermediate + // Booleans for each disjoint interval, we should reuse them here if + // possible. + // + // TODO(user): process the "at most one" part of a == 1 separately? + const IntegerValue rhs_domain_min = + IntegerValue(constraint_proto.linear().domain(0)); + const IntegerValue rhs_domain_max = + IntegerValue(constraint_proto.linear().domain( + constraint_proto.linear().domain_size() - 1)); + if (rhs_domain_min == kint64min && rhs_domain_max == kint64max) return; + + if (!HasEnforcementLiteral(constraint_proto)) { + LinearConstraintBuilder lc(&model, rhs_domain_min, rhs_domain_max); + for (int i = 0; i < constraint_proto.linear().vars_size(); i++) { + const int ref = constraint_proto.linear().vars(i); + const int64 coeff = constraint_proto.linear().coeffs(i); + lc.AddTerm(mapping->Integer(ref), IntegerValue(coeff)); + } + relaxation->linear_constraints.push_back(lc.Build()); + return; + } + + // Reified version. + if (linearization_level < 2) return; + + // We linearize reified constraints of size 1 together in a different way. + if (constraint_proto.linear().vars_size() <= 1) return; + + // Compute the implied bounds on the linear expression. + IntegerValue min_sum(0); + IntegerValue max_sum(0); + for (int i = 0; i < constraint_proto.linear().vars_size(); i++) { + int ref = constraint_proto.linear().vars(i); + IntegerValue coeff(constraint_proto.linear().coeffs(i)); + if (!RefIsPositive(ref)) { + ref = PositiveRef(ref); + coeff = -coeff; + } + const IntegerVariable int_var = mapping->Integer(ref); + const auto* integer_trail = model.Get(); + integer_trail->LowerBound(int_var); + if (coeff > 0.0) { + min_sum += coeff * integer_trail->LowerBound(int_var); + max_sum += coeff * integer_trail->UpperBound(int_var); + } else { + min_sum += coeff * integer_trail->UpperBound(int_var); + max_sum += coeff * integer_trail->LowerBound(int_var); + } + } + + if (rhs_domain_min != kint64min) { + // And(ei) => terms >= rhs_domain_min + // <=> Sum_i (~ei * (rhs_domain_min - min_sum)) + terms >= rhs_domain_min + LinearConstraintBuilder lc(&model, rhs_domain_min, kMaxIntegerValue); + for (const int enforcement_ref : constraint_proto.enforcement_literal()) { + CHECK(lc.AddLiteralTerm(mapping->Literal(NegatedRef(enforcement_ref)), + rhs_domain_min - min_sum)); + } + for (int i = 0; i < constraint_proto.linear().vars_size(); i++) { + const int ref = constraint_proto.linear().vars(i); + lc.AddTerm(mapping->Integer(ref), + IntegerValue(constraint_proto.linear().coeffs(i))); + } + relaxation->linear_constraints.push_back(lc.Build()); + } + if (rhs_domain_max != kint64max) { + // And(ei) => terms <= rhs_domain_max + // <=> Sum_i (~ei * (rhs_domain_max - max_sum)) + terms <= rhs_domain_max + LinearConstraintBuilder lc(&model, kMinIntegerValue, rhs_domain_max); + for (const int enforcement_ref : constraint_proto.enforcement_literal()) { + CHECK(lc.AddLiteralTerm(mapping->Literal(NegatedRef(enforcement_ref)), + rhs_domain_max - max_sum)); + } + for (int i = 0; i < constraint_proto.linear().vars_size(); i++) { + const int ref = constraint_proto.linear().vars(i); + lc.AddTerm(mapping->Integer(ref), + IntegerValue(constraint_proto.linear().coeffs(i))); + } + relaxation->linear_constraints.push_back(lc.Build()); + } +} + } // namespace sat } // namespace operations_research diff --git a/ortools/sat/linear_relaxation.h b/ortools/sat/linear_relaxation.h index 79c1f6f32ac..5a23e7f72c8 100644 --- a/ortools/sat/linear_relaxation.h +++ b/ortools/sat/linear_relaxation.h @@ -16,6 +16,7 @@ #include +#include "ortools/sat/cp_model_loader.h" #include "ortools/sat/integer.h" #include "ortools/sat/linear_programming_constraint.h" #include "ortools/sat/model.h" @@ -68,6 +69,19 @@ void AppendPartialGreaterThanEncodingRelaxation(IntegerVariable var, const Model& model, LinearRelaxation* relaxation); +// Appends linear constraints to the relaxation. This also handles the +// relaxation of linear constraints with enforcement literals. +// A linear constraint lb <= ax <= ub with enforcement literals {ei} is relaxed +// as following. +// lb <= (Sum Negated(ei) * (lb - implied_lb)) + ax <= inf +// -inf <= (Sum Negated(ei) * (ub - implied_ub)) + ax <= ub +// Where implied_lb and implied_ub are trivial lower and upper bounds of the +// constraint. +void AppendLinearConstraintRelaxation(const ConstraintProto& constraint_proto, + const int linearization_level, + const Model& model, + LinearRelaxation* relaxation); + } // namespace sat } // namespace operations_research diff --git a/ortools/sat/model.h b/ortools/sat/model.h index d5ead31d325..79c6f9c457a 100644 --- a/ortools/sat/model.h +++ b/ortools/sat/model.h @@ -77,8 +77,6 @@ class Model { // // IMPORTANT: the Model* constructors function shouldn't form a cycle between // each other, otherwise this will crash the program. - // - // TODO(user): Rename to GetOrCreateSingleton(). template T* GetOrCreate() { const size_t type_id = gtl::FastTypeId(); diff --git a/ortools/sat/optimization.cc b/ortools/sat/optimization.cc index 16aaea5dc8d..825d41ff2c6 100644 --- a/ortools/sat/optimization.cc +++ b/ortools/sat/optimization.cc @@ -24,13 +24,14 @@ #include #include +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "ortools/base/int_type.h" #include "ortools/base/integral_types.h" -#include "ortools/base/join.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/map_util.h" -#include "ortools/base/stringprintf.h" +#include "ortools/base/random.h" #include "ortools/base/timer.h" #if !defined(__PORTABLE_PLATFORM__) #include "ortools/linear_solver/linear_solver.h" @@ -74,7 +75,7 @@ std::string CnfObjectiveLine(const LinearBooleanProblem& problem, Coefficient objective) { const double scaled_objective = AddOffsetAndScaleObjectiveValue(problem, objective); - return absl::StrFormat("o %lld", static_cast(scaled_objective)); + return absl::StrFormat("o %d", static_cast(scaled_objective)); } struct LiteralWithCoreIndex { @@ -246,6 +247,7 @@ void MinimizeCore(SatSolver* solver, std::vector* core) { void MinimizeCoreWithPropagation(SatSolver* solver, std::vector* core) { + if (solver->IsModelUnsat()) return; std::set moved_last; std::vector candidate(core->begin(), core->end()); @@ -260,7 +262,8 @@ void MinimizeCoreWithPropagation(SatSolver* solver, moved_last, solver->CurrentDecisionLevel(), &candidate); if (target_level == -1) break; solver->Backtrack(target_level); - while (solver->CurrentDecisionLevel() < candidate.size()) { + while (!solver->IsModelUnsat() && + solver->CurrentDecisionLevel() < candidate.size()) { const Literal decision = candidate[solver->CurrentDecisionLevel()]; if (solver->Assignment().LiteralIsTrue(decision)) { candidate.erase(candidate.begin() + solver->CurrentDecisionLevel()); @@ -371,7 +374,7 @@ SatSolver::Status SolveWithFuMalik(LogBehavior log, solver->Backtrack(0); // Print the search progress. - logger.Log(absl::StrFormat("c iter:%d core:%zu", iter, core.size())); + logger.Log(absl::StrFormat("c iter:%d core:%u", iter, core.size())); // Special case for a singleton core. if (core.size() == 1) { @@ -531,9 +534,9 @@ SatSolver::Status SolveWithWPM1(LogBehavior log, *std::max_element(costs.begin(), costs.end()); // Print the number of variables with a non-zero cost. - logger.Log(absl::StrFormat("c #weights:%zu #vars:%d #constraints:%d", - assumptions.size(), problem.num_variables(), - problem.constraints_size())); + logger.Log(absl::StrFormat("c #weights:%u #vars:%d #constraints:%d", + assumptions.size(), problem.num_variables(), + problem.constraints_size())); for (int iter = 0;; ++iter) { // This is called "hardening" in the literature. @@ -560,7 +563,7 @@ SatSolver::Status SolveWithWPM1(LogBehavior log, } } if (!to_delete.empty()) { - logger.Log(absl::StrFormat("c fixed %zu assumptions, %d with cost > %lld", + logger.Log(absl::StrFormat("c fixed %u assumptions, %d with cost > %d", to_delete.size(), num_above_threshold, hardening_threshold.value())); DeleteVectorIndices(to_delete, &assumptions); @@ -637,10 +640,9 @@ SatSolver::Status SolveWithWPM1(LogBehavior log, lower_bound += min_cost; // Print the search progress. - logger.Log( - absl::StrFormat("c iter:%d core:%zu lb:%lld min_cost:%lld strat:%lld", - iter, core.size(), lower_bound.value(), - min_cost.value(), stratified_lower_bound.value())); + logger.Log(absl::StrFormat( + "c iter:%d core:%u lb:%d min_cost:%d strat:%d", iter, core.size(), + lower_bound.value(), min_cost.value(), stratified_lower_bound.value())); // This simple line helps a lot on the packup-wpms instances! // @@ -816,7 +818,7 @@ SatSolver::Status SolveWithRandomParameters(LogBehavior log, solver->SetParameters(parameters); solver->ResetDecisionHeuristic(); - const bool use_obj = random.OneIn(4); + const bool use_obj = absl::Bernoulli(random, 1.0 / 4); if (use_obj) UseObjectiveForSatAssignmentPreference(problem, solver); const SatSolver::Status result = solver->Solve(); @@ -939,9 +941,9 @@ SatSolver::Status SolveWithCardinalityEncoding( } // Print the number of variables with a non-zero cost. - logger.Log(absl::StrFormat("c #weights:%zu #vars:%d #constraints:%d", - nodes.size(), problem.num_variables(), - problem.constraints_size())); + logger.Log(absl::StrFormat("c #weights:%u #vars:%d #constraints:%d", + nodes.size(), problem.num_variables(), + problem.constraints_size())); // Create the sorter network. solver->Backtrack(0); @@ -1003,9 +1005,9 @@ SatSolver::Status SolveWithCardinalityEncodingAndCore( } // Print the number of variables with a non-zero cost. - logger.Log(absl::StrFormat("c #weights:%zu #vars:%d #constraints:%d", - nodes.size(), problem.num_variables(), - problem.constraints_size())); + logger.Log(absl::StrFormat("c #weights:%u #vars:%d #constraints:%d", + nodes.size(), problem.num_variables(), + problem.constraints_size())); // This is used by the "stratified" approach. Coefficient stratified_lower_bound(0); @@ -1029,13 +1031,13 @@ SatSolver::Status SolveWithCardinalityEncodingAndCore( const std::string gap_string = (upper_bound == kCoefficientMax) ? "" - : absl::StrFormat(" gap:%lld", (upper_bound - lower_bound).value()); + : absl::StrFormat(" gap:%d", (upper_bound - lower_bound).value()); logger.Log( - absl::StrFormat("c iter:%d [%s] lb:%lld%s assumptions:%zu depth:%d", - iter, previous_core_info.c_str(), + absl::StrFormat("c iter:%d [%s] lb:%d%s assumptions:%u depth:%d", iter, + previous_core_info, lower_bound.value() - offset.value() + static_cast(problem.objective().offset()), - gap_string.c_str(), nodes.size(), max_depth)); + gap_string, nodes.size(), max_depth)); // Solve under the assumptions. const SatSolver::Status result = @@ -1069,7 +1071,7 @@ SatSolver::Status SolveWithCardinalityEncodingAndCore( // The lower bound will be increased by that much. const Coefficient min_weight = ComputeCoreMinWeight(nodes, core); previous_core_info = - absl::StrFormat("core:%zu mw:%lld", core.size(), min_weight.value()); + absl::StrFormat("core:%u mw:%d", core.size(), min_weight.value()); // Increase stratified_lower_bound according to the parameters. if (stratified_lower_bound < min_weight && @@ -1099,15 +1101,15 @@ void LogSolveInfo(SatSolver::Status result, const SatSolver& sat_solver, ? "OPTIMAL" : SatStatusString(result).c_str()); if (objective < kint64max) { - printf("objective: %lld\n", objective); + absl::PrintF("objective: %d\n", objective); } else { printf("objective: NA\n"); } - printf("best_bound: %lld\n", best_bound); + absl::PrintF("best_bound: %d\n", best_bound); printf("booleans: %d\n", sat_solver.NumVariables()); - printf("conflicts: %lld\n", sat_solver.num_failures()); - printf("branches: %lld\n", sat_solver.num_branches()); - printf("propagations: %lld\n", sat_solver.num_propagations()); + absl::PrintF("conflicts: %d\n", sat_solver.num_failures()); + absl::PrintF("branches: %d\n", sat_solver.num_branches()); + absl::PrintF("propagations: %d\n", sat_solver.num_propagations()); printf("walltime: %f\n", wall_timer.Get()); printf("usertime: %f\n", user_timer.Get()); printf("deterministic_time: %f\n", sat_solver.deterministic_time()); @@ -1410,7 +1412,7 @@ SatSolver::Status MinimizeWithCoreAndLazyEncoding( // TODO(user): The core is returned in the same order as the assumptions, // so we don't really need this map, we could just do a linear scan to // recover which node are part of the core. - std::map assumption_to_term_index; + std::map literal_to_term_index; // Start the algorithm. int max_depth = 0; @@ -1589,7 +1591,7 @@ SatSolver::Status MinimizeWithCoreAndLazyEncoding( // Convert integer_assumptions to Literals. std::vector assumptions; - assumption_to_term_index.clear(); + literal_to_term_index.clear(); for (int i = 0; i < integer_assumptions.size(); ++i) { assumptions.push_back(integer_encoder->GetOrCreateAssociatedLiteral( integer_assumptions[i])); @@ -1597,7 +1599,7 @@ SatSolver::Status MinimizeWithCoreAndLazyEncoding( // Tricky: In some rare case, it is possible that the same literal // correspond to more that one assumptions. In this case, we can just // pick one of them when converting back a core to term indices. - assumption_to_term_index[assumptions.back().Index()] = term_indices[i]; + literal_to_term_index[assumptions.back().Index()] = term_indices[i]; } // Solve under the assumptions. @@ -1634,7 +1636,7 @@ SatSolver::Status MinimizeWithCoreAndLazyEncoding( IntegerValue new_var_ub(0); int new_depth = 0; for (const Literal lit : core) { - const int index = gtl::FindOrDie(assumption_to_term_index, lit.Index()); + const int index = gtl::FindOrDie(literal_to_term_index, lit.Index()); min_weight = std::min(min_weight, terms[index].weight); max_weight = std::max(max_weight, terms[index].weight); new_depth = std::max(new_depth, terms[index].depth + 1); @@ -1654,9 +1656,9 @@ SatSolver::Status MinimizeWithCoreAndLazyEncoding( max_depth = std::max(max_depth, new_depth); if (log_info) { LOG(INFO) << absl::StrFormat( - "core:%zu weight:[%lld,%lld] domain:[%lld,%lld] depth:%d", - core.size(), min_weight.value(), max_weight.value(), - new_var_lb.value(), new_var_ub.value(), new_depth); + "core:%u weight:[%d,%d] domain:[%d,%d] depth:%d", core.size(), + min_weight.value(), max_weight.value(), new_var_lb.value(), + new_var_ub.value(), new_depth); } // We will "transfer" min_weight from all the variables of the core @@ -1671,7 +1673,7 @@ SatSolver::Status MinimizeWithCoreAndLazyEncoding( std::vector constraint_vars; std::vector constraint_coeffs; for (const Literal lit : core) { - const int index = gtl::FindOrDie(assumption_to_term_index, lit.Index()); + const int index = gtl::FindOrDie(literal_to_term_index, lit.Index()); terms[index].weight -= min_weight; constraint_vars.push_back(terms[index].var); constraint_coeffs.push_back(1); diff --git a/ortools/sat/pb_constraint.cc b/ortools/sat/pb_constraint.cc index e7cc2566a1d..060b8ded077 100644 --- a/ortools/sat/pb_constraint.cc +++ b/ortools/sat/pb_constraint.cc @@ -15,7 +15,7 @@ #include -#include "ortools/base/stringprintf.h" +#include "absl/strings/str_format.h" #include "ortools/base/thorough_hash.h" #include "ortools/util/saturated_arithmetic.h" @@ -277,10 +277,10 @@ std::string MutableUpperBoundedLinearConstraint::DebugString() { std::string result; for (BooleanVariable var : PossibleNonZeros()) { if (!result.empty()) result += " + "; - result += absl::StrFormat("%lld[%s]", GetCoefficient(var).value(), - GetLiteral(var).DebugString().c_str()); + result += absl::StrFormat("%d[%s]", GetCoefficient(var).value(), + GetLiteral(var).DebugString()); } - result += absl::StrFormat(" <= %lld", rhs_.value()); + result += absl::StrFormat(" <= %d", rhs_.value()); return result; } @@ -962,8 +962,8 @@ void PbConstraints::Untrail(const Trail& trail, int trail_index) { } } -absl::Span PbConstraints::Reason(const Trail& trail, - int trail_index) const { +absl::Span PbConstraints::Reason(const Trail& trail, + int trail_index) const { SCOPED_TIME_STAT(&stats_); const PbConstraintsEnqueueHelper::ReasonInfo& reason_info = enqueue_helper_.reasons[trail_index]; diff --git a/ortools/sat/pb_constraint.h b/ortools/sat/pb_constraint.h index 59d0bb85fc0..c6f6db892c5 100644 --- a/ortools/sat/pb_constraint.h +++ b/ortools/sat/pb_constraint.h @@ -18,16 +18,16 @@ #include #include #include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/types/span.h" #include "ortools/base/hash.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/span.h" #include "ortools/sat/sat_base.h" #include "ortools/sat/sat_parameters.pb.h" #include "ortools/util/bitset.h" @@ -533,7 +533,8 @@ class PbConstraints : public SatPropagator { bool Propagate(Trail* trail) final; void Untrail(const Trail& trail, int trail_index) final; - absl::Span Reason(const Trail& trail, int trail_index) const final; + absl::Span Reason(const Trail& trail, + int trail_index) const final; // Changes the number of variables. void Resize(int num_variables) { @@ -652,7 +653,7 @@ class PbConstraints : public SatPropagator { // Pointers to the constraints grouped by their hash. // This is used to find duplicate constraints by AddConstraint(). - std::unordered_map> + absl::flat_hash_map> possible_duplicates_; // Helper to enqueue propagated literals on the trail and store their reasons. diff --git a/ortools/sat/precedences.cc b/ortools/sat/precedences.cc index 31fb8c90240..dcf4569cd0e 100644 --- a/ortools/sat/precedences.cc +++ b/ortools/sat/precedences.cc @@ -236,10 +236,9 @@ void PrecedencesPropagator::AdjustSizeFor(IntegerVariable i) { } } -void PrecedencesPropagator::AddArc(IntegerVariable tail, IntegerVariable head, - IntegerValue offset, - IntegerVariable offset_var, - absl::Span presence_literals) { +void PrecedencesPropagator::AddArc( + IntegerVariable tail, IntegerVariable head, IntegerValue offset, + IntegerVariable offset_var, absl::Span presence_literals) { DCHECK_EQ(trail_->CurrentDecisionLevel(), 0); AdjustSizeFor(tail); AdjustSizeFor(head); @@ -729,7 +728,8 @@ bool PrecedencesPropagator::BellmanFordTarjan(Trail* trail) { void PrecedencesPropagator::AddGreaterThanAtLeastOneOfConstraints( Model* model) { VLOG(1) << "Detecting GreaterThanAtLeastOneOf() constraints..."; - SatSolver* solver = model->GetOrCreate(); + auto* solver = model->GetOrCreate(); + auto* time_limit = model->GetOrCreate(); // Fill the set of incoming conditional arcs for each variables. gtl::ITIVector> incoming_arcs_; @@ -750,6 +750,7 @@ void PrecedencesPropagator::AddGreaterThanAtLeastOneOfConstraints( int num_added_constraints = 0; for (IntegerVariable target(0); target < incoming_arcs_.size(); ++target) { if (incoming_arcs_[target].size() <= 1) continue; + if (time_limit->LimitReached()) return; // Detect set of incoming arcs for which at least one must be present. // TODO(user): Find more than one disjoint set of incoming arcs. diff --git a/ortools/sat/precedences.h b/ortools/sat/precedences.h index a6c30281bd4..f06bb0691d4 100644 --- a/ortools/sat/precedences.h +++ b/ortools/sat/precedences.h @@ -18,7 +18,7 @@ #include #include -#include "ortools/base/inlined_vector.h" +#include "absl/container/inlined_vector.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/integral_types.h" @@ -87,7 +87,7 @@ class PrecedencesPropagator : public SatPropagator, PropagatorInterface { void AddPrecedenceWithAllOptions(IntegerVariable i1, IntegerVariable i2, IntegerValue offset, IntegerVariable offset_var, - absl::Span presence_literals); + absl::Span presence_literals); // Finds all the IntegerVariable that are "after" at least two of the // IntegerVariable in vars. Returns a vector of these precedences relation @@ -153,7 +153,7 @@ class PrecedencesPropagator : public SatPropagator, PropagatorInterface { void AdjustSizeFor(IntegerVariable i); void AddArc(IntegerVariable tail, IntegerVariable head, IntegerValue offset, IntegerVariable offset_var, - absl::Span presence_literals); + absl::Span presence_literals); // Enqueue a new lower bound for the variable arc.head_lb that was deduced // from the current value of arc.tail_lb and the offset of this arc. @@ -304,7 +304,7 @@ inline void PrecedencesPropagator::AddPrecedenceWithVariableOffset( inline void PrecedencesPropagator::AddPrecedenceWithAllOptions( IntegerVariable i1, IntegerVariable i2, IntegerValue offset, - IntegerVariable offset_var, absl::Span presence_literals) { + IntegerVariable offset_var, absl::Span presence_literals) { AddArc(i1, i2, offset, offset_var, presence_literals); } diff --git a/ortools/sat/probing.cc b/ortools/sat/probing.cc index 31a1a963404..41f666ae937 100644 --- a/ortools/sat/probing.cc +++ b/ortools/sat/probing.cc @@ -35,9 +35,11 @@ bool ProbeBooleanVariables(double deterministic_time_limit, Model* model) { const int initial_num_fixed = sat_solver->LiteralTrail().Index(); const double initial_deterministic_time = sat_solver->deterministic_time(); const double limit = initial_deterministic_time + deterministic_time_limit; + auto* time_limit = model->GetOrCreate(); // For the new direct implication detected. - std::set> new_binary_clauses; + int64 num_new_binary = 0; + std::vector> new_binary_clauses; auto* implication_graph = model->GetOrCreate(); const int id = implication_graph->PropagatorId(); @@ -47,45 +49,90 @@ bool ProbeBooleanVariables(double deterministic_time_limit, Model* model) { auto* integer_trail = model->GetOrCreate(); std::vector new_integer_bounds; - bool limit_reached = false; + // To detect literal x that must be true because b => x and not(b) => x. + // When probing b, we add all propagated literal to propagated, and when + // probing not(b) we check if any are already there. + std::vector to_fix_at_true; const int num_variables = sat_solver->NumVariables(); + SparseBitset propagated(LiteralIndex(2 * num_variables)); + + bool limit_reached = false; + int num_probed = 0; + const auto& trail = *(model->Get()); for (BooleanVariable b(0); b < num_variables; ++b) { - if (sat_solver->deterministic_time() > limit) { + const Literal literal(b, true); + if (implication_graph->RepresentativeOf(literal) != literal) { + continue; + } + if (time_limit->LimitReached() || + sat_solver->deterministic_time() > limit) { limit_reached = true; break; } // Propagate b=1 and then b=0. + ++num_probed; new_integer_bounds.clear(); + new_binary_clauses.clear(); + to_fix_at_true.clear(); + propagated.SparseClearAll(); for (const Literal decision : {Literal(b, true), Literal(b, false)}) { if (!sat_solver->RestoreSolverToAssumptionLevel()) return false; if (sat_solver->Assignment().LiteralIsAssigned(decision)) continue; - const int saved_index = sat_solver->LiteralTrail().Index(); + const int saved_index = trail.Index(); sat_solver->EnqueueDecisionAndBackjumpOnConflict(decision); if (sat_solver->IsModelUnsat()) return false; + if (sat_solver->CurrentDecisionLevel() == 0) continue; + integer_trail->AppendNewBounds(&new_integer_bounds); - if (sat_solver->CurrentDecisionLevel() > 0) { - for (int i = saved_index + 1; i < sat_solver->LiteralTrail().Index(); - ++i) { - const Literal l = sat_solver->LiteralTrail()[i]; - if (sat_solver->LiteralTrail().AssignmentType(l.Variable()) == id) { - continue; + for (int i = saved_index + 1; i < trail.Index(); ++i) { + const Literal l = trail[i]; + + // We mark on the first run (b.IsPositive()) and check on the second. + if (decision.IsPositive()) { + propagated.Set(l.Index()); + } else { + if (propagated[l.Index()]) { + to_fix_at_true.push_back(l); } - new_binary_clauses.insert({decision.Negated(), l}); } + + // Anything not propagated by the BinaryImplicationGraph is a "new" + // binary clause. This is becaue the BinaryImplicationGraph has the + // highest priority of all propagators. + if (trail.AssignmentType(l.Variable()) == id) { + continue; + } + new_binary_clauses.push_back({decision.Negated(), l}); } } + sat_solver->Backtrack(0); + + // Fix variables that must be true. + for (const Literal l : to_fix_at_true) { + sat_solver->AddUnitClause(l); + } + + // Add the new binary clauses right away. + num_new_binary += new_binary_clauses.size(); + for (auto binary : new_binary_clauses) { + sat_solver->AddBinaryClause(binary.first, binary.second); + } + // We have at most two lower bounds for each variables (one for b==0 and one // for b==1), so the min of the two is a valid level zero bound! More // generally, the domain of a variable can be intersected with the union // of the two propagated domains. This also allow to detect "holes". // + // TODO(user): More generally, for any clauses (b or not(b) is one), we + // could probe all the literal inside, and for any integer variable, we can + // take the union of the propagated domain as a new domain. + // // TODO(user): fix binary variable in the same way? It might not be as // useful since probing on such variable will also fix it. But then we might // abort probing early, so it might still be good. - sat_solver->Backtrack(0); std::sort(new_integer_bounds.begin(), new_integer_bounds.end(), [](IntegerLiteral a, IntegerLiteral b) { return a.var < b.var; }); @@ -142,23 +189,15 @@ bool ProbeBooleanVariables(double deterministic_time_limit, Model* model) { } } - // Adds the newly discovered direct implication for a faster propagation - // and so that they can be used for binary equivalence detection. - // - // TODO(user): do that on the fly for a faster probing? also use hash_map. - sat_solver->Backtrack(0); - for (const auto binary : new_binary_clauses) { - sat_solver->AddBinaryClause(binary.first, binary.second); - } - // Display stats. const double time_diff = sat_solver->deterministic_time() - initial_deterministic_time; const int num_fixed = sat_solver->LiteralTrail().Index(); const int num_newly_fixed = num_fixed - initial_num_fixed; VLOG(1) << "Probing deterministic_time: " << time_diff - << " wall_time: " << wall_timer.Get() - << (limit_reached ? " (Aborted)" : ""); + << " wall_time: " << wall_timer.Get() << " (" + << (limit_reached ? "Aborted " : "") << num_probed << "/" + << num_variables << ")"; VLOG_IF(1, num_newly_fixed > 0) << "Probing new fixed binary: " << num_newly_fixed << " (" << num_fixed << "/" << num_variables << " overall)"; @@ -166,8 +205,8 @@ bool ProbeBooleanVariables(double deterministic_time_limit, Model* model) { << "Probing new integer holes: " << num_new_holes; VLOG_IF(1, num_new_integer_bounds > 0) << "Probing new integer bounds: " << num_new_integer_bounds; - VLOG_IF(1, !new_binary_clauses.empty()) - << "Probing new binary clause: " << new_binary_clauses.size(); + VLOG_IF(1, num_new_binary > 0) + << "Probing new binary clause: " << num_new_binary; return true; } diff --git a/ortools/sat/python/cp_model.py b/ortools/sat/python/cp_model.py index 8469d5001c0..486b5bc641b 100644 --- a/ortools/sat/python/cp_model.py +++ b/ortools/sat/python/cp_model.py @@ -1,4 +1,4 @@ -# Copyright 2010-2018 Google LLC +# Copyright 2010-2017 Google # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -97,21 +97,21 @@ def ShortName(model, i): class LinearExpression(object): """Holds an integer linear expression. - An linear expression is built from integer constants and variables. + An linear expression is built from integer constants and variables. - x + 2 * (y - z + 1) is one such linear expression, and can be written that - way directly in Python, provided x, y, and z are integer variables. + x + 2 * (y - z + 1) is one such linear expression, and can be written that + way directly in Python, provided x, y, and z are integer variables. - Linear expressions are used in two places in the cp_model. - When used with equality and inequality operators, they create linear - inequalities that can be added to the model as in: + Linear expressions are used in two places in the cp_model. + When used with equality and inequality operators, they create linear + inequalities that can be added to the model as in: - model.Add(x + 2 * y <= 5) - model.Add(sum(array_of_vars) == 5) + model.Add(x + 2 * y <= 5) + model.Add(sum(array_of_vars) == 5) - Linear expressions can also be used to specify the objective of the model. + Linear expressions can also be used to specify the objective of the model. - model.Minimize(x + 2 * y + z) + model.Minimize(x + 2 * y + z) """ def GetVarValueMap(self): @@ -307,16 +307,16 @@ def Constant(self): class IntVar(LinearExpression): """An integer variable. - An IntVar is an object that can take on any integer value within defined - ranges. Variables appears in constraint like: + An IntVar is an object that can take on any integer value within defined + ranges. Variables appears in constraint like: - x + y >= 5 - AllDifferent([x, y, z]) + x + y >= 5 + AllDifferent([x, y, z]) - Solving a model is equivalent to finding, for each variable, a single value - from the set of initial values (called the initial domain), such that the - model is feasible, or optimal if you provided an objective function. - """ + Solving a model is equivalent to finding, for each variable, a single value + from the set of initial values (called the initial domain), such that the + model is feasible, or optimal if you provided an objective function. + """ def __init__(self, model, bounds, name): """See CpModel.NewIntVar below.""" @@ -342,11 +342,11 @@ def Name(self): def Not(self): """Returns the negation of a Boolean variable. - This method implements the logical negation of a Boolean variable. - It is only valid of the variable has a Boolean domain (0 or 1). + This method implements the logical negation of a Boolean variable. + It is only valid of the variable has a Boolean domain (0 or 1). - Note that this method is nilpotent: x.Not().Not() == x. - """ + Note that this method is nilpotent: x.Not().Not() == x. + """ for bound in self.__var.domain: if bound < 0 or bound > 1: @@ -376,11 +376,11 @@ def __str__(self): class LinearInequality(object): """Represents a linear constraint: lb <= expression <= ub. - The only use of this class is to be added to the CpModel through - CpModel.Add(expression), as in: + The only use of this class is to be added to the CpModel through + CpModel.Add(expression), as in: - model.Add(x + 2 * y -1 >= z) - """ + model.Add(x + 2 * y -1 >= z) + """ def __init__(self, expr, bounds): self.__expr = expr @@ -416,17 +416,17 @@ def Bounds(self): class Constraint(object): """Base class for constraints. - Constraints are built by the CpModel through the Add methods. - Once created by the CpModel class, they are automatically added to the model. - The purpose of this class is to allow specification of enforcement literals - for this constraint. + Constraints are built by the CpModel through the Add methods. + Once created by the CpModel class, they are automatically added to the model. + The purpose of this class is to allow specification of enforcement literals + for this constraint. - b = model.BoolVar('b') - x = model.IntVar(0, 10, 'x') - y = model.IntVar(0, 10, 'y') + b = model.BoolVar('b') + x = model.IntVar(0, 10, 'x') + y = model.IntVar(0, 10, 'y') - model.Add(x + 2 * y == 5).OnlyEnforceIf(b.Not()) - """ + model.Add(x + 2 * y == 5).OnlyEnforceIf(b.Not()) + """ def __init__(self, constraints): self.__index = len(constraints) @@ -435,22 +435,22 @@ def __init__(self, constraints): def OnlyEnforceIf(self, boolvar): """Adds an enforcement literal to the constraint. - Args: - boolvar: A boolean literal or a list of boolean literals. + Args: + boolvar: A boolean literal or a list of boolean literals. - Returns: - self. + Returns: + self. - This method adds one or more literals (that is a boolean variable or its - negation) as enforcement literals. The conjunction of all these literals - decides whether the constraint is active or not. It acts as an - implication, so if the conjunction is true, it implies that the constraint - must be enforced. If it is false, then the constraint is ignored. + This method adds one or more literals (that is a boolean variable or its + negation) as enforcement literals. The conjunction of all these literals + decides whether the constraint is active or not. It acts as an + implication, so if the conjunction is true, it implies that the constraint + must be enforced. If it is false, then the constraint is ignored. - The following constraints support enforcement literals: - bool or, bool and, and any linear constraints support any number of - enforcement literals. - """ + The following constraints support enforcement literals: + bool or, bool and, and any linear constraints support any number of + enforcement literals. + """ if isinstance(boolvar, numbers.Integral) and boolvar == 1: # Always true. Do nothing. @@ -475,20 +475,20 @@ def ConstraintProto(self): class IntervalVar(object): """Represents a Interval variable. - An interval variable is both a constraint and a variable. It is defined by - three integer variables: start, size, and end. + An interval variable is both a constraint and a variable. It is defined by + three integer variables: start, size, and end. - It is a constraint because, internally, it enforces that start + size == end. + It is a constraint because, internally, it enforces that start + size == end. - It is also a variable as it can appear in specific scheduling constraints: - NoOverlap, NoOverlap2D, Cumulative. + It is also a variable as it can appear in specific scheduling constraints: + NoOverlap, NoOverlap2D, Cumulative. - Optionally, an enforcement literal can be added to this - constraint. This enforcement literal is understood by the same constraints. - These constraints ignore interval variables with enforcement literals assigned - to false. Conversely, these constraints will also set these enforcement - literals to false if they cannot fit these intervals into the schedule. - """ + Optionally, an enforcement literal can be added to this + constraint. This enforcement literal is understood by the same constraints. + These constraints ignore interval variables with enforcement literals assigned + to false. Conversely, these constraints will also set these enforcement + literals to false if they cannot fit these intervals into the schedule. + """ def __init__(self, model, start_index, size_index, end_index, is_present_index, name): @@ -530,10 +530,10 @@ def Name(self): class CpModel(object): """Wrapper class around the cp_model proto. - This class provides two types of methods: - - NewXXX to create integer, boolean, or interval variables. - - AddXXX to create new constraints and add them to the model. - """ + This class provides two types of methods: + - NewXXX to create integer, boolean, or interval variables. + - AddXXX to create new constraints and add them to the model. + """ def __init__(self): self.__model = cp_model_pb2.CpModelProto() @@ -549,16 +549,16 @@ def NewIntVar(self, lb, ub, name): def NewEnumeratedIntVar(self, bounds, name): """Creates an integer variable with an enumerated domain. - Args: - bounds: A flattened list of disjoint intervals. - name: The name of the variable. + Args: + bounds: A flattened list of disjoint intervals. + name: The name of the variable. - Returns: - a variable whose domain is union[bounds[2*i]..bounds[2*i + 1]]. + Returns: + a variable whose domain is union[bounds[2*i]..bounds[2*i + 1]]. - To create a variable with domain [1, 2, 3, 5, 7, 8], pass in the - array [1, 3, 5, 5, 7, 8]. - """ + To create a variable with domain [1, 2, 3, 5, 7, 8], pass in the + array [1, 3, 5, 5, 7, 8]. + """ return IntVar(self.__model, bounds, name) def NewBoolVar(self, name): @@ -620,14 +620,14 @@ def Add(self, ct): def AddAllDifferent(self, variables): """Adds AllDifferent(variables). - This constraint forces all variables to have different values. + This constraint forces all variables to have different values. - Args: - variables: a list of integer variables. + Args: + variables: a list of integer variables. - Returns: - An instance of the Constraint class. - """ + Returns: + An instance of the Constraint class. + """ ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.all_diff.vars.extend( @@ -651,25 +651,25 @@ def AddElement(self, index, variables, target): def AddCircuit(self, arcs): """Adds Circuit(arcs). - Adds a circuit constraint from a sparse list of arcs that encode the graph. + Adds a circuit constraint from a sparse list of arcs that encode the graph. - A circuit is a unique Hamiltonian path in a subgraph of the total - graph. In case a node 'i' is not in the path, then there must be a - loop arc 'i -> i' associated with a true literal. Otherwise - this constraint will fail. + A circuit is a unique Hamiltonian path in a subgraph of the total + graph. In case a node 'i' is not in the path, then there must be a + loop arc 'i -> i' associated with a true literal. Otherwise + this constraint will fail. - Args: - arcs: a list of arcs. An arc is a tuple (source_node, destination_node, - literal). The arc is selected in the circuit if the literal is true. - Both source_node and destination_node must be integer value between 0 - and the number of nodes - 1. + Args: + arcs: a list of arcs. An arc is a tuple (source_node, destination_node, + literal). The arc is selected in the circuit if the literal is true. + Both source_node and destination_node must be integer value between 0 + and the number of nodes - 1. - Returns: - An instance of the Constraint class. + Returns: + An instance of the Constraint class. - Raises: - ValueError: If the list of arc is empty. - """ + Raises: + ValueError: If the list of arc is empty. + """ if not arcs: raise ValueError('AddCircuit expects a non empty array of arcs') ct = Constraint(self.__model.constraints) @@ -692,18 +692,18 @@ def AddAllowedAssignments(self, variables, tuples_list): tuple_list. Args: - variables: A list of variables. - tuples_list: A list of admissible tuples. Each tuple must have the same - length as the variables, and the ith value of a tuple corresponds to the - ith variable. + variables: A list of variables. + tuples_list: A list of admissible tuples. Each tuple must have the same + length as the variables, and the ith value of a tuple corresponds to the + ith variable. Returns: - An instance of the Constraint class. + An instance of the Constraint class. Raises: - TypeError: If a tuple does not have the same size as the list of - variables. - ValueError: If the array of variables is empty. + TypeError: If a tuple does not have the same size as the list of + variables. + ValueError: If the array of variables is empty. """ if not variables: @@ -729,18 +729,18 @@ def AddForbiddenAssignments(self, variables, tuples_list): where the list of impossible combinations is provided in the tuples list. Args: - variables: A list of variables. - tuples_list: A list of forbidden tuples. Each tuple must have the same - length as the variables, and the ith value of a tuple corresponds to the - ith variable. + variables: A list of variables. + tuples_list: A list of forbidden tuples. Each tuple must have the same + length as the variables, and the ith value of a tuple corresponds to the + ith variable. Returns: - An instance of the Constraint class. + An instance of the Constraint class. Raises: - TypeError: If a tuple does not have the same size as the list of - variables. - ValueError: If the array of variables is empty. + TypeError: If a tuple does not have the same size as the list of + variables. + ValueError: If the array of variables is empty. """ if not variables: @@ -779,19 +779,19 @@ def AddAutomaton(self, transition_variables, starting_state, final_states, final phase. Args: - transition_variables: A non empty list of variables whose values - correspond to the labels of the arcs traversed by the automata. - starting_state: The initial state of the automata. - final_states: A non empty list of admissible final states. - transition_triples: A list of transition for the automata, in the - following format (current_state, variable_value, next_state). + transition_variables: A non empty list of variables whose values + correspond to the labels of the arcs traversed by the automata. + starting_state: The initial state of the automata. + final_states: A non empty list of admissible final states. + transition_triples: A list of transition for the automata, in the + following format (current_state, variable_value, next_state). Returns: - An instance of the Constraint class. + An instance of the Constraint class. Raises: - ValueError: if transition_variables, final_states, or transition_triples - are empty. + ValueError: if transition_variables, final_states, or transition_triples + are empty. """ if not transition_variables: @@ -827,20 +827,20 @@ def AddAutomaton(self, transition_variables, starting_state, final_states, def AddInverse(self, variables, inverse_variables): """Adds Inverse(variables, inverse_variables). - An inverse constraint enforces that if 'variables[i]' is assigned a value - 'j', then inverse_variables[j] is assigned a value 'i'. And vice versa. + An inverse constraint enforces that if 'variables[i]' is assigned a value + 'j', then inverse_variables[j] is assigned a value 'i'. And vice versa. - Args: - variables: An array of integer variables. - inverse_variables: An array of integer variables. + Args: + variables: An array of integer variables. + inverse_variables: An array of integer variables. - Returns: - An instance of the Constraint class. + Returns: + An instance of the Constraint class. - Raises: - TypeError: if variables and inverse_variables have different length, or - if they are empty. - """ + Raises: + TypeError: if variables and inverse_variables have different length, or + if they are empty. + """ if not variables or not inverse_variables: raise TypeError( @@ -872,20 +872,20 @@ def AddReservoirConstraint(self, times, demands, min_level, max_level): sum(demands[i] if times[i] <= t) in [min_level, max_level] Args: - times: A list of positive integer variables which specify the time of the - filling or emptying the reservoir. - demands: A list of integer values that specifies the amount of the - emptying or feeling. - min_level: At any time >= 0, the level of the reservoir must be greater of - equal than the min level. - max_level: At any time >= 0, the level of the reservoir must be less or - equal than the max level. + times: A list of positive integer variables which specify the time of the + filling or emptying the reservoir. + demands: A list of integer values that specifies the amount of the + emptying or feeling. + min_level: At any time >= 0, the level of the reservoir must be greater of + equal than the min level. + max_level: At any time >= 0, the level of the reservoir must be less or + equal than the max level. Returns: - An instance of the Constraint class. + An instance of the Constraint class. Raises: - ValueError: if max_level < min_level. + ValueError: if max_level < min_level. """ if max_level < min_level: @@ -894,8 +894,7 @@ def AddReservoirConstraint(self, times, demands, min_level, max_level): ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] - model_ct.reservoir.times.extend( - [self.GetOrMakeIndex(x) for x in times]) + model_ct.reservoir.times.extend([self.GetOrMakeIndex(x) for x in times]) model_ct.reservoir.demands.extend(demands) model_ct.reservoir.min_level = min_level model_ct.reservoir.max_level = max_level @@ -920,22 +919,22 @@ def AddReservoirConstraintWithActive(self, times, demands, actives, actions are actually performed. Args: - times: A list of positive integer variables which specify the time of - the filling or emptying the reservoir. - demands: A list of integer values that specifies the amount of the - emptying or feeling. - actives: a list of boolean variables. They indicates if the - emptying/refilling events actually take place. - min_level: At any time >= 0, the level of the reservoir must be greater - of equal than the min level. - max_level: At any time >= 0, the level of the reservoir must be less or - equal than the max level. + times: A list of positive integer variables which specify the time of the + filling or emptying the reservoir. + demands: A list of integer values that specifies the amount of the + emptying or feeling. + actives: a list of boolean variables. They indicates if the + emptying/refilling events actually take place. + min_level: At any time >= 0, the level of the reservoir must be greater of + equal than the min level. + max_level: At any time >= 0, the level of the reservoir must be less or + equal than the max level. Returns: - An instance of the Constraint class. + An instance of the Constraint class. Raises: - ValueError: if max_level < min_level. + ValueError: if max_level < min_level. """ if max_level < min_level: @@ -944,8 +943,7 @@ def AddReservoirConstraintWithActive(self, times, demands, actives, ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] - model_ct.reservoir.times.extend( - [self.GetOrMakeIndex(x) for x in times]) + model_ct.reservoir.times.extend([self.GetOrMakeIndex(x) for x in times]) model_ct.reservoir.demands.extend(demands) model_ct.reservoir.actives.extend(actives) model_ct.reservoir.min_level = min_level @@ -1064,22 +1062,22 @@ def AddProdEquality(self, target, args): def NewIntervalVar(self, start, size, end, name): """Creates an interval variable from start, size, and end. - An interval variable is a constraint, that is itself used in other - constraints like NoOverlap. + An interval variable is a constraint, that is itself used in other + constraints like NoOverlap. - Internally, it ensures that start + size == end. + Internally, it ensures that start + size == end. - Args: - start: The start of the interval. It can be an integer value, or an - integer variable. - size: The size of the interval. It can be an integer value, or an - integer variable. - end: The end of the interval. It can be an integer value, or an - integer variable. - name: The name of the interval variable. + Args: + start: The start of the interval. It can be an integer value, or an + integer variable. + size: The size of the interval. It can be an integer value, or an integer + variable. + end: The end of the interval. It can be an integer value, or an integer + variable. + name: The name of the interval variable. - Returns: - An IntervalVar object. + Returns: + An IntervalVar object. """ start_index = self.GetOrMakeIndex(start) @@ -1091,25 +1089,25 @@ def NewIntervalVar(self, start, size, end, name): def NewOptionalIntervalVar(self, start, size, end, is_present, name): """Creates an optional interval var from start, size, end and is_present. - An optional interval variable is a constraint, that is itself used in other - constraints like NoOverlap. This constraint is protected by an is_present - literal that indicates if it is active or not. - - Internally, it ensures that is_present implies start + size == end. - - Args: - start: The start of the interval. It can be an integer value, or an - integer variable. - size: The size of the interval. It can be an integer value, or an - integer variable. - end: The end of the interval. It can be an integer value, or an - integer variable. - is_present: A literal that indicates if the interval is active or - not. A inactive interval is simply ignored by all constraints. - name: The name of the interval variable. - - Returns: - An IntervalVar object. + An optional interval variable is a constraint, that is itself used in other + constraints like NoOverlap. This constraint is protected by an is_present + literal that indicates if it is active or not. + + Internally, it ensures that is_present implies start + size == end. + + Args: + start: The start of the interval. It can be an integer value, or an + integer variable. + size: The size of the interval. It can be an integer value, or an integer + variable. + end: The end of the interval. It can be an integer value, or an integer + variable. + is_present: A literal that indicates if the interval is active or not. A + inactive interval is simply ignored by all constraints. + name: The name of the interval variable. + + Returns: + An IntervalVar object. """ is_present_index = self.GetOrMakeBooleanIndex(is_present) start_index = self.GetOrMakeIndex(start) @@ -1121,15 +1119,15 @@ def NewOptionalIntervalVar(self, start, size, end, is_present, name): def AddNoOverlap(self, interval_vars): """Adds NoOverlap(interval_vars). - A NoOverlap constraint ensures that all present intervals do not overlap - in time. + A NoOverlap constraint ensures that all present intervals do not overlap + in time. - Args: - interval_vars: The list of interval variables to constrain. + Args: + interval_vars: The list of interval variables to constrain. - Returns: - An instance of the Constraint class. - """ + Returns: + An instance of the Constraint class. + """ ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.no_overlap.intervals.extend( @@ -1139,17 +1137,17 @@ def AddNoOverlap(self, interval_vars): def AddNoOverlap2D(self, x_intervals, y_intervals): """Adds NoOverlap2D(x_intervals, y_intervals). - A NoOverlap2D constraint ensures that all present rectangles do not overlap - on a plan. Each rectangle is aligned with the X and Y axis, and is defined - by two intervals which represent its projection onto the X and Y axis. + A NoOverlap2D constraint ensures that all present rectangles do not overlap + on a plan. Each rectangle is aligned with the X and Y axis, and is defined + by two intervals which represent its projection onto the X and Y axis. - Args: - x_intervals: The X coordinates of the rectangles. - y_intervals: The Y coordinates of the rectangles. + Args: + x_intervals: The X coordinates of the rectangles. + y_intervals: The Y coordinates of the rectangles. - Returns: - An instance of the Constraint class. - """ + Returns: + An instance of the Constraint class. + """ ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.no_overlap_2d.x_intervals.extend( @@ -1161,23 +1159,22 @@ def AddNoOverlap2D(self, x_intervals, y_intervals): def AddCumulative(self, intervals, demands, capacity): """Adds Cumulative(intervals, demands, capacity). - This constraint enforces that: - for all t: - sum(demands[i] - if (start(intervals[t]) <= t < end(intervals[t])) and - (t is present)) <= capacity - - Args: - intervals: The list of intervals. - demands: The list of demands for each interval. Each demand must - be >= 0. Each demand can be an integer value, or an integer - variable. - capacity: The maximum capacity of the cumulative constraint. It - must be a positive integer value or variable. - - Returns: - An instance of the Constraint class. - """ + This constraint enforces that: + for all t: + sum(demands[i] + if (start(intervals[t]) <= t < end(intervals[t])) and + (t is present)) <= capacity + + Args: + intervals: The list of intervals. + demands: The list of demands for each interval. Each demand must be >= 0. + Each demand can be an integer value, or an integer variable. + capacity: The maximum capacity of the cumulative constraint. It must be a + positive integer value or variable. + + Returns: + An instance of the Constraint class. + """ ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.cumulative.intervals.extend( @@ -1202,9 +1199,8 @@ def GetOrMakeIndex(self, arg): """Returns the index of a variables, its negation, or a number.""" if isinstance(arg, IntVar): return arg.Index() - elif (isinstance(arg, _ProductCst) - and isinstance(arg.Expression(), IntVar) - and arg.Coefficient() == -1): + elif (isinstance(arg, _ProductCst) and + isinstance(arg.Expression(), IntVar) and arg.Coefficient() == -1): return -arg.Expression().Index() - 1 elif isinstance(arg, numbers.Integral): cp_model_helper.AssertIsInt64(arg) @@ -1296,14 +1292,14 @@ def HasObjective(self): def AddDecisionStrategy(self, variables, var_strategy, domain_strategy): """Adds a search strategy to the model. - Args: - variables: a list of variables this strategy will assign. - var_strategy: heuristic to choose the next variable to assign. - domain_strategy: heuristic to reduce the domain of the selected variable. + Args: + variables: a list of variables this strategy will assign. + var_strategy: heuristic to choose the next variable to assign. + domain_strategy: heuristic to reduce the domain of the selected variable. Currently, this is advanced code, the union of all strategies added to the model must be complete, i.e. instantiates all variables. Otherwise, Solve() will fail. - """ + """ strategy = self.__model.search_strategy.add() for v in variables: @@ -1370,13 +1366,13 @@ def EvaluateBooleanExpression(literal, solution): class CpSolver(object): """Main solver class. - The purpose of this class is to search for a solution of a model given to the - Solve() method. + The purpose of this class is to search for a solution of a model given to the + Solve() method. - Once Solve() is called, this class allows inspecting the solution found - with the Value() and BooleanValue() methods, as well as general statistics - about the solve procedure. - """ + Once Solve() is called, this class allows inspecting the solution found + with the Value() and BooleanValue() methods, as well as general statistics + about the solve procedure. + """ def __init__(self): self.__model = None @@ -1399,16 +1395,16 @@ def SolveWithSolutionCallback(self, model, callback): def SearchForAllSolutions(self, model, callback): """Search for all solutions of a satisfiability problem. - This method searches for all feasible solution of a given model. - Then it feeds the solution to the callback. + This method searches for all feasible solution of a given model. + Then it feeds the solution to the callback. - Args: - model: The model to solve. - callback: The callback that will be called at each solution. + Args: + model: The model to solve. + callback: The callback that will be called at each solution. - Returns: - The status of the solve (FEASIBLE, INFEASIBLE...). - """ + Returns: + The status of the solve (FEASIBLE, INFEASIBLE...). + """ if model.HasObjective(): raise TypeError('Search for all solutions is only defined on ' 'satisfiability problems') diff --git a/ortools/sat/python/cp_model_helper.py b/ortools/sat/python/cp_model_helper.py index 7292e016beb..afe9ee1340e 100644 --- a/ortools/sat/python/cp_model_helper.py +++ b/ortools/sat/python/cp_model_helper.py @@ -25,55 +25,55 @@ def AssertIsInt64(x): - """Asserts that x is integer and x is in [min_int_64, max_int_64].""" - if not isinstance(x, numbers.Integral): - raise TypeError('Not an integer: %s' % x) - if x < INT_MIN or x > INT_MAX: - raise OverflowError('Does not fit in an int64: %s' % x) + """Asserts that x is integer and x is in [min_int_64, max_int_64].""" + if not isinstance(x, numbers.Integral): + raise TypeError('Not an integer: %s' % x) + if x < INT_MIN or x > INT_MAX: + raise OverflowError('Does not fit in an int64: %s' % x) def AssertIsInt32(x): - """Asserts that x is integer and x is in [min_int_32, max_int_32].""" - if not isinstance(x, numbers.Integral): - raise TypeError('Not an integer: %s' % x) - if x < INT32_MIN or x > INT32_MAX: - raise OverflowError('Does not fit in an int32: %s' % x) + """Asserts that x is integer and x is in [min_int_32, max_int_32].""" + if not isinstance(x, numbers.Integral): + raise TypeError('Not an integer: %s' % x) + if x < INT32_MIN or x > INT32_MAX: + raise OverflowError('Does not fit in an int32: %s' % x) def AssertIsBoolean(x): - """Asserts that x is 0 or 1.""" - if not isinstance(x, numbers.Integral) or x < 0 or x > 1: - raise TypeError('Not an boolean: %s' % x) + """Asserts that x is 0 or 1.""" + if not isinstance(x, numbers.Integral) or x < 0 or x > 1: + raise TypeError('Not an boolean: %s' % x) def CapInt64(v): - """Restrict v within [INT_MIN..INT_MAX] range.""" - if v > INT_MAX: - return INT_MAX - if v < INT_MIN: - return INT_MIN - return v + """Restrict v within [INT_MIN..INT_MAX] range.""" + if v > INT_MAX: + return INT_MAX + if v < INT_MIN: + return INT_MIN + return v def CapSub(x, y): - """Saturated arithmetics. Returns x - y truncated to the int64 range.""" - if not isinstance(x, numbers.Integral): - raise TypeError('Not integral: ' + str(x)) - if not isinstance(y, numbers.Integral): - raise TypeError('Not integral: ' + str(y)) - AssertIsInt64(x) - AssertIsInt64(y) - if y == 0: - return x - if x == y: + """Saturated arithmetics. Returns x - y truncated to the int64 range.""" + if not isinstance(x, numbers.Integral): + raise TypeError('Not integral: ' + str(x)) + if not isinstance(y, numbers.Integral): + raise TypeError('Not integral: ' + str(y)) + AssertIsInt64(x) + AssertIsInt64(y) + if y == 0: + return x + if x == y: + if x == INT_MAX or x == INT_MIN: + raise OverflowError( + 'Integer NaN: subtracting INT_MAX or INT_MIN to itself') + return 0 if x == INT_MAX or x == INT_MIN: - raise OverflowError( - 'Integer NaN: subtracting INT_MAX or INT_MIN to itself') - return 0 - if x == INT_MAX or x == INT_MIN: - return x - if y == INT_MAX: - return INT_MIN - if y == INT_MIN: - return INT_MAX - return CapInt64(x - y) + return x + if y == INT_MAX: + return INT_MIN + if y == INT_MIN: + return INT_MAX + return CapInt64(x - y) diff --git a/ortools/sat/restart.cc b/ortools/sat/restart.cc index ebe6b878e75..8d9d9892d33 100644 --- a/ortools/sat/restart.cc +++ b/ortools/sat/restart.cc @@ -12,10 +12,9 @@ // limitations under the License. #include "ortools/sat/restart.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/split.h" -#include "ortools/base/stringprintf.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_split.h" namespace operations_research { namespace sat { diff --git a/ortools/sat/restart.h b/ortools/sat/restart.h index 58fa5e55def..cc5e88d6111 100644 --- a/ortools/sat/restart.h +++ b/ortools/sat/restart.h @@ -28,7 +28,9 @@ namespace sat { class RestartPolicy { public: explicit RestartPolicy(Model* model) - : parameters_(*(model->GetOrCreate())) {} + : parameters_(*(model->GetOrCreate())) { + Reset(); + } // Resets the policy using the current model parameters. void Reset(); diff --git a/ortools/sat/sat_base.h b/ortools/sat/sat_base.h index 4d49c67cccd..e4012a27af7 100644 --- a/ortools/sat/sat_base.h +++ b/ortools/sat/sat_base.h @@ -22,14 +22,13 @@ #include #include +#include "absl/strings/str_format.h" +#include "absl/types/span.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/port.h" -#include "ortools/base/span.h" -#include "ortools/base/stringprintf.h" #include "ortools/sat/model.h" #include "ortools/util/bitset.h" @@ -111,7 +110,7 @@ inline std::ostream& operator<<(std::ostream& os, Literal literal) { } inline std::ostream& operator<<(std::ostream& os, - absl::Span literals) { + absl::Span literals) { for (const Literal literal : literals) { os << literal.DebugString() << ","; } @@ -284,7 +283,7 @@ class Trail { // already assigned to false, then MutableConflict() will be set appropriately // and this will return false otherwise this will enqueue the literal and // returns true. - bool EnqueueWithStoredReason(Literal true_literal) MUST_USE_RESULT { + ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(Literal true_literal) { if (assignment_.LiteralIsTrue(true_literal)) return true; if (assignment_.LiteralIsFalse(true_literal)) { *MutableConflict() = reasons_repository_[Index()]; @@ -305,7 +304,7 @@ class Trail { // Note that this shouldn't be called on a variable at level zero, because we // don't cleanup the reason data for these variables but the underlying // clauses may have been deleted. - absl::Span Reason(BooleanVariable var) const; + absl::Span Reason(BooleanVariable var) const; // Returns the "type" of an assignment (see AssignmentType). Note that this // function never returns kSameReasonAs or kCachedReason, it instead returns @@ -359,7 +358,7 @@ class Trail { } // Returns the last conflict. - absl::Span FailingClause() const { return conflict_; } + absl::Span FailingClause() const { return conflict_; } // Specific SatClause interface so we can update the conflict clause activity. // Note that MutableConflict() automatically sets this to nullptr, so we can @@ -430,7 +429,7 @@ class Trail { // variables, the memory address of the vectors (kept in reasons_) are still // valid. mutable std::deque> reasons_repository_; - mutable gtl::ITIVector> reasons_; + mutable gtl::ITIVector> reasons_; mutable gtl::ITIVector old_type_; // This is used by RegisterPropagator() and Reason(). @@ -484,8 +483,8 @@ class SatPropagator { // The returned Span has to be valid until the literal is untrailed. A client // can use trail_.GetEmptyVectorToStoreReason() if it doesn't have a memory // location that already contains the reason. - virtual absl::Span Reason(const Trail& trail, - int trail_index) const { + virtual absl::Span Reason(const Trail& trail, + int trail_index) const { LOG(FATAL) << "Not implemented."; return {}; } @@ -576,7 +575,7 @@ inline int Trail::AssignmentType(BooleanVariable var) const { return type != AssignmentType::kCachedReason ? type : old_type_[var]; } -inline absl::Span Trail::Reason(BooleanVariable var) const { +inline absl::Span Trail::Reason(BooleanVariable var) const { // Special case for AssignmentType::kSameReasonAs to avoid a recursive call. var = ReferenceVarWithSameReason(var); diff --git a/ortools/sat/sat_parameters.proto b/ortools/sat/sat_parameters.proto index 2c3a22e008a..7bab3e3eef9 100644 --- a/ortools/sat/sat_parameters.proto +++ b/ortools/sat/sat_parameters.proto @@ -21,7 +21,7 @@ package operations_research.sat; // Contains the definitions for all the sat algorithm parameters and their // default values. // -// NEXT TAG: 110 +// NEXT TAG: 111 message SatParameters { // ========================================================================== // Branching and polarity @@ -551,6 +551,9 @@ message SatParameters { // Whether we presolve the cp_model before solving it. optional bool cp_model_presolve = 86 [default = true]; + // How much effort do we spend on probing. 0 disables it completely. + optional int32 cp_model_probing_level = 110 [default = 2]; + // Whether we also use the sat presolve when cp_model_presolve is true. optional bool cp_model_use_sat_presolve = 93 [default = true]; diff --git a/ortools/sat/sat_solver.cc b/ortools/sat/sat_solver.cc index 46e4cc17924..1e8f9246ab5 100644 --- a/ortools/sat/sat_solver.cc +++ b/ortools/sat/sat_solver.cc @@ -21,12 +21,11 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/sysinfo.h" #include "ortools/port/proto_utils.h" #include "ortools/port/sysinfo.h" #include "ortools/sat/util.h" @@ -52,12 +51,14 @@ SatSolver::SatSolver(Model* model) problem_is_pure_sat_(true), drat_proof_handler_(nullptr), stats_("SatSolver") { - // TODO(user): move these 3 classes in the Model so that everyone can access + // TODO(user): move these 2 classes in the Model so that everyone can access // them if needed and we don't have the wiring here. trail_->RegisterPropagator(&clauses_propagator_); trail_->RegisterPropagator(&pb_constraints_); InitializePropagators(); - SetParameters(*parameters_); + + // TODO(user): use the model parameters directly in pb_constraints_. + pb_constraints_.SetParameters(*parameters_); } SatSolver::~SatSolver() { IF_STATS_ENABLED(LOG(INFO) << stats_.StatString()); } @@ -128,7 +129,8 @@ std::string SatSolver::Indent() const { } bool SatSolver::IsMemoryLimitReached() const { - const int64 memory_usage = GetProcessMemoryUsage(); + const int64 memory_usage = + ::operations_research::sysinfo::MemoryUsageProcess(); const int64 kMegaByte = 1024 * 1024; return memory_usage > kMegaByte * parameters_->max_memory_in_mb(); } @@ -172,7 +174,7 @@ bool SatSolver::AddTernaryClause(Literal a, Literal b, Literal c) { &tmp_pb_constraint_); } -bool SatSolver::AddProblemClause(absl::Span literals) { +bool SatSolver::AddProblemClause(absl::Span literals) { SCOPED_TIME_STAT(&stats_); // TODO(user): To avoid duplication, we currently just call @@ -710,9 +712,9 @@ bool SatSolver::PropagateAndStopAfterOneConflictResolution() { // clause LBD and even the backtracking level. switch (parameters_->binary_minimization_algorithm()) { case SatParameters::NO_BINARY_MINIMIZATION: - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; case SatParameters::BINARY_MINIMIZATION_FIRST: - FALLTHROUGH_INTENDED; + ABSL_FALLTHROUGH_INTENDED; case SatParameters::BINARY_MINIMIZATION_FIRST_WITH_TRANSITIVE_REDUCTION: break; case SatParameters::BINARY_MINIMIZATION_WITH_REACHABILITY: @@ -1405,9 +1407,9 @@ int SatSolver::ComputeLbd(const LiteralList& literals) { std::string SatSolver::StatusString(Status status) const { const double time_in_s = timer_.Get(); - return absl::StrFormat("\n status: %s\n", SatStatusString(status).c_str()) + + return absl::StrFormat("\n status: %s\n", SatStatusString(status)) + absl::StrFormat(" time: %fs\n", time_in_s) + - absl::StrFormat(" memory: %s\n", MemoryUsage().c_str()) + + absl::StrFormat(" memory: %s\n", MemoryUsage()) + absl::StrFormat( " num failures: %" GG_LL_FORMAT "d (%.0f /sec)\n", counters_.num_failures, @@ -1478,8 +1480,7 @@ std::string SatSolver::RunningStatisticsString() const { "%6.2fs, mem:%s, fails:%" GG_LL_FORMAT "d, " "depth:%d, clauses:%lld, tmp:%lld, bin:%llu, restarts:%d, vars:%d", - time_in_s, MemoryUsage().c_str(), counters_.num_failures, - CurrentDecisionLevel(), + time_in_s, MemoryUsage(), counters_.num_failures, CurrentDecisionLevel(), clauses_propagator_.num_clauses() - clauses_propagator_.num_removable_clauses(), clauses_propagator_.num_removable_clauses(), @@ -1724,13 +1725,12 @@ std::string SatSolver::DebugString(const SatClause& clause) const { ? "true" : (trail_->Assignment().LiteralIsFalse(literal) ? "false" : "undef"); - result.append( - absl::StrFormat("%s(%s)", literal.DebugString().c_str(), value.c_str())); + result.append(absl::StrFormat("%s(%s)", literal.DebugString(), value)); } return result; } -int SatSolver::ComputeMaxTrailIndex(absl::Span clause) const { +int SatSolver::ComputeMaxTrailIndex(absl::Span clause) const { SCOPED_TIME_STAT(&stats_); int trail_index = -1; for (const Literal literal : clause) { @@ -1781,7 +1781,7 @@ void SatSolver::ComputeFirstUIPConflict( // // This last literal will be the first UIP because by definition all the // propagation done at the current level will pass though it at some point. - absl::Span clause_to_expand = trail_->FailingClause(); + absl::Span clause_to_expand = trail_->FailingClause(); SatClause* sat_clause = trail_->FailingSatClause(); DCHECK(!clause_to_expand.empty()); int num_literal_at_highest_level_that_needs_to_be_processed = 0; @@ -2088,7 +2088,7 @@ void SatSolver::MinimizeConflictSimple(std::vector* conflict) { bool can_be_removed = false; if (DecisionLevel(var) != current_level) { // It is important not to call Reason(var) when it can be avoided. - const absl::Span reason = trail_->Reason(var); + const absl::Span reason = trail_->Reason(var); if (!reason.empty()) { can_be_removed = true; for (Literal literal : reason) { @@ -2347,7 +2347,7 @@ void SatSolver::MinimizeConflictExperimental(std::vector* conflict) { // A nullptr reason means that this was a decision variable from the // previous levels. - const absl::Span reason = trail_->Reason(var); + const absl::Span reason = trail_->Reason(var); if (reason.empty()) continue; // Compute how many and which literals from the current reason do not appear diff --git a/ortools/sat/sat_solver.h b/ortools/sat/sat_solver.h index f13920a87fc..a8c68d21983 100644 --- a/ortools/sat/sat_solver.h +++ b/ortools/sat/sat_solver.h @@ -23,17 +23,17 @@ #include #include #include -#include #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/types/span.h" #include "ortools/base/hash.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/integral_types.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/span.h" #include "ortools/base/timer.h" #include "ortools/sat/clause.h" #include "ortools/sat/drat_proof_handler.h" @@ -103,7 +103,7 @@ class SatSolver { // be UNSAT. // // TODO(user): Rename this to AddClause(). - bool AddProblemClause(absl::Span literals); + bool AddProblemClause(absl::Span literals); // Adds a pseudo-Boolean constraint to the problem. Returns false if the // problem is detected to be UNSAT. If the constraint is always true, this @@ -306,7 +306,7 @@ class SatSolver { // Extract the current problem clauses. The Output type must support the two // functions: // - void AddBinaryClause(Literal a, Literal b); - // - void AddClause(absl::Span clause); + // - void AddClause(absl::Span clause); // // TODO(user): also copy the removable clauses? template @@ -554,7 +554,7 @@ class SatSolver { // Returns the maximum trail_index of the literals in the given clause. // All the literals must be assigned. Returns -1 if the clause is empty. - int ComputeMaxTrailIndex(absl::Span clause) const; + int ComputeMaxTrailIndex(absl::Span clause) const; // Computes what is known as the first UIP (Unique implication point) conflict // clause starting from the failing clause. For a definition of UIP and a @@ -924,6 +924,22 @@ inline std::function ReifiedBoolOr( }; } +// enforcement_literals => clause. +inline std::function EnforcedClause( + absl::Span enforcement_literals, + absl::Span clause) { + return [=](Model* model) { + std::vector tmp; + for (const Literal l : enforcement_literals) { + tmp.push_back(l.Negated()); + } + for (const Literal l : clause) { + tmp.push_back(l); + } + model->Add(ClauseConstraint(tmp)); + }; +} + // r <=> (all literals are true). // // Note(user): we could have called ReifiedBoolOr() with everything negated. diff --git a/ortools/sat/simplification.cc b/ortools/sat/simplification.cc index 47e76ea2cf2..7bb6e35de7a 100644 --- a/ortools/sat/simplification.cc +++ b/ortools/sat/simplification.cc @@ -18,10 +18,10 @@ #include #include +#include "absl/memory/memory.h" #include "ortools/algorithms/dynamic_partition.h" #include "ortools/base/adjustable_priority_queue-inl.h" #include "ortools/base/logging.h" -#include "ortools/base/memory.h" #include "ortools/base/random.h" #include "ortools/base/stl_util.h" #include "ortools/base/timer.h" @@ -41,7 +41,7 @@ SatPostsolver::SatPostsolver(int num_variables) assignment_.Resize(num_variables); } -void SatPostsolver::Add(Literal x, absl::Span clause) { +void SatPostsolver::Add(Literal x, absl::Span clause) { CHECK(!clause.empty()); DCHECK(std::find(clause.begin(), clause.end(), x) != clause.end()); associated_literal_.push_back(ApplyReverseMapping(x)); @@ -155,7 +155,7 @@ std::vector SatPostsolver::PostsolveSolution( void SatPresolver::AddBinaryClause(Literal a, Literal b) { AddClause({a, b}); } -void SatPresolver::AddClause(absl::Span clause) { +void SatPresolver::AddClause(absl::Span clause) { CHECK_GT(clause.size(), 0) << "Added an empty clause to the presolver"; const ClauseIndex ci(clauses_.size()); clauses_.push_back(std::vector(clause.begin(), clause.end())); @@ -651,7 +651,7 @@ bool SatPresolver::CrossProduct(Literal x) { // // For more details, see the paper "Blocked clause elimination", Matti // Jarvisalo, Armin Biere, Marijn Heule. TACAS, volume 6015 of Lecture - // Notes in Computer Science, pages 129-144. Springer, 2010. + // Notes in Computer Science, pages 129–144. Springer, 2010. // // TODO(user): Choose if we use x or x.Negated() depending on the list // sizes? The function achieve the same if x = x.Negated(), however the diff --git a/ortools/sat/simplification.h b/ortools/sat/simplification.h index 4460824803f..b0616c6c0ca 100644 --- a/ortools/sat/simplification.h +++ b/ortools/sat/simplification.h @@ -24,12 +24,12 @@ #include #include +#include "absl/types/span.h" #include "ortools/base/adjustable_priority_queue.h" #include "ortools/base/int_type.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/integral_types.h" #include "ortools/base/macros.h" -#include "ortools/base/span.h" #include "ortools/sat/drat_proof_handler.h" #include "ortools/sat/sat_base.h" #include "ortools/sat/sat_parameters.pb.h" @@ -51,7 +51,7 @@ class SatPostsolver { // The postsolver will process the Add() calls in reverse order. If the given // clause has all its literals at false, it simply sets the literal x to true. // Note that x must be a literal of the given clause. - void Add(Literal x, const absl::Span clause); + void Add(Literal x, const absl::Span clause); // Tells the postsolver that the given literal must be true in any solution. // We currently check that the variable is not already fixed. @@ -154,7 +154,7 @@ class SatPresolver { // Adds new clause to the SatPresolver. void SetNumVariables(int num_variables); void AddBinaryClause(Literal a, Literal b); - void AddClause(absl::Span clause); + void AddClause(absl::Span clause); // Presolves the problem currently loaded. Returns false if the model is // proven to be UNSAT during the presolving. diff --git a/ortools/sat/symmetry.cc b/ortools/sat/symmetry.cc index 06109ca0c7b..93ac3e842a8 100644 --- a/ortools/sat/symmetry.cc +++ b/ortools/sat/symmetry.cc @@ -95,7 +95,7 @@ bool SymmetryPropagator::PropagateNext(Trail* trail) { // Set the conflict on the trail. // Note that we need to fetch a reason for this. std::vector* conflict = trail->MutableConflict(); - const absl::Span initial_reason = + const absl::Span initial_reason = trail->Reason(non_symmetric.literal.Variable()); Permute(p_index, initial_reason, conflict); conflict->push_back(non_symmetric.image); @@ -144,8 +144,8 @@ void SymmetryPropagator::Untrail(const Trail& trail, int trail_index) { } } -absl::Span SymmetryPropagator::Reason(const Trail& trail, - int trail_index) const { +absl::Span SymmetryPropagator::Reason(const Trail& trail, + int trail_index) const { SCOPED_TIME_STAT(&stats_); const ReasonInfo& reason_info = reasons_[trail_index]; std::vector* reason = trail.GetEmptyVectorToStoreReason(trail_index); @@ -189,7 +189,7 @@ bool SymmetryPropagator::Enqueue(const Trail& trail, Literal literal, return *index == p_trail->size(); } -void SymmetryPropagator::Permute(int index, absl::Span input, +void SymmetryPropagator::Permute(int index, absl::Span input, std::vector* output) const { SCOPED_TIME_STAT(&stats_); diff --git a/ortools/sat/symmetry.h b/ortools/sat/symmetry.h index 3935cbbf0a8..25ff003a5d6 100644 --- a/ortools/sat/symmetry.h +++ b/ortools/sat/symmetry.h @@ -17,10 +17,10 @@ #include #include +#include "absl/types/span.h" #include "ortools/algorithms/sparse_permutation.h" #include "ortools/base/int_type_indexed_vector.h" #include "ortools/base/macros.h" -#include "ortools/base/span.h" #include "ortools/sat/sat_base.h" #include "ortools/util/stats.h" @@ -65,7 +65,8 @@ class SymmetryPropagator : public SatPropagator { bool Propagate(Trail* trail) final; void Untrail(const Trail& trail, int trail_index) final; - absl::Span Reason(const Trail& trail, int trail_index) const final; + absl::Span Reason(const Trail& trail, + int trail_index) const final; // Adds a new permutation to this symmetry propagator. The ownership is // transferred. This must be an integer permutation such that: @@ -91,7 +92,7 @@ class SymmetryPropagator : public SatPropagator { // Permutes a list of literals from input into output using the permutation // with given index. This uses tmp_literal_mapping_ and has a complexity in // O(permutation_support + input_size). - void Permute(int index, absl::Span input, + void Permute(int index, absl::Span input, std::vector* output) const; private: diff --git a/ortools/sat/table.cc b/ortools/sat/table.cc index 82b87e68c9d..1e3bf589937 100644 --- a/ortools/sat/table.cc +++ b/ortools/sat/table.cc @@ -16,10 +16,10 @@ #include #include #include -#include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "ortools/base/int_type.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" @@ -49,9 +49,9 @@ std::vector> Transpose( } // Converts the vector representation returned by FullDomainEncoding() to a map. -std::unordered_map GetEncoding(IntegerVariable var, - Model* model) { - std::unordered_map encoding; +absl::flat_hash_map GetEncoding(IntegerVariable var, + Model* model) { + absl::flat_hash_map encoding; IntegerEncoder* encoder = model->GetOrCreate(); for (const auto& entry : encoder->FullDomainEncoding(var)) { encoding[entry.value] = entry.literal; @@ -60,7 +60,7 @@ std::unordered_map GetEncoding(IntegerVariable var, } void FilterValues(IntegerVariable var, Model* model, - std::unordered_set* values) { + absl::flat_hash_set* values) { const Domain domain = model->Get()->InitialVariableDomain(var); for (auto it = values->begin(); it != values->end();) { const int64 v = *it; @@ -76,12 +76,12 @@ void FilterValues(IntegerVariable var, Model* model, // controling if the lines are possible or not. The column has the given values, // and the Literal of the column variable can be retrieved using the encoding // map. -void ProcessOneColumn(const std::vector& line_literals, - const std::vector& values, - const std::unordered_map& encoding, - Model* model) { +void ProcessOneColumn( + const std::vector& line_literals, + const std::vector& values, + const absl::flat_hash_map& encoding, Model* model) { CHECK_EQ(line_literals.size(), values.size()); - std::unordered_map> + absl::flat_hash_map> value_to_list_of_line_literals; // If a value is false (i.e not possible), then the tuple with this value @@ -120,7 +120,7 @@ std::function TableConstraint( const int n = vars.size(); // Compute the set of possible values for each variable (from the table). - std::vector> values_per_var(n); + std::vector> values_per_var(n); for (const std::vector& tuple : tuples) { for (int i = 0; i < n; ++i) { values_per_var[i].insert(tuple[i]); @@ -203,7 +203,7 @@ std::function NegatedTableConstraint( const std::vector>& tuples) { return [=](Model* model) { const int n = vars.size(); - std::vector> mapping(n); + std::vector> mapping(n); for (int i = 0; i < n; ++i) { for (const auto pair : model->Add(FullyEncodeVariable(vars[i]))) { mapping[i][pair.value.value()] = pair.literal; @@ -275,7 +275,7 @@ std::function LiteralTableConstraint( CHECK_EQ(tuple_size, literal_tuples[i].size()); } - std::unordered_map> + absl::flat_hash_map> line_literals_per_literal; for (int i = 0; i < num_tuples; ++i) { const LiteralIndex selected_index = line_literals[i].Index(); @@ -332,7 +332,7 @@ std::function TransitionConstraint( } // Construct a table with the possible values of each vars. - std::vector> possible_values(n); + std::vector> possible_values(n); for (int time = 0; time < n; ++time) { const auto domain = integer_trail->InitialVariableDomain(vars[time]); for (const std::vector& transition : automata) { @@ -378,9 +378,9 @@ std::function TransitionConstraint( // initial state, and at time n we should be in one of the final states. We // don't need to create Booleans at at time when there is just one possible // state (like at time zero). - std::unordered_map encoding; - std::unordered_map in_encoding; - std::unordered_map out_encoding; + absl::flat_hash_map encoding; + absl::flat_hash_map in_encoding; + absl::flat_hash_map out_encoding; for (int time = 0; time < n; ++time) { // All these vector have the same size. We will use them to enforce a // local table constraint representing one step of the automata at the diff --git a/ortools/util/CMakeLists.txt b/ortools/util/CMakeLists.txt index 8309003550d..1d155aaa7ba 100644 --- a/ortools/util/CMakeLists.txt +++ b/ortools/util/CMakeLists.txt @@ -4,39 +4,52 @@ set(NAME ${PROJECT_NAME}_util) # Will be merge in libortools.so #add_library(${NAME} STATIC ${_SRCS}) add_library(${NAME} OBJECT ${_SRCS}) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD 11) -set_target_properties(${NAME} PROPERTIES CXX_STANDARD_REQUIRED ON) -set_target_properties(${NAME} PROPERTIES CXX_EXTENSIONS OFF) -set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(${NAME} PRIVATE - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR} - $ - $ - $ - $ - $) -target_compile_options(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -target_compile_definitions(${NAME} PRIVATE - $ - $ - $ - $ - $ - ) -# CMakeError: Object library may not link to anything. +set_target_properties(${NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + POSITION_INDEPENDENT_CODE ON +) +# CMake < 3.12: CMakeError: Object library may not link to anything. #target_link_libraries(${NAME} PRIVATE -# gflags::gflags -# glog::glog +# absl::base absl::memory absl::strings absl::str_format +# gflags::gflags glog::glog # protobuf::libprotobuf -# Cbc::Cbc # ${PROJECT_NAME}::proto) +target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + $ + $ + $ + $ + $ + $ + $ + $) +target_compile_options(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + ) +target_compile_definitions(${NAME} PRIVATE + $ + $ + $ + $ + $ + $ + $ + $ + ) add_dependencies(${NAME} - protobuf::libprotobuf gflags::gflags glog::glog Cbc::Cbc ${PROJECT_NAME}::proto) + absl::base absl::memory absl::strings absl::str_format + gflags::gflags glog::glog + protobuf::libprotobuf + ${PROJECT_NAME}::proto) add_library(${PROJECT_NAME}::util ALIAS ${NAME}) diff --git a/ortools/util/csharp/functions.i b/ortools/util/csharp/functions.i index b987da95f74..cd6e9a69383 100644 --- a/ortools/util/csharp/functions.i +++ b/ortools/util/csharp/functions.i @@ -40,6 +40,7 @@ %feature("director") operations_research::swig_util::LongToLong; %feature("director") operations_research::swig_util::LongLongToLong; %feature("director") operations_research::swig_util::IntIntToLong; +%feature("director") operations_research::swig_util::IntToLong; %feature("director") operations_research::swig_util::LongLongLongToLong; %feature("director") operations_research::swig_util::LongToBoolean; %feature("director") operations_research::swig_util::VoidToString; diff --git a/ortools/util/csharp/proto.i b/ortools/util/csharp/proto.i index db9dff1a3cd..d22161077b7 100644 --- a/ortools/util/csharp/proto.i +++ b/ortools/util/csharp/proto.i @@ -88,7 +88,7 @@ proto.MergeFrom(input); return proto; } catch (Google.Protobuf.InvalidProtocolBufferException /*e*/) { - throw new SystemException( + throw new System.Exception( "Unable to parse CSharpProtoType protocol message."); } } diff --git a/ortools/util/csharp/vector.i b/ortools/util/csharp/vector.i new file mode 100644 index 00000000000..f78b30a46ad --- /dev/null +++ b/ortools/util/csharp/vector.i @@ -0,0 +1,73 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// SWIG Macros to use std::vector and const std::vector& in Java, +// where Num is an atomic numeric type. +// +// Normally we'd simply use %include "std_vector.i" with the %template +// directive (see http://www.swig.org/Doc1.3/Library.html#Library_nn15), but +// in google3 we can't, because exceptions are forbidden. +// +// TODO(user): move to base/swig/java. + +%include "ortools/base/base.i" + +%{ +#include +#include "ortools/base/integral_types.h" +%} + +%define VECTOR_AS_CSHARP_ARRAY(TYPE, CTYPE, CSHARPTYPE) +%typemap(ctype) const std::vector& %{ int length$argnum, CTYPE* %} +%typemap(imtype) const std::vector& %{ int length$argnum, CSHARPTYPE[] %} +%typemap(cstype) const std::vector& %{ CSHARPTYPE[] %} +%typemap(csin) const std::vector& "$csinput.Length, $csinput" +%typemap(freearg) const std::vector& { delete $1; } +%typemap(in) const std::vector& %{ + $1 = new std::vector; + $1->reserve(length$argnum); + for(int i = 0; i < length$argnum; ++i) { + $1->emplace_back($input[i]); + } +%} +%enddef // VECTOR_AS_CSHARP_ARRAY + +%define MATRIX_AS_CSHARP_ARRAY_IN1(TYPE, CTYPE, CSHARPTYPE) +%typemap(ctype) const std::vector >& %{ + int len$argnum_1, int len$argnum_2[], CTYPE* +%} +%typemap(imtype) const std::vector >& %{ + int len$argnum_1, int[] len$argnum_2, CSHARPTYPE[] +%} +%typemap(cstype) const std::vector >& %{ CSHARPTYPE[][] %} +%typemap(csin) const std::vector >& "$csinput.GetLength(0), NestedArrayHelper.GetArraySecondSize($csinput), NestedArrayHelper.GetFlatArray($csinput)" +%typemap(in) const std::vector >& (std::vector > result) %{ + + result.clear(); + result.resize(len$argnum_1); + + TYPE* inner_array = reinterpret_cast($input); + + int actualIndex = 0; + for (int index1 = 0; index1 < len$argnum_1; ++index1) { + result[index1].reserve(len$argnum_2[index1]); + for (int index2 = 0; index2 < len$argnum_2[index1]; ++index2) { + const TYPE value = inner_array[actualIndex]; + result[index1].emplace_back(value); + actualIndex++; + } + } + + $1 = &result; +%} +%enddef // MATRIX_AS_CSHARP_ARRAY_IN1 diff --git a/ortools/util/file_util.h b/ortools/util/file_util.h index d3b2e4cc284..795b8372f23 100644 --- a/ortools/util/file_util.h +++ b/ortools/util/file_util.h @@ -17,10 +17,10 @@ #include #include +#include "absl/strings/string_view.h" #include "google/protobuf/message.h" #include "ortools/base/file.h" #include "ortools/base/recordio.h" -#include "ortools/base/string_view.h" namespace operations_research { diff --git a/ortools/util/functions_swig_helpers.h b/ortools/util/functions_swig_helpers.h index 66aa6a59bd9..a1cb1d8ee9f 100644 --- a/ortools/util/functions_swig_helpers.h +++ b/ortools/util/functions_swig_helpers.h @@ -37,6 +37,12 @@ class LongLongToLong { virtual int64 Run(int64, int64) = 0; }; +class IntToLong { + public: + virtual ~IntToLong() {} + virtual int64 Run(int) = 0; +}; + class IntIntToLong { public: virtual ~IntIntToLong() {} diff --git a/ortools/util/functions_swig_test_helpers.h b/ortools/util/functions_swig_test_helpers.h index 6a5077db026..d86b4a08d36 100644 --- a/ortools/util/functions_swig_test_helpers.h +++ b/ortools/util/functions_swig_test_helpers.h @@ -42,6 +42,10 @@ class FunctionSwigTestHelpers { return fun(x, y); } + static int64 NoOpIntToInt64(std::function fun, int x) { + return fun(x); + } + static int64 NoOpIntPairToInt64(std::function fun, int x, int y) { return fun(x, y); diff --git a/ortools/util/graph_export.cc b/ortools/util/graph_export.cc index 6ebd8d02942..5af37179816 100644 --- a/ortools/util/graph_export.cc +++ b/ortools/util/graph_export.cc @@ -15,11 +15,11 @@ #include +#include "absl/strings/str_format.h" #include "ortools/base/file.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" #include "ortools/base/status.h" -#include "ortools/base/stringprintf.h" namespace operations_research { @@ -52,20 +52,19 @@ class DotSyntax : public GraphSyntax { std::string Node(const std::string& name, const std::string& label, const std::string& shape, const std::string& color) override { - return absl::StrFormat("%s [shape=%s label=\"%s\" color=%s]\n", name.c_str(), - shape.c_str(), label.c_str(), color.c_str()); + return absl::StrFormat("%s [shape=%s label=\"%s\" color=%s]\n", name, shape, + label, color); } // Adds one link in the generated graph. std::string Link(const std::string& source, const std::string& destination, const std::string& label) override { - return absl::StrFormat("%s -> %s [label=%s]\n", source.c_str(), - destination.c_str(), label.c_str()); + return absl::StrFormat("%s -> %s [label=%s]\n", source, destination, label); } // File header. std::string Header(const std::string& name) override { - return absl::StrFormat("graph %s {\n", name.c_str()); + return absl::StrFormat("graph %s {\n", name); } // File footer. @@ -88,7 +87,7 @@ class GmlSyntax : public GraphSyntax { " fill \"%s\"\n" " ]\n" " ]\n", - name.c_str(), label.c_str(), shape.c_str(), color.c_str()); + name, label, shape, color); } // Adds one link in the generated graph. @@ -100,7 +99,7 @@ class GmlSyntax : public GraphSyntax { " source \"%s\"\n" " target \"%s\"\n" " ]\n", - label.c_str(), source.c_str(), destination.c_str()); + label, source, destination); } // File header. @@ -108,7 +107,7 @@ class GmlSyntax : public GraphSyntax { return absl::StrFormat( "graph [\n" " name \"%s\"\n", - name.c_str()); + name); } // File footer. diff --git a/ortools/util/java/functions.i b/ortools/util/java/functions.i index ed7969b394e..743ccd3dcbf 100644 --- a/ortools/util/java/functions.i +++ b/ortools/util/java/functions.i @@ -238,6 +238,8 @@ WRAP_STD_FUNCTION_JAVA_AUX(Package, "CppClass", CppClass, ReturnType, %rename (run) operations_research::swig_util::LongToLong::Run; %feature("director") operations_research::swig_util::LongLongToLong; %rename (run) operations_research::swig_util::LongLongToLong::Run; +%feature("director") operations_research::swig_util::IntToLong; +%rename (run) operations_research::swig_util::IntToLong::Run; %feature("director") operations_research::swig_util::IntIntToLong; %rename (run) operations_research::swig_util::IntIntToLong::Run; %feature("director") operations_research::swig_util::LongLongLongToLong; diff --git a/ortools/util/java/vector.i b/ortools/util/java/vector.i index adaaef897dd..21e70f72c9b 100644 --- a/ortools/util/java/vector.i +++ b/ortools/util/java/vector.i @@ -15,7 +15,7 @@ // where Num is an atomic numeric type. // // Normally we'd simply use %include "std_vector.i" with the %template -// directive (see http://www.i.org/Doc1.3/Library.html#Library_nn15), but +// directive (see http://www.swig.org/Doc1.3/Library.html#Library_nn15), but // in google3 we can't, because exceptions are forbidden. // // TODO(user): move to base/swig/java. diff --git a/ortools/util/monoid_operation_tree.h b/ortools/util/monoid_operation_tree.h index 80dadcf65ab..eb959172fa9 100644 --- a/ortools/util/monoid_operation_tree.h +++ b/ortools/util/monoid_operation_tree.h @@ -17,9 +17,9 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/stringprintf.h" namespace operations_research { @@ -215,7 +215,7 @@ std::string MonoidOperationTree::DebugString() const { ++layer; } absl::StrAppendFormat(&out, "Position %d: %s\n", i, - nodes_[i].DebugString().c_str()); + nodes_[i].DebugString()); } return out; } diff --git a/ortools/util/optional_boolean.proto b/ortools/util/optional_boolean.proto index 8efa3cd5034..b53e50db3e3 100644 --- a/ortools/util/optional_boolean.proto +++ b/ortools/util/optional_boolean.proto @@ -13,6 +13,10 @@ syntax = "proto3"; +option java_package = "com.google.ortools.util"; +option java_multiple_files = true; +option csharp_namespace = "Google.OrTools.Util"; + package operations_research; // A "three-way" boolean: unspecified, false or true. diff --git a/ortools/util/piecewise_linear_function.cc b/ortools/util/piecewise_linear_function.cc index 39809708b27..657007cbf3b 100644 --- a/ortools/util/piecewise_linear_function.cc +++ b/ortools/util/piecewise_linear_function.cc @@ -18,8 +18,8 @@ #include #include +#include "absl/strings/str_format.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" namespace operations_research { namespace { diff --git a/ortools/util/proto_tools.cc b/ortools/util/proto_tools.cc index 8815bcfe8fc..a59a67e2ed0 100644 --- a/ortools/util/proto_tools.cc +++ b/ortools/util/proto_tools.cc @@ -13,10 +13,10 @@ #include "ortools/util/proto_tools.h" +#include "absl/strings/str_cat.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/message.h" #include "google/protobuf/text_format.h" -#include "ortools/base/join.h" namespace operations_research { diff --git a/ortools/util/rev.h b/ortools/util/rev.h index c9cfef5663a..24168444bce 100644 --- a/ortools/util/rev.h +++ b/ortools/util/rev.h @@ -15,9 +15,9 @@ #ifndef OR_TOOLS_UTIL_REV_H_ #define OR_TOOLS_UTIL_REV_H_ -#include #include +#include "absl/container/flat_hash_map.h" #include "ortools/base/logging.h" #include "ortools/base/map_util.h" @@ -220,7 +220,7 @@ class RevGrowingMultiMap : ReversibleInterface { // TODO(user): use inlined vectors. Another datastructure that may be more // efficient is to use a linked list inside added_keys_ for the values sharing // the same key. - std::unordered_map> map_; + absl::flat_hash_map> map_; // Backtracking data. std::vector added_keys_; diff --git a/ortools/util/saturated_arithmetic.h b/ortools/util/saturated_arithmetic.h index 83ebf5480f3..44ad2772d43 100644 --- a/ortools/util/saturated_arithmetic.h +++ b/ortools/util/saturated_arithmetic.h @@ -16,7 +16,7 @@ #include "ortools/base/integral_types.h" -#include "ortools/base/casts.h" +#include "absl/base/casts.h" #include "ortools/util/bitset.h" namespace operations_research { diff --git a/ortools/util/sorted_interval_list.cc b/ortools/util/sorted_interval_list.cc index 941193556f2..06249469d07 100644 --- a/ortools/util/sorted_interval_list.cc +++ b/ortools/util/sorted_interval_list.cc @@ -15,8 +15,8 @@ #include +#include "absl/strings/str_format.h" #include "ortools/base/logging.h" -#include "ortools/base/stringprintf.h" #include "ortools/util/saturated_arithmetic.h" namespace operations_research { @@ -26,39 +26,8 @@ std::string ClosedInterval::DebugString() const { return absl::StrFormat("[%" GG_LL_FORMAT "d,%" GG_LL_FORMAT "d]", start, end); } -std::string IntervalsAsString(const std::vector& intervals) { - std::string result; - for (ClosedInterval interval : intervals) { - result += interval.DebugString(); - } - return result; -} - -// TODO(user): binary search if size is large? -bool SortedDisjointIntervalsContain(absl::Span intervals, - int64 value) { - for (const ClosedInterval& interval : intervals) { - if (interval.start <= value && interval.end >= value) return true; - } - return false; -} - -std::vector SortedDisjointIntervalsFromValues( - std::vector values) { - std::sort(values.begin(), values.end()); - std::vector result; - for (const int64 v : values) { - if (result.empty() || v > result.back().end + 1) { - result.push_back({v, v}); - } else { - result.back().end = v; - } - } - return result; -} - -bool IntervalsAreSortedAndDisjoint( - const std::vector& intervals) { +bool IntervalsAreSortedAndNonAdjacent( + absl::Span intervals) { if (intervals.empty()) return true; int64 previous_end; bool is_first_interval = true; @@ -77,9 +46,18 @@ bool IntervalsAreSortedAndDisjoint( namespace { +template +std::string IntervalsAsString(const Intervals& intervals) { + std::string result; + for (ClosedInterval interval : intervals) { + result += interval.DebugString(); + } + return result; +} + // Transforms a sorted list of intervals in a sorted DISJOINT list for which -// IntervalsAreSortedAndDisjoint() would return true. -void UnionOfSortedIntervals(std::vector* intervals) { +// IntervalsAreSortedAndNonAdjacent() would return true. +void UnionOfSortedIntervals(absl::InlinedVector* intervals) { DCHECK(std::is_sorted(intervals->begin(), intervals->end())); int new_size = 0; for (const ClosedInterval& i : *intervals) { @@ -91,132 +69,14 @@ void UnionOfSortedIntervals(std::vector* intervals) { } } intervals->resize(new_size); - DCHECK(IntervalsAreSortedAndDisjoint(*intervals)) << *intervals; -} - -} // namespace - -std::vector IntersectionOfSortedDisjointIntervals( - const std::vector& a, - const std::vector& b) { - DCHECK(IntervalsAreSortedAndDisjoint(a)); - DCHECK(IntervalsAreSortedAndDisjoint(b)); - std::vector result; - for (int i = 0, j = 0; i < a.size() && j < b.size();) { - const ClosedInterval intersection{std::max(a[i].start, b[j].start), - std::min(a[i].end, b[j].end)}; - if (intersection.start > intersection.end) { - // Intersection is empty, we advance past the first interval of the two. - if (a[i].start < b[j].start) { - i++; - } else { - j++; - } - } else { - // Intersection is non-empty, we add it to the result and advance past - // the first interval to finish. - result.push_back(intersection); - if (a[i].end < b[j].end) { - i++; - } else { - j++; - } - } - } - return result; -} - -std::vector ComplementOfSortedDisjointIntervals( - const std::vector& intervals) { - DCHECK(IntervalsAreSortedAndDisjoint(intervals)); - std::vector result; - int64 next_start = kint64min; - for (const ClosedInterval& interval : intervals) { - if (interval.start != kint64min) { - result.push_back({next_start, interval.start - 1}); - } - if (interval.end == kint64max) return result; - next_start = interval.end + 1; - } - result.push_back({next_start, kint64max}); - return result; -} - -std::vector NegationOfSortedDisjointIntervals( - std::vector intervals) { - DCHECK(IntervalsAreSortedAndDisjoint(intervals)); - if (intervals.empty()) return intervals; - std::reverse(intervals.begin(), intervals.end()); - if (intervals.back().end == kint64min) intervals.pop_back(); // corner-case - for (ClosedInterval& ref : intervals) { - std::swap(ref.start, ref.end); - ref.start = ref.start == kint64min ? kint64max : -ref.start; - ref.end = ref.end == kint64min ? kint64max : -ref.end; - } - return intervals; -} - -std::vector UnionOfSortedDisjointIntervals( - const std::vector& a, - const std::vector& b) { - DCHECK(IntervalsAreSortedAndDisjoint(a)); - DCHECK(IntervalsAreSortedAndDisjoint(b)); - std::vector result(a.size() + b.size()); - std::merge(a.begin(), a.end(), b.begin(), b.end(), result.begin()); - UnionOfSortedIntervals(&result); - return result; -} - -std::vector AdditionOfSortedDisjointIntervals( - const std::vector& a, - const std::vector& b) { - DCHECK(IntervalsAreSortedAndDisjoint(a)); - DCHECK(IntervalsAreSortedAndDisjoint(b)); - std::vector result; - for (const ClosedInterval& i : a) { - for (const ClosedInterval& j : b) { - result.push_back({CapAdd(i.start, j.start), CapAdd(i.end, j.end)}); - } - } - std::sort(result.begin(), result.end()); - UnionOfSortedIntervals(&result); - return result; -} -std::vector MultiplicationOfSortedDisjointIntervals( - std::vector intervals, int64 coeff) { - DCHECK(IntervalsAreSortedAndDisjoint(intervals)); - const int64 abs_coeff = std::abs(coeff); - for (ClosedInterval& i : intervals) { - i.start = CapProd(i.start, abs_coeff); - i.end = CapProd(i.end, abs_coeff); - } - UnionOfSortedIntervals(&intervals); - return coeff > 0 ? intervals : NegationOfSortedDisjointIntervals(intervals); + // This is important for InlinedVector in the case the result is a single + // intervals. + intervals->shrink_to_fit(); + DCHECK(IntervalsAreSortedAndNonAdjacent(*intervals)); } -std::vector PreciseMultiplicationOfSortedDisjointIntervals( - std::vector intervals, int64 coeff, bool* success) { - DCHECK(IntervalsAreSortedAndDisjoint(intervals)); - *success = true; - if (intervals.empty() || coeff == 0) return {}; - const int64 abs_coeff = std::abs(coeff); - if (abs_coeff != 1) { - if (CapSub(intervals.back().end, intervals.front().start) <= 1000) { - std::vector individual_values; - for (ClosedInterval& i : intervals) { - for (int v = i.start; v <= i.end; ++v) { - individual_values.push_back(CapProd(v, abs_coeff)); - } - } - intervals = SortedDisjointIntervalsFromValues(individual_values); - } else { - *success = false; - return {}; - } - } - return coeff > 0 ? intervals : NegationOfSortedDisjointIntervals(intervals); -} +} // namespace int64 CeilRatio(int64 value, int64 positive_coeff) { DCHECK_GT(positive_coeff, 0); @@ -232,43 +92,6 @@ int64 FloorRatio(int64 value, int64 positive_coeff) { return result - adjust; } -std::vector InverseMultiplicationOfSortedDisjointIntervals( - std::vector intervals, int64 coeff) { - DCHECK(IntervalsAreSortedAndDisjoint(intervals)); - if (coeff == 0) { - return SortedDisjointIntervalsContain(intervals, 0) - ? std::vector({{kint64min, kint64max}}) - : std::vector(); - } - int new_size = 0; - const int64 abs_coeff = std::abs(coeff); - for (const ClosedInterval& i : intervals) { - const int64 start = CeilRatio(i.start, abs_coeff); - const int64 end = FloorRatio(i.end, abs_coeff); - if (start > end) continue; - if (new_size > 0 && start == intervals[new_size - 1].end + 1) { - intervals[new_size - 1].end = end; - } else { - intervals[new_size++] = {start, end}; - } - } - intervals.resize(new_size); - return coeff > 0 ? intervals : NegationOfSortedDisjointIntervals(intervals); -} - -std::vector DivisionOfSortedDisjointIntervals( - std::vector intervals, int64 coeff) { - CHECK_NE(coeff, 0); - DCHECK(IntervalsAreSortedAndDisjoint(intervals)); - const int64 abs_coeff = std::abs(coeff); - for (ClosedInterval& i : intervals) { - i.start = i.start / abs_coeff; - i.end = i.end / abs_coeff; - } - UnionOfSortedIntervals(&intervals); - return coeff > 0 ? intervals : NegationOfSortedDisjointIntervals(intervals); -} - std::ostream& operator<<(std::ostream& out, const ClosedInterval& interval) { return out << interval.DebugString(); } @@ -279,7 +102,7 @@ std::ostream& operator<<(std::ostream& out, } std::ostream& operator<<(std::ostream& out, const Domain& domain) { - return out << IntervalsAsString(domain.intervals()); + return out << IntervalsAsString(domain); } Domain::Domain(int64 value) : intervals_({{value, value}}) {} @@ -290,14 +113,20 @@ Domain::Domain(int64 left, int64 right) : intervals_({{left, right}}) { Domain Domain::AllValues() { return Domain(kint64min, kint64max); } -Domain Domain::FromValues(absl::Span values) { +Domain Domain::FromValues(std::vector values) { + std::sort(values.begin(), values.end()); Domain result; - result.intervals_ = SortedDisjointIntervalsFromValues( - std::vector(values.begin(), values.end())); + for (const int64 v : values) { + if (result.intervals_.empty() || v > result.intervals_.back().end + 1) { + result.intervals_.push_back({v, v}); + } else { + result.intervals_.back().end = v; + } + } return result; } -Domain Domain::FromIntervals(absl::Span intervals) { +Domain Domain::FromIntervals(absl::Span intervals) { Domain result; result.intervals_.assign(intervals.begin(), intervals.end()); std::sort(result.intervals_.begin(), result.intervals_.end()); @@ -317,16 +146,17 @@ int64 Domain::Max() const { return intervals_.back().end; } -// TODO(user): In all the Domain::Function() remove the indirection and -// optimize. - +// TODO(user): binary search if size is large? bool Domain::Contains(int64 value) const { - return SortedDisjointIntervalsContain(intervals_, value); + for (const ClosedInterval& interval : intervals_) { + if (interval.start <= value && interval.end >= value) return true; + } + return false; } bool Domain::IsIncludedIn(const Domain& domain) const { int i = 0; - const std::vector& others = domain.intervals_; + const auto& others = domain.intervals_; for (const ClosedInterval interval : intervals_) { // Find the unique interval in others that contains interval if any. for (; i < others.size() && interval.end > others[i].end; ++i) { @@ -339,67 +169,166 @@ bool Domain::IsIncludedIn(const Domain& domain) const { Domain Domain::Complement() const { Domain result; - result.intervals_ = ComplementOfSortedDisjointIntervals(intervals_); + int64 next_start = kint64min; + for (const ClosedInterval& interval : intervals_) { + if (interval.start != kint64min) { + result.intervals_.push_back({next_start, interval.start - 1}); + } + if (interval.end == kint64max) return result; + next_start = interval.end + 1; + } + result.intervals_.push_back({next_start, kint64max}); + DCHECK(IntervalsAreSortedAndNonAdjacent(result.intervals_)); return result; } Domain Domain::Negation() const { - Domain result; - result.intervals_ = NegationOfSortedDisjointIntervals(intervals_); + Domain result = *this; + if (intervals_.empty()) return result; + std::reverse(result.intervals_.begin(), result.intervals_.end()); + if (result.intervals_.back().end == kint64min) { + // corner-case + result.intervals_.pop_back(); + } + for (ClosedInterval& ref : result.intervals_) { + std::swap(ref.start, ref.end); + ref.start = ref.start == kint64min ? kint64max : -ref.start; + ref.end = ref.end == kint64min ? kint64max : -ref.end; + } + DCHECK(IntervalsAreSortedAndNonAdjacent(result.intervals_)); return result; } Domain Domain::IntersectionWith(const Domain& domain) const { Domain result; - result.intervals_ = - IntersectionOfSortedDisjointIntervals(intervals_, domain.intervals_); + const auto& a = intervals_; + const auto& b = domain.intervals_; + for (int i = 0, j = 0; i < a.size() && j < b.size();) { + const ClosedInterval intersection{std::max(a[i].start, b[j].start), + std::min(a[i].end, b[j].end)}; + if (intersection.start > intersection.end) { + // Intersection is empty, we advance past the first interval of the two. + if (a[i].start < b[j].start) { + i++; + } else { + j++; + } + } else { + // Intersection is non-empty, we add it to the result and advance past + // the first interval to finish. + result.intervals_.push_back(intersection); + if (a[i].end < b[j].end) { + i++; + } else { + j++; + } + } + } + DCHECK(IntervalsAreSortedAndNonAdjacent(result.intervals_)); return result; } Domain Domain::UnionWith(const Domain& domain) const { Domain result; - result.intervals_ = - UnionOfSortedDisjointIntervals(intervals_, domain.intervals_); + const auto& a = intervals_; + const auto& b = domain.intervals_; + result.intervals_.resize(a.size() + b.size()); + std::merge(a.begin(), a.end(), b.begin(), b.end(), result.intervals_.begin()); + UnionOfSortedIntervals(&result.intervals_); return result; } Domain Domain::AdditionWith(const Domain& domain) const { Domain result; - result.intervals_ = - AdditionOfSortedDisjointIntervals(intervals_, domain.intervals_); + + const auto& a = intervals_; + const auto& b = domain.intervals_; + for (const ClosedInterval& i : a) { + for (const ClosedInterval& j : b) { + result.intervals_.push_back( + {CapAdd(i.start, j.start), CapAdd(i.end, j.end)}); + } + } + + std::sort(result.intervals_.begin(), result.intervals_.end()); + UnionOfSortedIntervals(&result.intervals_); return result; } Domain Domain::MultiplicationBy(int64 coeff, bool* success) const { + *success = true; + if (intervals_.empty() || coeff == 0) return {}; + Domain result; - result.intervals_ = PreciseMultiplicationOfSortedDisjointIntervals( - intervals_, coeff, success); - return result; + const int64 abs_coeff = std::abs(coeff); + if (abs_coeff != 1) { + if (CapSub(Max(), Min()) <= 1000) { + std::vector individual_values; + for (const ClosedInterval& i : intervals_) { + for (int v = i.start; v <= i.end; ++v) { + individual_values.push_back(CapProd(v, abs_coeff)); + } + } + result = Domain::FromValues(individual_values); + } else { + *success = false; + return {}; + } + } else { + result = *this; + } + return coeff > 0 ? result : result.Negation(); } Domain Domain::ContinuousMultiplicationBy(int64 coeff) const { - Domain result; - result.intervals_ = - MultiplicationOfSortedDisjointIntervals(intervals_, coeff); - return result; + Domain result = *this; + const int64 abs_coeff = std::abs(coeff); + for (ClosedInterval& i : result.intervals_) { + i.start = CapProd(i.start, abs_coeff); + i.end = CapProd(i.end, abs_coeff); + } + UnionOfSortedIntervals(&result.intervals_); + return coeff > 0 ? result : result.Negation(); } Domain Domain::DivisionBy(int64 coeff) const { - Domain result; - result.intervals_ = DivisionOfSortedDisjointIntervals(intervals_, coeff); - return result; + CHECK_NE(coeff, 0); + Domain result = *this; + const int64 abs_coeff = std::abs(coeff); + for (ClosedInterval& i : result.intervals_) { + i.start = i.start / abs_coeff; + i.end = i.end / abs_coeff; + } + UnionOfSortedIntervals(&result.intervals_); + return coeff > 0 ? result : result.Negation(); } Domain Domain::InverseMultiplicationBy(const int64 coeff) const { - Domain result; - result.intervals_ = - InverseMultiplicationOfSortedDisjointIntervals(intervals_, coeff); - return result; + if (coeff == 0) { + return Contains(0) ? Domain::AllValues() : Domain(); + } + Domain result = *this; + int new_size = 0; + const int64 abs_coeff = std::abs(coeff); + for (const ClosedInterval& i : result.intervals_) { + const int64 start = CeilRatio(i.start, abs_coeff); + const int64 end = FloorRatio(i.end, abs_coeff); + if (start > end) continue; + if (new_size > 0 && start == result.intervals_[new_size - 1].end + 1) { + result.intervals_[new_size - 1].end = end; + } else { + result.intervals_[new_size++] = {start, end}; + } + } + result.intervals_.resize(new_size); + result.intervals_.shrink_to_fit(); + DCHECK(IntervalsAreSortedAndNonAdjacent(result.intervals_)); + return coeff > 0 ? result : result.Negation(); } bool Domain::operator<(const Domain& other) const { - const std::vector& d1 = intervals_; - const std::vector& d2 = other.intervals_; + const auto& d1 = intervals_; + const auto& d2 = other.intervals_; const int common_size = std::min(d1.size(), d2.size()); for (int i = 0; i < common_size; ++i) { const ClosedInterval& i1 = d1[i]; diff --git a/ortools/util/sorted_interval_list.h b/ortools/util/sorted_interval_list.h index 07c60d291af..ce1099984a6 100644 --- a/ortools/util/sorted_interval_list.h +++ b/ortools/util/sorted_interval_list.h @@ -19,8 +19,9 @@ #include #include +#include "absl/container/inlined_vector.h" +#include "absl/types/span.h" #include "ortools/base/integral_types.h" -#include "ortools/base/span.h" namespace operations_research { @@ -50,10 +51,8 @@ std::ostream& operator<<(std::ostream& out, // - The intervals appear in increasing order. // - for all i: intervals[i].start <= intervals[i].end // - for all i but the last: intervals[i].end + 1 < intervals[i+1].start -// -// TODO(user): rename to IntervalsAreSortedAndNonAdjacent(). -bool IntervalsAreSortedAndDisjoint( - const std::vector& intervals); +bool IntervalsAreSortedAndNonAdjacent( + absl::Span intervals); // We call "domain" any subset of Int64 = [kint64min, kint64max]. // @@ -80,10 +79,10 @@ class Domain { // Creates a domain from the union of an unsorted list of integer values. // Input values may be repeated, with no consequence on the output - static Domain FromValues(absl::Span values); + static Domain FromValues(std::vector values); // Creates a domain from the union of an unsorted list of intervals. - static Domain FromIntervals(absl::Span intervals); + static Domain FromIntervals(absl::Span intervals); // Returns true if this is the empty set. bool IsEmpty() const; @@ -147,10 +146,6 @@ class Domain { // For instance Domain(1, 7).InverseMultiplicationBy(2) == Domain(1, 3). Domain InverseMultiplicationBy(const int64 coeff) const; - // Returns the representation of D as the unique sorted list of non-adjacent - // intervals. This will always satisfy IntervalsAreSortedAndDisjoint(). - std::vector intervals() const { return intervals_; } - // Returns a compact std::string of a vector of intervals like // "[1,4][6][10,20]" std::string ToString() const; @@ -166,19 +161,40 @@ class Domain { return intervals_ != other.intervals_; } + // Basic read-only std::vector<> wrapping to view a Domain as a sorted list of + // non-adjacent intervals. Note that we don't expose size() which might be + // confused with the number of values in the domain. + // + // Note(user): I used to provide a Domain::intervals() function instead that + // returned a reference to the underlying intervals_, but this was bug-prone + // with temporary like "domain.Negation().intervals()". + int NumIntervals() const { return intervals_.size(); } + ClosedInterval front() const { return intervals_.front(); } + ClosedInterval back() const { return intervals_.back(); } + ClosedInterval operator[](int i) const { return intervals_[i]; } + absl::InlinedVector::const_iterator begin() const { + return intervals_.begin(); + } + absl::InlinedVector::const_iterator end() const { + return intervals_.end(); + } + + // Deprecated. TODO(user): remove, this makes a copy. + std::vector intervals() const { + return {intervals_.begin(), intervals_.end()}; + } + private: - // Invariant: will always satisfy IntervalsAreSortedAndDisjoint(). - std::vector intervals_; + // Invariant: will always satisfy IntervalsAreSortedAndNonAdjacent(). + // + // Note that we use InlinedVector for the common case of single interal + // domains. This provide a good performance boost when working with a + // std::vector. + absl::InlinedVector intervals_; }; std::ostream& operator<<(std::ostream& out, const Domain& domain); -// TODO(user): Remove. These are deprecated. Use the Domain class instead. -std::vector UnionOfSortedDisjointIntervals( - const std::vector& a, const std::vector& b); -std::vector NegationOfSortedDisjointIntervals( - std::vector intervals); - // This class represents a sorted list of disjoint, closed intervals. When an // interval is inserted, all intervals that overlap it or that are even adjacent // to it are merged into one. I.e. [0,14] and [15,30] will be merged to [0,30]. @@ -262,6 +278,9 @@ class SortedDisjointIntervalList { const Iterator begin() const { return intervals_.begin(); } const Iterator end() const { return intervals_.end(); } + // Returns a const& to the last interval. The list must not be empty. + const ClosedInterval& last() const { return *intervals_.rbegin(); } + void clear() { intervals_.clear(); } void swap(SortedDisjointIntervalList& other) { intervals_.swap(other.intervals_); diff --git a/ortools/util/stats.cc b/ortools/util/stats.cc index 328ee2a5a1f..e34c1eb62b2 100644 --- a/ortools/util/stats.cc +++ b/ortools/util/stats.cc @@ -14,10 +14,9 @@ #include "ortools/util/stats.h" #include -#include "ortools/base/stringprintf.h" +#include "absl/strings/str_format.h" #include "ortools/base/stl_util.h" -#include "ortools/base/stringprintf.h" #include "ortools/port/sysinfo.h" #include "ortools/port/utf8.h" @@ -206,10 +205,9 @@ void TimeDistribution::AddTimeInCycles(double cycles) { std::string TimeDistribution::ValueAsString() const { return absl::StrFormat( - "%8llu [%8s, %8s] %8s %8s %8s\n", num_, PrintCyclesAsTime(min_).c_str(), - PrintCyclesAsTime(max_).c_str(), PrintCyclesAsTime(Average()).c_str(), - PrintCyclesAsTime(StdDeviation()).c_str(), - PrintCyclesAsTime(sum_).c_str()); + "%8u [%8s, %8s] %8s %8s %8s\n", num_, PrintCyclesAsTime(min_), + PrintCyclesAsTime(max_), PrintCyclesAsTime(Average()), + PrintCyclesAsTime(StdDeviation()), PrintCyclesAsTime(sum_)); } void RatioDistribution::Add(double value) { @@ -218,7 +216,7 @@ void RatioDistribution::Add(double value) { } std::string RatioDistribution::ValueAsString() const { - return absl::StrFormat("%8llu [%7.2lf%%, %7.2lf%%] %7.2lf%% %7.2lf%%\n", num_, + return absl::StrFormat("%8u [%7.2f%%, %7.2f%%] %7.2f%% %7.2f%%\n", num_, 100.0 * min_, 100.0 * max_, 100.0 * Average(), 100.0 * StdDeviation()); } @@ -226,7 +224,7 @@ std::string RatioDistribution::ValueAsString() const { void DoubleDistribution::Add(double value) { AddToDistribution(value); } std::string DoubleDistribution::ValueAsString() const { - return absl::StrFormat("%8llu [%8.1e, %8.1e] %8.1e %8.1e\n", num_, min_, max_, + return absl::StrFormat("%8u [%8.1e, %8.1e] %8.1e %8.1e\n", num_, min_, max_, Average(), StdDeviation()); } @@ -235,7 +233,7 @@ void IntegerDistribution::Add(int64 value) { } std::string IntegerDistribution::ValueAsString() const { - return absl::StrFormat("%8llu [%8.lf, %8.lf] %8.2lf %8.2lf %8.lf\n", num_, min_, + return absl::StrFormat("%8u [%8.f, %8.f] %8.2f %8.2f %8.f\n", num_, min_, max_, Average(), StdDeviation(), sum_); } diff --git a/ortools/util/stats.h b/ortools/util/stats.h index 4d414dcf6ba..8b3d33da29e 100644 --- a/ortools/util/stats.h +++ b/ortools/util/stats.h @@ -71,7 +71,7 @@ #include #include #ifdef HAS_PERF_SUBSYSTEM -#include "absl/ortools/base/str_replace.h" +#include "absl/strings/str_replace.h" #include "exegesis/exegesis/itineraries/perf_subsystem.h" #include "ortools/util/time_limit.h" #endif // HAS_PERF_SUBSYSTEM diff --git a/ortools/util/time_limit.cc b/ortools/util/time_limit.cc index 42af8ddd8e9..387ad0837b7 100644 --- a/ortools/util/time_limit.cc +++ b/ortools/util/time_limit.cc @@ -13,7 +13,7 @@ #include "ortools/util/time_limit.h" -#include "ortools/base/join.h" +#include "absl/strings/str_cat.h" DEFINE_bool(time_limit_use_usertime, false, "If true, rely on the user time in the TimeLimit class. This is " @@ -29,19 +29,16 @@ const double TimeLimit::kSafetyBufferSeconds = 1e-4; const int TimeLimit::kHistorySize = 100; std::string TimeLimit::DebugString() const { - std::string buffer = - absl::StrCat("Time left: ", absl::LegacyPrecision(GetTimeLeft()), - "\nDeterministic time left: ", - absl::LegacyPrecision(GetDeterministicTimeLeft()), - "\nElapsed time: ", absl::LegacyPrecision(GetElapsedTime()), - "\nElapsed deterministic time: ", - absl::LegacyPrecision(GetElapsedDeterministicTime())); + std::string buffer = absl::StrCat( + "Time left: ", (GetTimeLeft()), + "\nDeterministic time left: ", (GetDeterministicTimeLeft()), + "\nElapsed time: ", (GetElapsedTime()), + "\nElapsed deterministic time: ", (GetElapsedDeterministicTime())); #ifndef NDEBUG for (const auto& counter : deterministic_counters_) { const std::string& counter_name = counter.first; const double counter_value = counter.second; - absl::StrAppend(&buffer, "\n", counter_name, ": ", - absl::LegacyPrecision(counter_value)); + absl::StrAppend(&buffer, "\n", counter_name, ": ", (counter_value)); } #endif return buffer; diff --git a/ortools/util/time_limit.h b/ortools/util/time_limit.h index b8ec8cfec8b..18e640004e6 100644 --- a/ortools/util/time_limit.h +++ b/ortools/util/time_limit.h @@ -20,16 +20,14 @@ #include #include #include -#ifndef NDEBUG -#include -#endif +#include "absl/memory/memory.h" +#include "absl/time/clock.h" +#include "absl/container/flat_hash_map.h" +#include "absl/time/time.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/logging.h" #include "ortools/base/macros.h" -#include "ortools/base/memory.h" -#include "ortools/base/port.h" -#include "ortools/base/time_support.h" #include "ortools/base/timer.h" #include "ortools/util/running_stat.h" #ifdef HAS_PERF_SUBSYSTEM @@ -256,6 +254,7 @@ class TimeLimit { // any registered external boolean or calls to RegisterSigintHandler(). template void ResetLimitFromParameters(const Parameters& parameters); + void MergeWithGlobalTimeLimit(TimeLimit* other); // Returns information about the time limit object in a human-readable form. std::string DebugString() const; @@ -293,7 +292,7 @@ class TimeLimit { #ifndef NDEBUG // Contains the values of the deterministic time counters. - std::unordered_map deterministic_counters_; + absl::flat_hash_map deterministic_counters_; #endif friend class NestedTimeLimit; @@ -406,6 +405,17 @@ inline void TimeLimit::ResetLimitFromParameters(const Parameters& parameters) { std::numeric_limits::infinity()); } +inline void TimeLimit::MergeWithGlobalTimeLimit(TimeLimit* other) { + if (other == nullptr) return; + ResetTimers( + std::min(GetTimeLeft(), other->GetTimeLeft()), + std::min(GetDeterministicTimeLeft(), other->GetDeterministicTimeLeft()), + std::numeric_limits::infinity()); + if (other->ExternalBooleanAsLimit() != nullptr) { + RegisterExternalBooleanAsLimit(other->ExternalBooleanAsLimit()); + } +} + inline double TimeLimit::ReadInstructionCounter() { #ifdef HAS_PERF_SUBSYSTEM if (FLAGS_time_limit_use_instruction_count) { diff --git a/ortools/util/tuple_set.h b/ortools/util/tuple_set.h index f6e6eea7a34..ce4f1c79402 100644 --- a/ortools/util/tuple_set.h +++ b/ortools/util/tuple_set.h @@ -34,9 +34,9 @@ #define OR_TOOLS_UTIL_TUPLE_SET_H_ #include -#include -#include #include +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "ortools/base/hash.h" #include "ortools/base/integral_types.h" @@ -124,7 +124,7 @@ class IntTupleSet { // Maps a tuple's fingerprint to the list of tuples with this // fingerprint, represented by their start index in the // flat_tuples_ vector. - std::unordered_map > tuple_fprint_to_index_; + absl::flat_hash_map > tuple_fprint_to_index_; }; // Used to represent a light representation of a tuple. @@ -358,7 +358,7 @@ inline int IntTupleSet::NumDifferentValuesInColumn(int col) const { if (col < 0 || col >= data_->Arity()) { return 0; } - std::unordered_set values; + absl::flat_hash_set values; for (int i = 0; i < data_->NumTuples(); ++i) { values.insert(data_->Value(i, col)); } diff --git a/ortools/util/vector_map.h b/ortools/util/vector_map.h index 315f26a107b..7a21d2d4576 100644 --- a/ortools/util/vector_map.h +++ b/ortools/util/vector_map.h @@ -16,8 +16,8 @@ #ifndef OR_TOOLS_UTIL_VECTOR_MAP_H_ #define OR_TOOLS_UTIL_VECTOR_MAP_H_ -#include #include +#include "absl/container/flat_hash_map.h" #include "ortools/base/map_util.h" namespace operations_research { @@ -62,7 +62,7 @@ class VectorMap { } // TODO(user): explore a int-type version. - // Returns wether the element has already been added to the vector-map. + // Returns whether the element has already been added to the vector-map. bool Contains(const T& element) const { return gtl::ContainsKey(map_, element); } @@ -109,7 +109,7 @@ class VectorMap { private: std::vector list_; - std::unordered_map map_; + absl::flat_hash_map map_; }; } // namespace operations_research diff --git a/ortools/util/xml_helper.cc b/ortools/util/xml_helper.cc index 4532e4f993b..458a47a696a 100644 --- a/ortools/util/xml_helper.cc +++ b/ortools/util/xml_helper.cc @@ -16,9 +16,9 @@ #include #include -#include "ortools/base/join.h" -#include "ortools/base/stringprintf.h" -#include "ortools/base/strutil.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_replace.h" namespace operations_research { @@ -34,7 +34,7 @@ void XmlHelper::StartElement(const std::string& name) { content_.append(">\n"); } tags_.push(name); - absl::StrAppendFormat(&content_, "<%s", name.c_str()); + absl::StrAppendFormat(&content_, "<%s", name); direction_down_ = true; } @@ -70,8 +70,7 @@ void XmlHelper::AddAttribute(const std::string& key, const std::string& value) { } } - absl::StrAppendFormat(&content_, " %s=\"%s\"", key.c_str(), - escaped_value.str().c_str()); + absl::StrAppendFormat(&content_, " %s=\"%s\"", key, escaped_value.str()); } void XmlHelper::EndElement() { @@ -80,7 +79,7 @@ void XmlHelper::EndElement() { if (direction_down_) { content_.append(" />\n"); } else { - absl::StrAppendFormat(&content_, "\n", tag.c_str()); + absl::StrAppendFormat(&content_, "\n", tag); } direction_down_ = false; diff --git a/patches/abseil-cpp-master.patch b/patches/abseil-cpp-master.patch new file mode 100644 index 00000000000..c26d93f81d2 --- /dev/null +++ b/patches/abseil-cpp-master.patch @@ -0,0 +1,683 @@ +diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake +index 0c93417..46d7ba4 100644 +--- a/CMake/AbseilHelpers.cmake ++++ b/CMake/AbseilHelpers.cmake +@@ -36,80 +36,150 @@ set(ABSL_IDE_FOLDER Abseil) + # libraries are installed under CMAKE_INSTALL_FULL_LIBDIR by default + # + function(absl_library) ++ set(options ++ DISABLE_INSTALL # keep that in case we want to support installation one day ++ ) ++ set(oneValueArgs ++ TARGET ++ EXPORT_NAME ++ ) ++ set(multiValueArgs ++ SOURCES ++ PUBLIC_HEADER ++ INTERNAL_PUBLIC_HEADER ++ INTERNAL_STR_PUBLIC_HEADER ++ INTERNAL_CCTZ_PUBLIC_HEADER ++ PUBLIC_INCLUDE_DIRS ++ PUBLIC_LIBRARIES ++ PRIVATE_LIBRARIES ++ PRIVATE_COMPILE_FLAGS ++ PRIVATE_INCLUDE_DIRS ++ ) + cmake_parse_arguments(ABSL_LIB +- "DISABLE_INSTALL" # keep that in case we want to support installation one day +- "TARGET;EXPORT_NAME" +- "SOURCES;PUBLIC_LIBRARIES;PRIVATE_COMPILE_FLAGS" ++ "${options}" ++ "${oneValueArgs}" ++ "${multiValueArgs}" + ${ARGN} + ) + + set(_NAME ${ABSL_LIB_TARGET}) + string(TOUPPER ${_NAME} _UPPER_NAME) + +- add_library(${_NAME} STATIC ${ABSL_LIB_SOURCES}) ++ add_library(${_NAME} ${ABSL_LIB_SOURCES}) + + target_compile_options(${_NAME} PRIVATE ${ABSL_COMPILE_CXXFLAGS} ${ABSL_LIB_PRIVATE_COMPILE_FLAGS}) + target_link_libraries(${_NAME} PUBLIC ${ABSL_LIB_PUBLIC_LIBRARIES}) ++ target_link_libraries(${_NAME} PRIVATE ${ABSL_LIB_PRIVATE_LIBRARIES}) + target_include_directories(${_NAME} +- PUBLIC ${ABSL_COMMON_INCLUDE_DIRS} ${ABSL_LIB_PUBLIC_INCLUDE_DIRS} +- PRIVATE ${ABSL_LIB_PRIVATE_INCLUDE_DIRS} ++ PUBLIC ++ $ ++ $ ++ $ ++ PRIVATE ++ $ + ) + # Add all Abseil targets to a a folder in the IDE for organization. + set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}) ++ # Set the list of public headers to install ++ set_property(TARGET ${_NAME} PROPERTY PUBLIC_HEADER "${ABSL_LIB_PUBLIC_HEADER}") + + if(ABSL_LIB_EXPORT_NAME) + add_library(absl::${ABSL_LIB_EXPORT_NAME} ALIAS ${_NAME}) + endif() ++ if(NOT ${ABSL_LIB_DISABLE_INSTALL}) ++ install(TARGETS ${_NAME} ++ EXPORT ${PROJECT_NAME}-targets ++ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/absl/${ABSL_LIB_EXPORT_NAME} ++ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ++ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ++ ) ++ install(FILES ++ ${ABSL_LIB_INTERNAL_PUBLIC_HEADER} ++ DESTINATION include/absl/${ABSL_LIB_EXPORT_NAME}/internal ++ ) ++ install(FILES ++ ${ABSL_LIB_INTERNAL_STR_PUBLIC_HEADER} ++ DESTINATION include/absl/${ABSL_LIB_EXPORT_NAME}/internal/str_format ++ ) ++ install(FILES ++ ${ABSL_LIB_INTERNAL_CCTZ_PUBLIC_HEADER} ++ DESTINATION ++ include/absl/${ABSL_LIB_EXPORT_NAME}/internal/cctz/include/cctz ++ ) ++ endif() + endfunction() + +- +- + # + # header only virtual target creation + # + function(absl_header_library) ++ set(options ++ DISABLE_INSTALL # keep that in case we want to support installation one day ++ ) ++ set(oneValueArgs ++ TARGET ++ EXPORT_NAME ++ ) ++ set(multiValueArgs ++ PUBLIC_HEADER ++ INTERNAL_PUBLIC_HEADER ++ PUBLIC_INCLUDE_DIRS ++ PUBLIC_LIBRARIES ++ PRIVATE_COMPILE_FLAGS ++ ) + cmake_parse_arguments(ABSL_HO_LIB +- "DISABLE_INSTALL" +- "EXPORT_NAME;TARGET" +- "PUBLIC_LIBRARIES;PRIVATE_COMPILE_FLAGS;PUBLIC_INCLUDE_DIRS;PRIVATE_INCLUDE_DIRS" ++ "${options}" ++ "${oneValueArgs}" ++ "${multiValueArgs}" + ${ARGN} + ) + + set(_NAME ${ABSL_HO_LIB_TARGET}) + +- set(__dummy_header_only_lib_file "${CMAKE_CURRENT_BINARY_DIR}/${_NAME}_header_only_dummy.cc") +- +- if(NOT EXISTS ${__dummy_header_only_lib_file}) +- file(WRITE ${__dummy_header_only_lib_file} +- "/* generated file for header-only cmake target */ +- +- namespace absl { +- +- // single meaningless symbol +- void ${_NAME}__header_fakesym() {} +- } // namespace absl +- " +- ) +- endif() +- +- +- add_library(${_NAME} ${__dummy_header_only_lib_file}) +- target_link_libraries(${_NAME} PUBLIC ${ABSL_HO_LIB_PUBLIC_LIBRARIES}) ++ add_library(${_NAME} INTERFACE) ++ target_link_libraries(${_NAME} INTERFACE ${ABSL_HO_LIB_PUBLIC_LIBRARIES}) + target_include_directories(${_NAME} +- PUBLIC ${ABSL_COMMON_INCLUDE_DIRS} ${ABSL_HO_LIB_PUBLIC_INCLUDE_DIRS} +- PRIVATE ${ABSL_HO_LIB_PRIVATE_INCLUDE_DIRS} ++ INTERFACE ++ $ ++ $ ++ $ + ) + +- # Add all Abseil targets to a a folder in the IDE for organization. +- set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}) ++ # Folder property is not allowed for interface library ++ #set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}) ++ # Public headers is not allowed for interface library ++ # see: https://gitlab.kitware.com/cmake/cmake/issues/15234 ++ #set_property(TARGET ${_NAME} PROPERTY PUBLIC_HEADER "${ABSL_HO_LIB_PUBLIC_HEADER}") + + if(ABSL_HO_LIB_EXPORT_NAME) + add_library(absl::${ABSL_HO_LIB_EXPORT_NAME} ALIAS ${_NAME}) + endif() +- ++ if(NOT ${ABSL_HO_LIB_DISABLE_INSTALL}) ++ install(TARGETS ${_NAME} ++ EXPORT ${PROJECT_NAME}-targets ++ #PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/absl/${_NAME} ++ ) ++ install(FILES ++ ${ABSL_HO_LIB_PUBLIC_HEADER} ++ DESTINATION include/absl/${ABSL_HO_LIB_EXPORT_NAME} ++ ) ++ install(FILES ++ ${ABSL_HO_LIB_INTERNAL_PUBLIC_HEADER} ++ DESTINATION include/absl/${ABSL_HO_LIB_EXPORT_NAME}/internal ++ ) ++ endif() + endfunction() + + ++# ++# create a library in the absl namespace for testing purpose ++# ++function(absl_test_library) ++ if(ABSL_RUN_TESTS) ++ absl_library(${ARGN}) ++ endif(ABSL_RUN_TESTS) ++endfunction() ++ + # + # create an abseil unit_test and add it to the executed test list + # +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 9a7e103..a19164c 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -96,3 +96,12 @@ if(BUILD_TESTING) + endif() + + add_subdirectory(absl) ++ ++set(CMAKE_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) ++install(EXPORT ${PROJECT_NAME}-targets ++ NAMESPACE absl:: ++ DESTINATION ${CMAKE_INSTALL_CONFIGDIR} ++ ) ++install(FILES CMake/${PROJECT_NAME}-config.cmake ++ DESTINATION ${CMAKE_INSTALL_CONFIGDIR} ++ ) +diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt +index fdf45c5..3404fc3 100644 +--- a/absl/algorithm/CMakeLists.txt ++++ b/absl/algorithm/CMakeLists.txt +@@ -34,6 +34,8 @@ list(APPEND ALGORITHM_TEST_SRC + absl_header_library( + TARGET + absl_algorithm ++ PUBLIC_HEADER ++ ${ALGORITHM_PUBLIC_HEADERS} + EXPORT_NAME + algorithm + ) +diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt +index 04a6eb3..6fb78b4 100644 +--- a/absl/base/CMakeLists.txt ++++ b/absl/base/CMakeLists.txt +@@ -55,6 +55,10 @@ list(APPEND BASE_INTERNAL_HEADERS + "internal/tsan_mutex_interface.h" + "internal/unaligned_access.h" + "internal/unscaledcycleclock.h" ++ "internal/spinlock_akaros.inc" ++ "internal/spinlock_linux.inc" ++ "internal/spinlock_posix.inc" ++ "internal/spinlock_win32.inc" + ) + + +@@ -76,6 +80,10 @@ absl_library( + absl_base + SOURCES + ${BASE_SRC} ++ PUBLIC_HEADER ++ ${BASE_PUBLIC_HEADERS} ++ INTERNAL_PUBLIC_HEADER ++ ${BASE_INTERNAL_HEADERS} + PUBLIC_LIBRARIES + absl_dynamic_annotations + absl_spinlock_wait +@@ -162,6 +170,8 @@ absl_library( + ${MALLOC_INTERNAL_SRC} + PUBLIC_LIBRARIES + absl_dynamic_annotations ++ PRIVATE_LIBRARIES ++ absl::base + ) + + +diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt +index 9e40690..677d07e 100644 +--- a/absl/container/CMakeLists.txt ++++ b/absl/container/CMakeLists.txt +@@ -33,6 +33,7 @@ list(APPEND CONTAINER_INTERNAL_HEADERS + "internal/hash_policy_testing.h" + "internal/hash_policy_traits.h" + "internal/hashtable_debug.h" ++ "internal/hashtable_debug_hooks.h" + "internal/layout.h" + "internal/node_hash_policy.h" + "internal/raw_hash_map.h" +@@ -53,6 +54,10 @@ absl_library( + absl_container + SOURCES + "internal/raw_hash_set.cc" ++ PUBLIC_HEADER ++ ${CONTAINER_PUBLIC_HEADERS} ++ INTERNAL_PUBLIC_HEADER ++ ${CONTAINER_INTERNAL_HEADERS} + EXPORT_NAME + container + ) +@@ -69,9 +74,9 @@ list(APPEND TEST_INSTANCE_TRACKER_LIB_SRC + ) + + +-absl_library( ++absl_test_library( + TARGET +- test_instance_tracker_lib ++ absl_test_instance_tracker_lib + SOURCES + ${TEST_INSTANCE_TRACKER_LIB_SRC} + PUBLIC_LIBRARIES +@@ -82,7 +87,7 @@ absl_library( + + # test fixed_array_test + set(FIXED_ARRAY_TEST_SRC "fixed_array_test.cc") +-set(FIXED_ARRAY_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib) ++set(FIXED_ARRAY_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate absl_test_instance_tracker_lib) + + absl_test( + TARGET +@@ -128,7 +133,7 @@ absl_test( + + # test inlined_vector_test + set(INLINED_VECTOR_TEST_SRC "inlined_vector_test.cc") +-set(INLINED_VECTOR_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib) ++set(INLINED_VECTOR_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate absl_test_instance_tracker_lib) + + absl_test( + TARGET +@@ -153,7 +158,8 @@ absl_test( + + # test test_instance_tracker_test + set(TEST_INSTANCE_TRACKER_TEST_SRC "internal/test_instance_tracker_test.cc") +-set(TEST_INSTANCE_TRACKER_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib) ++set(TEST_INSTANCE_TRACKER_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate ++ absl_test_instance_tracker_lib) + + + absl_test( +@@ -172,5 +178,5 @@ absl_test( + SOURCES + "internal/raw_hash_set_test.cc" + PUBLIC_LIBRARIES +- absl::base absl::hash absl_throw_delegate test_instance_tracker_lib ++ absl::base absl::hash absl_throw_delegate absl_test_instance_tracker_lib + ) +diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h +index 1edc007..05270ef 100644 +--- a/absl/container/internal/raw_hash_map.h ++++ b/absl/container/internal/raw_hash_map.h +@@ -39,11 +39,14 @@ class raw_hash_map : public raw_hash_set { + using MappedConstReference = decltype(P::value( + std::addressof(std::declval()))); + ++ using KeyArgImpl = container_internal::KeyArg::value && ++ IsTransparent::value>; ++ + public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; +- template +- using key_arg = typename raw_hash_map::raw_hash_set::template key_arg; ++ template ++ using key_arg = typename KeyArgImpl::template type; + + static_assert(!std::is_reference::value, ""); + // TODO(alkis): remove this assertion and verify that reference mapped_type is +diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt +index 4af2ec8..44ba7ef 100644 +--- a/absl/debugging/CMakeLists.txt ++++ b/absl/debugging/CMakeLists.txt +@@ -19,6 +19,9 @@ list(APPEND DEBUGGING_PUBLIC_HEADERS + "leak_check.h" + "stacktrace.h" + "symbolize.h" ++ "symbolize_elf.inc" ++ "symbolize_unimplemented.inc" ++ "symbolize_win32.inc" + ) + + # TODO(cohenjon) The below is all kinds of wrong. Make this match what we do in +@@ -31,6 +34,13 @@ list(APPEND DEBUGGING_INTERNAL_HEADERS + "internal/stacktrace_config.h" + "internal/symbolize.h" + "internal/vdso_support.h" ++ "internal/stacktrace_aarch64-inl.inc" ++ "internal/stacktrace_arm-inl.inc" ++ "internal/stacktrace_generic-inl.inc" ++ "internal/stacktrace_powerpc-inl.inc" ++ "internal/stacktrace_unimplemented-inl.inc" ++ "internal/stacktrace_win32-inl.inc" ++ "internal/stacktrace_x86-inl.inc" + ) + + list(APPEND DEBUGGING_INTERNAL_SRC +@@ -108,6 +118,8 @@ absl_library( + absl_examine_stack + SOURCES + ${EXAMINE_STACK_SRC} ++ PUBLIC_LIBRARIES ++ absl::symbolize + EXPORT_NAME + examine_stack + ) +@@ -134,6 +146,10 @@ absl_library( + absl_header_library( + TARGET + absl_debugging ++ PUBLIC_HEADER ++ ${DEBUGGING_PUBLIC_HEADERS} ++ INTERNAL_PUBLIC_HEADER ++ ${DEBUGGING_INTERNAL_HEADERS} + PUBLIC_LIBRARIES + absl_stacktrace absl_leak_check + EXPORT_NAME +diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt +index 474092f..e10a447 100644 +--- a/absl/hash/CMakeLists.txt ++++ b/absl/hash/CMakeLists.txt +@@ -31,13 +31,17 @@ list(APPEND HASH_SRC + ${HASH_INTERNAL_HEADERS} + ) + +-set(HASH_PUBLIC_LIBRARIES absl::hash absl::container absl::strings absl::str_format absl::utility) ++set(HASH_PUBLIC_LIBRARIES absl::container absl::strings absl::str_format absl::utility) + + absl_library( + TARGET + absl_hash + SOURCES + ${HASH_SRC} ++ PUBLIC_HEADER ++ ${HASH_PUBLIC_HEADERS} ++ INTERNAL_PUBLIC_HEADER ++ ${HASH_INTERNAL_HEADERS} + PUBLIC_LIBRARIES + ${HASH_PUBLIC_LIBRARIES} + EXPORT_NAME +diff --git a/absl/memory/CMakeLists.txt b/absl/memory/CMakeLists.txt +index 5958f5c..bcaf3fa 100644 +--- a/absl/memory/CMakeLists.txt ++++ b/absl/memory/CMakeLists.txt +@@ -22,6 +22,8 @@ list(APPEND MEMORY_PUBLIC_HEADERS + absl_header_library( + TARGET + absl_memory ++ PUBLIC_HEADER ++ ${MEMORY_PUBLIC_HEADERS} + EXPORT_NAME + memory + ) +diff --git a/absl/meta/CMakeLists.txt b/absl/meta/CMakeLists.txt +index adb0ceb..e24eef5 100644 +--- a/absl/meta/CMakeLists.txt ++++ b/absl/meta/CMakeLists.txt +@@ -32,6 +32,8 @@ list(APPEND TYPE_TRAITS_TEST_SRC + absl_header_library( + TARGET + absl_meta ++ PUBLIC_HEADER ++ ${META_PUBLIC_HEADERS} + PUBLIC_LIBRARIES + absl::base + EXPORT_NAME +diff --git a/absl/numeric/CMakeLists.txt b/absl/numeric/CMakeLists.txt +index 3360b2e..20744f6 100644 +--- a/absl/numeric/CMakeLists.txt ++++ b/absl/numeric/CMakeLists.txt +@@ -16,6 +16,8 @@ + + list(APPEND NUMERIC_PUBLIC_HEADERS + "int128.h" ++ "int128_have_intrinsic.inc" ++ "int128_no_intrinsic.inc" + ) + + +@@ -39,6 +41,8 @@ absl_library( + absl_header_library( + TARGET + absl_numeric ++ PUBLIC_HEADER ++ ${NUMERIC_PUBLIC_HEADERS} + PUBLIC_LIBRARIES + absl::int128 + EXPORT_NAME +diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt +index f3e4162..05b9145 100644 +--- a/absl/strings/CMakeLists.txt ++++ b/absl/strings/CMakeLists.txt +@@ -22,6 +22,7 @@ list(APPEND STRINGS_PUBLIC_HEADERS + "match.h" + "numbers.h" + "str_cat.h" ++ "str_format.h" + "string_view.h" + "strip.h" + "str_join.h" +@@ -30,7 +31,6 @@ list(APPEND STRINGS_PUBLIC_HEADERS + "substitute.h" + ) + +- + list(APPEND STRINGS_INTERNAL_HEADERS + "internal/char_map.h" + "internal/charconv_bigint.h" +@@ -44,7 +44,15 @@ list(APPEND STRINGS_INTERNAL_HEADERS + "internal/utf8.h" + ) + +- ++list(APPEND STRINGS_INTERNAL_STR_HEADERS ++ "internal/str_format/arg.h" ++ "internal/str_format/bind.h" ++ "internal/str_format/checker.h" ++ "internal/str_format/extension.h" ++ "internal/str_format/float_conversion.h" ++ "internal/str_format/output.h" ++ "internal/str_format/parser.h" ++) + + # add string library + list(APPEND STRINGS_SRC +@@ -74,6 +82,12 @@ absl_library( + absl_strings + SOURCES + ${STRINGS_SRC} ++ PUBLIC_HEADER ++ ${STRINGS_PUBLIC_HEADERS} ++ INTERNAL_PUBLIC_HEADER ++ ${STRINGS_INTERNAL_HEADERS} ++ INTERNAL_STR_PUBLIC_HEADER ++ ${STRINGS_INTERNAL_STR_HEADERS} + PUBLIC_LIBRARIES + ${STRINGS_PUBLIC_LIBRARIES} + EXPORT_NAME +@@ -85,7 +99,7 @@ absl_header_library( + TARGET + absl_str_format + PUBLIC_LIBRARIES +- str_format_internal ++ absl_str_format_internal + EXPORT_NAME + str_format + ) +@@ -93,7 +107,7 @@ absl_header_library( + # str_format_internal + absl_library( + TARGET +- str_format_internal ++ absl_str_format_internal + SOURCES + "internal/str_format/arg.cc" + "internal/str_format/bind.cc" +@@ -109,7 +123,7 @@ absl_library( + "internal/str_format/output.h" + "internal/str_format/parser.h" + PUBLIC_LIBRARIES +- str_format_extension_internal ++ absl_str_format_extension_internal + absl::strings + absl::base + absl::numeric +@@ -120,7 +134,7 @@ absl_library( + # str_format_extension_internal + absl_library( + TARGET +- str_format_extension_internal ++ absl_str_format_extension_internal + SOURCES + "internal/str_format/extension.cc" + "internal/str_format/extension.h" +diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt +index de0d7b7..d1e7802 100644 +--- a/absl/synchronization/CMakeLists.txt ++++ b/absl/synchronization/CMakeLists.txt +@@ -29,6 +29,7 @@ list(APPEND SYNCHRONIZATION_INTERNAL_HEADERS + "internal/per_thread_sem.h" + "internal/thread_pool.h" + "internal/waiter.h" ++ "internal/mutex_nonprod.inc" + ) + + +@@ -52,6 +53,10 @@ absl_library( + absl_synchronization + SOURCES + ${SYNCHRONIZATION_SRC} ++ PUBLIC_HEADER ++ ${SYNCHRONIZATION_PUBLIC_HEADERS} ++ INTERNAL_PUBLIC_HEADER ++ ${SYNCHRONIZATION_INTERNAL_HEADERS} + PUBLIC_LIBRARIES + ${SYNCHRONIZATION_PUBLIC_LIBRARIES} + EXPORT_NAME +diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt +index 53216cd..7124bc8 100644 +--- a/absl/time/CMakeLists.txt ++++ b/absl/time/CMakeLists.txt +@@ -23,6 +23,12 @@ list(APPEND TIME_PUBLIC_HEADERS + + list(APPEND TIME_INTERNAL_HEADERS + "internal/test_util.h" ++ "internal/get_current_time_chrono.inc" ++ "internal/get_current_time_posix.inc" ++ "internal/zoneinfo.inc" ++) ++ ++list(APPEND TIME_INTERNAL_CCTZ_HEADERS + "internal/cctz/include/cctz/civil_time.h" + "internal/cctz/include/cctz/civil_time_detail.h" + "internal/cctz/include/cctz/time_zone.h" +@@ -54,6 +60,7 @@ list(APPEND TIME_SRC + "internal/cctz/src/zone_info_source.cc" + ${TIME_PUBLIC_HEADERS} + ${TIME_INTERNAL_HEADERS} ++ ${TIME_INTERNAL_CCTZ_HEADERS} + ) + set(TIME_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::int128 absl::strings) + +@@ -62,6 +69,12 @@ absl_library( + absl_time + SOURCES + ${TIME_SRC} ++ PUBLIC_HEADER ++ ${TIME_PUBLIC_HEADERS} ++ INTERNAL_PUBLIC_HEADER ++ ${TIME_INTERNAL_HEADERS} ++ INTERNAL_CCTZ_PUBLIC_HEADER ++ ${TIME_INTERNAL_CCTZ_HEADERS} + PUBLIC_LIBRARIES + ${TIME_PUBLIC_LIBRARIES} + EXPORT_NAME +diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt +index 2f2e3a7..e2b0518 100644 +--- a/absl/types/CMakeLists.txt ++++ b/absl/types/CMakeLists.txt +@@ -18,11 +18,34 @@ list(APPEND TYPES_PUBLIC_HEADERS + "any.h" + "bad_any_cast.h" + "bad_optional_access.h" ++ "bad_variant_access.h" + "optional.h" + "span.h" + "variant.h" + ) + ++list(APPEND TYPES_INTERNAL_HEADERS ++ "internal/variant.h" ++) ++ ++# type header library to install header files ++absl_header_library( ++ TARGET ++ absl_types ++ PUBLIC_HEADER ++ ${TYPES_PUBLIC_HEADERS} ++ INTERNAL_PUBLIC_HEADER ++ ${TYPES_INTERNAL_HEADERS} ++ PUBLIC_LIBRARIES ++ absl::any ++ absl::span ++ absl::bad_any_cast ++ absl::optional ++ absl::bad_optional_access ++ absl::variant ++ EXPORT_NAME ++ types ++) + + # any library + absl_header_library( +@@ -123,7 +146,8 @@ absl_library( + + # test any_test + set(ANY_TEST_SRC "any_test.cc") +-set(ANY_TEST_PUBLIC_LIBRARIES absl::base absl::throw_delegate absl::any absl::bad_any_cast test_instance_tracker_lib) ++set(ANY_TEST_PUBLIC_LIBRARIES absl::base absl::throw_delegate absl::any ++ absl::bad_any_cast absl_test_instance_tracker_lib) + + absl_test( + TARGET +@@ -169,7 +193,8 @@ absl_test( + + # test span_test + set(SPAN_TEST_SRC "span_test.cc") +-set(SPAN_TEST_PUBLIC_LIBRARIES absl::base absl::strings absl::throw_delegate absl::span test_instance_tracker_lib) ++set(SPAN_TEST_PUBLIC_LIBRARIES absl::base absl::strings absl::throw_delegate ++ absl::span absl_test_instance_tracker_lib) + + absl_test( + TARGET +diff --git a/absl/utility/CMakeLists.txt b/absl/utility/CMakeLists.txt +index dc3a631..3357dbd 100644 +--- a/absl/utility/CMakeLists.txt ++++ b/absl/utility/CMakeLists.txt +@@ -22,6 +22,8 @@ list(APPEND UTILITY_PUBLIC_HEADERS + absl_header_library( + TARGET + absl_utility ++ PUBLIC_HEADER ++ ${UTILITY_PUBLIC_HEADERS} + PUBLIC_LIBRARIES + absl::base + EXPORT_NAME diff --git a/patches/absl-config.cmake b/patches/absl-config.cmake new file mode 100644 index 00000000000..c6f539c92bc --- /dev/null +++ b/patches/absl-config.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/absl-targets.cmake"