diff --git a/.gitmodules b/.gitmodules index f62e168cb673c2b69686d39ffa67659ebac9bd6a..162643016c226397eccab7485ee04e83df98d345 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,3 @@ [submodule "libs/polylabel"] path = libs/polylabel url = https://github.com/mapbox/polylabel.git -[submodule "libs/ortools-with-cmake"] - path = libs/ortools-with-cmake - url = https://github.com/jwdinius/ortools-with-cmake.git diff --git a/CMakeCache.txt b/CMakeCache.txt index ff730841a3899b7a5cc98059c6b3fd3b98b5aff3..4785601e7603b8e564e1fa8442b6c33328ece84b 100644 --- a/CMakeCache.txt +++ b/CMakeCache.txt @@ -152,6 +152,12 @@ CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy //Path to a program. CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump +//Value Computed by CMake +CMAKE_PROJECT_DESCRIPTION:STATIC= + +//Value Computed by CMake +CMAKE_PROJECT_HOMEPAGE_URL:STATIC= + //Value Computed by CMake CMAKE_PROJECT_NAME:STATIC=QGroundControl @@ -205,7 +211,10 @@ CMAKE_STRIP:FILEPATH=/usr/bin/strip CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE //The directory containing a CMake configuration file for GeographicLib. -GeographicLib_DIR:PATH=/usr/local/lib/cmake/GeographicLib +GeographicLib_DIR:PATH=GeographicLib_DIR-NOTFOUND + +//Path to a library. +GeographicLib_LIBRARIES:FILEPATH=/usr/lib/x86_64-linux-gnu/libGeographic.so //Path to a library. M_LIB:FILEPATH=/usr/lib/x86_64-linux-gnu/libm.so @@ -240,6 +249,9 @@ Qt5Multimedia_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt5Multimedia //The directory containing a CMake configuration file for Qt5Network. Qt5Network_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt5Network +//The directory containing a CMake configuration file for Qt5PositioningQuick. +Qt5PositioningQuick_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt5PositioningQuick + //The directory containing a CMake configuration file for Qt5Positioning. Qt5Positioning_DIR:PATH=/usr/lib/x86_64-linux-gnu/cmake/Qt5Positioning @@ -294,15 +306,15 @@ USE_RPATH:BOOL=ON //The directory containing a CMake configuration file for ortools. ortools_DIR:PATH=ortools_DIR-NOTFOUND +//Path to a library. +polyclipping:FILEPATH=polyclipping-NOTFOUND + //Value Computed by CMake polyclipping_BINARY_DIR:STATIC=/home/valentin/Desktop/drones/qgroundcontrol/libs/clipper //The directory containing a CMake configuration file for polyclipping. polyclipping_DIR:PATH=polyclipping_DIR-NOTFOUND -//Dependencies for target -polyclipping_LIB_DEPENDS:STATIC= - //Value Computed by CMake polyclipping_SOURCE_DIR:STATIC=/home/valentin/Desktop/drones/qgroundcontrol/libs/clipper @@ -318,8 +330,8 @@ shapelib_SOURCE_DIR:STATIC=/home/valentin/Desktop/drones/qgroundcontrol/libs/sha //Dependencies for the target shp_LIB_DEPENDS:STATIC=general;-lm; -//Dependencies for target -snake_LIB_DEPENDS:STATIC= +//Dependencies for the target +snake_LIB_DEPENDS:STATIC=general;/usr/lib/x86_64-linux-gnu/libGeographic.so;general;polyclipping; ######################## @@ -335,9 +347,9 @@ CMAKE_CACHEFILE_DIR:INTERNAL=/home/valentin/Desktop/drones/qgroundcontrol //Major version of cmake used to create the current loaded cache CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 //Minor version of cmake used to create the current loaded cache -CMAKE_CACHE_MINOR_VERSION:INTERNAL=10 +CMAKE_CACHE_MINOR_VERSION:INTERNAL=13 //Patch version of cmake used to create the current loaded cache -CMAKE_CACHE_PATCH_VERSION:INTERNAL=2 +CMAKE_CACHE_PATCH_VERSION:INTERNAL=4 //ADVANCED property for variable: CMAKE_COLOR_MAKEFILE CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1 //Path to CMake executable. @@ -396,6 +408,8 @@ CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1 CMAKE_EXTRA_GENERATOR:INTERNAL= //Name of generator. CMAKE_GENERATOR:INTERNAL=Unix Makefiles +//Generator instance identifier. +CMAKE_GENERATOR_INSTANCE:INTERNAL= //Name of generator platform. CMAKE_GENERATOR_PLATFORM:INTERNAL= //Name of generator toolset. @@ -432,7 +446,7 @@ CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1 //ADVANCED property for variable: CMAKE_RANLIB CMAKE_RANLIB-ADVANCED:INTERNAL=1 //Path to CMake installation. -CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.10 +CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.13 //ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1 //ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG @@ -463,4 +477,8 @@ CMAKE_STRIP-ADVANCED:INTERNAL=1 CMAKE_UNAME:INTERNAL=/bin/uname //ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1 +//Details about finding GeographicLib +FIND_PACKAGE_MESSAGE_DETAILS_GeographicLib:INTERNAL=[/usr/lib/x86_64-linux-gnu][/usr/lib/x86_64-linux-gnu/libGeographic.so][/usr/include][v()] +//ADVANCED property for variable: GeographicLib_LIBRARIES +GeographicLib_LIBRARIES-ADVANCED:INTERNAL=1 diff --git a/libs/or-tools-bin/LICENSE-2.0.txt b/libs/or-tools-bin/LICENSE-2.0.txt new file mode 100644 index 0000000000000000000000000000000000000000..0f3822be4adba1970288328d9a838ad4e87935bf --- /dev/null +++ b/libs/or-tools-bin/LICENSE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2010 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. diff --git a/libs/or-tools-bin/Makefile b/libs/or-tools-bin/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..5743569ca2b1ad9cf884f1aa7268065abadb10e0 --- /dev/null +++ b/libs/or-tools-bin/Makefile @@ -0,0 +1,666 @@ +# Let's discover something about where we run +ifeq ($(OS),Windows_NT) +OS = Windows +endif +ifeq ($(OS),Windows) +SYSTEM = win +else +SYSTEM = unix +endif + +.SECONDARY: + +# Useful directories. +INC_DIR = include +EX_DIR = examples +CPP_EX_DIR = examples/cpp +CPP_EX_PATH = $(subst /,$S,$(CPP_EX_DIR)) +JAVA_EX_DIR = examples/java +JAVA_EX_PATH = $(subst /,$S,$(JAVA_EX_DIR)) +DOTNET_EX_DIR = examples/dotnet +DOTNET_EX_PATH = $(subst /,$S,$(DOTNET_EX_DIR)) +OBJ_DIR = objs +CLASS_DIR = classes +LIB_DIR = lib +BIN_DIR = bin + +# Unix specific part. +ifeq ($(SYSTEM),unix) + OS = $(shell uname -s) +# C++ + ifeq ($(OS),Linux) + CXX = g++ + LDFLAGS = \ +-Wl,-rpath,"\$$ORIGIN" \ +-Wl,-rpath,"\$$ORIGIN/../lib64" \ +-Wl,-rpath,"\$$ORIGIN/../lib" \ +-lz -lrt -lpthread + LBITS = $(shell getconf LONG_BIT) + ifeq ($(LBITS),64) + PORT = Linux64 + ARCH = -DARCH_K8 + NETPLATFORM = anycpu + else + PORT = Linux32 + ARCH = + NETPLATFORM = x86 + endif + MONO = LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) mono + L = .so + endif # ifeq($(OS),Linux) + ifeq ($(OS),Darwin) # Assume Mac Os X + CXX = clang++ + LDFLAGS = \ +-Wl,-rpath,@loader_path \ +-Wl,-rpath,@loader_path/../lib \ +-lz -framework CoreFoundation + PORT = MacOsX64 + ARCH = -DARCH_K8 + NETPLATFORM = x64 + MONO = DYLD_FALLBACK_LIBRARY_PATH=$(LIB_DIR):$(DYLD_LIBRARY_PATH) mono + L = .dylib + endif # ifeq($(OS),Darwin) + CXX_BIN := $(shell command -v $(CXX) 2> /dev/null) + DEBUG = -O4 -DNDEBUG + CXXFLAGS = -fPIC -std=c++11 $(DEBUG) \ + -I$(INC_DIR) -I. $(ARCH) -Wno-deprecated \ + -DUSE_CBC -DUSE_CLP -DUSE_BOP -DUSE_GLOP + LIB_PREFIX = lib + PRE_LIB = -Llib -Llib64 + CBC_LNK = -lCbcSolver -lCbc -lOsiCbc -lCgl -lClpSolver -lClp -lOsiClp -lOsi -lCoinUtils + OR_TOOLS_LNK = $(PRE_LIB) -lprotobuf -lglog -lgflags $(CBC_LNK) -lortools + OBJ_OUT = -o # + EXE_OUT = -o # + O = .o + J = .jar + D = .dll + E = +# Java +ifneq ($(JAVA_HOME),) + JAVAC_BIN := $(shell command -v $(JAVA_HOME)/bin/javac 2> /dev/null) + JAR_BIN := $(shell command -v $(JAVA_HOME)/bin/jar 2> /dev/null) + JAVA_BIN := $(shell command -v $(JAVA_HOME)/bin/java 2> /dev/null) +else + JAVAC_BIN := $(shell command -v javac 2> /dev/null) + JAR_BIN := $(shell command -v jar 2> /dev/null) + JAVA_BIN := $(shell command -v java 2> /dev/null) +endif + JAVAFLAGS = -Djava.library.path=$(LIB_DIR) + CPSEP = : +# .Net + DOTNET = dotnet + DOTNET_BIN := $(shell command -v $(DOTNET) 2> /dev/null) +# Makefile + S = / + DEL = rm -f + DEL_REC = rm -rf + MKDIR = mkdir +endif # SYSTEM == unix + +# Windows specific part. +ifeq ($(SYSTEM),win) + WHICH = tools\\win\\which.exe + ifeq ("$(Platform)","X64") + PLATFORM = Win64 + endif + ifeq ("$(Platform)","x64") + PLATFORM = Win64 + endif + ifeq ("$(PLATFORM)","Win64") + PORT = VisualStudio$(VISUAL_STUDIO)-64b + NETPLATFORM = x64 + else + PORT = VisualStudio$(VISUAL_STUDIO)-32b + NETPLATFORM = x86 + endif + CXX = cl + # We can't use `where` since it's return all matching pathnames + # so we ship which.exe and use it + CXX_BIN := $(shell $(WHICH) $(CXX) 2> NUL) + DEBUG = /O2 -DNDEBUG + CXXFLAGS = /EHsc /MD /nologo /D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS -nologo $(DEBUG) \ + /D__WIN32__ /DNOMINMAX /DWIN32_LEAN_AND_MEAN=1 /D_CRT_SECURE_NO_WARNINGS \ + /DGFLAGS_DLL_DECL= /DGFLAGS_DLL_DECLARE_FLAG= /DGFLAGS_DLL_DEFINE_FLAG= \ + /I$(INC_DIR)\\src\\windows /I$(INC_DIR) /I. \ + -DUSE_CBC -DUSE_CLP -DUSE_BOP -DUSE_GLOP + LDFLAGS = psapi.lib ws2_32.lib + LIB_PREFIX = + OR_TOOLS_LNK = lib\\ortools.lib + OBJ_OUT = /Fo + EXE_OUT = /Fe + O = .obj + L = .lib + J = .jar + D = .dll + E = .exe +# Java + JAVAC_BIN := $(shell $(WHICH) javac 2> NUL) + JAR_BIN := $(shell $(WHICH) jar 2> NUL) + JAVA_BIN := $(shell $(WHICH) java 2> NUL) + JAVAFLAGS = -Djava.library.path=$(LIB_DIR) + CPSEP = ; +# .Net + DOTNET = dotnet + DOTNET_BIN := $(shell $(WHICH) $(DOTNET) 2> NUL) +# Makefile + S = \\ + DEL = del + DEL_REC = rd /S /Q + MKDIR = md +endif # SYSTEM == win + +OR_TOOLS_LIBS = $(LIB_DIR)/$(LIB_PREFIX)ortools$L + +.PHONY: all +all: detect cc java dotnet test + +.PHONY: detect +detect: detect_port detect_cc detect_java detect_dotnet + @echo SOURCE = $(SOURCE) + @echo SOURCE_PATH = $(SOURCE_PATH) + @echo SOURCE_NAME = $(SOURCE_NAME) + @echo SOURCE_SUFFIX = $(SOURCE_SUFFIX) +ifeq ($(SYSTEM),win) + @echo off & echo( +else + @echo +endif + +.PHONY: test +test: test_cc test_java test_dotnet + +.PHONY: clean +clean: + -$(DEL) $(EXE) + -$(DEL_REC) $(OBJ_DIR) + -$(DEL_REC) $(CLASS_DIR) + +.PHONY: detect_port +detect_port: + @echo SHELL = $(SHELL) + @echo SYSTEM = $(SYSTEM) + @echo PORT = $(PORT) + @echo OS = $(OS) +ifeq ($(SYSTEM),win) + @echo off & echo( +else + @echo +endif + +############## +## SOURCE ## +############## +# Check SOURCE argument +SOURCE_SUFFIX = $(suffix $(SOURCE)) +# will contain “/any/path/foo.cc” on unix and “\\any\\path\\foo.cc” on windows +SOURCE_PATH = $(subst /,$S,$(SOURCE)) +SOURCE_NAME = $(basename $(notdir $(SOURCE))) +ifeq ($(SOURCE),) # Those rules will be used if SOURCE is empty +.PHONY: build run +build run: + $(error no source file provided, the "$@" target must be used like so: \ + make $@ SOURCE=relative/path/to/filename.extension) +else +ifeq (,$(wildcard $(SOURCE))) +$(error File "$(SOURCE)" does not exist !) +endif +endif + +########### +## C++ ## +########### +EXE = \ +$(BIN_DIR)/simple_ls_program$E \ +$(BIN_DIR)/rabbits_and_pheasants_cp$E \ +$(BIN_DIR)/nurses_cp$E \ +$(BIN_DIR)/minimal_jobshop_cp$E \ +\ +$(BIN_DIR)/constraint_programming_cp$E \ +$(BIN_DIR)/costas_array_sat$E \ +$(BIN_DIR)/cvrp_disjoint_tw$E \ +$(BIN_DIR)/cvrptw$E \ +$(BIN_DIR)/cvrptw_with_breaks$E \ +$(BIN_DIR)/cvrptw_with_refueling$E \ +$(BIN_DIR)/cvrptw_with_resources$E \ +$(BIN_DIR)/cvrptw_with_stop_times_and_resources$E \ +$(BIN_DIR)/dimacs_assignment$E \ +$(BIN_DIR)/dobble_ls$E \ +$(BIN_DIR)/flow_api$E \ +$(BIN_DIR)/frequency_assignment_problem$E \ +$(BIN_DIR)/golomb_sat$E \ +$(BIN_DIR)/integer_programming$E \ +$(BIN_DIR)/jobshop_sat$E \ +$(BIN_DIR)/knapsack$E \ +$(BIN_DIR)/linear_assignment_api$E \ +$(BIN_DIR)/linear_programming$E \ +$(BIN_DIR)/linear_solver_protocol_buffers$E \ +$(BIN_DIR)/magic_square_sat$E \ +$(BIN_DIR)/max_flow$E \ +$(BIN_DIR)/min_cost_flow$E \ +$(BIN_DIR)/mps_driver$E \ +$(BIN_DIR)/network_routing_sat$E \ +$(BIN_DIR)/nqueens$E \ +$(BIN_DIR)/random_tsp$E \ +$(BIN_DIR)/pdptw$E \ +$(BIN_DIR)/shift_minimization_sat$E \ +$(BIN_DIR)/solve$E \ +$(BIN_DIR)/sports_scheduling_sat$E \ +$(BIN_DIR)/strawberry_fields_with_column_generation$E \ +$(BIN_DIR)/tsp$E \ +$(BIN_DIR)/vrp$E \ +$(BIN_DIR)/weighted_tardiness_sat$E + +.PHONY: cc test_cc +ifndef CXX_BIN +cc test_cc: + @echo the $(CXX) command was not found in your PATH + exit 127 +else +cc: $(EXE) +test_cc: detect_cc + $(MAKE) run SOURCE=examples/cpp/simple_knapsack_program.cc + $(MAKE) run SOURCE=examples/cpp/simple_max_flow_program.cc + $(MAKE) run SOURCE=examples/cpp/simple_min_cost_flow_program.cc + $(MAKE) run SOURCE=examples/cpp/simple_lp_program.cc + $(MAKE) run SOURCE=examples/cpp/simple_mip_program.cc + $(MAKE) run SOURCE=examples/cpp/simple_sat_program.cc + $(MAKE) run SOURCE=examples/cpp/simple_ls_program.cc + $(MAKE) run SOURCE=examples/cpp/tsp.cc + $(MAKE) run SOURCE=examples/cpp/vrp.cc + $(MAKE) run SOURCE=examples/cpp/nurses_cp.cc + $(MAKE) run SOURCE=examples/cpp/minimal_jobshop_cp.cc + +################## +## C++ SOURCE ## +################## +ifeq ($(SOURCE_SUFFIX),.cc) # Those rules will be used if SOURCE contain a .cc file +$(OBJ_DIR)/$(SOURCE_NAME).$O: $(SOURCE) | $(OBJ_DIR) + $(CXX) $(CXXFLAGS) \ + -c $(SOURCE_PATH) \ + $(OBJ_OUT)$(OBJ_DIR)$S$(SOURCE_NAME).$O + +$(BIN_DIR)/$(SOURCE_NAME)$E: $(OBJ_DIR)/$(SOURCE_NAME).$O | $(BIN_DIR) + $(CXX) $(CXXFLAGS) \ + $(OBJ_DIR)$S$(SOURCE_NAME).$O \ + $(OR_TOOLS_LNK) $(LDFLAGS) \ + $(EXE_OUT)$(BIN_DIR)$S$(SOURCE_NAME)$E + +.PHONY: build # Build a C++ program. +build: $(BIN_DIR)/$(SOURCE_NAME)$E + +.PHONY: run # Run a C++ program. +run: build + $(BIN_DIR)$S$(SOURCE_NAME)$E $(ARGS) +endif # ifeq ($(SOURCE_SUFFIX),.cc) + +endif # ifndef CXX_BIN + +$(OBJ_DIR): + -$(MKDIR) $(OBJ_DIR) + +$(OBJ_DIR)/%$O: $(CPP_EX_DIR)/%.cc | $(OBJ_DIR) + $(CXX) $(CXXFLAGS) -c $(CPP_EX_PATH)$S$*.cc $(OBJ_OUT)$(OBJ_DIR)$S$*$O + +$(BIN_DIR)/%$E: $(OBJ_DIR)/%$O + $(CXX) $(CXXFLAGS) $(OBJ_DIR)$S$*$O $(OR_TOOLS_LNK) $(LDFLAGS) $(EXE_OUT)$(BIN_DIR)$S$*$E + +.PHONY: detect_cc +detect_cc: + @echo CXX = $(CXX) + @echo CXX_BIN = $(CXX_BIN) + @echo CXXFLAGS = $(CXXFLAGS) + @echo LDFLAGS = $(LDFLAGS) + @echo OR_TOOLS_LNK = $(OR_TOOLS_LNK) +ifeq ($(SYSTEM),win) + @echo off & echo( +else + @echo +endif + +############ +## JAVA ## +############ +JAR = \ +$(LIB_DIR)/LinearProgramming$J \ +$(LIB_DIR)/IntegerProgramming$J \ +$(LIB_DIR)/RabbitsPheasants$J \ +$(LIB_DIR)/Tsp$J \ +$(LIB_DIR)/Vrp$J \ +$(LIB_DIR)/Knapsack$J \ +\ +$(LIB_DIR)/AllDifferentExcept0$J \ +$(LIB_DIR)/AllInterval$J \ +$(LIB_DIR)/CapacitatedVehicleRoutingProblemWithTimeWindows$J \ +$(LIB_DIR)/Circuit$J \ +$(LIB_DIR)/CoinsGrid$J \ +$(LIB_DIR)/CoinsGridMIP$J \ +$(LIB_DIR)/ColoringMIP$J \ +$(LIB_DIR)/CoveringOpl$J \ +$(LIB_DIR)/Crossword$J \ +$(LIB_DIR)/DeBruijn$J \ +$(LIB_DIR)/Diet$J \ +$(LIB_DIR)/DietMIP$J \ +$(LIB_DIR)/DivisibleBy9Through1$J \ +$(LIB_DIR)/FlowExample$J \ +$(LIB_DIR)/GolombRuler$J \ +$(LIB_DIR)/Issue173$J \ +$(LIB_DIR)/KnapsackMIP$J \ +$(LIB_DIR)/LeastDiff$J \ +$(LIB_DIR)/LinearAssignmentAPI$J \ +$(LIB_DIR)/MagicSquare$J \ +$(LIB_DIR)/Map2$J \ +$(LIB_DIR)/Map$J \ +$(LIB_DIR)/Minesweeper$J \ +$(LIB_DIR)/MultiThreadTest$J \ +$(LIB_DIR)/NQueens2$J \ +$(LIB_DIR)/NQueens$J \ +$(LIB_DIR)/Partition$J \ +$(LIB_DIR)/QuasigroupCompletion$J \ +$(LIB_DIR)/SendMoreMoney2$J \ +$(LIB_DIR)/SendMoreMoney$J \ +$(LIB_DIR)/SendMostMoney$J \ +$(LIB_DIR)/Seseman$J \ +$(LIB_DIR)/SetCovering2$J \ +$(LIB_DIR)/SetCovering3$J \ +$(LIB_DIR)/SetCovering4$J \ +$(LIB_DIR)/SetCoveringDeployment$J \ +$(LIB_DIR)/SetCovering$J \ +$(LIB_DIR)/SimpleRoutingTest$J \ +$(LIB_DIR)/StableMarriage$J \ +$(LIB_DIR)/StiglerMIP$J \ +$(LIB_DIR)/Strimko2$J \ +$(LIB_DIR)/Sudoku$J \ +$(LIB_DIR)/SurvoPuzzle$J \ +$(LIB_DIR)/ToNum$J \ +$(LIB_DIR)/WhoKilledAgatha$J \ +$(LIB_DIR)/Xkcd$J \ +$(LIB_DIR)/YoungTableaux$J + +HAS_JAVA = true +ifndef JAVAC_BIN +HAS_JAVA = +endif +ifndef JAR_BIN +HAS_JAVA = +endif +ifndef JAVA_BIN +HAS_JAVA = +endif + +.PHONY: java test_java +ifndef HAS_JAVA +java test_java: + @echo the command 'java', 'javac' or 'jar' was not found in your PATH + exit 127 +else +java: $(JAR) +test_java: detect_java + $(MAKE) run SOURCE=examples/java/SimpleLpProgram.java + $(MAKE) run SOURCE=examples/java/SimpleMipProgram.java + $(MAKE) run SOURCE=examples/java/SimpleSatProgram.java + $(MAKE) run SOURCE=examples/java/Tsp.java + $(MAKE) run SOURCE=examples/java/Vrp.java + $(MAKE) run SOURCE=examples/java/Knapsack.java + +################### +## Java SOURCE ## +################### +ifeq ($(SOURCE_SUFFIX),.java) # Those rules will be used if SOURCE contain a .java file +$(CLASS_DIR)/$(SOURCE_NAME): $(SOURCE) | $(CLASS_DIR) + -$(MKDIR) $(CLASS_DIR)$S$(SOURCE_NAME) + "$(JAVAC_BIN)" -d $(CLASS_DIR)$S$(SOURCE_NAME) \ + -cp $(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)$(LIB_DIR)$Sprotobuf.jar \ + $(SOURCE_PATH) + +$(LIB_DIR)/$(SOURCE_NAME)$J: $(CLASS_DIR)/$(SOURCE_NAME) | $(LIB_DIR) + -$(DEL) $(LIB_DIR)$S$(SOURCE_NAME)$J + "$(JAR_BIN)" cvf $(LIB_DIR)$S$(SOURCE_NAME)$J -C $(CLASS_DIR)$S$(SOURCE_NAME) . + +.PHONY: build # Build a Java program. +build: $(LIB_DIR)/$(SOURCE_NAME)$J + +.PHONY: run # Run a Java program. +run: build + "$(JAVA_BIN)" -Xss2048k $(JAVAFLAGS) \ + -cp $(LIB_DIR)$S$(SOURCE_NAME)$J$(CPSEP)$(LIB_DIR)$Scom.google.ortools.jar$(CPSEP)$(LIB_DIR)$Sprotobuf.jar \ + $(SOURCE_NAME) $(ARGS) +endif # ifeq ($(SOURCE_SUFFIX),.java) + +endif # ifndef HAS_JAVA + +$(CLASS_DIR): + -$(MKDIR) $(CLASS_DIR) + +$(CLASS_DIR)/%: $(JAVA_EX_DIR)/%.java | $(CLASS_DIR) + -$(MKDIR) $(CLASS_DIR)$S$* + "$(JAVAC_BIN)" -d $(CLASS_DIR)$S$* \ + -cp $(LIB_DIR)$Scom.google.ortools$J$(CPSEP)$(LIB_DIR)$Sprotobuf$J \ + $(JAVA_EX_PATH)$S$*.java + +$(LIB_DIR)/%$J: $(CLASS_DIR)/% | $(LIB_DIR) + "$(JAR_BIN)" cvf $(LIB_DIR)$S$*$J -C $(CLASS_DIR)$S$* . + +.PHONY: detect_java +detect_java: + @echo JAVA_HOME = $(JAVA_HOME) + @echo JAVAC_BIN = $(JAVAC_BIN) + @echo CLASS_DIR = $(CLASS_DIR) + @echo JAR_BIN = $(JAR_BIN) + @echo JAVA_BIN = $(JAVA_BIN) + @echo JAVAFLAGS = $(JAVAFLAGS) +ifeq ($(SYSTEM),win) + @echo off & echo( +else + @echo +endif + +############## +## DOTNET ## +############## +.PHONY: dotnet test_dotnet +ifndef DOTNET_BIN +dotnet test_dotnet: + @echo the command 'dotnet' was not found in your PATH + exit 127 +else +dotnet: + $(MAKE) run SOURCE=examples/dotnet/3_jugs_regular.cs +# Linear Solver + $(MAKE) run SOURCE=examples/dotnet/SimpleLpProgram.cs + $(MAKE) run SOURCE=examples/dotnet/SimpleMipProgram.cs +# Constraint Solver + $(MAKE) run SOURCE=examples/dotnet/Tsp.cs + $(MAKE) run SOURCE=examples/dotnet/Vrp.cs +# Sat + $(MAKE) run SOURCE=examples/dotnet/NursesSat.cs + $(MAKE) run SOURCE=examples/dotnet/JobshopSat.cs + $(MAKE) run SOURCE=examples/dotnet/JobshopFt06Sat.cs + $(MAKE) run SOURCE=examples/dotnet/GateSchedulingSat.cs + $(MAKE) run SOURCE=examples/dotnet/TaskSchedulingSat.cs +# Misc + $(MAKE) run SOURCE=examples/dotnet/cslinearprogramming.cs + $(MAKE) run SOURCE=examples/dotnet/csintegerprogramming.cs + $(MAKE) run SOURCE=examples/dotnet/assignment.cs + $(MAKE) run SOURCE=examples/dotnet/alldifferent_except_0.cs + $(MAKE) run SOURCE=examples/dotnet/all_interval.cs + $(MAKE) run SOURCE=examples/dotnet/a_puzzle.cs + $(MAKE) run SOURCE=examples/dotnet/a_round_of_golf.cs + $(MAKE) run SOURCE=examples/dotnet/broken_weights.cs + $(MAKE) run SOURCE=examples/dotnet/bus_schedule.cs + $(MAKE) run SOURCE=examples/dotnet/circuit2.cs + $(MAKE) run SOURCE=examples/dotnet/circuit.cs + $(MAKE) run SOURCE=examples/dotnet/coins3.cs + $(MAKE) run SOURCE=examples/dotnet/coins_grid.cs ARGS="5 2" + $(MAKE) run SOURCE=examples/dotnet/combinatorial_auction2.cs + $(MAKE) run SOURCE=examples/dotnet/contiguity_regular.cs + $(MAKE) run SOURCE=examples/dotnet/contiguity_transition.cs + $(MAKE) run SOURCE=examples/dotnet/costas_array.cs + $(MAKE) run SOURCE=examples/dotnet/covering_opl.cs + $(MAKE) run SOURCE=examples/dotnet/crew.cs + $(MAKE) run SOURCE=examples/dotnet/crossword.cs + $(MAKE) run SOURCE=examples/dotnet/crypta.cs + $(MAKE) run SOURCE=examples/dotnet/crypto.cs + $(MAKE) run SOURCE=examples/dotnet/cscvrptw.cs + $(MAKE) run SOURCE=examples/dotnet/csflow.cs + $(MAKE) run SOURCE=examples/dotnet/csknapsack.cs + $(MAKE) run SOURCE=examples/dotnet/csls_api.cs + $(MAKE) run SOURCE=examples/dotnet/csrabbitspheasants.cs + $(MAKE) run SOURCE=examples/dotnet/cstsp.cs + $(MAKE) run SOURCE=examples/dotnet/curious_set_of_integers.cs + $(MAKE) run SOURCE=examples/dotnet/debruijn.cs + $(MAKE) run SOURCE=examples/dotnet/csdiet.cs + $(MAKE) run SOURCE=examples/dotnet/discrete_tomography.cs + $(MAKE) run SOURCE=examples/dotnet/divisible_by_9_through_1.cs + $(MAKE) run SOURCE=examples/dotnet/dudeney.cs + $(MAKE) run SOURCE=examples/dotnet/einav_puzzle2.cs + $(MAKE) run SOURCE=examples/dotnet/eq10.cs + $(MAKE) run SOURCE=examples/dotnet/eq20.cs + $(MAKE) run SOURCE=examples/dotnet/fill_a_pix.cs + $(MAKE) run SOURCE=examples/dotnet/furniture_moving.cs + $(MAKE) run SOURCE=examples/dotnet/furniture_moving_intervals.cs + $(MAKE) run SOURCE=examples/dotnet/futoshiki.cs + $(MAKE) run SOURCE=examples/dotnet/golomb_ruler.cs + $(MAKE) run SOURCE=examples/dotnet/grocery.cs + $(MAKE) run SOURCE=examples/dotnet/hidato_table.cs + $(MAKE) run SOURCE=examples/dotnet/just_forgotten.cs + $(MAKE) run SOURCE=examples/dotnet/kakuro.cs + $(MAKE) run SOURCE=examples/dotnet/kenken2.cs + $(MAKE) run SOURCE=examples/dotnet/killer_sudoku.cs + $(MAKE) run SOURCE=examples/dotnet/labeled_dice.cs + $(MAKE) run SOURCE=examples/dotnet/langford.cs + $(MAKE) run SOURCE=examples/dotnet/least_diff.cs + $(MAKE) run SOURCE=examples/dotnet/lectures.cs + $(MAKE) run SOURCE=examples/dotnet/magic_sequence.cs + $(MAKE) run SOURCE=examples/dotnet/magic_square_and_cards.cs + $(MAKE) run SOURCE=examples/dotnet/magic_square.cs + $(MAKE) run SOURCE=examples/dotnet/map2.cs + $(MAKE) run SOURCE=examples/dotnet/map.cs + $(MAKE) run SOURCE=examples/dotnet/marathon2.cs + $(MAKE) run SOURCE=examples/dotnet/max_flow_taha.cs + $(MAKE) run SOURCE=examples/dotnet/max_flow_winston1.cs + $(MAKE) run SOURCE=examples/dotnet/minesweeper.cs + $(MAKE) run SOURCE=examples/dotnet/mr_smith.cs +# $(MAKE) run SOURCE=examples/dotnet/nontransitive_dice.cs # too long + $(MAKE) run SOURCE=examples/dotnet/nqueens.cs + $(MAKE) run SOURCE=examples/dotnet/nurse_rostering_regular.cs + $(MAKE) run SOURCE=examples/dotnet/nurse_rostering_transition.cs + $(MAKE) run SOURCE=examples/dotnet/olympic.cs + $(MAKE) run SOURCE=examples/dotnet/organize_day.cs + $(MAKE) run SOURCE=examples/dotnet/organize_day_intervals.cs + $(MAKE) run SOURCE=examples/dotnet/pandigital_numbers.cs +# $(MAKE) run SOURCE=examples/dotnet/partition.cs # too long + $(MAKE) run SOURCE=examples/dotnet/perfect_square_sequence.cs + $(MAKE) run SOURCE=examples/dotnet/photo_problem.cs + $(MAKE) run SOURCE=examples/dotnet/place_number_puzzle.cs + $(MAKE) run SOURCE=examples/dotnet/p_median.cs + $(MAKE) run SOURCE=examples/dotnet/post_office_problem2.cs + $(MAKE) run SOURCE=examples/dotnet/quasigroup_completion.cs + $(MAKE) run SOURCE=examples/dotnet/regex.cs + $(MAKE) run SOURCE=examples/dotnet/rogo2.cs + $(MAKE) run SOURCE=examples/dotnet/scheduling_speakers.cs + $(MAKE) run SOURCE=examples/dotnet/secret_santa2.cs +# $(MAKE) run SOURCE=examples/dotnet/secret_santa.cs # too long + $(MAKE) run SOURCE=examples/dotnet/send_more_money2.cs + $(MAKE) run SOURCE=examples/dotnet/send_more_money.cs + $(MAKE) run SOURCE=examples/dotnet/send_most_money.cs + $(MAKE) run SOURCE=examples/dotnet/seseman.cs + $(MAKE) run SOURCE=examples/dotnet/set_covering2.cs + $(MAKE) run SOURCE=examples/dotnet/set_covering3.cs + $(MAKE) run SOURCE=examples/dotnet/set_covering4.cs + $(MAKE) run SOURCE=examples/dotnet/set_covering.cs + $(MAKE) run SOURCE=examples/dotnet/set_covering_deployment.cs + $(MAKE) run SOURCE=examples/dotnet/set_covering_skiena.cs + $(MAKE) run SOURCE=examples/dotnet/set_partition.cs + $(MAKE) run SOURCE=examples/dotnet/sicherman_dice.cs + $(MAKE) run SOURCE=examples/dotnet/ski_assignment.cs + $(MAKE) run SOURCE=examples/dotnet/stable_marriage.cs + $(MAKE) run SOURCE=examples/dotnet/strimko2.cs + $(MAKE) run SOURCE=examples/dotnet/subset_sum.cs + $(MAKE) run SOURCE=examples/dotnet/sudoku.cs + $(MAKE) run SOURCE=examples/dotnet/survo_puzzle.cs + $(MAKE) run SOURCE=examples/dotnet/to_num.cs + $(MAKE) run SOURCE=examples/dotnet/traffic_lights.cs + $(MAKE) run SOURCE=examples/dotnet/volsay.cs + $(MAKE) run SOURCE=examples/dotnet/volsay2.cs + $(MAKE) run SOURCE=examples/dotnet/volsay3.cs + $(MAKE) run SOURCE=examples/dotnet/wedding_optimal_chart.cs + $(MAKE) run SOURCE=examples/dotnet/who_killed_agatha.cs + $(MAKE) run SOURCE=examples/dotnet/xkcd.cs + $(MAKE) run SOURCE=examples/dotnet/young_tableaux.cs + $(MAKE) run SOURCE=examples/dotnet/zebra.cs + $(MAKE) run SOURCE=examples/dotnet/fsintegerprogramming.fs + $(MAKE) run SOURCE=examples/dotnet/fslinearprogramming.fs + $(MAKE) run SOURCE=examples/dotnet/fsdiet.fs + $(MAKE) run SOURCE=examples/dotnet/fsequality.fs + $(MAKE) run SOURCE=examples/dotnet/fsequality-inequality.fs + $(MAKE) run SOURCE=examples/dotnet/fsinteger-linear-program.fs + $(MAKE) run SOURCE=examples/dotnet/fsknapsack.fs + $(MAKE) run SOURCE=examples/dotnet/fsnetwork-max-flow.fs + $(MAKE) run SOURCE=examples/dotnet/fsnetwork-max-flow-lpSolve.fs + $(MAKE) run SOURCE=examples/dotnet/fsnetwork-min-cost-flow.fs + $(MAKE) run SOURCE=examples/dotnet/fsProgram.fs + $(MAKE) run SOURCE=examples/dotnet/fsrabbit-pheasant.fs + $(MAKE) run SOURCE=examples/dotnet/fsvolsay3.fs + $(MAKE) run SOURCE=examples/dotnet/fsvolsay3-lpSolve.fs + $(MAKE) run SOURCE=examples/dotnet/fsvolsay.fs +# $(MAKE) run SOURCE=examples/dotnet/word_square.cs # depends on /usr/share/dict/words + +test_dotnet: detect_dotnet + $(MAKE) run SOURCE=examples/dotnet/SimpleLpProgram.cs + $(MAKE) run SOURCE=examples/dotnet/SimpleMipProgram.cs + $(MAKE) run SOURCE=examples/dotnet/Tsp.cs + $(MAKE) run SOURCE=examples/dotnet/Vrp.cs + $(MAKE) run SOURCE=examples/dotnet/JobshopSat.cs + $(MAKE) run SOURCE=examples/dotnet/NursesSat.cs + +################### +## .NET SOURCE ## +################### +# .Net C# +ifeq ($(SOURCE_SUFFIX),.cs) # Those rules will be used if SOURCE contain a .cs file +ifeq (,$(wildcard $(SOURCE)proj)) +$(error File "$(SOURCE)proj" does not exist !) +endif + +.PHONY: build # Build a .Net C# program. +build: $(SOURCE) $(SOURCE)proj + "$(DOTNET_BIN)" build $(SOURCE_PATH)proj + +.PHONY: run # Run a .Net C# program. +run: build + "$(DOTNET_BIN)" run --no-build --project $(SOURCE_PATH)proj -- $(ARGS) +endif # ifeq ($(SOURCE_SUFFIX),.cs) + +# .Net F# +ifeq ($(SOURCE_SUFFIX),.fs) # Those rules will be used if SOURCE contain a .cs file +ifeq (,$(wildcard $(SOURCE)proj)) +$(error File "$(SOURCE)proj" does not exist !) +endif + +.PHONY: build # Build a .Net F# program. +build: $(SOURCE) $(SOURCE)proj + "$(DOTNET_BIN)" build $(SOURCE_PATH)proj + +.PHONY: run # Run a .Net F# program. +run: build + "$(DOTNET_BIN)" run --no-build --project $(SOURCE_PATH)proj -- $(ARGS) +endif # ifeq ($(SOURCE_SUFFIX),.fs) + +endif # ifndef DOTNET_BIN + +.PHONY: detect_dotnet +detect_dotnet: + @echo DOTNET_BIN = $(DOTNET_BIN) +ifeq ($(SYSTEM),win) + @echo off & echo( +else + @echo +endif + +############ +## MISC ## +############ +# Include user makefile +-include Makefile.user + +print-% : ; @echo $* = $($*) diff --git a/libs/or-tools-bin/README.md b/libs/or-tools-bin/README.md new file mode 100644 index 0000000000000000000000000000000000000000..50b2c5c43dd40f724b1723a728567c58d8ac2726 --- /dev/null +++ b/libs/or-tools-bin/README.md @@ -0,0 +1,98 @@ +# Introduction +This file describes how to install the OR-Tools C++, Java and .Net binary archive. + +OR-Tools is located at https://developers.google.com/optimization + +These modules have been tested under: + - Ubuntu 14.04, 16.04, 17.10 and 18.04 (64-bit). + - macOS 10.13 High Sierra with Xcode 9.4 (64 bit). + - Microsoft Windows with Visual Studio 2015 and 2017 (64-bit) + +Upon decompressing the archive, you will get the following structure: + +``` +or-tools/ + LICENSE-2.0.txt <- Apache 2.0 License + README.md <- This file + Makefile <- Main Makefile for C++,Java and .Net + examples/ <- C++, Java and .Net examples + include/ <- all include files + objs/ <- directory containing C++ compiled object files (*.o) + classes/ <- directory containing Java class files. + packages/ <- directory containing .Net nuget packages. + lib/ <- directory containing libraries and jar files. + bin/ <- directory containing executable files +``` + +# C++ + +Running the examples will involve compiling them, then running them. +We have provided a makefile target to help you. + +Use Makefile: +```shell +make run SOURCE=examples/cpp/golomb.cc +``` + +**OR** this is equivalent to compiling and running +`examples/cpp/golomb.cc`. +- on Unix: + ```shell + make bin/golomb + ./bin/golomb + ``` +- on Windows: + ```shell + make bin\\golomb.exe + bin\\golomb.exe + ``` + +# Java + +Running the examples will involve compiling them, then running them. +We have provided a makefile target to help you. You need to have the +java and javac tools accessible from the command line. + +Use Makefile: +```shell +make run SOURCE=examples/java/RabbitsPheasants.java +``` + +**OR** this is equivalent to compiling and running +`examples/java/RabbitsPheasants.java`. +- on Unix: + ```shell + javac -d classes/RabbitsPheasants -cp lib/com.google.ortools.jar:lib/protobuf.jar examples/java/RabbitsPheasants.java + jar cvf lib/RabbitsPheasants.jar -C classes/RabbitsPheasants . + java -Djava.library.path=lib -cp lib/RabbitsPheasants.jar:lib/com.google.ortools.jar:lib/protobuf.jar RabbitsPheasants + ``` +- on Windows: + ```shell + javac -d class/RabbitsPheasants -cp lib/com.google.ortools.jar;lib/protobuf.jar examples/java/RabbitsPheasants.java + jar cvf lib/RabbitsPheasants.jar -C classes/RabbitsPheasants . + java -Djava.library.path=lib -cp lib/RabbitPheasants.jar;lib/com.google.ortools.jar;lib/protobuf.jar RabbitsPheasants + ``` + +# .Net + +Running the examples will involve compiling them, then running them. +We have provided a makefile target to help you. You need to have the +dotnet/cli tools accessible from the command line. + +Use Makefile: +```shell +make run SOURCE=examples/dotnet/csflow.cs +``` + +**OR** this is equivalent to compiling and running +`examples/dotnet/csflow.cs`. +- on Unix: + ```shell + dotnet build examples/dotnet/csflow.csproj + dotnet run --no-build --project examples/dotnet/csflow.csproj + ``` +- on Windows: + ```shell + dotnet build examples\dotnet\csflow.csproj + dotnet run --no-build --project examples\dotnet\csflow.csproj + ``` diff --git a/libs/or-tools/bin/cbc b/libs/or-tools-bin/bin/cbc similarity index 100% rename from libs/or-tools/bin/cbc rename to libs/or-tools-bin/bin/cbc diff --git a/libs/or-tools/bin/clp b/libs/or-tools-bin/bin/clp similarity index 100% rename from libs/or-tools/bin/clp rename to libs/or-tools-bin/bin/clp diff --git a/libs/or-tools/bin/gflags_completions.sh b/libs/or-tools-bin/bin/gflags_completions.sh similarity index 100% rename from libs/or-tools/bin/gflags_completions.sh rename to libs/or-tools-bin/bin/gflags_completions.sh diff --git a/libs/or-tools/bin/minimal_jobshop_cp b/libs/or-tools-bin/bin/minimal_jobshop_cp similarity index 100% rename from libs/or-tools/bin/minimal_jobshop_cp rename to libs/or-tools-bin/bin/minimal_jobshop_cp diff --git a/libs/or-tools/bin/nurses_cp b/libs/or-tools-bin/bin/nurses_cp similarity index 100% rename from libs/or-tools/bin/nurses_cp rename to libs/or-tools-bin/bin/nurses_cp diff --git a/libs/or-tools/bin/protoc b/libs/or-tools-bin/bin/protoc similarity index 100% rename from libs/or-tools/bin/protoc rename to libs/or-tools-bin/bin/protoc diff --git a/libs/or-tools/bin/protoc-3.11.2.0 b/libs/or-tools-bin/bin/protoc-3.11.2.0 similarity index 100% rename from libs/or-tools/bin/protoc-3.11.2.0 rename to libs/or-tools-bin/bin/protoc-3.11.2.0 diff --git a/libs/or-tools/bin/simple_knapsack_program b/libs/or-tools-bin/bin/simple_knapsack_program similarity index 100% rename from libs/or-tools/bin/simple_knapsack_program rename to libs/or-tools-bin/bin/simple_knapsack_program diff --git a/libs/or-tools/bin/simple_lp_program b/libs/or-tools-bin/bin/simple_lp_program similarity index 100% rename from libs/or-tools/bin/simple_lp_program rename to libs/or-tools-bin/bin/simple_lp_program diff --git a/libs/or-tools/bin/simple_ls_program b/libs/or-tools-bin/bin/simple_ls_program similarity index 100% rename from libs/or-tools/bin/simple_ls_program rename to libs/or-tools-bin/bin/simple_ls_program diff --git a/libs/or-tools/bin/simple_max_flow_program b/libs/or-tools-bin/bin/simple_max_flow_program similarity index 100% rename from libs/or-tools/bin/simple_max_flow_program rename to libs/or-tools-bin/bin/simple_max_flow_program diff --git a/libs/or-tools/bin/simple_min_cost_flow_program b/libs/or-tools-bin/bin/simple_min_cost_flow_program similarity index 100% rename from libs/or-tools/bin/simple_min_cost_flow_program rename to libs/or-tools-bin/bin/simple_min_cost_flow_program diff --git a/libs/or-tools/bin/simple_mip_program b/libs/or-tools-bin/bin/simple_mip_program similarity index 100% rename from libs/or-tools/bin/simple_mip_program rename to libs/or-tools-bin/bin/simple_mip_program diff --git a/libs/or-tools/bin/simple_sat_program b/libs/or-tools-bin/bin/simple_sat_program similarity index 100% rename from libs/or-tools/bin/simple_sat_program rename to libs/or-tools-bin/bin/simple_sat_program diff --git a/libs/or-tools/bin/tsp b/libs/or-tools-bin/bin/tsp similarity index 100% rename from libs/or-tools/bin/tsp rename to libs/or-tools-bin/bin/tsp diff --git a/libs/or-tools/bin/vrp b/libs/or-tools-bin/bin/vrp similarity index 100% rename from libs/or-tools/bin/vrp rename to libs/or-tools-bin/bin/vrp diff --git a/libs/or-tools-bin/examples/cpp/README.md b/libs/or-tools-bin/examples/cpp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f4aa1d0b888d194878bedb2a15790211214021ff --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/README.md @@ -0,0 +1,64 @@ +# C++ examples +The following examples showcase how to use the different Operations Research libraries. + +## Examples list +- Constraint Solver examples: + - cryptarithm.cc Demonstrates the use of basic modeling objects + (integer variables, arithmetic constraints and expressions, + simple search). + - golomb.cc Demonstrates how to handle objective functions and collect + solutions found during the search. + - magic_square.cc Shows how to use the automatic search to solve your + problem. + - costas_array.cc Solves the problem of Costas Array (a constrained + assignment problem used for radars) with two version. On version is + a feasibility version with hard constraints, the other version is + an optimization version with soft constraints and violation costs. + - jobshop.cc Demonstrates scheduling of jobs on different machines. + - jobshop_ls.cc Demonstrates scheduling of jobs on different machines with + a search using Local Search and Large Neighorhood Search. + - nqueens.cc Solves the n-queen problem. It also demonstrates how to break + symmetries during search. + - network_routing.cc Solves a multicommodity mono-routing + problem with capacity constraints and a max usage cost structure. + - sports_scheduling.cc Finds a soccer championship schedule. Its uses an + original approach where all constraints attached to either one team, + or one week are regrouped into one global 'AllowedAssignment' constraints. + - dobble_ls.cc Shows how to write Local Search operators and Local Search + filters in a context of an assignment/partitioning problem. It also + shows how to write a simple constraint. + +- Routing examples: + - tsp.cc Travelling Salesman Problem. + - cvrptw.cc Capacitated Vehicle Routing Problem with Time Windows. + - pdptw.cc Pickup and Delivery Problem with Time Windows. + +- Graph examples: + - flow_api.cc Demonstrates how to use Min-Cost Flow and Max-Flow api. + - linear_assignment_api.cc Demonstrates how to use the Linear Sum + Assignment solver. + - dimacs_assignment.cc Solves DIMACS challenge on assignment + problems. + +- Linear and integer programming examples: + - linear_programming.cc Demonstrates how to use the linear solver + wrapper API to solve Linear Programming problems. + - integer_programming.cc Demonstrates how to use the linear solver + wrapper API to solve Integer Programming problems. + - linear_solver_protocol_buffers.cc Demonstrates how protocol + buffers can be used as input and output to the linear solver wrapper. + - strawberry_fields_with_column_generation.cc Complex example that + demonstrates how to use dynamic column generation to solve a 2D + covering problem. + +- Utilities + - model_util.cc A utility to manipulate model files (.cp) dumped by the + solver. + +# Execution +Running the examples will involve building them, then running them. +You can run the following command from the **top** directory: +```shell +make build SOURCE=examples/cpp/.cc +make run SOURCE=examples/cpp/.cc +``` diff --git a/libs/or-tools/examples/cpp/binpacking_problem_sat.cc b/libs/or-tools-bin/examples/cpp/binpacking_problem_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/binpacking_problem_sat.cc rename to libs/or-tools-bin/examples/cpp/binpacking_problem_sat.cc diff --git a/libs/or-tools/examples/cpp/bool_or_sample_sat.cc b/libs/or-tools-bin/examples/cpp/bool_or_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/bool_or_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/bool_or_sample_sat.cc diff --git a/libs/or-tools/examples/cpp/channeling_sample_sat.cc b/libs/or-tools-bin/examples/cpp/channeling_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/channeling_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/channeling_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/constraint_programming_cp.cc b/libs/or-tools-bin/examples/cpp/constraint_programming_cp.cc new file mode 100644 index 0000000000000000000000000000000000000000..5f1d4d4b8d6c800a84de5681a9257de874361750 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/constraint_programming_cp.cc @@ -0,0 +1,61 @@ +// 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. + +// Constraint programming example that shows how to use the API. + +#include "ortools/base/logging.h" +#include "ortools/constraint_solver/constraint_solver.h" + +namespace operations_research { +void RunConstraintProgrammingExample() { + // Instantiate the solver. + Solver solver("ConstraintProgrammingExample"); + const int64 numVals = 3; + + // Define decision variables. + IntVar* const x = solver.MakeIntVar(0, numVals - 1, "x"); + IntVar* const y = solver.MakeIntVar(0, numVals - 1, "y"); + IntVar* const z = solver.MakeIntVar(0, numVals - 1, "z"); + + // Define constraints. + std::vector xyvars = {x, y}; + solver.AddConstraint(solver.MakeAllDifferent(xyvars)); + + LOG(INFO) << "Number of constraints: " << solver.constraints(); + + // Create decision builder to search for solutions. + std::vector allvars = {x, y, z}; + DecisionBuilder* const db = solver.MakePhase( + allvars, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + LOG(INFO) << "Solution" + << ": x = " << x->Value() << "; y = " << x->Value() + << "; z = " << z->Value(); + } + solver.EndSearch(); + LOG(INFO) << "Number of solutions: " << solver.solutions(); + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << solver.wall_time() << "ms"; + LOG(INFO) << "Memory usage: " << Solver::MemoryUsage() << " bytes"; +} +} // namespace operations_research + +int main(int argc, char** argv) { + google::InitGoogleLogging(argv[0]); + FLAGS_logtostderr = 1; + operations_research::RunConstraintProgrammingExample(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/costas_array_sat.cc b/libs/or-tools-bin/examples/cpp/costas_array_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..8df6eaa0e2878692ea537b2c13e7285a4594fd8b --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/costas_array_sat.cc @@ -0,0 +1,323 @@ +// 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. + +// +// Costas Array Problem +// +// Finds an NxN matrix of 0s and 1s, with only one 1 per row, +// and one 1 per column, such that all displacement vectors +// between each pair of 1s are distinct. +// +// This example contains two separate implementations. CostasHard() +// uses hard constraints, whereas CostasSoft() uses a minimizer to +// minimize the number of duplicates. +#include +#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/logging.h" +#include "ortools/base/random.h" +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" + +DEFINE_int32(minsize, 0, "Minimum problem size."); +DEFINE_int32(maxsize, 0, "Maximum problem size."); +DEFINE_int32(model, 1, + "Model type: 1 integer variables hard, 2 boolean variables, 3 " + "boolean_variable soft"); +DEFINE_string(params, "", "Sat parameters."); + +namespace operations_research { +namespace sat { + +// Checks that all pairwise distances are unique and returns all violators +void CheckConstraintViolators(const std::vector &vars, + std::vector *const violators) { + int dim = vars.size(); + + // Check that all indices are unique + for (int i = 0; i < dim; ++i) { + for (int k = i + 1; k < dim; ++k) { + if (vars[i] == vars[k]) { + violators->push_back(i); + violators->push_back(k); + } + } + } + + // Check that all differences are unique for each level + for (int level = 1; level < dim; ++level) { + for (int i = 0; i < dim - level; ++i) { + const int difference = vars[i + level] - vars[i]; + + for (int k = i + 1; k < dim - level; ++k) { + if (difference == vars[k + level] - vars[k]) { + violators->push_back(k + level); + violators->push_back(k); + violators->push_back(i + level); + violators->push_back(i); + } + } + } + } +} + +// Check that all pairwise differences are unique +bool CheckCostas(const std::vector &vars) { + std::vector violators; + + CheckConstraintViolators(vars, &violators); + + return violators.empty(); +} + +// Computes a Costas Array. +void CostasHard(const int dim) { + CpModelBuilder cp_model; + + // create the variables + std::vector vars; + Domain domain(1, dim); + for (int i = 0; i < dim; ++i) { + vars.push_back( + cp_model.NewIntVar(domain).WithName(absl::StrCat("var_", i))); + } + + cp_model.AddAllDifferent(vars); + + // Check that the pairwise difference is unique + for (int i = 1; i < dim; ++i) { + std::vector subset; + Domain diff(-dim, dim); + + for (int j = 0; j < dim - i; ++j) { + subset.push_back(cp_model.NewIntVar(diff)); + cp_model.AddEquality(LinearExpr::Sum({subset[j], vars[j]}), vars[j + i]); + } + + cp_model.AddAllDifferent(subset); + } + + Model model; + if (!FLAGS_params.empty()) { + model.Add(NewSatParameters(FLAGS_params)); + } + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + + if (response.status() == CpSolverStatus::FEASIBLE) { + std::vector costas_matrix; + std::string output; + + for (int n = 0; n < dim; ++n) { + const int64 v = SolutionIntegerValue(response, vars[n]); + costas_matrix.push_back(v); + absl::StrAppendFormat(&output, "%3lld", v); + } + + LOG(INFO) << output << " (" << response.wall_time() << " s)"; + + CHECK(CheckCostas(costas_matrix)) + << ": Solution is not a valid Costas Matrix."; + } else { + LOG(INFO) << "No solution found."; + } +} + +// Computes a Costas Array. +void CostasBool(const int dim) { + CpModelBuilder cp_model; + + // create the variables + std::vector> vars(dim); + std::vector> transposed_vars(dim); + for (int i = 0; i < dim; ++i) { + for (int j = 0; j < dim; ++j) { + const BoolVar var = cp_model.NewBoolVar(); + vars[i].push_back(var); + transposed_vars[j].push_back(var); + } + } + + for (int i = 0; i < dim; ++i) { + cp_model.AddEquality(LinearExpr::BooleanSum(vars[i]), 1); + cp_model.AddEquality(LinearExpr::BooleanSum(transposed_vars[i]), 1); + } + + // Check that the pairwise difference is unique + for (int step = 1; step < dim; ++step) { + for (int diff = 1; diff < dim - 1; ++diff) { + std::vector positive_diffs; + std::vector negative_diffs; + for (int var = 0; var < dim - step; ++var) { + for (int value = 0; value < dim - diff; ++value) { + const BoolVar pos = cp_model.NewBoolVar(); + const BoolVar neg = cp_model.NewBoolVar(); + positive_diffs.push_back(pos); + negative_diffs.push_back(neg); + cp_model.AddBoolOr({Not(vars[var][value]), + Not(vars[var + step][value + diff]), pos}); + cp_model.AddBoolOr({Not(vars[var][value + diff]), + Not(vars[var + step][value]), neg}); + } + } + cp_model.AddLessOrEqual(LinearExpr::BooleanSum(positive_diffs), 1); + cp_model.AddLessOrEqual(LinearExpr::BooleanSum(negative_diffs), 1); + } + } + + Model model; + if (!FLAGS_params.empty()) { + model.Add(NewSatParameters(FLAGS_params)); + } + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + + if (response.status() == CpSolverStatus::FEASIBLE) { + std::vector costas_matrix; + std::string output; + + for (int n = 0; n < dim; ++n) { + for (int v = 0; v < dim; ++v) { + if (SolutionBooleanValue(response, vars[n][v])) { + costas_matrix.push_back(v + 1); + absl::StrAppendFormat(&output, "%3lld", v + 1); + break; + } + } + } + + LOG(INFO) << output << " (" << response.wall_time() << " s)"; + + CHECK(CheckCostas(costas_matrix)) + << ": Solution is not a valid Costas Matrix."; + } else { + LOG(INFO) << "No solution found."; + } +} + +// Computes a Costas Array with a minimization objective. +void CostasBoolSoft(const int dim) { + CpModelBuilder cp_model; + + // create the variables + std::vector> vars(dim); + std::vector> transposed_vars(dim); + for (int i = 0; i < dim; ++i) { + for (int j = 0; j < dim; ++j) { + const BoolVar var = cp_model.NewBoolVar(); + vars[i].push_back(var); + transposed_vars[j].push_back(var); + } + } + + for (int i = 0; i < dim; ++i) { + cp_model.AddEquality(LinearExpr::BooleanSum(vars[i]), 1); + cp_model.AddEquality(LinearExpr::BooleanSum(transposed_vars[i]), 1); + } + + std::vector all_violations; + // Check that the pairwise difference is unique + for (int step = 1; step < dim; ++step) { + for (int diff = 1; diff < dim - 1; ++diff) { + std::vector positive_diffs; + std::vector negative_diffs; + for (int var = 0; var < dim - step; ++var) { + for (int value = 0; value < dim - diff; ++value) { + const BoolVar pos = cp_model.NewBoolVar(); + const BoolVar neg = cp_model.NewBoolVar(); + positive_diffs.push_back(pos); + negative_diffs.push_back(neg); + cp_model.AddBoolOr({Not(vars[var][value]), + Not(vars[var + step][value + diff]), pos}); + cp_model.AddBoolOr({Not(vars[var][value + diff]), + Not(vars[var + step][value]), neg}); + } + } + const IntVar pos_var = + cp_model.NewIntVar(Domain(0, positive_diffs.size())); + const IntVar neg_var = + cp_model.NewIntVar(Domain(0, negative_diffs.size())); + cp_model.AddGreaterOrEqual( + pos_var, LinearExpr::BooleanSum(positive_diffs).AddConstant(-1)); + cp_model.AddGreaterOrEqual( + neg_var, LinearExpr::BooleanSum(negative_diffs).AddConstant(-1)); + all_violations.push_back(pos_var); + all_violations.push_back(neg_var); + } + } + + cp_model.Minimize(LinearExpr::Sum(all_violations)); + + Model model; + if (!FLAGS_params.empty()) { + model.Add(NewSatParameters(FLAGS_params)); + } + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + + if (response.status() == CpSolverStatus::OPTIMAL) { + std::vector costas_matrix; + std::string output; + + for (int n = 0; n < dim; ++n) { + for (int v = 0; v < dim; ++v) { + if (SolutionBooleanValue(response, vars[n][v])) { + costas_matrix.push_back(v + 1); + absl::StrAppendFormat(&output, "%3lld", v + 1); + break; + } + } + } + + LOG(INFO) << output << " (" << response.wall_time() << " s)"; + + CHECK(CheckCostas(costas_matrix)) + << ": Solution is not a valid Costas Matrix."; + } else { + LOG(INFO) << "No solution found."; + } +} + +} // namespace sat +} // namespace operations_research + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + int min = 1; + int max = 10; + + if (FLAGS_minsize != 0) { + min = FLAGS_minsize; + + if (FLAGS_maxsize != 0) { + max = FLAGS_maxsize; + } else { + max = min; + } + } + + for (int size = min; size <= max; ++size) { + LOG(INFO) << "Computing Costas Array for dim = " << size; + if (FLAGS_model == 1) { + operations_research::sat::CostasHard(size); + } else if (FLAGS_model == 2) { + operations_research::sat::CostasBool(size); + } else if (FLAGS_model == 3) { + operations_research::sat::CostasBoolSoft(size); + } + } + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/cp_is_fun_sat.cc b/libs/or-tools-bin/examples/cpp/cp_is_fun_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/cp_is_fun_sat.cc rename to libs/or-tools-bin/examples/cpp/cp_is_fun_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/cvrp_disjoint_tw.cc b/libs/or-tools-bin/examples/cpp/cvrp_disjoint_tw.cc new file mode 100644 index 0000000000000000000000000000000000000000..aef1eb2f3ff69c1893e61b140c95446f99e7df69 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/cvrp_disjoint_tw.cc @@ -0,0 +1,185 @@ +// 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. + +// +// Capacitated Vehicle Routing Problem with Disjoint Time Windows (and optional +// orders). +// A description of the problem can be found here: +// http://en.wikipedia.org/wiki/Vehicle_routing_problem. +// The variant which is tackled by this model includes a capacity dimension, +// disjoint time windows and optional orders, with a penalty cost if orders are +// not performed. For the sake of simplicty, orders are randomly located and +// distances are computed using the Manhattan distance. Distances are assumed +// to be in meters and times in seconds. + +#include + +#include "examples/cpp/cvrptw_lib.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_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."); +DEFINE_int32(vrp_windows, 5, "Number of disjoint windows per node."); +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"; +const int64 kMaxNodesPerGroup = 10; +const int64 kSameVehicleCost = 1000; + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0."; + CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size."; + // 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 RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); + + // Setting up locations. + const int64 kXMax = 100000; + const int64 kYMax = 100000; + const int64 kSpeed = 10; + LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed); + for (int location = 0; location <= FLAGS_vrp_orders; ++location) { + locations.AddRandomLocation(kXMax, kYMax); + } + + // Setting the cost function. + 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(manager.num_nodes(), kDepot, + FLAGS_vrp_use_deterministic_random_seed); + demand.Initialize(); + 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, + [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { + return demand.Demand(i, j); + }, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); + routing.AddDimension( + 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 disjoint time windows. + Solver* solver = routing.solver(); + ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed)); + 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); + } + std::sort(forbid_points.begin(), forbid_points.end()); + std::vector forbid_starts(1, 0); + std::vector forbid_ends; + for (int i = 0; i < forbid_points.size(); i += 2) { + forbid_ends.push_back(forbid_points[i]); + forbid_starts.push_back(forbid_points[i + 1]); + } + forbid_ends.push_back(kHorizon); + solver->AddConstraint(solver->MakeNotMemberCt( + time_dimension.CumulVar(order), forbid_starts, forbid_ends)); + } + + // Adding penalty costs to allow skipping orders. + const int64 kPenalty = 10000000; + 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 (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(); + } + } + if (!group.empty()) { + routing.AddSoftSameVehicleConstraint(group, kSameVehicleCost); + } + } + + // Solve, returns a solution if any (owned by RoutingModel). + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); + const Assignment* solution = routing.SolveWithParameters(parameters); + if (solution != nullptr) { + DisplayPlan(manager, routing, *solution, FLAGS_vrp_use_same_vehicle_costs, + kMaxNodesPerGroup, kSameVehicleCost, + routing.GetDimensionOrDie(kCapacity), + routing.GetDimensionOrDie(kTime)); + } else { + LOG(INFO) << "No solution found."; + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/cvrptw.cc b/libs/or-tools-bin/examples/cpp/cvrptw.cc new file mode 100644 index 0000000000000000000000000000000000000000..9696115f7e7058fbbe2fb6c7f054419940feaeba --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/cvrptw.cc @@ -0,0 +1,171 @@ +// 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. + +// +// Capacitated Vehicle Routing Problem with Time Windows (and optional orders). +// A description of the problem can be found here: +// http://en.wikipedia.org/wiki/Vehicle_routing_problem. +// The variant which is tackled by this model includes a capacity dimension, +// time windows and optional orders, with a penalty cost if orders are not +// performed. For the sake of simplicty, orders are randomly located and +// distances are computed using the Manhattan distance. Distances are assumed +// to be in meters and times in seconds. + +#include + +#include "examples/cpp/cvrptw_lib.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_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; + +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_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"; +const int64 kMaxNodesPerGroup = 10; +const int64 kSameVehicleCost = 1000; + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0."; + CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size."; + // 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 RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); + + // Setting up locations. + const int64 kXMax = 100000; + const int64 kYMax = 100000; + const int64 kSpeed = 10; + LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed); + for (int location = 0; location <= FLAGS_vrp_orders; ++location) { + locations.AddRandomLocation(kXMax, kYMax); + } + + // Setting the cost function. + 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(manager.num_nodes(), kDepot, + FLAGS_vrp_use_deterministic_random_seed); + demand.Initialize(); + 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, + [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { + return demand.Demand(i, j); + }, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); + routing.AddDimension( + 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 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 < 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 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 (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(); + } + } + if (!group.empty()) { + routing.AddSoftSameVehicleConstraint(group, kSameVehicleCost); + } + } + + // Solve, returns a solution if any (owned by RoutingModel). + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); + const Assignment* solution = routing.SolveWithParameters(parameters); + if (solution != nullptr) { + DisplayPlan(manager, routing, *solution, FLAGS_vrp_use_same_vehicle_costs, + kMaxNodesPerGroup, kSameVehicleCost, + routing.GetDimensionOrDie(kCapacity), + routing.GetDimensionOrDie(kTime)); + } else { + LOG(INFO) << "No solution found."; + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/cvrptw_lib.h b/libs/or-tools-bin/examples/cpp/cvrptw_lib.h new file mode 100644 index 0000000000000000000000000000000000000000..887821e6b51c51148cb24c4811d1387eb556421b --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/cvrptw_lib.h @@ -0,0 +1,342 @@ +// 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 header provides functions to help creating random instaces of the +// vehicle routing problem; random capacities and random time windows. +#ifndef OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_ +#define OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_ + +#include +#include + +#include "absl/strings/str_format.h" +#include "ortools/base/logging.h" +#include "ortools/base/random.h" +#include "ortools/constraint_solver/routing.h" + +namespace operations_research { + +typedef std::function + RoutingNodeEvaluator2; + +// Random seed generator. +int32 GetSeed(bool deterministic); + +// Location container, contains positions of orders and can be used to obtain +// Manhattan distances/times between locations. +class LocationContainer { + public: + LocationContainer(int64 speed, bool use_deterministic_seed); + 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(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: + class Location { + public: + Location(); + Location(int64 x, int64 y); + int64 DistanceTo(const Location& location) const; + bool IsAtSameLocation(const Location& location) const; + + private: + static int64 Abs(int64 value); + + int64 x_; + int64 y_; + }; + + MTRandom randomizer_; + const int64 speed_; + gtl::ITIVector locations_; +}; + +// Random demand. +class RandomDemand { + public: + RandomDemand(int size, RoutingIndexManager::NodeIndex depot, + bool use_deterministic_seed); + void Initialize(); + int64 Demand(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const; + + private: + std::unique_ptr demand_; + const int size_; + const RoutingIndexManager::NodeIndex depot_; + const bool use_deterministic_seed_; +}; + +// Service time (proportional to demand) + transition time callback. +class ServiceTimePlusTransition { + public: + ServiceTimePlusTransition( + int64 time_per_demand_unit, + RoutingNodeEvaluator2 demand, + RoutingNodeEvaluator2 transition_time); + int64 Compute(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const; + + private: + const int64 time_per_demand_unit_; + RoutingNodeEvaluator2 demand_; + RoutingNodeEvaluator2 transition_time_; +}; + +// Stop service time + transition time callback. +class StopServiceTimePlusTransition { + public: + StopServiceTimePlusTransition( + int64 stop_time, const LocationContainer& location_container, + RoutingNodeEvaluator2 transition_time); + int64 Compute(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const; + + private: + const int64 stop_time_; + const LocationContainer& location_container_; + RoutingNodeEvaluator2 demand_; + 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, + const operations_research::RoutingDimension& capacity_dimension, + const operations_research::RoutingDimension& time_dimension); + +using NodeIndex = RoutingIndexManager::NodeIndex; + +int32 GetSeed(bool deterministic) { + if (deterministic) { + return ACMRandom::DeterministicSeed(); + } else { + return ACMRandom::HostnamePidTimeSeed(); + } +} + +LocationContainer::LocationContainer(int64 speed, bool use_deterministic_seed) + : randomizer_(GetSeed(use_deterministic_seed)), speed_(speed) { + CHECK_LT(0, speed_); +} + +void LocationContainer::AddRandomLocation(int64 x_max, int64 y_max) { + AddRandomLocation(x_max, y_max, 1); +} + +void LocationContainer::AddRandomLocation(int64 x_max, int64 y_max, + int duplicates) { + const int64 x = randomizer_.Uniform(x_max + 1); + const int64 y = randomizer_.Uniform(y_max + 1); + for (int i = 0; i < duplicates; ++i) { + AddLocation(x, y); + } +} + +int64 LocationContainer::ManhattanDistance(NodeIndex from, NodeIndex to) const { + return locations_[from].DistanceTo(locations_[to]); +} + +int64 LocationContainer::NegManhattanDistance(NodeIndex from, + NodeIndex to) const { + return -ManhattanDistance(from, to); +} + +int64 LocationContainer::ManhattanTime(NodeIndex from, NodeIndex to) const { + return ManhattanDistance(from, to) / speed_; +} + +bool LocationContainer::SameLocation(NodeIndex node1, NodeIndex node2) const { + if (node1 < locations_.size() && node2 < locations_.size()) { + return locations_[node1].IsAtSameLocation(locations_[node2]); + } + return false; +} +int64 LocationContainer::SameLocationFromIndex(int64 node1, int64 node2) const { + // The direct conversion from constraint model indices to routing model + // nodes is correct because the depot is node 0. + // TODO(user): Fetch proper indices from routing model. + return SameLocation(NodeIndex(node1), NodeIndex(node2)); +} + +LocationContainer::Location::Location() : x_(0), y_(0) {} + +LocationContainer::Location::Location(int64 x, int64 y) : x_(x), y_(y) {} + +int64 LocationContainer::Location::DistanceTo(const Location& location) const { + return Abs(x_ - location.x_) + Abs(y_ - location.y_); +} + +bool LocationContainer::Location::IsAtSameLocation( + const Location& location) const { + return x_ == location.x_ && y_ == location.y_; +} + +int64 LocationContainer::Location::Abs(int64 value) { + return std::max(value, -value); +} + +RandomDemand::RandomDemand(int size, NodeIndex depot, + bool use_deterministic_seed) + : size_(size), + depot_(depot), + use_deterministic_seed_(use_deterministic_seed) { + CHECK_LT(0, size_); +} + +void RandomDemand::Initialize() { + const int64 kDemandMax = 5; + const int64 kDemandMin = 1; + demand_ = absl::make_unique(size_); + MTRandom randomizer(GetSeed(use_deterministic_seed_)); + for (int order = 0; order < size_; ++order) { + if (order == depot_) { + demand_[order] = 0; + } else { + demand_[order] = + kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1); + } + } +} + +int64 RandomDemand::Demand(NodeIndex from, NodeIndex /*to*/) const { + return demand_[from.value()]; +} + +ServiceTimePlusTransition::ServiceTimePlusTransition( + int64 time_per_demand_unit, RoutingNodeEvaluator2 demand, + RoutingNodeEvaluator2 transition_time) + : time_per_demand_unit_(time_per_demand_unit), + demand_(std::move(demand)), + transition_time_(std::move(transition_time)) {} + +int64 ServiceTimePlusTransition::Compute(NodeIndex from, NodeIndex to) const { + return time_per_demand_unit_ * demand_(from, to) + transition_time_(from, to); +} + +StopServiceTimePlusTransition::StopServiceTimePlusTransition( + int64 stop_time, const LocationContainer& location_container, + RoutingNodeEvaluator2 transition_time) + : stop_time_(stop_time), + location_container_(location_container), + transition_time_(std::move(transition_time)) {} + +int64 StopServiceTimePlusTransition::Compute(NodeIndex from, + NodeIndex to) const { + return location_container_.SameLocation(from, to) + ? 0 + : stop_time_ + transition_time_(from, to); +} + +void DisplayPlan( + const RoutingIndexManager& manager, const RoutingModel& routing, + const operations_research::Assignment& plan, bool use_same_vehicle_costs, + int64 max_nodes_per_group, int64 same_vehicle_cost, + const operations_research::RoutingDimension& capacity_dimension, + const operations_research::RoutingDimension& time_dimension) { + // Display plan cost. + std::string plan_output = absl::StrFormat("Cost %d\n", plan.ObjectiveValue()); + + // Display dropped orders. + std::string dropped; + for (int64 order = 0; order < routing.Size(); ++order) { + if (routing.IsStart(order) || routing.IsEnd(order)) continue; + if (plan.Value(routing.NextVar(order)) == order) { + if (dropped.empty()) { + absl::StrAppendFormat(&dropped, " %d", + manager.IndexToNode(order).value()); + } else { + absl::StrAppendFormat(&dropped, ", %d", + manager.IndexToNode(order).value()); + } + } + } + if (!dropped.empty()) { + plan_output += "Dropped orders:" + dropped + "\n"; + } + + if (use_same_vehicle_costs) { + int group_size = 0; + int64 group_same_vehicle_cost = 0; + std::set visited; + for (int64 order = 0; order < routing.Size(); ++order) { + if (routing.IsStart(order) || routing.IsEnd(order)) continue; + ++group_size; + visited.insert(plan.Value(routing.VehicleVar(order))); + if (group_size == max_nodes_per_group) { + if (visited.size() > 1) { + group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost; + } + group_size = 0; + visited.clear(); + } + } + if (visited.size() > 1) { + group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost; + } + LOG(INFO) << "Same vehicle costs: " << group_same_vehicle_cost; + } + + // Display actual output for each vehicle. + for (int route_number = 0; route_number < routing.vehicles(); + ++route_number) { + int64 order = routing.Start(route_number); + absl::StrAppendFormat(&plan_output, "Route %d: ", route_number); + if (routing.IsEnd(plan.Value(routing.NextVar(order)))) { + plan_output += "Empty\n"; + } else { + while (true) { + operations_research::IntVar* const load_var = + capacity_dimension.CumulVar(order); + operations_research::IntVar* const time_var = + time_dimension.CumulVar(order); + operations_research::IntVar* const slack_var = + routing.IsEnd(order) ? nullptr : time_dimension.SlackVar(order); + if (slack_var != nullptr && plan.Contains(slack_var)) { + absl::StrAppendFormat( + &plan_output, "%d Load(%d) Time(%d, %d) Slack(%d, %d)", + manager.IndexToNode(order).value(), plan.Value(load_var), + plan.Min(time_var), plan.Max(time_var), plan.Min(slack_var), + plan.Max(slack_var)); + } else { + absl::StrAppendFormat(&plan_output, "%d Load(%d) Time(%d, %d)", + manager.IndexToNode(order).value(), + plan.Value(load_var), plan.Min(time_var), + plan.Max(time_var)); + } + if (routing.IsEnd(order)) break; + plan_output += " -> "; + order = plan.Value(routing.NextVar(order)); + } + plan_output += "\n"; + } + } + LOG(INFO) << plan_output; +} +} // namespace operations_research + +#endif // OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_ diff --git a/libs/or-tools-bin/examples/cpp/cvrptw_with_breaks.cc b/libs/or-tools-bin/examples/cpp/cvrptw_with_breaks.cc new file mode 100644 index 0000000000000000000000000000000000000000..0c5ac3af2147ad29fb089d9253169cb38a0d9a44 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/cvrptw_with_breaks.cc @@ -0,0 +1,224 @@ +// 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. + +// +// Capacitated Vehicle Routing Problem with Time Windows and Breaks. +// A description of the Capacitated Vehicle Routing Problem with Time Windows +// can be found here: +// http://en.wikipedia.org/wiki/Vehicle_routing_problem. +// The variant which is tackled by this model includes a capacity dimension, +// time windows and optional orders, with a penalty cost if orders are not +// performed. For the sake of simplicty, orders are randomly located and +// distances are computed using the Manhattan distance. Distances are assumed +// to be in meters and times in seconds. +// This variant also includes vehicle breaks which must happen during the day +// with two alternate breaks schemes: either a long break in the middle of the +// day or two smaller ones which can be taken during a longer period of the day. + +#include + +#include "absl/strings/str_cat.h" +#include "examples/cpp/cvrptw_lib.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_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"; + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0."; + CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size."; + // 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 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( + FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION); + + // Setting up locations. + const int64 kXMax = 100000; + const int64 kYMax = 100000; + const int64 kSpeed = 10; + LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed); + for (int location = 0; location <= FLAGS_vrp_orders; ++location) { + locations.AddRandomLocation(kXMax, kYMax); + } + + // Setting the cost function. + 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(manager.num_nodes(), kDepot, + FLAGS_vrp_use_deterministic_random_seed); + demand.Initialize(); + 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, + [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { + return demand.Demand(i, j); + }, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); + routing.AddDimension( + 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); + 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 < 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)); + } + + // Minimize time variables. + for (int i = 0; i < routing.Size(); ++i) { + routing.AddVariableMinimizedByFinalizer(time_dimension->CumulVar(i)); + } + for (int j = 0; j < FLAGS_vrp_vehicles; ++j) { + routing.AddVariableMinimizedByFinalizer( + time_dimension->CumulVar(routing.Start(j))); + routing.AddVariableMinimizedByFinalizer( + time_dimension->CumulVar(routing.End(j))); + } + + // Adding vehicle breaks: + // - 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++) { + if (node >= routing.nodes()) { + service_times[node] = 0; + } else { + const RoutingIndexManager::NodeIndex index(node); + service_times[node] = kTimePerDemandUnit * demand.Demand(index, index); + } + } + 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}}; + Solver* const solver = routing.solver(); + for (int vehicle = 0; vehicle < FLAGS_vrp_vehicles; ++vehicle) { + std::vector breaks; + for (int i = 0; i < break_data.size(); ++i) { + 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 + solver->AddConstraint(solver->MakeEquality(breaks[1]->PerformedExpr(), + breaks[2]->PerformedExpr())); + // break2 start 1h after break1. + solver->AddConstraint(solver->MakeIntervalVarRelationWithDelay( + 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, + service_times); + } + + // Adding penalty costs to allow skipping orders. + const int64 kPenalty = 10000000; + const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; + order < routing.nodes(); ++order) { + std::vector orders(1, manager.NodeToIndex(order)); + routing.AddDisjunction(orders, kPenalty); + } + + // Solve, returns a solution if any (owned by RoutingModel). + const Assignment* solution = routing.SolveWithParameters(parameters); + if (solution != nullptr) { + LOG(INFO) << "Breaks: "; + for (const auto& break_interval : + solution->IntervalVarContainer().elements()) { + if (break_interval.PerformedValue() == 1) { + LOG(INFO) << break_interval.Var()->name() << " " + << break_interval.DebugString(); + } else { + LOG(INFO) << break_interval.Var()->name() << " unperformed"; + } + } + DisplayPlan(manager, routing, *solution, false, 0, 0, + routing.GetDimensionOrDie(kCapacity), + routing.GetDimensionOrDie(kTime)); + } else { + LOG(INFO) << "No solution found."; + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/cvrptw_with_refueling.cc b/libs/or-tools-bin/examples/cpp/cvrptw_with_refueling.cc new file mode 100644 index 0000000000000000000000000000000000000000..9eb02136d02a4a6bfef59c6d949e6d54aee47db6 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/cvrptw_with_refueling.cc @@ -0,0 +1,177 @@ +// 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. + +// Capacitated Vehicle Routing Problem with Time Windows and refueling +// constraints. +// This is an extension to the model in cvrptw.cc so refer to that file for +// more information on the common part of the model. The model implemented here +// takes into account refueling constraints using a specific dimension: vehicles +// must visit certain nodes (refueling nodes) before the quantity of fuel +// reaches zero. Fuel consumption is proportional to the distance traveled. + +#include + +#include "examples/cpp/cvrptw_lib.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_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; + +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"; +const char* kFuel = "Fuel"; + +// Returns true if node is a refueling node (based on node / refuel node ratio). +bool IsRefuelNode(int64 node) { + const int64 kRefuelNodeRatio = 10; + return (node % kRefuelNodeRatio == 0); +} + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0."; + CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size."; + // 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 RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); + + // Setting up locations. + const int64 kXMax = 100000; + const int64 kYMax = 100000; + const int64 kSpeed = 10; + LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed); + for (int location = 0; location <= FLAGS_vrp_orders; ++location) { + locations.AddRandomLocation(kXMax, kYMax); + } + + // Setting the cost function. + 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(manager.num_nodes(), kDepot, + FLAGS_vrp_use_deterministic_random_seed); + demand.Initialize(); + 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, + [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { + return demand.Demand(i, j); + }, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); + routing.AddDimension( + 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 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 < manager.num_nodes(); ++order) { + if (!IsRefuelNode(order)) { + const int64 start = randomizer.Uniform(kHorizon - kTWDuration); + time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration); + } + } + + // Adding fuel dimension. This dimension consumes a quantity equal to the + // distance traveled. Only refuel nodes can make the quantity of dimension + // increase by letting slack variable replenish the fuel. + const int64 kFuelCapacity = kXMax + kYMax; + routing.AddDimension( + 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 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)) { + fuel_dimension.SlackVar(order)->SetValue(0); + } + // Needed to instantiate fuel quantity at each node. + routing.AddVariableMinimizedByFinalizer(fuel_dimension.CumulVar(order)); + } + + // Adding penalty costs to allow skipping orders. + const int64 kPenalty = 100000; + const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; + order < routing.nodes(); ++order) { + std::vector orders(1, manager.NodeToIndex(order)); + routing.AddDisjunction(orders, kPenalty); + } + + // Solve, returns a solution if any (owned by RoutingModel). + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); + const Assignment* solution = routing.SolveWithParameters(parameters); + if (solution != nullptr) { + DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false, + /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, + routing.GetDimensionOrDie(kCapacity), + routing.GetDimensionOrDie(kTime)); + } else { + LOG(INFO) << "No solution found."; + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/cvrptw_with_resources.cc b/libs/or-tools-bin/examples/cpp/cvrptw_with_resources.cc new file mode 100644 index 0000000000000000000000000000000000000000..93bc87c898babc0b38f661899bfc1d23bc3d3fee --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/cvrptw_with_resources.cc @@ -0,0 +1,177 @@ +// 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. + +// Capacitated Vehicle Routing Problem with Time Windows and capacitated +// resources. +// This is an extension to the model in cvrptw.cc so refer to that file for +// more information on the common part of the model. The model implemented here +// limits the number of vehicles which can simultaneously leave or enter the +// depot due to limited resources (or capacity) available. +// TODO(user): The current model consumes resources even for vehicles with +// empty routes; fix this when we have an API on the cumulative constraints +// with variable demands. + +#include + +#include "examples/cpp/cvrptw_lib.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_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::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"; + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0."; + CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size."; + // 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 RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); + + // Setting up locations. + const int64 kXMax = 100000; + const int64 kYMax = 100000; + const int64 kSpeed = 10; + LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed); + for (int location = 0; location <= FLAGS_vrp_orders; ++location) { + locations.AddRandomLocation(kXMax, kYMax); + } + + // Setting the cost function. + 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(manager.num_nodes(), kDepot, + FLAGS_vrp_use_deterministic_random_seed); + demand.Initialize(); + 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, + [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { + return demand.Demand(i, j); + }, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); + routing.AddDimension( + 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 < manager.num_nodes(); ++order) { + const int64 start = randomizer.Uniform(kHorizon - 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; + for (int i = 0; i < FLAGS_vrp_vehicles; ++i) { + 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; + 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. + const int64 kDepotCapacity = 5; + std::vector depot_usage(start_end_times.size(), 1); + solver->AddConstraint( + solver->MakeCumulative(intervals, depot_usage, kDepotCapacity, "depot")); + // Instantiate route start and end times to produce feasible times. + for (int i = 0; i < start_end_times.size(); ++i) { + routing.AddVariableMinimizedByFinalizer(start_end_times[i]); + } + + // Adding penalty costs to allow skipping orders. + const int64 kPenalty = 100000; + 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). + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); + const Assignment* solution = routing.SolveWithParameters(parameters); + if (solution != nullptr) { + DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false, + /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, + routing.GetDimensionOrDie(kCapacity), + routing.GetDimensionOrDie(kTime)); + } else { + LOG(INFO) << "No solution found."; + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/cvrptw_with_stop_times_and_resources.cc b/libs/or-tools-bin/examples/cpp/cvrptw_with_stop_times_and_resources.cc new file mode 100644 index 0000000000000000000000000000000000000000..1020afc99214ae77a689ac93f884332fc2d2d262 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/cvrptw_with_stop_times_and_resources.cc @@ -0,0 +1,210 @@ +// 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. + +// Capacitated Vehicle Routing Problem with Time Windows, fixed stop times and +// capacitated resources. A stop is defined as consecutive nodes at the same +// location. +// This is an extension to the model in cvrptw.cc so refer to that file for +// more information on the common part of the model. The model implemented here +// limits the number of vehicles which can simultaneously leave or enter a node +// to one. + +#include + +#include "absl/strings/str_cat.h" +#include "examples/cpp/cvrptw_lib.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_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; + +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"; + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + CHECK_LT(0, FLAGS_vrp_stops) << "Specify an instance size greater than 0."; + CHECK_LT(0, FLAGS_vrp_orders_per_stop) + << "Specify an instance size greater than 0."; + CHECK_LT(0, FLAGS_vrp_vehicles) << "Specify a non-null vehicle fleet size."; + 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 RoutingIndexManager::NodeIndex kDepot(0); + RoutingIndexManager manager(vrp_orders + 1, FLAGS_vrp_vehicles, kDepot); + RoutingModel routing(manager); + + // Setting up locations. + const int64 kXMax = 100000; + const int64 kYMax = 100000; + const int64 kSpeed = 10; + LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed); + for (int stop = 0; stop <= FLAGS_vrp_stops; ++stop) { + const int num_orders = stop == 0 ? 1 : FLAGS_vrp_orders_per_stop; + locations.AddRandomLocation(kXMax, kYMax, num_orders); + } + + // Setting the cost function. + 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(manager.num_nodes(), kDepot, + FLAGS_vrp_use_deterministic_random_seed); + demand.Initialize(); + 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, + [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { + return locations.ManhattanTime(i, j); + }); + routing.AddDimension( + 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)); + const int64 kTWDuration = 5 * 3600; + for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) { + const int64 start = randomizer.Uniform(kHorizon - kTWDuration); + 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; + time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration); + } + } + + // Adding resource constraints at order locations. + Solver* const solver = routing.solver(); + std::vector intervals; + for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) { + std::vector stop_intervals; + 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; + IntervalVar* const interval = solver->MakeFixedDurationIntervalVar( + 0, kHorizon, kStopTime, true, absl::StrCat("Order", order)); + intervals.push_back(interval); + stop_intervals.push_back(interval); + // Link order and interval. + 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. + IntVar* const is_null_duration = + solver + ->MakeElement( + [&locations, order](int64 index) { + return locations.SameLocationFromIndex(order, index); + }, + routing.NextVar(order)) + ->Var(); + solver->AddConstraint( + solver->MakeNonEquality(interval->PerformedExpr(), is_null_duration)); + routing.AddIntervalToAssignment(interval); + // We are minimizing route durations by minimizing route ends; so we can + // maximize order starts to pack them together. + routing.AddVariableMaximizedByFinalizer(order_start); + } + // Only one order can happen at the same time at a given location. + std::vector location_usage(stop_intervals.size(), 1); + solver->AddConstraint(solver->MakeCumulative( + stop_intervals, location_usage, 1, absl::StrCat("Client", stop))); + } + // Minimizing route duration. + for (int vehicle = 0; vehicle < manager.num_vehicles(); ++vehicle) { + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.End(vehicle))); + } + + // Adding penalty costs to allow skipping orders. + const int64 kPenalty = 100000; + const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); + for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; + order < routing.nodes(); ++order) { + std::vector orders(1, manager.NodeToIndex(order)); + routing.AddDisjunction(orders, kPenalty); + } + + // Solve, returns a solution if any (owned by RoutingModel). + RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + FLAGS_routing_search_parameters, ¶meters)); + const Assignment* solution = routing.SolveWithParameters(parameters); + if (solution != nullptr) { + DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false, + /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, + routing.GetDimensionOrDie(kCapacity), + routing.GetDimensionOrDie(kTime)); + LOG(INFO) << "Stop intervals:"; + for (IntervalVar* const interval : intervals) { + if (solution->PerformedValue(interval)) { + LOG(INFO) << interval->name() << ": " << solution->StartValue(interval); + } + } + } else { + LOG(INFO) << "No solution found."; + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/dimacs_assignment.cc b/libs/or-tools-bin/examples/cpp/dimacs_assignment.cc new file mode 100644 index 0000000000000000000000000000000000000000..cc0570d9566aef51a5860c28e14ad93fd4c37a5f --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/dimacs_assignment.cc @@ -0,0 +1,202 @@ +// 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 "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/timer.h" +#include "ortools/graph/ebert_graph.h" +#include "ortools/graph/linear_assignment.h" + +DEFINE_bool(assignment_compare_hungarian, false, + "Compare result and speed against Hungarian method."); +DEFINE_string(assignment_problem_output_file, "", + "Print the problem to this file in DIMACS format (after layout " + "is optimized, if applicable)."); +DEFINE_bool(assignment_reverse_arcs, false, + "Ignored if --assignment_static_graph=true. Use StarGraph " + "if true, ForwardStarGraph if false."); +DEFINE_bool(assignment_static_graph, true, + "Use the ForwardStarStaticGraph representation, " + "otherwise ForwardStarGraph or StarGraph according " + "to --assignment_reverse_arcs."); + +namespace operations_research { + +typedef ForwardStarStaticGraph GraphType; + +template +CostValue BuildAndSolveHungarianInstance( + const LinearSumAssignment& assignment) { + const GraphType& graph = assignment.Graph(); + typedef std::vector HungarianRow; + typedef std::vector HungarianProblem; + HungarianProblem hungarian_cost; + hungarian_cost.resize(assignment.NumLeftNodes()); + // First we have to find the biggest cost magnitude so we can + // initialize the arc costs that aren't really there. + CostValue largest_cost_magnitude = 0; + for (typename GraphType::ArcIterator arc_it(graph); arc_it.Ok(); + arc_it.Next()) { + ArcIndex arc = arc_it.Index(); + CostValue cost_magnitude = std::abs(assignment.ArcCost(arc)); + largest_cost_magnitude = std::max(largest_cost_magnitude, cost_magnitude); + } + double missing_arc_cost = static_cast( + (assignment.NumLeftNodes() * largest_cost_magnitude) + 1); + for (HungarianProblem::iterator row = hungarian_cost.begin(); + row != hungarian_cost.end(); ++row) { + row->resize(assignment.NumNodes() - assignment.NumLeftNodes(), + missing_arc_cost); + } + // We're using a graph representation without forward arcs, so in + // order to use the generic GraphType::ArcIterator we would + // need to increase our memory footprint by building the array of + // arc tails (since we need tails to build the input to the + // hungarian algorithm). We opt for the alternative of iterating + // over hte arcs via adjacency lists, which gives us the arc tails + // implicitly. + for (typename GraphType::NodeIterator node_it(graph); node_it.Ok(); + node_it.Next()) { + NodeIndex node = node_it.Index(); + NodeIndex tail = (node - GraphType::kFirstNode); + for (typename GraphType::OutgoingArcIterator arc_it(graph, node); + arc_it.Ok(); arc_it.Next()) { + ArcIndex arc = arc_it.Index(); + NodeIndex head = + (graph.Head(arc) - assignment.NumLeftNodes() - GraphType::kFirstNode); + double cost = static_cast(assignment.ArcCost(arc)); + hungarian_cost[tail][head] = cost; + } + } + absl::flat_hash_map result; + absl::flat_hash_map wish_this_could_be_null; + WallTimer timer; + VLOG(1) << "Beginning Hungarian method."; + timer.Start(); + MinimizeLinearAssignment(hungarian_cost, &result, &wish_this_could_be_null); + double elapsed = timer.GetInMs() / 1000.0; + LOG(INFO) << "Hungarian result computed in " << elapsed << " seconds."; + double result_cost = 0.0; + for (int i = 0; i < assignment.NumLeftNodes(); ++i) { + int mate = result[i]; + result_cost += hungarian_cost[i][mate]; + } + return static_cast(result_cost); +} + +template +void DisplayAssignment(const LinearSumAssignment& assignment) { + for (typename LinearSumAssignment::BipartiteLeftNodeIterator + node_it(assignment); + node_it.Ok(); node_it.Next()) { + const NodeIndex left_node = node_it.Index(); + const ArcIndex matching_arc = assignment.GetAssignmentArc(left_node); + const NodeIndex right_node = assignment.Head(matching_arc); + VLOG(5) << "assigned (" << left_node << ", " << right_node + << "): " << assignment.ArcCost(matching_arc); + } +} + +template +int SolveDimacsAssignment(int argc, char* argv[]) { + std::string error_message; + // Handle on the graph we will need to delete because the + // LinearSumAssignment object does not take ownership of it. + GraphType* graph = nullptr; + DimacsAssignmentParser parser(argv[1]); + LinearSumAssignment* assignment = + parser.Parse(&error_message, &graph); + if (assignment == nullptr) { + LOG(FATAL) << error_message; + } + if (!FLAGS_assignment_problem_output_file.empty()) { + // The following tail array management stuff is done in a generic + // way so we can plug in different types of graphs for which the + // TailArrayManager template can be instantiated, even though we + // know the type of the graph explicitly. In this way, the type of + // the graph can be switched just by changing the graph type in + // this file and making no other changes to the code. + TailArrayManager tail_array_manager(graph); + PrintDimacsAssignmentProblem( + *assignment, tail_array_manager, FLAGS_assignment_problem_output_file); + tail_array_manager.ReleaseTailArrayIfForwardGraph(); + } + CostValue hungarian_cost = 0.0; + bool hungarian_solved = false; + if (FLAGS_assignment_compare_hungarian) { + hungarian_cost = BuildAndSolveHungarianInstance(*assignment); + hungarian_solved = true; + } + WallTimer timer; + timer.Start(); + bool success = assignment->ComputeAssignment(); + double elapsed = timer.GetInMs() / 1000.0; + if (success) { + CostValue cost = assignment->GetCost(); + DisplayAssignment(*assignment); + LOG(INFO) << "Cost of optimum assignment: " << cost; + LOG(INFO) << "Computed in " << elapsed << " seconds."; + LOG(INFO) << assignment->StatsString(); + if (hungarian_solved && (cost != hungarian_cost)) { + LOG(ERROR) << "Optimum cost mismatch: " << cost << " vs. " + << hungarian_cost << "."; + } + } else { + LOG(WARNING) << "Given problem is infeasible."; + } + delete assignment; + delete graph; + return EXIT_SUCCESS; +} +} // namespace operations_research + +static const char* const kUsageTemplate = "usage: %s "; + +using ::operations_research::ForwardStarGraph; +using ::operations_research::ForwardStarStaticGraph; +using ::operations_research::SolveDimacsAssignment; +using ::operations_research::StarGraph; + +int main(int argc, char* argv[]) { + std::string usage; + if (argc < 1) { + usage = absl::StrFormat(kUsageTemplate, "solve_dimacs_assignment"); + } else { + usage = absl::StrFormat(kUsageTemplate, argv[0]); + } + gflags::SetUsageMessage(usage); + gflags::ParseCommandLineFlags(&argc, &argv, true); + + if (argc < 2) { + LOG(FATAL) << usage; + } + + if (FLAGS_assignment_static_graph) { + return SolveDimacsAssignment(argc, argv); + } else if (FLAGS_assignment_reverse_arcs) { + return SolveDimacsAssignment(argc, argv); + } else { + return SolveDimacsAssignment(argc, argv); + } +} diff --git a/libs/or-tools-bin/examples/cpp/dobble_ls.cc b/libs/or-tools-bin/examples/cpp/dobble_ls.cc new file mode 100644 index 0000000000000000000000000000000000000000..e0e8eeabc3feebfb6a2f6e2790e3032289878190 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/dobble_ls.cc @@ -0,0 +1,764 @@ +// 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 problem is inspired by the Dobble game (aka Spot-It in the +// USA). In this game, we have 57 cards, 57 symbols, and 8 symbols +// per card. We want to assign symbols per card such that any two +// cards have exactly one symbol in common. These numbers can be +// generalized: we have N cards, each with K different symbols, and +// there are N different symbols overall. +// +// 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. +// +// Furthermore, we solve this problem using local search, and with a +// dedicated constraint. +// +// The purpose of the example is to demonstrates how to write local +// search operators and local search filters. + +#include +#include + +#include "absl/random/random.h" +#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/constraint_solver/constraint_solveri.h" +#include "ortools/util/bitset.h" + +DEFINE_int32(symbols_per_card, 8, "Number of symbols per card."); +DEFINE_int32(ls_seed, 1, + "Seed for the random number generator (used by " + "the Local Neighborhood Search)."); +DEFINE_bool(use_filter, true, "Use filter in the local search to prune moves."); +DEFINE_int32(num_swaps, 4, + "If num_swap > 0, the search for an optimal " + "solution will be allowed to use an operator that swaps the " + "symbols of up to num_swap pairs ((card1, symbol on card1), " + "(card2, symbol on card2))."); +DEFINE_int32(time_limit_in_ms, 60000, + "Time limit for the global search in ms."); + +namespace operations_research { + +// ----- Dedicated constraint to count the symbols shared by two cards ----- + +// This constraint maintains: +// sum_i(card1_symbol_vars[i]*card2_symbol_vars[i]) == count_var. +// with all card_symbol_vars[i] being boolean variables. +class SymbolsSharedByTwoCardsConstraint : public Constraint { + public: + // This constructor does not take any ownership on its arguments. + SymbolsSharedByTwoCardsConstraint( + Solver* const solver, const std::vector& card1_symbol_vars, + const std::vector& card2_symbol_vars, + IntVar* const num_symbols_in_common_var) + : Constraint(solver), + card1_symbol_vars_(card1_symbol_vars), + card2_symbol_vars_(card2_symbol_vars), + num_symbols_(card1_symbol_vars.size()), + num_symbols_in_common_var_(num_symbols_in_common_var) { + // Checks that cards have the same size. + CHECK_EQ(card1_symbol_vars.size(), card2_symbol_vars.size()); + + // Verify that we are really dealing with boolean variables. + for (int i = 0; i < num_symbols_; ++i) { + CHECK_GE(card1_symbol_vars_[i]->Min(), 0); + CHECK_GE(card2_symbol_vars_[i]->Min(), 0); + CHECK_LE(card1_symbol_vars_[i]->Max(), 1); + CHECK_LE(card2_symbol_vars_[i]->Max(), 1); + } + } + + ~SymbolsSharedByTwoCardsConstraint() override {} + + // Adds observers (named Demon) to variable events. These demons are + // responsible for implementing the propagation algorithm of the + // constraint. + void Post() override { + // Create a demon 'global_demon' that will bind events on + // variables to the calling of the 'InitialPropagate()' method. As + // this method is expensive, 'global_demon' has a low priority. As + // such, InitialPropagate will be called after all normal demons + // and constraints have reached a fixed point. Note + // that ownership of the 'global_demon' belongs to the solver. + Demon* const global_demon = + solver()->MakeDelayedConstraintInitialPropagateCallback(this); + // Attach to all variables. + for (int i = 0; i < num_symbols_; ++i) { + card1_symbol_vars_[i]->WhenBound(global_demon); + card2_symbol_vars_[i]->WhenBound(global_demon); + } + // Attach to cardinality variable. + num_symbols_in_common_var_->WhenBound(global_demon); + } + + // This is the main propagation method. + // + // It scans all card1_symbol_vars * card2_symbol_vars and increments 3 + // counters: + // - min_symbols_in_common if both booleans variables are bound to true. + // - max_symbols_in_common if both booleans are not bound to false. + // + // Then we know that num_symbols_in_common_var is in the range + // [min_symbols_in_common .. max_symbols_in_common]. + // + // Now, if num_symbols_in_common_var->Max() == + // min_symbols_in_common, then all products that contribute to + // max_symbols_in_common but not to min_symbols_in_common should be + // set to 0. + // + // Conversely, if num_symbols_in_common_var->Min() == + // max_symbols_in_common, then all products that contribute to + // max_symbols_in_common should be set to 1. + void InitialPropagate() override { + int max_symbols_in_common = 0; + int min_symbols_in_common = 0; + for (int i = 0; i < num_symbols_; ++i) { + if (card1_symbol_vars_[i]->Min() == 1 && + card2_symbol_vars_[i]->Min() == 1) { + min_symbols_in_common++; + } + if (card1_symbol_vars_[i]->Max() == 1 && + card2_symbol_vars_[i]->Max() == 1) { + max_symbols_in_common++; + } + } + num_symbols_in_common_var_->SetRange(min_symbols_in_common, + max_symbols_in_common); + // If min_symbols_in_common == max_symbols_in_common, it means + // that num_symbols_in_common_var_ is already fully determined: we + // have nothing to do. + if (min_symbols_in_common == max_symbols_in_common) { + DCHECK_EQ(min_symbols_in_common, num_symbols_in_common_var_->Max()); + DCHECK_EQ(min_symbols_in_common, num_symbols_in_common_var_->Min()); + return; + } + if (num_symbols_in_common_var_->Max() == min_symbols_in_common) { + // All undecided product terms should be forced to 0. + for (int i = 0; i < num_symbols_; ++i) { + // If both Min() are 0, then we can't force either variable to + // be zero (even if we know that their product is zero), + // because either variable could be 1 as long as the other is + // 0. + if (card1_symbol_vars_[i]->Min() == 1 && + card2_symbol_vars_[i]->Min() == 0) { + card2_symbol_vars_[i]->SetValue(0); + } else if (card2_symbol_vars_[i]->Min() == 1 && + card1_symbol_vars_[i]->Min() == 0) { + card1_symbol_vars_[i]->SetValue(0); + } + } + } else if (num_symbols_in_common_var_->Min() == max_symbols_in_common) { + // All undecided product terms should be forced to 1. + for (int i = 0; i < num_symbols_; ++i) { + if (card1_symbol_vars_[i]->Max() == 1 && + card2_symbol_vars_[i]->Max() == 1) { + // Note that we also force already-decided product terms, + // but this doesn't change anything. + card1_symbol_vars_[i]->SetValue(1); + card2_symbol_vars_[i]->SetValue(1); + } + } + } + } + + private: + std::vector card1_symbol_vars_; + std::vector card2_symbol_vars_; + const int num_symbols_; + IntVar* const num_symbols_in_common_var_; +}; + +// Creates two integer variables: one that counts the number of +// symbols common to two cards, and one that counts the absolute +// difference between the first var and 1 (i.e. the violation of the +// objective). Returns the latter (both vars are owned by the Solver +// anyway). +IntVar* CreateViolationVar(Solver* const solver, + const std::vector& card1_symbol_vars, + const std::vector& card2_symbol_vars, + int num_symbols_per_card) { + IntVar* const num_symbols_in_common_var = + solver->MakeIntVar(0, num_symbols_per_card); + // RevAlloc transfers the ownership of the constraint to the solver. + solver->AddConstraint(solver->RevAlloc(new SymbolsSharedByTwoCardsConstraint( + solver, card1_symbol_vars, card2_symbol_vars, + num_symbols_in_common_var))); + return solver->MakeAbs(solver->MakeSum(num_symbols_in_common_var, -1))->Var(); +} + +// ---------- Local Search ---------- + +// The "local search", or "local neighborhood search", works like +// this: starting from a given solution to the problem, other +// solutions in its neighborhood are generated from it, some of them +// might be selected (because they're better, for example) to become a +// starting point for other neighborhood searches, and so on.. The +// detailed search algorithm can vary and depends on the problem to +// solve. +// +// The fundamental building block for the local search is the "search +// operator", which has three fundamental methods in its API: +// +// - Its constructor, which keeps (mutable) references to the +// solver's internal variables (here, the card symbol variables). +// +// - OnStart(), which is called at the start of a local search, and +// after each solution (i.e. when the local search starts again from +// that new solution). The solver variables are supposed to represent +// a valid solution at this point. This method is used by the search +// operator to initialize its state and be ready to start the +// exploration of the neighborhood of the given solution. +// +// - MakeOneNeighbor(), which picks a neighbor of the initial +// solution, and changes the solver's internal variables accordingly +// to represent that new state. +// +// All local search operators on this problem will derive from the +// parent class below, which contains some shared code to store a +// compact representation of which symbols appeal on each cards. +class DobbleOperator : public IntVarLocalSearchOperator { + public: + DobbleOperator(const std::vector& card_symbol_vars, int num_cards, + int num_symbols, int num_symbols_per_card) + : IntVarLocalSearchOperator(card_symbol_vars), + num_cards_(num_cards), + num_symbols_(num_symbols), + num_symbols_per_card_(num_symbols_per_card), + symbols_per_card_(num_cards) { + CHECK_GT(num_cards, 0); + CHECK_GT(num_symbols, 0); + CHECK_GT(num_symbols_per_card, 0); + for (int card = 0; card < num_cards; ++card) { + symbols_per_card_[card].assign(num_symbols_per_card, -1); + } + } + + ~DobbleOperator() override {} + + protected: + // OnStart() simply stores the current symbols per card in + // symbols_per_card_, and defers further initialization to the + // subclass's InitNeighborhoodSearch() method. + void OnStart() override { + for (int card = 0; card < num_cards_; ++card) { + int found = 0; + for (int symbol = 0; symbol < num_symbols_; ++symbol) { + if (Value(VarIndex(card, symbol)) == 1) { + symbols_per_card_[card][found++] = symbol; + } + } + DCHECK_EQ(num_symbols_per_card_, found); + } + InitNeighborhoodSearch(); + } + + virtual void InitNeighborhoodSearch() = 0; + + // Find the index of the variable corresponding to the given symbol + // on the given card. + int VarIndex(int card, int symbol) { return card * num_symbols_ + symbol; } + + // Move symbol1 from card1 to card2, and symbol2 from card2 to card1. + void SwapTwoSymbolsOnCards(int card1, int symbol1, int card2, int symbol2) { + SetValue(VarIndex(card1, symbol1), 0); + SetValue(VarIndex(card2, symbol2), 0); + SetValue(VarIndex(card1, symbol2), 1); + SetValue(VarIndex(card2, symbol1), 1); + } + + const int num_cards_; + const int num_symbols_; + const int num_symbols_per_card_; + std::vector > symbols_per_card_; +}; + +// ----- Swap 2 symbols ----- + +// This operator explores *all* pairs (card1, some symbol on card1), +// (card2, some symbol on card2) and swaps the symbols between the two +// cards. +// +// Note that this could create invalid moves (for example, by adding a +// symbol to a card that already had it); see the DobbleFilter class +// below to see how we filter those out. +class SwapSymbols : public DobbleOperator { + public: + SwapSymbols(const std::vector& card_symbol_vars, int num_cards, + int num_symbols, int num_symbols_per_card) + : DobbleOperator(card_symbol_vars, num_cards, num_symbols, + num_symbols_per_card), + current_card1_(-1), + current_card2_(-1), + current_symbol1_(-1), + current_symbol2_(-1) {} + + ~SwapSymbols() override {} + + // Finds the next swap, returns false when it has finished. + bool MakeOneNeighbor() override { + if (!PickNextSwap()) { + VLOG(1) << "finished neighborhood"; + return false; + } + + const int symbol1 = symbols_per_card_[current_card1_][current_symbol1_]; + const int symbol2 = symbols_per_card_[current_card2_][current_symbol2_]; + SwapTwoSymbolsOnCards(current_card1_, symbol1, current_card2_, symbol2); + return true; + } + + private: + // Reinit the exploration loop. + void InitNeighborhoodSearch() override { + current_card1_ = 0; + current_card2_ = 1; + current_symbol1_ = 0; + current_symbol2_ = -1; + } + + // Compute the next move. It returns false when there are none. + bool PickNextSwap() { + current_symbol2_++; + if (current_symbol2_ == num_symbols_per_card_) { + current_symbol2_ = 0; + current_symbol1_++; + if (current_symbol1_ == num_symbols_per_card_) { + current_symbol1_ = 0; + current_card2_++; + if (current_card2_ == num_cards_) { + current_card1_++; + current_card2_ = current_card1_ + 1; + } + } + } + return current_card1_ < num_cards_ - 1; + } + + int current_card1_; + int current_card2_; + int current_symbol1_; + int current_symbol2_; +}; + +// Multiple swaps of two symbols. This operator is an expanded version +// of the previous operator. +// +// At each step, it will pick a number num_swaps at random in +// [2 .. max_num_swaps], and then pick num_swaps random pairs (card1, +// some symbol on card1), (card2, some symbol on card2), and swap the +// symbols of each pair. +// +// As the search space (the "neighborhood") is huge, we use a +// randomized "infinite" version instead of an iterative, exhaustive +// one. +class SwapSymbolsOnCardPairs : public DobbleOperator { + public: + SwapSymbolsOnCardPairs(const std::vector& card_symbol_vars, + int num_cards, int num_symbols, + int num_symbols_per_card, int max_num_swaps) + : DobbleOperator(card_symbol_vars, num_cards, num_symbols, + num_symbols_per_card), + rand_(FLAGS_ls_seed), + max_num_swaps_(max_num_swaps) { + CHECK_GE(max_num_swaps, 2); + } + + ~SwapSymbolsOnCardPairs() override {} + + protected: + bool MakeOneNeighbor() override { + const int num_swaps = + absl::Uniform(rand_, 0, max_num_swaps_ - 1) + 2; + for (int i = 0; i < num_swaps; ++i) { + const int card_1 = absl::Uniform(rand_, 0, num_cards_); + const int symbol_index_1 = + absl::Uniform(rand_, 0, num_symbols_per_card_); + const int symbol_1 = symbols_per_card_[card_1][symbol_index_1]; + const int card_2 = absl::Uniform(rand_, 0, num_cards_); + const int symbol_index_2 = + absl::Uniform(rand_, 0, num_symbols_per_card_); + const int symbol_2 = symbols_per_card_[card_2][symbol_index_2]; + SwapTwoSymbolsOnCards(card_1, symbol_1, card_2, symbol_2); + } + return true; + } + + void InitNeighborhoodSearch() override {} + + private: + std::mt19937 rand_; + const int max_num_swaps_; +}; + +// ----- Local Search Filter ----- + +// A filter is responsible for rejecting a local search move faster +// than what the propagation of the constraint solver would do. +// Its API consists in: +// - The constructor, which takes as input a reference to all the +// variables relevant to the filter. +// - OnSynchronize(), called at the beginning of the search and +// after each move to a new solution (when the local search +// restarts from it). +// - Accept(), which takes as input an attempted move (in the form +// of a Delta to tentatively apply to the variables), and returns +// true iff this move is found valid. +// +// To decide if a move is valid, first this DobbleFilter builds a +// bitmap of symbols per card. Then for each move, it updates the +// bitmap according to the move and checks the following constraints: +// - First, each card still has num_symbols_per_card symbols. - The +// cost of the assignment described by the move is better than the +// current one. + +// After the check is done, the original bitmap is restored if the +// move was rejected, so as to be ready for the next evaluation. +// +// Please note that this filter uses a fixed size bitset and +// effectively limits the number of cards to 63, and thus the number +// of symbols per card to 8. +class DobbleFilter : public IntVarLocalSearchFilter { + public: + DobbleFilter(const std::vector& card_symbol_vars, int num_cards, + int num_symbols, int num_symbols_per_card) + : IntVarLocalSearchFilter(card_symbol_vars), + num_cards_(num_cards), + num_symbols_(num_symbols), + num_symbols_per_card_(num_symbols_per_card), + temporary_bitset_(0), + symbol_bitmask_per_card_(num_cards, 0), + violation_costs_(num_cards_, std::vector(num_cards_, 0)) {} + + // We build the current bitmap and the matrix of violation cost + // between any two cards. + void OnSynchronize(const Assignment* delta) override { + symbol_bitmask_per_card_.assign(num_cards_, 0); + for (int card = 0; card < num_cards_; ++card) { + for (int symbol = 0; symbol < num_symbols_; ++symbol) { + if (Value(VarIndex(card, symbol))) { + SetBit64(&symbol_bitmask_per_card_[card], symbol); + } + } + } + for (int card1 = 0; card1 < num_cards_; ++card1) { + for (int card2 = 0; card2 < num_cards_; ++card2) { + violation_costs_[card1][card2] = ViolationCost(BitCount64( + symbol_bitmask_per_card_[card1] & symbol_bitmask_per_card_[card2])); + } + } + DCHECK(CheckCards()); + } + + // The LocalSearchFilter::Accept() API also takes a deltadelta, + // which is the difference between the current delta and the last + // delta that was given to Accept() -- but we don't use it here. + bool Accept(const Assignment* delta, const Assignment* unused_deltadelta, + int64 objective_min, int64 objective_max) override { + const Assignment::IntContainer& solution_delta = delta->IntVarContainer(); + const int solution_delta_size = solution_delta.Size(); + + // The input const Assignment* delta given to Accept() may + // actually contain "Deactivated" elements, which represent + // variables that have been freed -- they are not bound to a + // single value anymore. This happens with LNS-type (Large + // Neighborhood Search) LocalSearchOperator, which are not used in + // this example as of 2012-01; and we refer the reader to + // ./routing.cc for an example of such LNS-type operators. + // + // For didactical purposes, we will assume for a moment that a + // LNS-type operator might be applied. The Filter will still be + // called, but our DobbleFilter here won't be able to work, since + // it needs every variable to be bound (i.e. have a fixed value), + // in the assignment that it considers. Therefore, we include here + // a snippet of code that will detect if the input assignment is + // not fully bound. For further details, read ./routing.cc -- but + // we strongly advise the reader to first try and understand all + // of this file. + for (int i = 0; i < solution_delta_size; ++i) { + if (!solution_delta.Element(i).Activated()) { + VLOG(1) << "Element #" << i << " of the delta assignment given to" + << " DobbleFilter::Accept() is not activated (i.e. its variable" + << " is not bound to a single value anymore). This means that" + << " we are in a LNS phase, and the DobbleFilter won't be able" + << " to filter anything. Returning true."; + return true; + } + } + VLOG(1) << "No LNS, size = " << solution_delta_size; + + // Collect the set of cards that have been modified by this move. + std::vector touched_cards; + ComputeTouchedCards(solution_delta, &touched_cards); + + // Check basic metrics to fail fast. + if (!CheckCards()) { + RestoreBitsetPerCard(); + DCHECK(CheckCards()); + VLOG(1) << "reject by size"; + return false; + } + + // Compute new cost. + const int cost_delta = ComputeNewCost(touched_cards); + + // Reset the data structure to the state before the evaluation. + RestoreBitsetPerCard(); + + // And exit (this is only valid for a greedy descent and would + // reject valid moves in tabu search for instance). + if (cost_delta >= 0) { + VLOG(1) << "reject"; + } + return cost_delta < 0; + } + + private: + // Undo information after an evaluation. + struct UndoChange { + UndoChange(int c, uint64 b) : card(c), bitset(b) {} + int card; + uint64 bitset; + }; + + int VarIndex(int card, int symbol) { return card * num_symbols_ + symbol; } + + void ClearBitset() { temporary_bitset_ = 0; } + + // For each touched card, compare against all others to compute the + // delta in term of cost. We use an bitset to avoid counting twice + // between two cards appearing in the local search move. + int ComputeNewCost(const std::vector& touched_cards) { + ClearBitset(); + int cost_delta = 0; + for (int i = 0; i < touched_cards.size(); ++i) { + const int touched = touched_cards[i]; + SetBit64(&temporary_bitset_, touched); + const uint64 card_bitset = symbol_bitmask_per_card_[touched]; + const std::vector& row_cost = violation_costs_[touched]; + for (int other_card = 0; other_card < num_cards_; ++other_card) { + if (!IsBitSet64(&temporary_bitset_, other_card)) { + cost_delta += ViolationCost( + BitCount64(card_bitset & symbol_bitmask_per_card_[other_card])); + cost_delta -= row_cost[other_card]; + } + } + } + return cost_delta; + } + + // Collects all card indices appearing in the local search move. + void ComputeTouchedCards(const Assignment::IntContainer& solution_delta, + std::vector* const touched_cards) { + ClearBitset(); + const int solution_delta_size = solution_delta.Size(); + const int kUnassigned = -1; + for (int index = 0; index < solution_delta_size; ++index) { + int64 touched_var = kUnassigned; + FindIndex(solution_delta.Element(index).Var(), &touched_var); + CHECK_NE(touched_var, kUnassigned); + const int card = touched_var / num_symbols_; + const int symbol = touched_var % num_symbols_; + const int new_value = solution_delta.Element(index).Value(); + if (!IsBitSet64(&temporary_bitset_, card)) { + SaveRestoreInformation(card); + touched_cards->push_back(card); + SetBit64(&temporary_bitset_, card); + } + if (new_value) { + SetBit64(&symbol_bitmask_per_card_[card], symbol); + } else { + ClearBit64(&symbol_bitmask_per_card_[card], symbol); + } + } + } + + // Undo all modifications done when evaluating a move. + void RestoreBitsetPerCard() { + for (int i = 0; i < restore_information_.size(); ++i) { + symbol_bitmask_per_card_[restore_information_[i].card] = + restore_information_[i].bitset; + } + restore_information_.clear(); + } + + // Stores undo information for a given card. + void SaveRestoreInformation(int card) { + restore_information_.push_back( + UndoChange(card, symbol_bitmask_per_card_[card])); + } + + // Checks that after the local search move, each card would still have + // num_symbols_per_card symbols on it. + bool CheckCards() { + for (int i = 0; i < num_cards_; ++i) { + if (num_symbols_per_card_ != BitCount64(symbol_bitmask_per_card_[i])) { + VLOG(1) << "card " << i << " has bitset of size " + << BitCount64(symbol_bitmask_per_card_[i]); + return false; + } + } + return true; + } + + int ViolationCost(uint64 cardinality) const { + return (cardinality > 0 ? cardinality - 1 : 1); + } + + const int num_cards_; + const int num_symbols_; + const int num_symbols_per_card_; + uint64 temporary_bitset_; + std::vector symbol_bitmask_per_card_; + std::vector > violation_costs_; + std::vector restore_information_; +}; + +// ----- Main Method ----- + +void SolveDobble(int num_cards, int num_symbols, int num_symbols_per_card) { + LOG(INFO) << "Solving dobble assignment problem:"; + LOG(INFO) << " - " << num_cards << " cards"; + LOG(INFO) << " - " << num_symbols << " symbols"; + LOG(INFO) << " - " << num_symbols_per_card << " symbols per card"; + + // Creates the solver. + Solver solver("dobble"); + // Creates the matrix of boolean variables (cards * symbols). + 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, + 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( + card_symbol_vars[card_index][symbol_index]); + } + } + // Creates cardinality intersection variables and remember the + // violation variables. + std::vector violation_vars; + for (int card1 = 0; card1 < num_cards; ++card1) { + for (int card2 = 0; card2 < num_cards; ++card2) { + if (card1 != card2) { + violation_vars.push_back( + CreateViolationVar(&solver, card_symbol_vars[card1], + card_symbol_vars[card2], num_symbols_per_card)); + } + } + } + // Create the objective variable. + IntVar* const objective_var = solver.MakeSum(violation_vars)->Var(); + + // Add constraint: there must be exactly num_symbols_per_card + // symbols per card. + for (int card = 0; card < num_cards; ++card) { + solver.AddConstraint( + solver.MakeSumEquality(card_symbol_vars[card], num_symbols_per_card)); + } + + // IMPORTANT OPTIMIZATION: + // Add constraint: each symbol appears on exactly + // num_symbols_per_card cards (i.e. symbols are evenly + // distributed). This constraint is actually redundant, because it + // is a (non-trivial) consequence of the other constraints and of + // the model. But adding it makes the search go faster. + for (int symbol_index = 0; symbol_index < num_symbols; ++symbol_index) { + std::vector tmp; + for (int card_index = 0; card_index < num_cards; ++card_index) { + tmp.push_back(card_symbol_vars[card_index][symbol_index]); + } + solver.AddConstraint(solver.MakeSumEquality(tmp, num_symbols_per_card)); + } + + // Search. + LOG(INFO) << "Solving with Local Search"; + LOG(INFO) << " - time limit = " << FLAGS_time_limit_in_ms << " ms"; + + // Start a DecisionBuilder phase to find a first solution, using the + // strategy "Pick some random, yet unassigned card symbol variable + // and set its value to 1". + DecisionBuilder* const build_db = solver.MakePhase( + all_card_symbol_vars, Solver::CHOOSE_RANDOM, // Solver::IntVarStrategy + Solver::ASSIGN_MAX_VALUE); // Solver::IntValueStrategy + + // Creates local search operators. + std::vector operators; + LocalSearchOperator* const switch_operator = solver.RevAlloc(new SwapSymbols( + all_card_symbol_vars, num_cards, num_symbols, num_symbols_per_card)); + operators.push_back(switch_operator); + LOG(INFO) << " - add switch operator"; + if (FLAGS_num_swaps > 0) { + LocalSearchOperator* const swaps_operator = solver.RevAlloc( + new SwapSymbolsOnCardPairs(all_card_symbol_vars, num_cards, num_symbols, + num_symbols_per_card, FLAGS_num_swaps)); + operators.push_back(swaps_operator); + LOG(INFO) << " - add swaps operator with at most " << FLAGS_num_swaps + << " swaps"; + } + + // Creates filter. + std::vector filters; + if (FLAGS_use_filter) { + filters.push_back(solver.RevAlloc(new DobbleFilter( + all_card_symbol_vars, num_cards, num_symbols, num_symbols_per_card))); + } + + // Main decision builder that regroups the first solution decision + // builder and the combination of local search operators and + // filters. + DecisionBuilder* const final_db = solver.MakeLocalSearchPhase( + all_card_symbol_vars, build_db, + solver.MakeLocalSearchPhaseParameters( + objective_var, solver.ConcatenateOperators(operators, true), + nullptr, // Sub decision builder, not needed here. + nullptr, // Limit the search for improving move, we will stop + // the exploration of the local search at the first + // improving solution (first accept). + filters)); + + std::vector monitors; + // Optimize var search monitor. + OptimizeVar* const optimize = solver.MakeMinimize(objective_var, 1); + monitors.push_back(optimize); + + // Search log. + SearchMonitor* const log = solver.MakeSearchLog(100000, optimize); + monitors.push_back(log); + + // Search limit. + SearchLimit* const time_limit = + solver.MakeLimit(FLAGS_time_limit_in_ms, kint64max, kint64max, kint64max); + monitors.push_back(time_limit); + + // And solve! + solver.Solve(final_db, monitors); +} +} // namespace operations_research + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + // These constants comes directly from the dobble game. + // There are actually 55 cards, but we can create up to 57 cards. + const int kSymbolsPerCard = FLAGS_symbols_per_card; + const int kCards = kSymbolsPerCard * (kSymbolsPerCard - 1) + 1; + const int kSymbols = kCards; + operations_research::SolveDobble(kCards, kSymbols, kSymbolsPerCard); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/earliness_tardiness_cost_sample_sat.cc b/libs/or-tools-bin/examples/cpp/earliness_tardiness_cost_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/earliness_tardiness_cost_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/earliness_tardiness_cost_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/fap_model_printer.h b/libs/or-tools-bin/examples/cpp/fap_model_printer.h new file mode 100644 index 0000000000000000000000000000000000000000..7528da83cd50fa762fd9ac6c4b76402f5d13710e --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/fap_model_printer.h @@ -0,0 +1,118 @@ +// 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. + +// +// Prints a model of Frequency Assignment Problem. +// Format: http://www.inra.fr/mia/T/schiex/Doc/CELAR.shtml#synt +// + +#ifndef OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_ +#define OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_ + +#include +#include +#include + +#include "absl/strings/str_format.h" +#include "examples/cpp/fap_parser.h" + +namespace operations_research { + +// Prints the instance of the Frequency Assignment Problem. +class FapModelPrinter { + public: + FapModelPrinter(const std::map& variables, + const std::vector& constraints, + const std::string& objective, const std::vector& values); + ~FapModelPrinter(); + + void PrintFapObjective(); + void PrintFapVariables(); + void PrintFapConstraints(); + void PrintFapValues(); + + private: + const std::map variables_; + const std::vector constraints_; + const std::string objective_; + const std::vector values_; + DISALLOW_COPY_AND_ASSIGN(FapModelPrinter); +}; + +FapModelPrinter::FapModelPrinter(const std::map& variables, + const std::vector& constraints, + const std::string& objective, + const std::vector& values) + : variables_(variables), + constraints_(constraints), + objective_(objective), + values_(values) {} + +FapModelPrinter::~FapModelPrinter() {} + +void FapModelPrinter::PrintFapVariables() { + LOG(INFO) << "Variable File:"; + for (const auto& it : variables_) { + std::string domain = "{"; + for (const int value : it.second.domain) { + absl::StrAppendFormat(&domain, "%d ", value); + } + domain.append("}"); + + std::string hard = " "; + if (it.second.hard) { + hard = " hard"; + } + + LOG(INFO) << "Variable " << absl::StrFormat("%3d: ", it.first) + << absl::StrFormat("(degree: %2d) ", it.second.degree) + << absl::StrFormat("%3d", it.second.domain_index) + << absl::StrFormat("%3d", it.second.initial_position) + << absl::StrFormat("%3d", it.second.mobility_index) + << absl::StrFormat("%8d", it.second.mobility_cost) + << absl::StrFormat(" (%2d) ", it.second.domain_size) << domain + << hard; + } +} + +void FapModelPrinter::PrintFapConstraints() { + LOG(INFO) << "Constraint File:"; + for (const FapConstraint& ct : constraints_) { + std::string hard = " "; + if (ct.hard) { + hard = " hard"; + } + + LOG(INFO) << absl::StrFormat("%3d ", ct.variable1) + << absl::StrFormat("%3d ", ct.variable2) << ct.type << " " + << ct.operation << " " << absl::StrFormat("%3d", ct.value) + << absl::StrFormat("%3d", ct.weight_index) + << absl::StrFormat("%8d", ct.weight_cost) << hard; + } +} + +void FapModelPrinter::PrintFapObjective() { + LOG(INFO) << "Objective: " << objective_; +} + +void FapModelPrinter::PrintFapValues() { + LOG(INFO) << absl::StrFormat("Values(%d): ", values_.size()); + std::string domain = " "; + for (const int value : values_) { + absl::StrAppendFormat(&domain, "%d ", value); + } + LOG(INFO) << domain; +} + +} // namespace operations_research +#endif // OR_TOOLS_EXAMPLES_FAP_MODEL_PRINTER_H_ diff --git a/libs/or-tools-bin/examples/cpp/fap_parser.h b/libs/or-tools-bin/examples/cpp/fap_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..74027507c7b357fcfecc48bdae21eb6b5d312dc7 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/fap_parser.h @@ -0,0 +1,615 @@ +// 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. + +// +// Reading and parsing the data of Frequency Assignment Problem +// Format: http://www.inra.fr/mia/T/schiex/Doc/CELAR.shtml#synt +// + +#ifndef OR_TOOLS_EXAMPLES_FAP_PARSER_H_ +#define OR_TOOLS_EXAMPLES_FAP_PARSER_H_ + +#include +#include +#include +#include "absl/container/flat_hash_map.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "ortools/base/file.h" +#include "ortools/base/logging.h" +#include "ortools/base/map_util.h" + +namespace operations_research { + +// Takes a filename and a buffer and fills the lines buffer +// with the lines of the file corresponding to the filename. +void ParseFileByLines(const std::string& filename, + std::vector* lines); + +// The FapVariable struct represents a radio link of the +// frequency assignment problem. +struct FapVariable { + FapVariable() + : domain_index(-1), + domain_size(0), + domain(), + degree(0), + initial_position(-1), + mobility_index(-1), + mobility_cost(-1), + hard(false) {} + + ~FapVariable() {} + + // Fields: + // the index of a subset of all available frequencies of the instance + int domain_index; + + // the number of the frequencies available for the link + int domain_size; + + // the link's domain, i.e. a finite set of frequencies that can be + // assigned to this link + std::vector domain; + + // the number of constraints in which the link appears + int degree; + + // if positive, it means that the link has already been assigned a frequency + // of that value + int initial_position; + + // the index of mobility cost + int mobility_index; + + // the cost of modification of a link's pre-assigned value + int mobility_cost; + + // if true, it means that the link's pre-assigned position cannot be modified + bool hard; +}; + +// The FapConstraint struct represents a constraint between two +// radio links of the frequency assignment problem. +struct FapConstraint { + FapConstraint() + : variable1(-1), + variable2(-1), + impact(0), + type(""), + operation(""), + value(-1), + weight_index(-1), + weight_cost(-1), + hard(false) {} + ~FapConstraint() {} + + // Fields: + // the index of the first variable appearing in the constraint + int variable1; + + // the index of the second variable appearing in the constraint + int variable2; + + // the importance of a constraint based on the degree of its variables, + // the operator used in the constraint ("=" or ">") and whether it is a hard + // or soft constraint and with what weight cost. + // impact = (max_degree + min_degree + operator_impact + hardness_impact) + int impact; + + // the constraint type (D (difference), C (viscosity), F (fixed),P (prefix) + // or L (far fields)) which is not used in practice + std::string type; + + // the operator used in the constraint ("=" or ">") + std::string operation; + + // the constraint deviation: it defines the constant k12 mentioned in RLFAP + // description + int value; + + // the index of weight cost + int weight_index; + + // the cost of not satisfaction of the constraint + int weight_cost; + + // if true, it means that the constraint must be satisfied + bool hard; +}; + +// The FapComponent struct represents an component of the RLFAP graph. +// It models an independent sub-problem of the initial instance. +struct FapComponent { + FapComponent() : variables(), constraints() {} + ~FapComponent() {} + + // Fields: + // the variable set of the sub-problem, i.e. the vertices of the component + std::map variables; + + // the constraint set of the sub-problem, i.e. the edges of the component + std::vector constraints; +}; + +// Parser of the var.txt file. +// This file describes all the variables in the instance. +// Each line corresponds to one variable. +class VariableParser { + public: + explicit VariableParser(const std::string& data_directory); + ~VariableParser(); + + const std::map& variables() const { return variables_; } + + void Parse(); + + private: + const std::string filename_; + // A map is used because in the model, the variables have ids which may not + // be consecutive, may be very sparse and don't have a specific upper-bound. + // The key of the map, is the link's id. + std::map variables_; + + DISALLOW_COPY_AND_ASSIGN(VariableParser); +}; + +// Parser of the dom.txt file. +// This file describes the domains used by the variables of the problem. +// Each line describes one domain. +class DomainParser { + public: + explicit DomainParser(const std::string& data_directory); + ~DomainParser(); + + const std::map >& domains() const { return domains_; } + + void Parse(); + + private: + const std::string filename_; + // A map is used because in the model, the ids of the different available + // domains may be random values, since they are used as names. The key of the + // map is the subset's id. + std::map > domains_; + + DISALLOW_COPY_AND_ASSIGN(DomainParser); +}; + +// Parse ctr.txt file. +// This file describes the constraints of the instance. +// Each line defines a binary constraint. +class ConstraintParser { + public: + explicit ConstraintParser(const std::string& data_directory); + ~ConstraintParser(); + + const std::vector& constraints() const { return constraints_; } + + void Parse(); + + private: + const std::string filename_; + std::vector constraints_; + + DISALLOW_COPY_AND_ASSIGN(ConstraintParser); +}; + +// Parse cst.txt file. +// This file defines the criterion on which the solution will be based. +// It may also contain 8 coefficients: 4 for different constraint violation +// costs and 4 for different variable mobility costs. +class ParametersParser { + public: + explicit ParametersParser(const std::string& data_directory); + ~ParametersParser(); + + std::string objective() const { return objective_; } + const std::vector& constraint_weights() const { + return constraint_weights_; + } + const std::vector& variable_weights() const { return variable_weights_; } + + void Parse(); + + private: + const std::string filename_; + static const int constraint_coefficient_no_ = 4; + static const int variable_coefficient_no_ = 4; + static const int coefficient_no_ = 8; + std::string objective_; + std::vector constraint_weights_; + std::vector variable_weights_; +}; + +namespace { +int strtoint32(const std::string& word) { + int result; + CHECK(absl::SimpleAtoi(word, &result)); + return result; +} +} // namespace + +// Function that finds the disjoint sub-graphs of the graph of the instance. +void FindComponents(const std::vector& constraints, + const std::map& variables, + const int maximum_variable_id, + absl::flat_hash_map* components); + +// Function that computes the impact of a constraint. +int EvaluateConstraintImpact(const std::map& variables, + const int max_weight_cost, + const FapConstraint constraint); + +// Function that parses an instance of frequency assignment problem. +void ParseInstance(const std::string& data_directory, bool find_components, + std::map* variables, + std::vector* constraints, + std::string* objective, std::vector* frequencies, + absl::flat_hash_map* components); + +void ParseFileByLines(const std::string& filename, + std::vector* lines) { + CHECK(lines != nullptr); + std::string result; + CHECK_OK(file::GetContents(filename, &result, file::Defaults())); + *lines = absl::StrSplit(result, '\n', absl::SkipEmpty()); +} + +// VariableParser Implementation +VariableParser::VariableParser(const std::string& data_directory) + : filename_(data_directory + "/var.txt") {} + +VariableParser::~VariableParser() {} + +void VariableParser::Parse() { + std::vector lines; + ParseFileByLines(filename_, &lines); + for (const std::string& line : lines) { + std::vector tokens = + absl::StrSplit(line, ' ', absl::SkipEmpty()); + if (tokens.empty()) { + continue; + } + CHECK_GE(tokens.size(), 2); + + FapVariable variable; + variable.domain_index = strtoint32(tokens[1].c_str()); + if (tokens.size() > 3) { + variable.initial_position = strtoint32(tokens[2].c_str()); + variable.mobility_index = strtoint32(tokens[3].c_str()); + } + gtl::InsertOrUpdate(&variables_, strtoint32(tokens[0].c_str()), variable); + } +} + +// DomainParser Implementation +DomainParser::DomainParser(const std::string& data_directory) + : filename_(data_directory + "/dom.txt") {} + +DomainParser::~DomainParser() {} + +void DomainParser::Parse() { + std::vector lines; + ParseFileByLines(filename_, &lines); + for (const std::string& line : lines) { + std::vector tokens = + absl::StrSplit(line, ' ', absl::SkipEmpty()); + if (tokens.empty()) { + continue; + } + CHECK_GE(tokens.size(), 2); + + const int key = strtoint32(tokens[0].c_str()); + + std::vector domain; + domain.clear(); + for (int i = 2; i < tokens.size(); ++i) { + domain.push_back(strtoint32(tokens[i].c_str())); + } + + if (!domain.empty()) { + gtl::InsertOrUpdate(&domains_, key, domain); + } + } +} + +// ConstraintParser Implementation +ConstraintParser::ConstraintParser(const std::string& data_directory) + : filename_(data_directory + "/ctr.txt") {} + +ConstraintParser::~ConstraintParser() {} + +void ConstraintParser::Parse() { + std::vector lines; + ParseFileByLines(filename_, &lines); + for (const std::string& line : lines) { + std::vector tokens = + absl::StrSplit(line, ' ', absl::SkipEmpty()); + if (tokens.empty()) { + continue; + } + CHECK_GE(tokens.size(), 5); + + FapConstraint constraint; + constraint.variable1 = strtoint32(tokens[0].c_str()); + constraint.variable2 = strtoint32(tokens[1].c_str()); + constraint.type = tokens[2]; + constraint.operation = tokens[3]; + constraint.value = strtoint32(tokens[4].c_str()); + + if (tokens.size() > 5) { + constraint.weight_index = strtoint32(tokens[5].c_str()); + } + constraints_.push_back(constraint); + } +} + +// ParametersParser Implementation +const int ParametersParser::constraint_coefficient_no_; +const int ParametersParser::variable_coefficient_no_; +const int ParametersParser::coefficient_no_; + +ParametersParser::ParametersParser(const std::string& data_directory) + : filename_(data_directory + "/cst.txt"), + objective_(""), + constraint_weights_(constraint_coefficient_no_, 0), + variable_weights_(variable_coefficient_no_, 0) {} + +ParametersParser::~ParametersParser() {} + +void ParametersParser::Parse() { + bool objective = true; + bool largest_token = false; + bool value_token = false; + bool number_token = false; + bool values_token = false; + bool coefficient = false; + std::vector coefficients; + std::vector lines; + + ParseFileByLines(filename_, &lines); + for (const std::string& line : lines) { + if (objective) { + largest_token = + largest_token || (line.find("largest") != std::string::npos); + value_token = value_token || (line.find("value") != std::string::npos); + number_token = number_token || (line.find("number") != std::string::npos); + values_token = values_token || (line.find("values") != std::string::npos); + coefficient = + coefficient || (line.find("coefficient") != std::string::npos); + } + + if (coefficient) { + CHECK_EQ(coefficient_no_, + constraint_coefficient_no_ + variable_coefficient_no_); + objective = false; + if (line.find("=") != std::string::npos) { + std::vector tokens = + absl::StrSplit(line, ' ', absl::SkipEmpty()); + CHECK_GE(tokens.size(), 3); + coefficients.push_back(strtoint32(tokens[2].c_str())); + } + } + } + + if (coefficient) { + CHECK_EQ(coefficient_no_, coefficients.size()); + for (int i = 0; i < coefficient_no_; i++) { + if (i < constraint_coefficient_no_) { + constraint_weights_[i] = coefficients[i]; + } else { + variable_weights_[i - constraint_coefficient_no_] = coefficients[i]; + } + } + } + + if (largest_token && value_token) { + objective_ = "Minimize the largest assigned value."; + } else if (number_token && values_token) { + objective_ = "Minimize the number of assigned values."; + } else { + // Should not reach this point. + LOG(WARNING) << "Cannot read the objective of the instance."; + } +} + +// TODO(user): Make FindComponents linear instead of quadratic. +void FindComponents(const std::vector& constraints, + const std::map& variables, + const int maximum_variable_id, + absl::flat_hash_map* components) { + std::vector in_component(maximum_variable_id + 1, -1); + int constraint_index = 0; + for (const FapConstraint& constraint : constraints) { + const int variable_id1 = constraint.variable1; + const int variable_id2 = constraint.variable2; + const FapVariable& variable1 = gtl::FindOrDie(variables, variable_id1); + const FapVariable& variable2 = gtl::FindOrDie(variables, variable_id2); + CHECK_LT(variable_id1, in_component.size()); + CHECK_LT(variable_id2, in_component.size()); + if (in_component[variable_id1] < 0 && in_component[variable_id2] < 0) { + // None of the variables belong to an existing component. + // Create a new one. + FapComponent component; + const int component_index = constraint_index; + gtl::InsertOrUpdate(&(component.variables), variable_id1, variable1); + gtl::InsertOrUpdate(&(component.variables), variable_id2, variable2); + in_component[variable_id1] = component_index; + in_component[variable_id2] = component_index; + component.constraints.push_back(constraint); + gtl::InsertOrUpdate(components, component_index, component); + } else if (in_component[variable_id1] >= 0 && + in_component[variable_id2] < 0) { + // If variable1 belongs to an existing component, variable2 should + // also be included in the same component. + const int component_index = in_component[variable_id1]; + CHECK(gtl::ContainsKey(*components, component_index)); + gtl::InsertOrUpdate(&((*components)[component_index].variables), + variable_id2, variable2); + in_component[variable_id2] = component_index; + (*components)[component_index].constraints.push_back(constraint); + } else if (in_component[variable_id1] < 0 && + in_component[variable_id2] >= 0) { + // If variable2 belongs to an existing component, variable1 should + // also be included in the same component. + const int component_index = in_component[variable_id2]; + CHECK(gtl::ContainsKey(*components, component_index)); + gtl::InsertOrUpdate(&((*components)[component_index].variables), + variable_id1, variable1); + in_component[variable_id1] = component_index; + (*components)[component_index].constraints.push_back(constraint); + } else { + // The current constraint connects two different components. + const int component_index1 = in_component[variable_id1]; + const int component_index2 = in_component[variable_id2]; + const int min_component_index = + std::min(component_index1, component_index2); + const int max_component_index = + std::max(component_index1, component_index2); + CHECK(gtl::ContainsKey(*components, min_component_index)); + CHECK(gtl::ContainsKey(*components, max_component_index)); + if (min_component_index != max_component_index) { + // Update the component_index of maximum indexed component's variables. + for (const auto& variable : + (*components)[max_component_index].variables) { + int variable_id = variable.first; + in_component[variable_id] = min_component_index; + } + // Insert all the variables of the maximum indexed component to the + // variables of the minimum indexed component. + ((*components)[min_component_index]) + .variables.insert( + ((*components)[max_component_index]).variables.begin(), + ((*components)[max_component_index]).variables.end()); + // Insert all the constraints of the maximum indexed component to the + // constraints of the minimum indexed component. + ((*components)[min_component_index]) + .constraints.insert( + ((*components)[min_component_index]).constraints.end(), + ((*components)[max_component_index]).constraints.begin(), + ((*components)[max_component_index]).constraints.end()); + (*components)[min_component_index].constraints.push_back(constraint); + // Delete the maximum indexed component from the components set. + components->erase(max_component_index); + } else { + // Both variables belong to the same component, just add the constraint. + (*components)[min_component_index].constraints.push_back(constraint); + } + } + constraint_index++; + } +} + +int EvaluateConstraintImpact(const std::map& variables, + const int max_weight_cost, + const FapConstraint constraint) { + const FapVariable& variable1 = + gtl::FindOrDie(variables, constraint.variable1); + const FapVariable& variable2 = + gtl::FindOrDie(variables, constraint.variable2); + const int degree1 = variable1.degree; + const int degree2 = variable2.degree; + const int max_degree = std::max(degree1, degree2); + const int min_degree = std::min(degree1, degree2); + const int operator_impact = + constraint.operation == "=" ? max_degree : min_degree; + const int kHardnessBias = 10; + int hardness_impact = 0; + if (constraint.hard) { + hardness_impact = max_weight_cost > 0 ? kHardnessBias * max_weight_cost : 0; + } else { + hardness_impact = constraint.weight_cost; + } + return max_degree + min_degree + operator_impact + hardness_impact; +} + +void ParseInstance(const std::string& data_directory, bool find_components, + std::map* variables, + std::vector* constraints, + std::string* objective, std::vector* frequencies, + absl::flat_hash_map* components) { + CHECK(variables != nullptr); + CHECK(constraints != nullptr); + CHECK(objective != nullptr); + CHECK(frequencies != nullptr); + + // Parse the data files. + VariableParser var(data_directory); + var.Parse(); + *variables = var.variables(); + const int maximum_variable_id = variables->rbegin()->first; + + ConstraintParser ctr(data_directory); + ctr.Parse(); + *constraints = ctr.constraints(); + + DomainParser dom(data_directory); + dom.Parse(); + + ParametersParser cst(data_directory); + cst.Parse(); + const int maximum_weight_cost = *std::max_element( + (cst.constraint_weights()).begin(), (cst.constraint_weights()).end()); + + // Make the variables of the instance. + for (auto& it : *variables) { + it.second.domain = gtl::FindOrDie(dom.domains(), it.second.domain_index); + it.second.domain_size = it.second.domain.size(); + + if ((it.second.mobility_index == -1) || (it.second.mobility_index == 0)) { + it.second.mobility_cost = -1; + if (it.second.initial_position != -1) { + it.second.hard = true; + } + } else { + it.second.mobility_cost = + (cst.variable_weights())[it.second.mobility_index - 1]; + } + } + // Make the constraints of the instance. + for (FapConstraint& ct : *constraints) { + if ((ct.weight_index == -1) || (ct.weight_index == 0)) { + ct.weight_cost = -1; + ct.hard = true; + } else { + ct.weight_cost = (cst.constraint_weights())[ct.weight_index - 1]; + ct.hard = false; + } + ++((*variables)[ct.variable1]).degree; + ++((*variables)[ct.variable2]).degree; + } + // Make the available frequencies of the instance. + *frequencies = gtl::FindOrDie(dom.domains(), 0); + // Make the objective of the instance. + *objective = cst.objective(); + + if (find_components) { + CHECK(components != nullptr); + FindComponents(*constraints, *variables, maximum_variable_id, components); + // Evaluate each components's constraints impacts. + for (auto& component : *components) { + for (auto& constraint : component.second.constraints) { + constraint.impact = EvaluateConstraintImpact( + *variables, maximum_weight_cost, constraint); + } + } + } else { + for (FapConstraint& constraint : *constraints) { + constraint.impact = + EvaluateConstraintImpact(*variables, maximum_weight_cost, constraint); + } + } +} +} // namespace operations_research +#endif // OR_TOOLS_EXAMPLES_FAP_PARSER_H_ diff --git a/libs/or-tools-bin/examples/cpp/fap_utilities.h b/libs/or-tools-bin/examples/cpp/fap_utilities.h new file mode 100644 index 0000000000000000000000000000000000000000..a8195340ab4ed6530bc95bac2dc4fd14e96b69b4 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/fap_utilities.h @@ -0,0 +1,227 @@ +// 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. + +// +// Utilities used by frequency_assignment_problem.cc. +// + +#ifndef OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_ +#define OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_ + +#include +#include +#include + +#include "absl/strings/str_format.h" +#include "examples/cpp/fap_parser.h" +#include "ortools/constraint_solver/constraint_solver.h" +#include "ortools/base/logging.h" +#include "ortools/base/map_util.h" + +namespace operations_research { + +// Checks if the solution given from the Solver satisfies all +// the hard binary constraints specified in the ctr.txt. +bool CheckConstraintSatisfaction( + const std::vector& data_constraints, + const std::vector& variables, + const std::map& index_from_key); + +// Checks if the solution given from the Solver has not modified the values of +// the variables that were initially assigned and denoted as hard in var.txt. +bool CheckVariablePosition(const std::map& data_variables, + const std::vector& variables, + const std::map& index_from_key); + +// Counts the number of different values in the variable vector. +int NumberOfAssignedValues(const std::vector& variables); + +// Prints the duration of the solving process. +void PrintElapsedTime(const int64 time1, const int64 time2); + +// Prints the solution found by the Hard Solver for feasible instances. +void PrintResultsHard(SolutionCollector* const collector, + const std::vector& variables, + IntVar* const objective_var, + const std::map& data_variables, + const std::vector& data_constraints, + const std::map& index_from_key, + const std::vector& key_from_index); + +// Prints the solution found by the Soft Solver for unfeasible instances. +void PrintResultsSoft(SolutionCollector* const collector, + const std::vector& variables, + IntVar* const total_cost, + const std::map& hard_variables, + const std::vector& hard_constraints, + const std::map& soft_variables, + const std::vector& soft_constraints, + const std::map& index_from_key, + const std::vector& key_from_index); + +bool CheckConstraintSatisfaction( + const std::vector& data_constraints, + const std::vector& variables, + const std::map& index_from_key) { + bool status = true; + for (const FapConstraint& ct : data_constraints) { + const int index1 = gtl::FindOrDie(index_from_key, ct.variable1); + const int index2 = gtl::FindOrDie(index_from_key, ct.variable2); + CHECK_LT(index1, variables.size()); + CHECK_LT(index2, variables.size()); + const int var1 = variables[index1]; + const int var2 = variables[index2]; + const int absolute_difference = abs(var1 - var2); + + if ((ct.operation == ">") && (absolute_difference <= ct.value)) { + LOG(INFO) << " Violation of contraint between variable " << ct.variable1 + << " and variable " << ct.variable2 << "."; + LOG(INFO) << " Expected |" << var1 << " - " << var2 + << "| (= " << absolute_difference << ") > " << ct.value << "."; + status = false; + } else if ((ct.operation == "=") && (absolute_difference != ct.value)) { + LOG(INFO) << " Violation of contraint between variable " << ct.variable1 + << " and variable " << ct.variable2 << "."; + LOG(INFO) << " Expected |" << var1 << " - " << var2 + << "| (= " << absolute_difference << ") = " << ct.value << "."; + status = false; + } + } + return status; +} + +bool CheckVariablePosition(const std::map& data_variables, + const std::vector& variables, + const std::map& index_from_key) { + bool status = true; + for (const auto& it : data_variables) { + const int index = gtl::FindOrDie(index_from_key, it.first); + CHECK_LT(index, variables.size()); + const int var = variables[index]; + + if (it.second.hard && (it.second.initial_position != -1) && + (var != it.second.initial_position)) { + LOG(INFO) << " Change of position of hard variable " << it.first << "."; + LOG(INFO) << " Expected " << it.second.initial_position + << " instead of given " << var << "."; + status = false; + } + } + return status; +} + +int NumberOfAssignedValues(const std::vector& variables) { + std::set assigned(variables.begin(), variables.end()); + return static_cast(assigned.size()); +} + +void PrintElapsedTime(const int64 time1, const int64 time2) { + LOG(INFO) << "End of solving process."; + LOG(INFO) << "The Solve method took " << (time2 - time1) / 1000.0 + << " seconds."; +} + +void PrintResultsHard(SolutionCollector* const collector, + const std::vector& variables, + IntVar* const objective_var, + const std::map& data_variables, + const std::vector& data_constraints, + const std::map& index_from_key, + const std::vector& key_from_index) { + LOG(INFO) << "Printing..."; + LOG(INFO) << "Number of Solutions: " << collector->solution_count(); + for (int solution_index = 0; solution_index < collector->solution_count(); + ++solution_index) { + Assignment* const solution = collector->solution(solution_index); + std::vector results(variables.size()); + LOG(INFO) << "------------------------------------------------------------"; + LOG(INFO) << "Solution " << solution_index + 1; + LOG(INFO) << "Cost: " << solution->Value(objective_var); + for (int i = 0; i < variables.size(); ++i) { + results[i] = solution->Value(variables[i]); + LOG(INFO) << " Variable " << key_from_index[i] << ": " << results[i]; + } + if (CheckConstraintSatisfaction(data_constraints, results, + index_from_key)) { + LOG(INFO) << "All hard constraints satisfied."; + } else { + LOG(INFO) << "Warning! Hard constraint violation detected."; + } + if (CheckVariablePosition(data_variables, results, index_from_key)) { + LOG(INFO) << "All hard variables stayed unharmed."; + } else { + LOG(INFO) << "Warning! Hard variable modification detected."; + } + + LOG(INFO) << "Values used: " << NumberOfAssignedValues(results); + LOG(INFO) << "Maximum value used: " + << *std::max_element(results.begin(), results.end()); + LOG(INFO) << " Failures: " << collector->failures(solution_index); + } + LOG(INFO) << " ============================================================"; +} + +void PrintResultsSoft(SolutionCollector* const collector, + const std::vector& variables, + IntVar* const total_cost, + const std::map& hard_variables, + const std::vector& hard_constraints, + const std::map& soft_variables, + const std::vector& soft_constraints, + const std::map& index_from_key, + const std::vector& key_from_index) { + LOG(INFO) << "Printing..."; + LOG(INFO) << "Number of Solutions: " << collector->solution_count(); + for (int solution_index = 0; solution_index < collector->solution_count(); + ++solution_index) { + Assignment* const solution = collector->solution(solution_index); + std::vector results(variables.size()); + LOG(INFO) << "------------------------------------------------------------"; + LOG(INFO) << "Solution"; + for (int i = 0; i < variables.size(); ++i) { + results[i] = solution->Value(variables[i]); + LOG(INFO) << " Variable " << key_from_index[i] << ": " << results[i]; + } + if (CheckConstraintSatisfaction(hard_constraints, results, + index_from_key)) { + LOG(INFO) << "All hard constraints satisfied."; + } else { + LOG(INFO) << "Warning! Hard constraint violation detected."; + } + if (CheckVariablePosition(hard_variables, results, index_from_key)) { + LOG(INFO) << "All hard variables stayed unharmed."; + } else { + LOG(INFO) << "Warning! Hard constraint violation detected."; + } + + if (CheckConstraintSatisfaction(soft_constraints, results, + index_from_key) && + CheckVariablePosition(soft_variables, results, index_from_key)) { + LOG(INFO) << "Problem feasible: " + "Soft constraints and soft variables satisfied."; + LOG(INFO) << " Weighted Sum: " << solution->Value(total_cost); + } else { + LOG(INFO) << "Problem unfeasible. Optimized weighted sum of violations."; + LOG(INFO) << " Weighted Sum: " << solution->Value(total_cost); + } + + LOG(INFO) << "Values used: " << NumberOfAssignedValues(results); + LOG(INFO) << "Maximum value used: " + << *std::max_element(results.begin(), results.end()); + LOG(INFO) << " Failures: " << collector->failures(solution_index); + } + LOG(INFO) << " ============================================================"; +} + +} // namespace operations_research +#endif // OR_TOOLS_EXAMPLES_FAP_UTILITIES_H_ diff --git a/libs/or-tools-bin/examples/cpp/flow_api.cc b/libs/or-tools-bin/examples/cpp/flow_api.cc new file mode 100644 index 0000000000000000000000000000000000000000..8dcaaa0a3dc94b92cf3fa1c839f2eeb5c1706004 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/flow_api.cc @@ -0,0 +1,88 @@ +// 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/commandlineflags.h" +#include "ortools/base/logging.h" +#include "ortools/graph/ebert_graph.h" +#include "ortools/graph/max_flow.h" +#include "ortools/graph/min_cost_flow.h" + +namespace operations_research { + +// ----- Min Cost Flow ----- + +// Test on a 4x4 matrix. Example taken from +// http://www.ee.oulu.fi/~mpa/matreng/eem1_2-1.htm +void MinCostFlowOn4x4Matrix() { + LOG(INFO) << "Min Cost Flow on 4x4 Matrix"; + const int kNumSources = 4; + const int kNumTargets = 4; + const CostValue kCost[kNumSources][kNumTargets] = {{90, 75, 75, 80}, + {35, 85, 55, 65}, + {125, 95, 90, 105}, + {45, 110, 95, 115}}; + const CostValue kExpectedCost = 275; + StarGraph graph(kNumSources + kNumTargets, kNumSources * kNumTargets); + MinCostFlow min_cost_flow(&graph); + for (NodeIndex source = 0; source < kNumSources; ++source) { + for (NodeIndex target = 0; target < kNumTargets; ++target) { + ArcIndex arc = graph.AddArc(source, kNumSources + target); + min_cost_flow.SetArcUnitCost(arc, kCost[source][target]); + min_cost_flow.SetArcCapacity(arc, 1); + } + } + for (NodeIndex source = 0; source < kNumSources; ++source) { + min_cost_flow.SetNodeSupply(source, 1); + } + for (NodeIndex target = 0; target < kNumTargets; ++target) { + min_cost_flow.SetNodeSupply(kNumSources + target, -1); + } + CHECK(min_cost_flow.Solve()); + CHECK_EQ(MinCostFlow::OPTIMAL, min_cost_flow.status()); + CostValue total_flow_cost = min_cost_flow.GetOptimalCost(); + CHECK_EQ(kExpectedCost, total_flow_cost); +} + +// ----- Max Flow ----- + +void MaxFeasibleFlow() { + LOG(INFO) << "Max Feasible Flow"; + const int kNumNodes = 6; + const int kNumArcs = 9; + const NodeIndex kTail[kNumArcs] = {0, 0, 0, 0, 1, 2, 3, 3, 4}; + const NodeIndex kHead[kNumArcs] = {1, 2, 3, 4, 3, 4, 4, 5, 5}; + const FlowQuantity kCapacity[kNumArcs] = {5, 8, 5, 3, 4, 5, 6, 6, 4}; + const FlowQuantity kExpectedFlow[kNumArcs] = {1, 1, 5, 3, 1, 1, 0, 6, 4}; + const FlowQuantity kExpectedTotalFlow = 10; + StarGraph graph(kNumNodes, kNumArcs); + MaxFlow max_flow(&graph, 0, kNumNodes - 1); + for (int i = 0; i < kNumArcs; ++i) { + ArcIndex arc = graph.AddArc(kTail[i], kHead[i]); + max_flow.SetArcCapacity(arc, kCapacity[i]); + } + CHECK(max_flow.Solve()); + CHECK_EQ(MaxFlow::OPTIMAL, max_flow.status()); + FlowQuantity total_flow = max_flow.GetOptimalFlow(); + CHECK_EQ(total_flow, kExpectedTotalFlow); + for (int i = 0; i < kNumArcs; ++i) { + CHECK_EQ(kExpectedFlow[i], max_flow.Flow(i)) << " i = " << i; + } +} +} // namespace operations_research + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + operations_research::MinCostFlowOn4x4Matrix(); + operations_research::MaxFeasibleFlow(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/frequency_assignment_problem.cc b/libs/or-tools-bin/examples/cpp/frequency_assignment_problem.cc new file mode 100644 index 0000000000000000000000000000000000000000..016802ae7360ff0ac9fb4cbf5733a9200b483bfd --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/frequency_assignment_problem.cc @@ -0,0 +1,886 @@ +// 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. + +// +// Frequency Assignment Problem +// The Radio Link Frequency Assignment Problem consists in assigning frequencies +// to a set of radio links defined between pairs of sites in order to avoid +// interferences. Each radio link is represented by a variable whose domain is +// the set of all frequencies that are available for this link. +// The essential constraint involving two variables of the problem F1 and F2, +// which represent two frequencies in the spectrum, is +// |F1 - F2| > k12, where k12 is a predefined constant value. +// The Frequency Assignment Problem is an NP-complete problem as proved by means +// of reduction from k-Colorability problem for undirected graphs. +// The solution of the problem can be based on various criteria: +// - Simple satisfaction +// - Minimizing the number of distinct frequencies used +// - Minimizing the maximum frequency used, i.e minimizing the total width of +// the spectrum +// - Minimizing a weighted sum of violated constraints if the problem is +// inconsistent +// More on the Frequency Assignment Problem and the data format of its instances +// can be found at: http://www.inra.fr/mia/T/schiex/Doc/CELAR.shtml#synt +// +// Implementation +// Two solvers are implemented: The HardFapSolver finds the solution to +// feasible instances of the problem with objective either the minimization of +// the largest frequency assigned or the minimization of the number of +// frequencies used to the solution. +// The SoftFapSolver is optimizes the unfeasible instances. Some of the +// constraints of these instances may actually be soft constraints which may be +// violated at some predefined constant cost. The SoftFapSolver aims to minimize +// the total cost of violated constraints, i.e. to minimize the sum of all the +// violation costs. +// If the latter solver is forced to solve a feasible instance, the main +// function redirects to the former, afterwards. +// + +#include +#include +#include +#include + +#include "examples/cpp/fap_model_printer.h" +#include "examples/cpp/fap_parser.h" +#include "examples/cpp/fap_utilities.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/base/logging.h" +#include "ortools/base/map_util.h" +#include "ortools/constraint_solver/constraint_solver.h" + +DEFINE_string(directory, "", "Specifies the directory of the data."); +DEFINE_string(value_evaluator, "", + "Specifies if a value evaluator will be used by the " + "decision builder."); +DEFINE_string(variable_evaluator, "", + "Specifies if a variable evaluator will be used by the " + "decision builder."); +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, <= 0 means no limit."); +DEFINE_int32(choose_next_variable_strategy, 1, + "Selection strategy for variable: " + "1 = CHOOSE_FIRST_UNBOUND, " + "2 = CHOOSE_MIN_SIZE_LOWEST_MIN, " + "3 = CHOOSE_MIN_SIZE_HIGHEST_MAX, " + "4 = CHOOSE_RANDOM, "); +DEFINE_int32(restart, -1, "Parameter for constant restart monitor."); +DEFINE_bool(find_components, false, + "If possible, split the problem into independent sub-problems."); +DEFINE_bool(luby, false, + "Use luby restart monitor instead of constant restart monitor."); +DEFINE_bool(log_search, true, "Create a search log."); +DEFINE_bool(soft, false, "Use soft solver instead of hard solver."); +DEFINE_bool(display_time, true, + "Print how much time the solving process took."); +DEFINE_bool(display_results, true, "Print the results of the solving process."); + +namespace operations_research { + +// Decision on the relative order that the two variables of a constraint +// will have. It takes as parameters the components of the constraint. +class OrderingDecision : public Decision { + public: + OrderingDecision(IntVar* const variable1, IntVar* const variable2, int value, + std::string operation) + : variable1_(variable1), + variable2_(variable2), + value_(value), + operator_(std::move(operation)) {} + ~OrderingDecision() override {} + + // Apply will be called first when the decision is executed. + void Apply(Solver* const s) override { + // variable1 < variable2 + MakeDecision(s, variable1_, variable2_); + } + + // Refute will be called after a backtrack. + void Refute(Solver* const s) override { + // variable1 > variable2 + MakeDecision(s, variable2_, variable1_); + } + + private: + void MakeDecision(Solver* s, IntVar* variable1, IntVar* variable2) { + if (operator_ == ">") { + IntExpr* difference = (s->MakeDifference(variable2, variable1)); + s->AddConstraint(s->MakeGreater(difference, value_)); + } else if (operator_ == "=") { + IntExpr* difference = (s->MakeDifference(variable2, variable1)); + s->AddConstraint(s->MakeEquality(difference, value_)); + } else { + LOG(FATAL) << "No right operator specified."; + } + } + + IntVar* const variable1_; + IntVar* const variable2_; + const int value_; + const std::string operator_; + + DISALLOW_COPY_AND_ASSIGN(OrderingDecision); +}; + +// Decision on whether a soft constraint will be added to a model +// or if it will be violated. +class ConstraintDecision : public Decision { + public: + explicit ConstraintDecision(IntVar* const constraint_violation) + : constraint_violation_(constraint_violation) {} + + ~ConstraintDecision() override {} + + // Apply will be called first when the decision is executed. + void Apply(Solver* const s) override { + // The constraint with which the builder is dealing, will be satisfied. + constraint_violation_->SetValue(0); + } + + // Refute will be called after a backtrack. + void Refute(Solver* const s) override { + // The constraint with which the builder is dealing, will not be satisfied. + constraint_violation_->SetValue(1); + } + + private: + IntVar* const constraint_violation_; + + DISALLOW_COPY_AND_ASSIGN(ConstraintDecision); +}; + +// The ordering builder resolves the relative order of the two variables +// included in each of the constraints of the problem. In that way the +// solving becomes much more efficient since we are branching on the +// disjunction implied by the absolute value expression. +class OrderingBuilder : public DecisionBuilder { + public: + enum Order { LESS = -1, EQUAL = 0, GREATER = 1 }; + + OrderingBuilder(const std::map& data_variables, + const std::vector& data_constraints, + const std::vector& variables, + const std::vector& violated_constraints, + const std::map& index_from_key) + : data_variables_(data_variables), + data_constraints_(data_constraints), + variables_(variables), + violated_constraints_(violated_constraints), + index_from_key_(index_from_key), + size_(data_constraints.size()), + iter_(0), + checked_iter_(0) { + for (const auto& it : data_variables_) { + int first_element = (it.second.domain)[0]; + minimum_value_available_.push_back(first_element); + variable_state_.push_back(EQUAL); + } + CHECK_EQ(minimum_value_available_.size(), variables_.size()); + CHECK_EQ(variable_state_.size(), variables_.size()); + } + + ~OrderingBuilder() override {} + + Decision* Next(Solver* const s) override { + if (iter_ < size_) { + FapConstraint constraint = data_constraints_[iter_]; + const int index1 = gtl::FindOrDie(index_from_key_, constraint.variable1); + const int index2 = gtl::FindOrDie(index_from_key_, constraint.variable2); + IntVar* variable1 = variables_[index1]; + IntVar* variable2 = variables_[index2]; + + // checked_iter is equal to 0 means that whether the constraint is to be + // added or dropped hasn't been checked. + // If it is equal to 1, this has already been checked and the ordering + // of the constraint is to be done. + if (!checked_iter_ && !constraint.hard) { + // New Soft Constraint: Check if it will be added or dropped. + ConstraintDecision* constraint_decision = + new ConstraintDecision(violated_constraints_[iter_]); + + s->SaveAndAdd(&checked_iter_, 1); + return s->RevAlloc(constraint_decision); + } + + // The constraint is either hard or soft and checked already. + if (violated_constraints_[iter_]->Bound() && + violated_constraints_[iter_]->Value() == 0) { + // If the constraint is added, do the ordering of its variables. + OrderingDecision* ordering_decision; + Order hint = Hint(constraint); + if (hint == LESS || hint == EQUAL) { + ordering_decision = new OrderingDecision( + variable1, variable2, constraint.value, constraint.operation); + } else { + ordering_decision = new OrderingDecision( + variable2, variable1, constraint.value, constraint.operation); + } + // Proceed to the next constraint. + s->SaveAndAdd(&iter_, 1); + // Assign checked_iter_ back to 0 to flag a new unchecked constraint. + s->SaveAndSetValue(&checked_iter_, 0); + return s->RevAlloc(ordering_decision); + } else { + // The constraint was dropped. + return nullptr; + } + } else { + // All the constraints were processed. No decision to take. + return nullptr; + } + } + + private: + Order Variable1LessVariable2(const int variable1, const int variable2, + const int value) { + minimum_value_available_[variable2] = + std::max(minimum_value_available_[variable2], + minimum_value_available_[variable1] + value); + return LESS; + } + + Order Variable1GreaterVariable2(const int variable1, const int variable2, + const int value) { + minimum_value_available_[variable1] = + std::max(minimum_value_available_[variable1], + minimum_value_available_[variable2] + value); + return GREATER; + } + // The Hint() function takes as parameter a constraint of the model and + // returns the most probable relative order that the two variables + // involved in the constraint should have. + // The function reaches such a decision, by taking into consideration if + // variable1 or variable2 or both have been denoted as less (state = -1) + // or greater (state = 1) than another variable in a previous constraint + // and tries to maintain the same state in the current constraint too. + // If both variables have the same state, the variable whose minimum value is + // the smallest is set to be lower than the other one. + // If none of the above are applicable variable1 is set to be lower than + // variable2. This ordering is more efficient if used with the + // Solver::ASSIGN_MIN_VALUE value selection strategy. + // It returns 1 if variable1 > variable2 or -1 if variable1 < variable2. + Order Hint(const FapConstraint& constraint) { + const int id1 = constraint.variable1; + const int id2 = constraint.variable2; + const int variable1 = gtl::FindOrDie(index_from_key_, id1); + const int variable2 = gtl::FindOrDie(index_from_key_, id2); + const int value = constraint.value; + CHECK_LT(variable1, variable_state_.size()); + CHECK_LT(variable2, variable_state_.size()); + CHECK_LT(variable1, minimum_value_available_.size()); + CHECK_LT(variable2, minimum_value_available_.size()); + + if (variable_state_[variable1] > variable_state_[variable2]) { + variable_state_[variable1] = GREATER; + variable_state_[variable2] = LESS; + return Variable1GreaterVariable2(variable1, variable2, value); + } else if (variable_state_[variable1] < variable_state_[variable2]) { + variable_state_[variable1] = LESS; + variable_state_[variable2] = GREATER; + return Variable1LessVariable2(variable1, variable2, value); + } else { + if (variable_state_[variable1] == 0 && variable_state_[variable2] == 0) { + variable_state_[variable1] = LESS; + variable_state_[variable2] = GREATER; + return Variable1LessVariable2(variable1, variable2, value); + } else { + if (minimum_value_available_[variable1] > + minimum_value_available_[variable2]) { + return Variable1GreaterVariable2(variable1, variable2, value); + } else { + return Variable1LessVariable2(variable1, variable2, value); + } + } + } + } + + // Passed as arguments from the function that creates the Decision Builder. + const std::map data_variables_; + const std::vector data_constraints_; + const std::vector variables_; + const std::vector violated_constraints_; + const std::map index_from_key_; + // Used by Next() for monitoring decisions. + const int size_; + int iter_; + int checked_iter_; + // Used by Hint() for indicating the most probable ordering. + std::vector variable_state_; + std::vector minimum_value_available_; + + DISALLOW_COPY_AND_ASSIGN(OrderingBuilder); +}; + +// A comparator for sorting the constraints depending on their impact. +bool ConstraintImpactComparator(FapConstraint constraint1, + FapConstraint constraint2) { + if (constraint1.impact == constraint2.impact) { + return (constraint1.value > constraint2.value); + } + return (constraint1.impact > constraint2.impact); +} + +int64 ValueEvaluator( + 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. + int64 ranking = -1; + for (const auto& it : *value_evaluator_map) { + if ((it.first != variable_index) && (it.second.first == value)) { + ranking = -2; + break; + } + } + + // Update the history of assigned values and their rankings of each variable. + absl::flat_hash_map>::iterator it; + int64 new_value = value; + int64 new_ranking = ranking; + if ((it = value_evaluator_map->find(variable_index)) != + value_evaluator_map->end()) { + std::pair existing_value_ranking = it->second; + // Replace only if the current choice for this variable has smaller + // ranking or same ranking but smaller value of the existing choice. + if (!(existing_value_ranking.second > ranking || + (existing_value_ranking.second == ranking && + existing_value_ranking.first > value))) { + new_value = existing_value_ranking.first; + new_ranking = existing_value_ranking.second; + } + } + std::pair new_value_ranking = + std::make_pair(new_value, new_ranking); + gtl::InsertOrUpdate(value_evaluator_map, variable_index, new_value_ranking); + + return new_ranking; +} + +// The variables which participate in more constraints and have the +// smaller domain should be in higher priority for assignment. +int64 VariableEvaluator(const std::vector& key_from_index, + const std::map& data_variables, + int64 variable_index) { + FapVariable variable = + gtl::FindOrDie(data_variables, key_from_index[variable_index]); + int64 result = -(variable.degree * 100 / variable.domain_size); + return result; +} + +// Creates the variables of the solver from the parsed data. +void CreateModelVariables(const std::map& data_variables, + Solver* solver, std::vector* model_variables, + std::map* index_from_key, + std::vector* key_from_index) { + CHECK(solver != nullptr); + CHECK(model_variables != nullptr); + CHECK(index_from_key != nullptr); + CHECK(key_from_index != nullptr); + + const int number_of_variables = static_cast(data_variables.size()); + model_variables->resize(number_of_variables); + key_from_index->resize(number_of_variables); + + int index = 0; + for (const auto& it : data_variables) { + CHECK_LT(index, model_variables->size()); + (*model_variables)[index] = solver->MakeIntVar(it.second.domain); + gtl::InsertOrUpdate(index_from_key, it.first, index); + (*key_from_index)[index] = it.first; + + if ((it.second.initial_position != -1) && (it.second.hard)) { + CHECK_LT(it.second.mobility_cost, 0); + solver->AddConstraint(solver->MakeEquality((*model_variables)[index], + it.second.initial_position)); + } + index++; + } +} + +// Creates the constraints of the instance from the parsed data. +void CreateModelConstraints(const std::vector& data_constraints, + const std::vector& variables, + const std::map& index_from_key, + Solver* solver) { + CHECK(solver != nullptr); + + for (const FapConstraint& ct : data_constraints) { + const int index1 = gtl::FindOrDie(index_from_key, ct.variable1); + const int index2 = gtl::FindOrDie(index_from_key, ct.variable2); + CHECK_LT(index1, variables.size()); + CHECK_LT(index2, variables.size()); + IntVar* var1 = variables[index1]; + IntVar* var2 = variables[index2]; + IntVar* absolute_difference = + solver->MakeAbs(solver->MakeDifference(var1, var2))->Var(); + if (ct.operation == ">") { + solver->AddConstraint(solver->MakeGreater(absolute_difference, ct.value)); + } else if (ct.operation == "=") { + solver->AddConstraint( + solver->MakeEquality(absolute_difference, ct.value)); + } else { + LOG(FATAL) << "Invalid operator detected."; + return; + } + } +} + +// According to the value of a command line flag, chooses the strategy which +// determines the selection of the variable to be assigned next. +void ChooseVariableStrategy(Solver::IntVarStrategy* variable_strategy) { + CHECK(variable_strategy != nullptr); + + switch (FLAGS_choose_next_variable_strategy) { + case 1: { + *variable_strategy = Solver::CHOOSE_FIRST_UNBOUND; + LOG(INFO) << "Using Solver::CHOOSE_FIRST_UNBOUND " + "for variable selection strategy."; + break; + } + case 2: { + *variable_strategy = Solver::CHOOSE_MIN_SIZE_LOWEST_MIN; + LOG(INFO) << "Using Solver::CHOOSE_MIN_SIZE_LOWEST_MIN " + "for variable selection strategy."; + break; + } + case 3: { + *variable_strategy = Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX; + LOG(INFO) << "Using Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX " + "for variable selection strategy."; + break; + } + case 4: { + *variable_strategy = Solver::CHOOSE_RANDOM; + LOG(INFO) << "Using Solver::CHOOSE_RANDOM " + "for variable selection strategy."; + break; + } + default: { + LOG(FATAL) << "Should not be here"; + return; + } + } +} + +// According to the values of some command line flags, adds some monitors +// for the search of the Solver. +void CreateAdditionalMonitors(OptimizeVar* const objective, Solver* solver, + std::vector* monitors) { + CHECK(solver != nullptr); + CHECK(monitors != nullptr); + + // Search Log + if (FLAGS_log_search) { + SearchMonitor* const log = solver->MakeSearchLog(100000, objective); + monitors->push_back(log); + } + + // Time Limit + if (FLAGS_time_limit_in_ms != 0) { + LOG(INFO) << "Adding time limit of " << FLAGS_time_limit_in_ms << " ms."; + SearchLimit* const limit = solver->MakeLimit( + FLAGS_time_limit_in_ms, kint64max, kint64max, kint64max); + monitors->push_back(limit); + } + + // Search Restart + SearchMonitor* const restart = + FLAGS_restart != -1 + ? (FLAGS_luby ? solver->MakeLubyRestart(FLAGS_restart) + : solver->MakeConstantRestart(FLAGS_restart)) + : nullptr; + if (restart) { + monitors->push_back(restart); + } +} + +// The Hard Solver is dealing with finding the solution to feasible +// instances of the problem with objective either the minimization of +// the largest frequency assigned or the minimization of the number +// of frequencies used to the solution. +void HardFapSolver(const std::map& data_variables, + const std::vector& data_constraints, + const std::string& data_objective, + const std::vector& values) { + Solver solver("HardFapSolver"); + std::vector monitors; + + // Create Model Variables. + std::vector variables; + std::map index_from_key; + std::vector key_from_index; + CreateModelVariables(data_variables, &solver, &variables, &index_from_key, + &key_from_index); + + // Create Model Constraints. + CreateModelConstraints(data_constraints, variables, index_from_key, &solver); + + // Order the constraints according to their impact in the instance. + std::vector ordered_constraints(data_constraints); + std::sort(ordered_constraints.begin(), ordered_constraints.end(), + ConstraintImpactComparator); + + std::vector violated_constraints; + solver.MakeIntVarArray(ordered_constraints.size(), 0, 0, + &violated_constraints); + + // Objective: + // Either minimize the largest assigned frequency or + // minimize the number of different frequencies assigned. + IntVar* objective_var; + OptimizeVar* objective; + if (data_objective == "Minimize the largest assigned value.") { + LOG(INFO) << "Minimize the largest assigned value."; + // The objective_var is set to hold the maximum value assigned + // in the variables vector. + objective_var = solver.MakeMax(variables)->Var(); + objective = solver.MakeMinimize(objective_var, 1); + } else if (data_objective == "Minimize the number of assigned values.") { + LOG(INFO) << "Minimize the number of assigned values."; + + std::vector cardinality; + solver.MakeIntVarArray(static_cast(values.size()), 0, + static_cast(variables.size()), &cardinality); + solver.AddConstraint(solver.MakeDistribute(variables, values, cardinality)); + std::vector value_not_assigned; + for (int val = 0; val < values.size(); ++val) { + value_not_assigned.push_back( + solver.MakeIsEqualCstVar(cardinality[val], 0)); + } + CHECK(!value_not_assigned.empty()); + // The objective_var is set to maximize the number of values + // that have not been assigned to a variable. + objective_var = solver.MakeSum(value_not_assigned)->Var(); + objective = solver.MakeMaximize(objective_var, 1); + } else { + LOG(FATAL) << "No right objective specified."; + return; + } + monitors.push_back(objective); + + // Ordering Builder + OrderingBuilder* ob = solver.RevAlloc( + new OrderingBuilder(data_variables, ordered_constraints, variables, + violated_constraints, index_from_key)); + + // Decision Builder Configuration + // Choose the next variable selection strategy. + Solver::IntVarStrategy variable_strategy; + ChooseVariableStrategy(&variable_strategy); + // Choose the value selection strategy. + DecisionBuilder* db; + 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, + int64 value) { + return ValueEvaluator(&history, var, value); + }; + LOG(INFO) << "Using ValueEvaluator for value selection strategy."; + db = solver.MakePhase(variables, variable_strategy, index_evaluator2); + } else { + LOG(INFO) << "Using Solver::ASSIGN_MIN_VALUE for value selection strategy."; + db = solver.MakePhase(variables, variable_strategy, + Solver::ASSIGN_MIN_VALUE); + } + + DecisionBuilder* final_db = solver.Compose(ob, db); + + // Create Additional Monitors. + CreateAdditionalMonitors(objective, &solver, &monitors); + + // Collector + SolutionCollector* const collector = solver.MakeLastSolutionCollector(); + collector->Add(variables); + collector->Add(objective_var); + monitors.push_back(collector); + + // Solve. + LOG(INFO) << "Solving..."; + const int64 time1 = solver.wall_time(); + solver.Solve(final_db, monitors); + const int64 time2 = solver.wall_time(); + + // Display Time. + if (FLAGS_display_time) { + PrintElapsedTime(time1, time2); + } + // Display Results. + if (FLAGS_display_results) { + PrintResultsHard(collector, variables, objective_var, data_variables, + data_constraints, index_from_key, key_from_index); + } +} + +// Splits variables of the instance to hard and soft. +void SplitVariablesHardSoft(const std::map& data_variables, + std::map* hard_variables, + std::map* soft_variables) { + for (const auto& it : data_variables) { + if (it.second.initial_position != -1) { + if (it.second.hard) { + CHECK_LT(it.second.mobility_cost, 0); + gtl::InsertOrUpdate(hard_variables, it.first, it.second); + } else { + CHECK_GE(it.second.mobility_cost, 0); + gtl::InsertOrUpdate(soft_variables, it.first, it.second); + } + } + } +} + +// Splits constraints of the instance to hard and soft. +void SplitConstraintHardSoft(const std::vector& data_constraints, + std::vector* hard_constraints, + std::vector* soft_constraints) { + for (const FapConstraint& ct : data_constraints) { + if (ct.hard) { + CHECK_LT(ct.weight_cost, 0); + hard_constraints->push_back(ct); + } else { + CHECK_GE(ct.weight_cost, 0); + soft_constraints->push_back(ct); + } + } +} + +// Penalize the modification of the initial position of soft variable of +// the instance. +void PenalizeVariablesViolation( + const std::map& soft_variables, + const std::map& index_from_key, + const std::vector& variables, std::vector* cost, + Solver* solver) { + for (const auto& it : soft_variables) { + const int index = gtl::FindOrDie(index_from_key, it.first); + CHECK_LT(index, variables.size()); + IntVar* const displaced = solver->MakeIsDifferentCstVar( + variables[index], it.second.initial_position); + IntVar* const weight = + solver->MakeProd(displaced, it.second.mobility_cost)->Var(); + cost->push_back(weight); + } +} + +// Penalize the violation of soft constraints of the instance. +void PenalizeConstraintsViolation( + const std::vector& constraints, + const std::vector& soft_constraints, + const std::map& index_from_key, + const std::vector& variables, std::vector* cost, + std::vector* violated_constraints, Solver* solver) { + int violated_constraints_index = 0; + for (const FapConstraint& ct : constraints) { + CHECK_LT(violated_constraints_index, violated_constraints->size()); + if (!ct.hard) { + // The violated_constraints_index will stop at the first soft constraint. + break; + } + IntVar* const hard_violation = solver->MakeIntVar(0, 0); + (*violated_constraints)[violated_constraints_index] = hard_violation; + violated_constraints_index++; + } + + for (const FapConstraint& ct : soft_constraints) { + const int index1 = gtl::FindOrDie(index_from_key, ct.variable1); + const int index2 = gtl::FindOrDie(index_from_key, ct.variable2); + CHECK_LT(index1, variables.size()); + CHECK_LT(index2, variables.size()); + IntVar* const absolute_difference = + solver + ->MakeAbs( + solver->MakeDifference(variables[index1], variables[index2])) + ->Var(); + IntVar* violation = nullptr; + if (ct.operation == ">") { + violation = solver->MakeIsLessCstVar(absolute_difference, ct.value); + } else if (ct.operation == "=") { + violation = solver->MakeIsDifferentCstVar(absolute_difference, ct.value); + } else { + LOG(FATAL) << "Invalid operator detected."; + } + IntVar* const weight = solver->MakeProd(violation, ct.weight_cost)->Var(); + cost->push_back(weight); + CHECK_LT(violated_constraints_index, violated_constraints->size()); + (*violated_constraints)[violated_constraints_index] = violation; + violated_constraints_index++; + } + CHECK_EQ(violated_constraints->size(), constraints.size()); +} + +// The Soft Solver is dealing with the optimization of unfeasible instances +// and aims to minimize the total cost of violated constraints. Returning value +// equal to 0 denotes that the instance is feasible. +int SoftFapSolver(const std::map& data_variables, + const std::vector& data_constraints, + const std::string& data_objective, + const std::vector& values) { + Solver solver("SoftFapSolver"); + std::vector monitors; + + // Split variables to hard and soft. + std::map hard_variables; + std::map soft_variables; + SplitVariablesHardSoft(data_variables, &hard_variables, &soft_variables); + + // Order instance's constraints by their impact and then split them to + // hard and soft. + std::vector ordered_constraints(data_constraints); + std::sort(ordered_constraints.begin(), ordered_constraints.end(), + ConstraintImpactComparator); + std::vector hard_constraints; + std::vector soft_constraints; + SplitConstraintHardSoft(ordered_constraints, &hard_constraints, + &soft_constraints); + + // Create Model Variables. + std::vector variables; + std::map index_from_key; + std::vector key_from_index; + CreateModelVariables(data_variables, &solver, &variables, &index_from_key, + &key_from_index); + + // Create Model Constraints. + CreateModelConstraints(hard_constraints, variables, index_from_key, &solver); + + // Penalize variable and constraint violations. + std::vector cost; + std::vector violated_constraints(ordered_constraints.size(), + nullptr); + PenalizeVariablesViolation(soft_variables, index_from_key, variables, &cost, + &solver); + PenalizeConstraintsViolation(ordered_constraints, soft_constraints, + index_from_key, variables, &cost, + &violated_constraints, &solver); + + // Objective + // Minimize the sum of violation penalties. + IntVar* objective_var = solver.MakeSum(cost)->Var(); + OptimizeVar* objective = solver.MakeMinimize(objective_var, 1); + monitors.push_back(objective); + + // Ordering Builder + OrderingBuilder* ob = solver.RevAlloc( + new OrderingBuilder(data_variables, ordered_constraints, variables, + violated_constraints, index_from_key)); + + // Decision Builder Configuration + // Choose the next variable selection strategy. + DecisionBuilder* db; + if (FLAGS_variable_evaluator == "variable_evaluator") { + LOG(INFO) << "Using VariableEvaluator for variable selection strategy and " + "Solver::ASSIGN_MIN_VALUE for value selection strategy."; + Solver::IndexEvaluator1 var_evaluator = [&key_from_index, + &data_variables](int64 index) { + return VariableEvaluator(key_from_index, data_variables, index); + }; + db = solver.MakePhase(variables, var_evaluator, Solver::ASSIGN_MIN_VALUE); + } else { + LOG(INFO) << "Using Solver::CHOOSE_FIRST_UNBOUND for variable selection " + "strategy and Solver::ASSIGN_MIN_VALUE for value selection " + "strategy."; + db = solver.MakePhase(variables, Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); + } + DecisionBuilder* final_db = solver.Compose(ob, db); + + // Create Additional Monitors. + CreateAdditionalMonitors(objective, &solver, &monitors); + + // Collector + SolutionCollector* const collector = solver.MakeLastSolutionCollector(); + collector->Add(variables); + collector->Add(objective_var); + monitors.push_back(collector); + + // Solve. + LOG(INFO) << "Solving..."; + const int64 time1 = solver.wall_time(); + solver.Solve(final_db, monitors); + const int64 time2 = solver.wall_time(); + + int violation_sum = + collector->Value(collector->solution_count() - 1, objective_var); + // Display Time. + if (FLAGS_display_time) { + PrintElapsedTime(time1, time2); + } + // Display Results. + if (FLAGS_display_results) { + PrintResultsSoft(collector, variables, objective_var, hard_variables, + hard_constraints, soft_variables, soft_constraints, + index_from_key, key_from_index); + } + + return violation_sum; +} + +void SolveProblem(const std::map& variables, + const std::vector& constraints, + const std::string& objective, const std::vector& values, + bool soft) { + // Print Instance! + FapModelPrinter model_printer(variables, constraints, objective, values); + model_printer.PrintFapObjective(); + model_printer.PrintFapVariables(); + model_printer.PrintFapConstraints(); + model_printer.PrintFapValues(); + // Create Model & Solve! + if (!soft) { + LOG(INFO) << "Running HardFapSolver"; + HardFapSolver(variables, constraints, objective, values); + } else { + LOG(INFO) << "Running SoftFapSolver"; + int violation = SoftFapSolver(variables, constraints, objective, values); + if (violation == 0) { + LOG(INFO) << "The instance is feasible. " + "Now the HardFapSolver will be executed."; + LOG(INFO) << "Running HardFapSolver"; + HardFapSolver(variables, constraints, objective, values); + } + } +} + +} // namespace operations_research + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + + CHECK(!FLAGS_directory.empty()) << "Requires --directory="; + + LOG(INFO) << "Solving instance in directory " << FLAGS_directory; + // Parse! + std::map variables; + std::vector constraints; + std::string objective; + std::vector values; + absl::flat_hash_map components; + operations_research::ParseInstance(FLAGS_directory, FLAGS_find_components, + &variables, &constraints, &objective, + &values, &components); + if (!FLAGS_find_components) { + operations_research::SolveProblem(variables, constraints, objective, values, + FLAGS_soft); + } else { + int component_id = 1; + LOG(INFO) << "Number of components in the RLFAP graph " + << components.size(); + for (const auto& component : components) { + LOG(INFO) << "Solving Component " << component_id; + operations_research::SolveProblem(component.second.variables, + component.second.constraints, objective, + values, FLAGS_soft); + component_id++; + } + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/golomb_sat.cc b/libs/or-tools-bin/examples/cpp/golomb_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..d8b2a2bd42867c7ff2a47255d4f6e2a51e532d6c --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/golomb_sat.cc @@ -0,0 +1,128 @@ +// 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. + +// +// Golomb ruler problem +// +// find minimal ruler so that the differences between ticks are unique. +// +// First solutions: +// 0, 1 +// 0, 1, 3 +// 0, 1, 4, 6 +// 0, 1, 4, 9, 11 +// 0, 1, 4, 10, 12, 17 +// 0, 1, 4, 10, 18, 23, 25 + +#include + +#include "google/protobuf/text_format.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/base/integral_types.h" +#include "ortools/base/logging.h" +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" + +DEFINE_bool(print, false, "If true, print the minimal solution."); +DEFINE_int32( + size, 0, + "Size of the problem. If equal to 0, will test several increasing sizes."); +DEFINE_string(params, "", "Sat parameters."); + +static const int kBestSolutions[] = {0, 1, 3, 6, 11, 17, 25, 34, 44, 55, 72, 85, + // just for the optimistics ones, the rest: + 106, 127, 151, 177, 199, 216, 246}; + +static const int kKnownSolutions = 19; + +namespace operations_research { +namespace sat { + +void GolombRuler(int size) { + CHECK_GE(size, 1); + CpModelBuilder cp_model; + + std::vector ticks(size); + ticks[0] = cp_model.NewConstant(0); + const int64 max = size * size; + Domain domain(1, max); + for (int i = 1; i < size; ++i) { + ticks[i] = cp_model.NewIntVar(domain); + } + std::vector diffs; + for (int i = 0; i < size; ++i) { + for (int j = i + 1; j < size; ++j) { + const IntVar diff = cp_model.NewIntVar(domain); + cp_model.AddEquality(LinearExpr::Sum({diff, ticks[i]}), ticks[j]); + diffs.push_back(diff); + } + } + + cp_model.AddAllDifferent(diffs); + + // Symmetry breaking. + if (size > 2) { + cp_model.AddLessThan(diffs.front(), diffs.back()); + } + + // Objective. + cp_model.Minimize(ticks.back()); + + // Search strategy. + cp_model.AddDecisionStrategy(ticks, DecisionStrategyProto::CHOOSE_FIRST, + DecisionStrategyProto::SELECT_MIN_VALUE); + + Model model; + SatParameters parameters; + parameters.set_search_branching(SatParameters::FIXED_SEARCH); + // Parse the --params flag. + if (!FLAGS_params.empty()) { + CHECK(google::protobuf::TextFormat::MergeFromString(FLAGS_params, + ¶meters)) + << FLAGS_params; + } + model.Add(NewSatParameters(parameters)); + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + + if (response.status() == CpSolverStatus::OPTIMAL) { + const int64 result = SolutionIntegerValue(response, ticks.back()); + const int64 num_failures = response.num_conflicts(); + printf("N = %d, optimal length = %lld (conflicts:%lld, time=%f s)\n", size, result, + num_failures, response.wall_time()); + if (size - 1 < kKnownSolutions) { + CHECK_EQ(result, kBestSolutions[size - 1]); + } + if (FLAGS_print) { + for (int i = 0; i < size; ++i) { + const int64 tick = SolutionIntegerValue(response, ticks[i]); + printf("%d ", static_cast(tick)); + } + printf("\n"); + } + } +} + +} // namespace sat +} // namespace operations_research + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_size != 0) { + operations_research::sat::GolombRuler(FLAGS_size); + } else { + for (int n = 1; n < 11; ++n) { + operations_research::sat::GolombRuler(n); + } + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/integer_programming.cc b/libs/or-tools-bin/examples/cpp/integer_programming.cc new file mode 100644 index 0000000000000000000000000000000000000000..406a3cfb9b4103918512c34f11bdd6c4e93f1875 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/integer_programming.cc @@ -0,0 +1,96 @@ +// 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. + +// Integer programming example that shows how to use the API. + +#include "ortools/base/logging.h" +#include "ortools/linear_solver/linear_solver.h" + +namespace operations_research { +void RunIntegerProgrammingExample( + MPSolver::OptimizationProblemType optimization_problem_type) { + MPSolver solver("IntegerProgrammingExample", optimization_problem_type); + const double infinity = solver.infinity(); + // x and y are integer non-negative variables. + MPVariable* const x = solver.MakeIntVar(0.0, infinity, "x"); + MPVariable* const y = solver.MakeIntVar(0.0, infinity, "y"); + + // Maximize x + 10 * y. + MPObjective* const objective = solver.MutableObjective(); + objective->SetCoefficient(x, 1); + objective->SetCoefficient(y, 10); + objective->SetMaximization(); + + // x + 7 * y <= 17.5. + MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 17.5); + c0->SetCoefficient(x, 1); + c0->SetCoefficient(y, 7); + + // x <= 3.5 + MPConstraint* const c1 = solver.MakeRowConstraint(-infinity, 3.5); + c1->SetCoefficient(x, 1); + c1->SetCoefficient(y, 0); + + LOG(INFO) << "Number of variables = " << solver.NumVariables(); + LOG(INFO) << "Number of constraints = " << solver.NumConstraints(); + + solver.SetNumThreads(8); + solver.EnableOutput(); + + 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) << "Problem solved in " << solver.nodes() + << " branch-and-bound nodes"; +} + +void RunAllExamples() { +#if defined(USE_CBC) + LOG(INFO) << "---- Integer programming example with CBC ----"; + RunIntegerProgrammingExample(MPSolver::CBC_MIXED_INTEGER_PROGRAMMING); +#endif +#if defined(USE_GLPK) + LOG(INFO) << "---- Integer programming example with GLPK ----"; + RunIntegerProgrammingExample(MPSolver::GLPK_MIXED_INTEGER_PROGRAMMING); +#endif +#if defined(USE_SCIP) + LOG(INFO) << "---- Integer programming example with SCIP ----"; + RunIntegerProgrammingExample(MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING); +#endif +#if defined(USE_GUROBI) + LOG(INFO) << "---- Integer programming example with Gurobi ----"; + RunIntegerProgrammingExample(MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING); +#endif // USE_GUROBI +#if defined(USE_CPLEX) + LOG(INFO) << "---- Integer programming example with CPLEX ----"; + RunIntegerProgrammingExample(MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING); +#endif // USE_CPLEX +} +} // namespace operations_research + +int main(int argc, char** argv) { + google::InitGoogleLogging(argv[0]); + FLAGS_logtostderr = 1; + operations_research::RunAllExamples(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/integer_programming_example.cc b/libs/or-tools-bin/examples/cpp/integer_programming_example.cc similarity index 100% rename from libs/or-tools/examples/cpp/integer_programming_example.cc rename to libs/or-tools-bin/examples/cpp/integer_programming_example.cc diff --git a/libs/or-tools/examples/cpp/interval_sample_sat.cc b/libs/or-tools-bin/examples/cpp/interval_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/interval_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/interval_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/jobshop_sat.cc b/libs/or-tools-bin/examples/cpp/jobshop_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..9401587222475e1ccdcfb79540708ee088e41589 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/jobshop_sat.cc @@ -0,0 +1,393 @@ +// 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 "absl/strings/match.h" +#include "google/protobuf/text_format.h" +#include "google/protobuf/wrappers.pb.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/base/logging.h" +#include "ortools/base/timer.h" +#include "ortools/data/jobshop_scheduling.pb.h" +#include "ortools/data/jobshop_scheduling_parser.h" +#include "ortools/sat/cp_model.h" +#include "ortools/sat/cp_model.pb.h" +#include "ortools/sat/model.h" + +DEFINE_string(input, "", "Jobshop data file name."); +DEFINE_string(params, "", "Sat parameters in text proto format."); +DEFINE_bool(use_optional_variables, true, + "Whether we use optional variables for bounds of an optional " + "interval or not."); +DEFINE_bool(display_model, false, "Display jobshop proto before solving."); +DEFINE_bool(display_sat_model, false, "Display sat proto before solving."); + +using operations_research::data::jssp::Job; +using operations_research::data::jssp::JobPrecedence; +using operations_research::data::jssp::JsspInputProblem; +using operations_research::data::jssp::Machine; +using operations_research::data::jssp::Task; +using operations_research::data::jssp::TransitionTimeMatrix; + +namespace operations_research { +namespace sat { + +// Compute a valid horizon from a problem. +int64 ComputeHorizon(const JsspInputProblem& problem) { + int64 sum_of_durations = 0; + int64 max_latest_end = 0; + int64 max_earliest_start = 0; + for (const Job& job : problem.jobs()) { + if (job.has_latest_end()) { + max_latest_end = std::max(max_latest_end, job.latest_end().value()); + } else { + max_latest_end = kint64max; + } + if (job.has_earliest_start()) { + max_earliest_start = + std::max(max_earliest_start, job.earliest_start().value()); + } + for (const Task& task : job.tasks()) { + int64 max_duration = 0; + for (int64 d : task.duration()) { + max_duration = std::max(max_duration, d); + } + sum_of_durations += max_duration; + } + } + + const int num_jobs = problem.jobs_size(); + int64 sum_of_transitions = 0; + for (const Machine& machine : problem.machines()) { + if (!machine.has_transition_time_matrix()) continue; + const TransitionTimeMatrix& matrix = machine.transition_time_matrix(); + for (int i = 0; i < num_jobs; ++i) { + int64 max_transition = 0; + for (int j = 0; j < num_jobs; ++j) { + max_transition = + std::max(max_transition, matrix.transition_time(i * num_jobs + j)); + } + sum_of_transitions += max_transition; + } + } + return std::min(max_latest_end, + sum_of_durations + sum_of_transitions + max_earliest_start); + // TODO(user): Uses transitions. +} + +// Solve a JobShop scheduling problem using SAT. +void Solve(const JsspInputProblem& problem) { + if (FLAGS_display_model) { + LOG(INFO) << problem.DebugString(); + } + + CpModelBuilder cp_model; + + const int num_jobs = problem.jobs_size(); + const int num_machines = problem.machines_size(); + const int64 horizon = ComputeHorizon(problem); + + std::vector starts; + std::vector ends; + + const Domain all_horizon(0, horizon); + + const IntVar makespan = cp_model.NewIntVar(all_horizon); + + std::vector> machine_to_intervals(num_machines); + std::vector> machine_to_jobs(num_machines); + std::vector> machine_to_starts(num_machines); + std::vector> machine_to_ends(num_machines); + std::vector> machine_to_presences(num_machines); + std::vector job_starts(num_jobs); + std::vector job_ends(num_jobs); + std::vector task_starts; + int64 objective_offset = 0; + std::vector objective_vars; + std::vector objective_coeffs; + + for (int j = 0; j < num_jobs; ++j) { + const Job& job = problem.jobs(j); + IntVar previous_end; + const int64 hard_start = + job.has_earliest_start() ? job.earliest_start().value() : 0L; + const int64 hard_end = + job.has_latest_end() ? job.latest_end().value() : horizon; + + for (int t = 0; t < job.tasks_size(); ++t) { + const Task& task = job.tasks(t); + const int num_alternatives = task.machine_size(); + CHECK_EQ(num_alternatives, task.duration_size()); + + // Add the "main" task interval. It will englobe all the alternative ones + // if there is many, or be a normal task otherwise. + int64 min_duration = task.duration(0); + int64 max_duration = task.duration(0); + for (int i = 1; i < num_alternatives; ++i) { + min_duration = std::min(min_duration, task.duration(i)); + max_duration = std::max(max_duration, task.duration(i)); + } + const IntVar start = cp_model.NewIntVar(Domain(hard_start, hard_end)); + const IntVar duration = + cp_model.NewIntVar(Domain(min_duration, max_duration)); + const IntVar end = cp_model.NewIntVar(Domain(hard_start, hard_end)); + const IntervalVar interval = + cp_model.NewIntervalVar(start, duration, end); + + // Store starts and ends of jobs for precedences. + if (t == 0) { + job_starts[j] = start; + } + if (t == job.tasks_size() - 1) { + job_ends[j] = end; + } + task_starts.push_back(start); + + // Chain the task belonging to the same job. + if (t > 0) { + cp_model.AddLessOrEqual(previous_end, start); + } + previous_end = end; + + if (num_alternatives == 1) { + const int m = task.machine(0); + machine_to_intervals[m].push_back(interval); + machine_to_jobs[m].push_back(j); + machine_to_starts[m].push_back(start); + machine_to_ends[m].push_back(end); + machine_to_presences[m].push_back(cp_model.TrueVar()); + if (task.cost_size() > 0) { + objective_offset += task.cost(0); + } + } else { + std::vector presences; + for (int a = 0; a < num_alternatives; ++a) { + const BoolVar presence = cp_model.NewBoolVar(); + const IntVar local_start = + FLAGS_use_optional_variables + ? cp_model.NewIntVar(Domain(hard_start, hard_end)) + : start; + const IntVar local_duration = cp_model.NewConstant(task.duration(a)); + const IntVar local_end = + FLAGS_use_optional_variables + ? cp_model.NewIntVar(Domain(hard_start, hard_end)) + : end; + const IntervalVar local_interval = cp_model.NewOptionalIntervalVar( + local_start, local_duration, local_end, presence); + + // Link local and global variables. + if (FLAGS_use_optional_variables) { + cp_model.AddEquality(start, local_start).OnlyEnforceIf(presence); + cp_model.AddEquality(end, local_end).OnlyEnforceIf(presence); + + // TODO(user): Experiment with the following implication. + cp_model.AddEquality(duration, local_duration) + .OnlyEnforceIf(presence); + } + + // Record relevant variables for later use. + const int m = task.machine(a); + machine_to_intervals[m].push_back(local_interval); + machine_to_jobs[m].push_back(j); + machine_to_starts[m].push_back(local_start); + machine_to_ends[m].push_back(local_end); + machine_to_presences[m].push_back(presence); + + // Add cost if present. + if (task.cost_size() > 0) { + objective_vars.push_back(presence); + objective_coeffs.push_back(task.cost(a)); + } + // Collect presence variables. + presences.push_back(presence); + } + // Exactly one alternative interval is present. + cp_model.AddEquality(LinearExpr::BooleanSum(presences), 1); + } + } + + // The makespan will be greater than the end of each job. + if (problem.makespan_cost_per_time_unit() != 0L) { + cp_model.AddLessOrEqual(previous_end, makespan); + } + + const int64 lateness_penalty = job.lateness_cost_per_time_unit(); + // Lateness cost. + if (lateness_penalty != 0L) { + const int64 due_date = job.late_due_date(); + if (due_date == 0) { + objective_vars.push_back(previous_end); + objective_coeffs.push_back(lateness_penalty); + } else { + const IntVar shifted_var = + cp_model.NewIntVar(Domain(-due_date, horizon - due_date)); + cp_model.AddEquality(shifted_var, + LinearExpr(previous_end).AddConstant(-due_date)); + const IntVar lateness_var = cp_model.NewIntVar(all_horizon); + cp_model.AddMaxEquality(lateness_var, + {cp_model.NewConstant(0), shifted_var}); + objective_vars.push_back(lateness_var); + objective_coeffs.push_back(lateness_penalty); + } + } + const int64 earliness_penalty = job.earliness_cost_per_time_unit(); + // Earliness cost. + if (earliness_penalty != 0L) { + const int64 due_date = job.early_due_date(); + if (due_date > 0) { + const IntVar shifted_var = + cp_model.NewIntVar(Domain(due_date - horizon, due_date)); + cp_model.AddEquality(LinearExpr::Sum({shifted_var, previous_end}), + due_date); + const IntVar earliness_var = cp_model.NewIntVar(all_horizon); + cp_model.AddMaxEquality(earliness_var, + {cp_model.NewConstant(0), shifted_var}); + objective_vars.push_back(earliness_var); + objective_coeffs.push_back(earliness_penalty); + } + } + } + + // Add one no_overlap constraint per machine. + for (int m = 0; m < num_machines; ++m) { + cp_model.AddNoOverlap(machine_to_intervals[m]); + + if (problem.machines(m).has_transition_time_matrix()) { + const TransitionTimeMatrix& transitions = + problem.machines(m).transition_time_matrix(); + const int num_intervals = machine_to_intervals[m].size(); + + // Create circuit constraint on a machine. + // Node 0 and num_intervals + 1 are source and sink. + CircuitConstraint circuit = cp_model.AddCircuitConstraint(); + for (int i = 0; i < num_intervals; ++i) { + const int job_i = machine_to_jobs[m][i]; + // Source to nodes. + circuit.AddArc(0, i + 1, cp_model.NewBoolVar()); + // Node to sink. + circuit.AddArc(i + 1, 0, cp_model.NewBoolVar()); + // Node to node. + for (int j = 0; j < num_intervals; ++j) { + if (i == j) { + circuit.AddArc(i + 1, i + 1, Not(machine_to_presences[m][i])); + } else { + const int job_j = machine_to_jobs[m][j]; + const int64 transition = + transitions.transition_time(job_i * num_jobs + job_j); + const BoolVar lit = cp_model.NewBoolVar(); + const IntVar start = machine_to_starts[m][j]; + const IntVar end = machine_to_ends[m][i]; + circuit.AddArc(i + 1, j + 1, lit); + // Push the new start with an extra transition. + cp_model + .AddLessOrEqual(LinearExpr(end).AddConstant(transition), start) + .OnlyEnforceIf(lit); + } + } + } + } + } + + // Add job precedences. + for (const JobPrecedence& precedence : problem.precedences()) { + const IntVar start = job_starts[precedence.second_job_index()]; + const IntVar end = job_ends[precedence.first_job_index()]; + cp_model.AddLessOrEqual(LinearExpr(end).AddConstant(precedence.min_delay()), + start); + } + + // Add objective. + if (problem.makespan_cost_per_time_unit() != 0L) { + objective_coeffs.push_back(problem.makespan_cost_per_time_unit()); + objective_vars.push_back(makespan); + } + cp_model.Minimize(LinearExpr::ScalProd(objective_vars, objective_coeffs) + .AddConstant(objective_offset)); + if (problem.has_scaling_factor()) { + cp_model.ScaleObjectiveBy(problem.scaling_factor().value()); + } + + // Decision strategy. + cp_model.AddDecisionStrategy(task_starts, + DecisionStrategyProto::CHOOSE_LOWEST_MIN, + DecisionStrategyProto::SELECT_MIN_VALUE); + + LOG(INFO) << "#machines:" << num_machines; + LOG(INFO) << "#jobs:" << num_jobs; + LOG(INFO) << "horizon:" << horizon; + + if (FLAGS_display_sat_model) { + LOG(INFO) << cp_model.Proto().DebugString(); + } + + LOG(INFO) << CpModelStats(cp_model.Proto()); + + Model model; + model.Add(NewSatParameters(FLAGS_params)); + + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + LOG(INFO) << CpSolverResponseStats(response); + + // Abort if we don't have any solution. + if (response.status() != CpSolverStatus::OPTIMAL && + response.status() != CpSolverStatus::FEASIBLE) + return; + + // Check cost, recompute it from scratch. + int64 final_cost = 0; + if (problem.makespan_cost_per_time_unit() != 0) { + int64 makespan = 0; + for (IntVar v : job_ends) { + makespan = std::max(makespan, SolutionIntegerValue(response, v)); + } + final_cost += makespan * problem.makespan_cost_per_time_unit(); + } + + for (int i = 0; i < job_ends.size(); ++i) { + const int64 early_due_date = problem.jobs(i).early_due_date(); + const int64 late_due_date = problem.jobs(i).late_due_date(); + const int64 early_penalty = problem.jobs(i).earliness_cost_per_time_unit(); + const int64 late_penalty = problem.jobs(i).lateness_cost_per_time_unit(); + const int64 end = SolutionIntegerValue(response, job_ends[i]); + if (end < early_due_date && early_penalty != 0) { + final_cost += (early_due_date - end) * early_penalty; + } + if (end > late_due_date && late_penalty != 0) { + final_cost += (end - late_due_date) * late_penalty; + } + } + + // TODO(user): Support alternative cost in check. + const double tolerance = 1e-6; + CHECK_GE(response.objective_value(), final_cost - tolerance); + CHECK_LE(response.objective_value(), final_cost + tolerance); +} + +} // namespace sat +} // namespace operations_research + +int main(int argc, char** argv) { + absl::SetFlag(&FLAGS_logtostderr, true); + gflags::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_input.empty()) { + LOG(FATAL) << "Please supply a data file with --input="; + } + + operations_research::data::jssp::JsspParser parser; + CHECK(parser.ParseFile(FLAGS_input)); + operations_research::sat::Solve(parser.problem()); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/knapsack.cc b/libs/or-tools-bin/examples/cpp/knapsack.cc new file mode 100644 index 0000000000000000000000000000000000000000..d3306a9be65a629ab1a65e4d260c5f8b8bdaa645 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/knapsack.cc @@ -0,0 +1,88 @@ +// 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. + +// [START program] +// [START import] +#include +#include +#include + +#include "ortools/algorithms/knapsack_solver.h" +// [END import] + +namespace operations_research { +void RunKnapsackExample() { + // Instantiate the solver. + // [START solver] + KnapsackSolver solver( + KnapsackSolver::KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, + "KnapsackExample"); + // [END solver] + + // [START data] + std::vector values = { + 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}; + + std::vector> 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}}; + + std::vector capacities = {850}; + // [END data] + + // [START solve] + solver.Init(values, weights, capacities); + int64 computed_value = solver.Solve(); + // [END solve] + + // Print solution + // [START print_solution] + std::vector packed_items; + for (std::size_t i = 0; i < values.size(); ++i) { + if (solver.BestSolutionContains(i)) packed_items.push_back(i); + } + std::ostringstream packed_items_ss; + std::copy(packed_items.begin(), packed_items.end() - 1, + std::ostream_iterator(packed_items_ss, ", ")); + packed_items_ss << packed_items.back(); + + std::vector packed_weights; + packed_weights.reserve(packed_items.size()); + for (const auto &it : packed_items) { + packed_weights.push_back(weights[0][it]); + } + std::ostringstream packed_weights_ss; + std::copy(packed_weights.begin(), packed_weights.end() - 1, + std::ostream_iterator(packed_weights_ss, ", ")); + packed_weights_ss << packed_weights.back(); + + int64 total_weights = + std::accumulate(packed_weights.begin(), packed_weights.end(), int64{0}); + + LOG(INFO) << "Total value: " << computed_value; + LOG(INFO) << "Packed items: {" << packed_items_ss.str() << "}"; + LOG(INFO) << "Total weight: " << total_weights; + LOG(INFO) << "Packed weights: {" << packed_weights_ss.str() << "}"; + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char **argv) { + operations_research::RunKnapsackExample(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-bin/examples/cpp/linear_assignment_api.cc b/libs/or-tools-bin/examples/cpp/linear_assignment_api.cc new file mode 100644 index 0000000000000000000000000000000000000000..4a7664e4673d0311f5988c0ecc42c91b9b7d9d25 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/linear_assignment_api.cc @@ -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. + +#include "ortools/base/commandlineflags.h" +#include "ortools/base/logging.h" +#include "ortools/graph/ebert_graph.h" +#include "ortools/graph/linear_assignment.h" + +namespace operations_research { + +// 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. +void AssignmentOn4x4Matrix() { + LOG(INFO) << "Assignment on 4x4 Matrix"; + const int kNumSources = 4; + const int kNumTargets = 4; + const CostValue kCost[kNumSources][kNumTargets] = {{90, 76, 75, 80}, + {35, 85, 55, 65}, + {125, 95, 90, 105}, + {45, 110, 95, 115}}; + const CostValue kExpectedCost = + kCost[0][3] + kCost[1][2] + kCost[2][1] + kCost[3][0]; + ForwardStarGraph graph(kNumSources + kNumTargets, kNumSources * kNumTargets); + LinearSumAssignment assignment(graph, kNumSources); + for (NodeIndex source = 0; source < kNumSources; ++source) { + for (NodeIndex target = 0; target < kNumTargets; ++target) { + ArcIndex arc = graph.AddArc(source, kNumSources + target); + assignment.SetArcCost(arc, kCost[source][target]); + } + } + CHECK(assignment.ComputeAssignment()); + CostValue total_cost = assignment.GetCost(); + CHECK_EQ(kExpectedCost, total_cost); +} + +void AnotherAssignment() { + LOG(INFO) << "Another assignment on 4x4 matrix"; + std::vector> matrice( + {{8, 7, 9, 9}, {5, 2, 7, 8}, {6, 1, 4, 9}, {2, 3, 2, 6}}); + const int kSize = matrice.size(); + ForwardStarGraph graph(2 * kSize, kSize * kSize); + LinearSumAssignment assignement(graph, kSize); + for (int i = 0; i < kSize; ++i) { + CHECK_EQ(kSize, matrice[i].size()); + for (int j = 0; j < kSize; ++j) { + int arcIndex = graph.AddArc(i, j + kSize); + assignement.SetArcCost(arcIndex, matrice[i][j]); + } + } + + assignement.ComputeAssignment(); + LOG(INFO) << "Cost : " << assignement.GetCost(); +} + +} // namespace operations_research + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + operations_research::AssignmentOn4x4Matrix(); + operations_research::AnotherAssignment(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/linear_programming.cc b/libs/or-tools-bin/examples/cpp/linear_programming.cc new file mode 100644 index 0000000000000000000000000000000000000000..e37d6f0aa159f7c152f52f9b8253aeb020d6157c --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/linear_programming.cc @@ -0,0 +1,83 @@ +// 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. + +// Linear programming example that shows how to use the API. + +#include "ortools/base/logging.h" +#include "ortools/linear_solver/linear_solver.h" +#include "ortools/linear_solver/linear_solver.pb.h" + +namespace operations_research { +void RunLinearProgrammingExample() { + MPSolver solver("LinearProgrammingExample", + MPSolver::GLOP_LINEAR_PROGRAMMING); + 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(); + + // 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); + + // 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(); + + 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()]; +} +} // namespace operations_research + +int main(int argc, char** argv) { + google::InitGoogleLogging(argv[0]); + FLAGS_logtostderr = 1; + operations_research::RunLinearProgrammingExample(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/linear_programming_example.cc b/libs/or-tools-bin/examples/cpp/linear_programming_example.cc similarity index 100% rename from libs/or-tools/examples/cpp/linear_programming_example.cc rename to libs/or-tools-bin/examples/cpp/linear_programming_example.cc diff --git a/libs/or-tools-bin/examples/cpp/linear_solver_protocol_buffers.cc b/libs/or-tools-bin/examples/cpp/linear_solver_protocol_buffers.cc new file mode 100644 index 0000000000000000000000000000000000000000..c9757bb6973a82c916e5b79e1f897906ad67e49c --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/linear_solver_protocol_buffers.cc @@ -0,0 +1,105 @@ +// 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 "ortools/base/commandlineflags.h" +#include "ortools/base/logging.h" +#include "ortools/linear_solver/linear_solver.h" +#include "ortools/linear_solver/linear_solver.pb.h" + +namespace operations_research { +void BuildLinearProgrammingMaxExample(MPSolver::OptimizationProblemType type) { + const double kObjCoef[] = {10.0, 6.0, 4.0}; + const std::string kVarName[] = {"x1", "x2", "x3"}; + const int numVars = 3; + const int kNumConstraints = 3; + const std::string kConstraintName[] = {"c1", "c2", "c3"}; + const double kConstraintCoef1[] = {1.0, 1.0, 1.0}; + const double kConstraintCoef2[] = {10.0, 4.0, 5.0}; + const double kConstraintCoef3[] = {2.0, 2.0, 6.0}; + const double* kConstraintCoef[] = {kConstraintCoef1, kConstraintCoef2, + kConstraintCoef3}; + const double kConstraintUb[] = {100.0, 600.0, 300.0}; + + const double infinity = MPSolver::infinity(); + MPModelProto model_proto; + model_proto.set_name("Max_Example"); + + // Create variables and objective function + for (int j = 0; j < numVars; ++j) { + MPVariableProto* x = model_proto.add_variable(); + x->set_name(kVarName[j]); // Could be skipped (optional). + x->set_lower_bound(0.0); + x->set_upper_bound(infinity); // Could be skipped (default value). + x->set_is_integer(false); // Could be skipped (default value). + x->set_objective_coefficient(kObjCoef[j]); + } + model_proto.set_maximize(true); + + // Create constraints + for (int i = 0; i < kNumConstraints; ++i) { + MPConstraintProto* constraint_proto = model_proto.add_constraint(); + constraint_proto->set_name(kConstraintName[i]); // Could be skipped. + constraint_proto->set_lower_bound(-infinity); // Could be skipped. + constraint_proto->set_upper_bound(kConstraintUb[i]); + for (int j = 0; j < numVars; ++j) { + // These two lines may be skipped when the coefficient is zero. + constraint_proto->add_var_index(j); + constraint_proto->add_coefficient(kConstraintCoef[i][j]); + } + } + + MPModelRequest model_request; + *model_request.mutable_model() = model_proto; +#if defined(USE_GLOP) + if (type == MPSolver::GLOP_LINEAR_PROGRAMMING) { + model_request.set_solver_type(MPModelRequest::GLOP_LINEAR_PROGRAMMING); + } +#endif // USE_GLOP +#if defined(USE_CLP) + if (type == MPSolver::CLP_LINEAR_PROGRAMMING) { + model_request.set_solver_type(MPModelRequest::CLP_LINEAR_PROGRAMMING); + } +#endif // USE_CLP + + MPSolutionResponse solution_response; + MPSolver::SolveWithProto(model_request, &solution_response); + + // The problem has an optimal solution. + CHECK_EQ(MPSOLVER_OPTIMAL, solution_response.status()); + + LOG(INFO) << "objective = " << solution_response.objective_value(); + for (int j = 0; j < numVars; ++j) { + LOG(INFO) << model_proto.variable(j).name() << " = " + << solution_response.variable_value(j); + } +} + +void RunAllExamples() { +#if defined(USE_GLOP) + LOG(INFO) << "----- Running Max Example with GLOP -----"; + BuildLinearProgrammingMaxExample(MPSolver::GLOP_LINEAR_PROGRAMMING); +#endif // USE_GLOP +#if defined(USE_CLP) + LOG(INFO) << "----- Running Max Example with Coin LP -----"; + BuildLinearProgrammingMaxExample(MPSolver::CLP_LINEAR_PROGRAMMING); +#endif // USE_CLP +} +} // namespace operations_research + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + operations_research::RunAllExamples(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/literal_sample_sat.cc b/libs/or-tools-bin/examples/cpp/literal_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/literal_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/literal_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/magic_square_sat.cc b/libs/or-tools-bin/examples/cpp/magic_square_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..a411385958162521500df846e4a3e8133781e145 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/magic_square_sat.cc @@ -0,0 +1,100 @@ +// 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 "absl/strings/str_format.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" + +DEFINE_int32(size, 7, "Size of the magic square"); +DEFINE_string(params, "", "Sat paramters"); + +namespace operations_research { +namespace sat { + +void MagicSquare(int size) { + CpModelBuilder builder; + + std::vector> square(size); + std::vector> transposed(size); + std::vector diag1; + std::vector diag2; + std::vector all_variables; + Domain domain(1, size * size); + for (int i = 0; i < size; ++i) { + for (int j = 0; j < size; ++j) { + const IntVar var = builder.NewIntVar(domain); + square[i].push_back(var); + transposed[j].push_back(var); + all_variables.push_back(var); + if (i == j) { + diag1.push_back(var); + } + if (i + j == size) { + diag2.push_back(var); + } + } + } + + // All Diff. + for (int i = 0; i < size; ++i) { + builder.AddAllDifferent(all_variables); + } + + const int sum = size * (size * size + 1) / 2; + // Sum on rows. + for (int i = 0; i < size; ++i) { + builder.AddEquality(LinearExpr::Sum(square[i]), sum); + } + + // Sum on columns. + for (int i = 0; i < size; ++i) { + builder.AddEquality(LinearExpr::Sum(transposed[i]), sum); + } + + // Sum on diagonals. + builder.AddEquality(LinearExpr::Sum(diag1), sum); + builder.AddEquality(LinearExpr::Sum(diag2), sum); + + Model model; + model.Add(NewSatParameters(FLAGS_params)); + + const CpSolverResponse response = SolveCpModel(builder.Build(), &model); + + if (response.status() == CpSolverStatus::FEASIBLE) { + for (int n = 0; n < size; ++n) { + std::string output; + for (int m = 0; m < size; ++m) { + absl::StrAppendFormat(&output, "%3d ", + SolutionIntegerValue(response, square[n][m])); + } + LOG(INFO) << output; + } + } else { + LOG(INFO) << "No solution found!"; + } + LOG(INFO) << CpSolverResponseStats(response); +} + +} // namespace sat +} // namespace sat + +int main(int argc, char **argv) { + absl::SetFlag(&FLAGS_logtostderr, true); + gflags::ParseCommandLineFlags(&argc, &argv, true); + operations_research::sat::MagicSquare(FLAGS_size); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/max_flow.cc b/libs/or-tools-bin/examples/cpp/max_flow.cc new file mode 100644 index 0000000000000000000000000000000000000000..4f212c5d5e29e3c73cb05e4e1ebe3bf96b1ef542 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/max_flow.cc @@ -0,0 +1,58 @@ +// Copyright 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/graph/max_flow.h" +#include "ortools/base/logging.h" + +namespace operations_research { +void SolveMaxFlow() { + const int num_nodes = 5; + // Add each arc + // Can't use std::tuple + // Initialization list is not working on std:tuple cf. N4387 + // Arc are stored as {{begin_node, end_node}, capacity} + std::vector, FlowQuantity>> arcs = { + {{0, 1}, 20}, {{0, 2}, 30}, {{0, 3}, 10}, {{1, 2}, 40}, {{1, 4}, 30}, + {{2, 3}, 10}, {{2, 4}, 20}, {{3, 2}, 5}, {{3, 4}, 20}}; + StarGraph graph(num_nodes, arcs.size()); + MaxFlow max_flow(&graph, 0, num_nodes - 1); + for (const auto& it : arcs) { + ArcIndex arc = graph.AddArc(it.first.first, it.first.second); + max_flow.SetArcCapacity(arc, it.second); + } + + LOG(INFO) << "Solving max flow with: " << graph.num_nodes() << " nodes, and " + << graph.num_arcs() << " arcs."; + + // Find the maximum flow between node 0 and node 4. + max_flow.Solve(); + if (MaxFlow::OPTIMAL != max_flow.status()) { + LOG(FATAL) << "Solving the max flow is not optimal!"; + } + FlowQuantity total_flow = max_flow.GetOptimalFlow(); + LOG(INFO) << "Maximum flow: " << total_flow; + LOG(INFO) << ""; + LOG(INFO) << " Arc : Flow / Capacity"; + for (int i = 0; i < arcs.size(); ++i) { + LOG(INFO) << graph.Tail(i) << " -> " << graph.Head(i) << ": " + << max_flow.Flow(i) << " / " << max_flow.Capacity(i); + } +} +} // namespace operations_research + +int main(int argc, char** argv) { + google::InitGoogleLogging(argv[0]); + FLAGS_logtostderr = 1; + operations_research::SolveMaxFlow(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/min_cost_flow.cc b/libs/or-tools-bin/examples/cpp/min_cost_flow.cc new file mode 100644 index 0000000000000000000000000000000000000000..3ef937a81239f450112e5f0b5a54ff6255bd3b2b --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/min_cost_flow.cc @@ -0,0 +1,74 @@ +// Copyright 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/graph/min_cost_flow.h" +#include "ortools/base/logging.h" + +namespace operations_research { +struct Arc { + std::pair nodes; + FlowQuantity capacity; + FlowQuantity unit_cost; +}; + +void SolveMinCostFlow() { + // Define supply of each node. + const std::vector> supplies = { + {0, 20}, {1, 0}, {2, 0}, {3, -5}, {4, -15}}; + + // Define each arc + // Can't use std::tuple + // Initialization list is not working on std:tuple cf. N4387 + // Arc are stored as {{begin_node, end_node}, capacity} + const std::vector arcs = { + {{0, 1}, 15, 4}, {{0, 2}, 8, 4}, {{1, 2}, 20, 2}, + {{1, 3}, 4, 2}, {{1, 4}, 10, 6}, {{2, 3}, 15, 1}, + {{2, 4}, 4, 3}, {{3, 4}, 20, 2}, {{4, 2}, 5, 3}}; + + StarGraph graph(supplies.size(), arcs.size()); + MinCostFlow min_cost_flow(&graph); + for (const auto &it : arcs) { + ArcIndex arc = graph.AddArc(it.nodes.first, it.nodes.second); + min_cost_flow.SetArcCapacity(arc, it.capacity); + min_cost_flow.SetArcUnitCost(arc, it.unit_cost); + } + for (const auto &it : supplies) { + min_cost_flow.SetNodeSupply(it.first, it.second); + } + + LOG(INFO) << "Solving min cost flow with: " << graph.num_nodes() + << " nodes, and " << graph.num_arcs() << " arcs."; + + // Find the maximum flow between node 0 and node 4. + min_cost_flow.Solve(); + if (MinCostFlow::OPTIMAL != min_cost_flow.status()) { + LOG(FATAL) << "Solving the max flow is not optimal!"; + } + FlowQuantity total_flow_cost = min_cost_flow.GetOptimalCost(); + LOG(INFO) << "Minimum cost flow: " << total_flow_cost; + LOG(INFO) << ""; + LOG(INFO) << "Arc : Flow / Capacity / Cost"; + for (int i = 0; i < arcs.size(); ++i) { + LOG(INFO) << graph.Tail(i) << " -> " << graph.Head(i) << ": " + << min_cost_flow.Flow(i) << " / " << min_cost_flow.Capacity(i) + << " / " << min_cost_flow.UnitCost(i); + } +} +} // namespace operations_research + +int main(int argc, char **argv) { + google::InitGoogleLogging(argv[0]); + FLAGS_logtostderr = 1; + operations_research::SolveMinCostFlow(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/minimal_jobshop_cp.cc b/libs/or-tools-bin/examples/cpp/minimal_jobshop_cp.cc similarity index 100% rename from libs/or-tools/examples/cpp/minimal_jobshop_cp.cc rename to libs/or-tools-bin/examples/cpp/minimal_jobshop_cp.cc diff --git a/libs/or-tools/examples/cpp/mip_var_array.cc b/libs/or-tools-bin/examples/cpp/mip_var_array.cc similarity index 100% rename from libs/or-tools/examples/cpp/mip_var_array.cc rename to libs/or-tools-bin/examples/cpp/mip_var_array.cc diff --git a/libs/or-tools-bin/examples/cpp/mps_driver.cc b/libs/or-tools-bin/examples/cpp/mps_driver.cc new file mode 100644 index 0000000000000000000000000000000000000000..a517ef5f834211e6c08073868220d49fcf91360e --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/mps_driver.cc @@ -0,0 +1,153 @@ +// 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. + +// Driver for reading and solving files in the MPS format and in +// the linear_solver.proto format. + +#include + +#include + +#include "absl/strings/match.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" +#include "google/protobuf/text_format.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/base/file.h" +#include "ortools/base/logging.h" +#include "ortools/base/status.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."); +DEFINE_bool(mps_solve, true, "Solves problem."); +DEFINE_bool(mps_terse_result, false, + "Displays the result in form of a single CSV line."); +DEFINE_bool(mps_verbose_result, true, "Displays the result in verbose form."); +DEFINE_bool(mps_display_full_path, true, + "Displays the full path of the input file in the result line."); +DEFINE_string(input, "", "File pattern for problems to be optimized."); +DEFINE_string(params_file, "", "Path to a GlopParameters file in text format."); +DEFINE_string(params, "", + "GlopParameters in text format. If --params_file was " + "also specified, the --params will be merged onto " + "them (i.e. in case of conflicts, --params wins)"); + +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; +using operations_research::glop::LPSolver; +using operations_research::glop::MPModelProtoToLinearProgram; +using operations_research::glop::MPSReader; +using operations_research::glop::ProblemStatus; +using operations_research::glop::ToDouble; + +// Parse glop parameters from the flags --params_file and --params. +void ReadGlopParameters(GlopParameters* parameters) { + if (!FLAGS_params_file.empty()) { + std::string params; + CHECK(TextFormat::ParseFromString(params, parameters)) << params; + } + if (!FLAGS_params.empty()) { + CHECK(TextFormat::MergeFromString(FLAGS_params, parameters)) + << FLAGS_params; + } + if (FLAGS_mps_verbose_result) { + printf("GlopParameters {\n%s}\n", + FullProtocolMessageAsString(*parameters, 1).c_str()); + } +} + +int main(int argc, char* argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + + GlopParameters parameters; + ReadGlopParameters(¶meters); + + LinearProgram linear_program; + std::vector file_list; + // Replace this with your favorite match function. + file_list.push_back(FLAGS_input); + for (int i = 0; i < file_list.size(); ++i) { + const std::string& file_name = file_list[i]; + MPSReader mps_reader; + operations_research::MPModelProto model_proto; + if (absl::EndsWith(file_name, ".mps") || + absl::EndsWith(file_name, ".mps.gz")) { + const util::Status parse_status = + mps_reader.ParseFile(file_name, &linear_program); + if (!parse_status.ok()) { + LOG(INFO) << "Parse error for " << file_name << ": " << parse_status; + continue; + } + } else { + ReadFileToProto(file_name, &model_proto); + MPModelProtoToLinearProgram(model_proto, &linear_program); + } + if (FLAGS_mps_dump_problem) { + printf("%s", linear_program.Dump().c_str()); + } + + // Create the solver with the correct parameters. + LPSolver solver; + solver.SetParameters(parameters); + ProblemStatus solve_status = ProblemStatus::INIT; + + std::string status_string; + double objective_value; + double solving_time_in_sec = 0; + if (FLAGS_mps_solve) { + ScopedWallTime timer(&solving_time_in_sec); + solve_status = solver.Solve(linear_program); + status_string = GetProblemStatusString(solve_status); + objective_value = ToDouble(solver.GetObjectiveValue()); + } + + if (FLAGS_mps_terse_result) { + if (FLAGS_mps_display_full_path) { + printf("%s,", file_name.c_str()); + } + printf("%s,", linear_program.name().c_str()); + if (FLAGS_mps_solve) { + printf("%15.15e,%s,%-6.4g,", objective_value, status_string.c_str(), + solving_time_in_sec); + } + printf("%s,%s\n", linear_program.GetProblemStats().c_str(), + linear_program.GetNonZeroStats().c_str()); + } + + if (FLAGS_mps_verbose_result) { + if (FLAGS_mps_display_full_path) { + printf("%-45s: %s\n", "File path", file_name.c_str()); + } + printf("%-45s: %s\n", "Problem name", linear_program.name().c_str()); + if (FLAGS_mps_solve) { + printf("%-45s: %15.15e\n", "Objective value", objective_value); + printf("%-45s: %s\n", "Problem status", status_string.c_str()); + printf("%-45s: %-6.4g\n", "Solving time", solving_time_in_sec); + } + printf("%s%s", linear_program.GetPrettyProblemStats().c_str(), + linear_program.GetPrettyNonZeroStats().c_str()); + } + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/multi_knapsack_sat.cc b/libs/or-tools-bin/examples/cpp/multi_knapsack_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..9b4661f5a27559b84bcf2ef72aec80f33f91ca51 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/multi_knapsack_sat.cc @@ -0,0 +1,113 @@ +// 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. + +// Solve a scaled constrained two dimensional knapsack problem. +// Each bin must be filled with items with min and max weights, and min and max +// volumes. As is a knapsack, the objective is to maximize total value. It turns +// out that the objective is to maximize weights. +// +// Data is for 1 bin and 10 items. Scaling is done my having m bins and m copies +// of each items. + +#include + +#include "ortools/base/commandlineflags.h" +#include "ortools/base/logging.h" +#include "ortools/sat/cp_model.h" + +DEFINE_int32(size, 16, "scaling factor of the model"); +DEFINE_string(params, "", "Sat parameters"); + +namespace operations_research { +namespace sat { + +static const int kWeightMin = 16000; +static const int kWeightMax = 22000; +static const int kVolumeMin = 1156; +static const int kVolumeMax = 1600; + +// Data for a single bin problem +static const int kItemsWeights[] = {1008, 2087, 5522, 5250, 5720, + 4998, 275, 3145, 12580, 382}; +static const int kItemsVolumes[] = {281, 307, 206, 111, 275, + 79, 23, 65, 261, 40}; +static const int kNumItems = 10; + +void MultiKnapsackSat(int scaling, const std::string& params) { + CpModelBuilder builder; + + const int num_items = scaling * kNumItems; + const int num_bins = scaling; + + std::vector> items_in_bins(num_bins); + for (int b = 0; b < num_bins; ++b) { + for (int i = 0; i < num_items; ++i) { + items_in_bins[b].push_back(builder.NewBoolVar()); + } + } + + std::vector selected_items(num_items); + for (int i = 0; i < num_items; ++i) { + selected_items[i] = builder.NewBoolVar(); + } + + // Fill up scaled values, weights, volumes; + std::vector values(num_items); + std::vector weights(num_items); + std::vector volumes(num_items); + for (int i = 0; i < num_items; ++i) { + const int index = i % kNumItems; + weights[i] = kItemsWeights[index]; + volumes[i] = kItemsVolumes[index]; + } + + // Constraints per bins. + std::vector bin_weights; + for (int b = 0; b < num_bins; ++b) { + IntVar bin_weight = builder.NewIntVar({kWeightMin, kWeightMax}); + bin_weights.push_back(bin_weight); + builder.AddEquality(LinearExpr::BooleanScalProd(items_in_bins[b], weights), + bin_weight); + builder.AddLinearConstraint( + LinearExpr::BooleanScalProd(items_in_bins[b], volumes), + {kVolumeMin, kVolumeMax}); + } + + // Each item is selected at most one time. + for (int i = 0; i < num_items; ++i) { + std::vector bin_contain_item(num_bins); + for (int b = 0; b < num_bins; ++b) { + bin_contain_item[b] = items_in_bins[b][i]; + } + builder.AddEquality(LinearExpr::BooleanSum(bin_contain_item), + selected_items[i]); + } + + // Maximize the sums of weights. + builder.Maximize(LinearExpr::Sum(bin_weights)); + + // And solve. + const CpSolverResponse response = + SolveWithParameters(builder.Build(), params); + LOG(INFO) << CpSolverResponseStats(response); +} + +} // namespace sat +} // namespace operations_research + +int main(int argc, char** argv) { + absl::SetFlag(&FLAGS_logtostderr, true); + gflags::ParseCommandLineFlags(&argc, &argv, true); + operations_research::sat::MultiKnapsackSat(FLAGS_size, FLAGS_params); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/multiple_knapsack_sat.cc b/libs/or-tools-bin/examples/cpp/multiple_knapsack_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/multiple_knapsack_sat.cc rename to libs/or-tools-bin/examples/cpp/multiple_knapsack_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/network_routing_sat.cc b/libs/or-tools-bin/examples/cpp/network_routing_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..6d339dc6f58fb6e0457f7c4f80f7056a89f8e66d --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/network_routing_sat.cc @@ -0,0 +1,678 @@ +// 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 model solves a multicommodity mono-routing problem with +// capacity constraints and a max usage cost structure. This means +// that given a graph with capacity on edges, and a set of demands +// (source, destination, traffic), the goal is to assign one unique +// path for each demand such that the cost is minimized. The cost is +// defined by the maximum ratio utilization (traffic/capacity) for all +// arcs. There is also a penalty associated with an traffic of an arc +// being above the comfort zone, 85% of the capacity by default. +// Please note that constraint programming is well suited here because +// we cannot have multiple active paths for a single demand. +// Otherwise, a approach based on a linear solver is a better match. + +// A random problem generator is also included. + +#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/map_util.h" +#include "ortools/base/random.h" +#include "ortools/graph/shortestpaths.h" +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" +#include "ortools/util/time_limit.h" + +// ----- Data Generator ----- +DEFINE_int32(clients, 0, + "Number of network clients nodes. If equal to zero, " + "then all backbones nodes are also client nodes."); +DEFINE_int32(backbones, 0, "Number of backbone nodes"); +DEFINE_int32(demands, 0, "Number of network demands."); +DEFINE_int32(traffic_min, 0, "Min traffic of a demand."); +DEFINE_int32(traffic_max, 0, "Max traffic of a demand."); +DEFINE_int32(min_client_degree, 0, + "Min number of connections from a client to the backbone."); +DEFINE_int32(max_client_degree, 0, + "Max number of connections from a client to the backbone."); +DEFINE_int32(min_backbone_degree, 0, + "Min number of connections from a backbone node to the rest of " + "the backbone nodes."); +DEFINE_int32(max_backbone_degree, 0, + "Max number of connections from a backbone node to the rest of " + "the backbone nodes."); +DEFINE_int32(max_capacity, 0, "Max traffic on any arc."); +DEFINE_int32(fixed_charge_cost, 0, "Fixed charged cost when using an arc."); +DEFINE_int32(seed, 0, "Random seed"); + +// ----- CP Model ----- +DEFINE_double(comfort_zone, 0.85, + "Above this limit in 1/1000th, the link is said to be " + "congestioned."); +DEFINE_int32(extra_hops, 6, + "When creating all paths for a demand, we look at paths with " + "maximum length 'shortest path + extra_hops'"); +DEFINE_int32(max_paths, 1200, "Max number of possible paths for a demand."); + +// ----- Reporting ----- +DEFINE_bool(print_model, false, "Print details of the model."); + +// ----- Sat parameters ----- +DEFINE_string(params, "", "Sat parameters."); + +namespace operations_research { +namespace sat { +// ---------- Data and Data Generation ---------- +static const int64 kDisconnectedDistance = -1LL; + +// ----- Data ----- +// Contains problem data. It assumes capacities are symmetrical: +// (capacity(i->j) == capacity(j->i)). +// Demands are not symmetrical. +class NetworkRoutingData { + public: + NetworkRoutingData() + : name_(""), num_nodes_(-1), max_capacity_(-1), fixed_charge_cost_(-1) {} + + // Name of the problem. + const std::string &name() const { return name_; } + + // Properties of the model. + int num_nodes() const { return num_nodes_; } + + int num_arcs() const { return all_arcs_.size(); } + + int num_demands() const { return all_demands_.size(); } + + // Returns the capacity of an arc, and 0 if the arc is not defined. + int Capacity(int node1, int node2) const { + return gtl::FindWithDefault( + all_arcs_, + std::make_pair(std::min(node1, node2), std::max(node1, node2)), 0); + } + + // Returns the demand between the source and the destination, and 0 if + // there are no demands between the source and the destination. + int Demand(int source, int destination) const { + return gtl::FindWithDefault(all_demands_, + std::make_pair(source, destination), 0); + } + + // External building API. + void set_num_nodes(int num_nodes) { num_nodes_ = num_nodes; } + void AddArc(int node1, int node2, int capacity) { + all_arcs_[std::make_pair(std::min(node1, node2), std::max(node1, node2))] = + capacity; + } + void AddDemand(int source, int destination, int traffic) { + all_demands_[std::make_pair(source, destination)] = traffic; + } + void set_name(const std::string &name) { name_ = name; } + void set_max_capacity(int max_capacity) { max_capacity_ = max_capacity; } + void set_fixed_charge_cost(int cost) { fixed_charge_cost_ = cost; } + + private: + std::string name_; + int num_nodes_; + int max_capacity_; + int fixed_charge_cost_; + std::unordered_map, int> all_arcs_; + std::unordered_map, int> all_demands_; +}; + +// ----- Data Generation ----- + +// Random generator of problem. This generator creates a random +// problem. This problem uses a special topology. There are +// 'num_backbones' nodes and 'num_clients' nodes. if 'num_clients' is +// null, then all backbones nodes are also client nodes. All traffic +// originates and terminates in client nodes. Each client node is +// connected to 'min_client_degree' - 'max_client_degree' backbone +// nodes. Each backbone node is connected to 'min_backbone_degree' - +// 'max_backbone_degree' other backbone nodes. There are 'num_demands' +// demands, with a traffic between 'traffic_min' and 'traffic_max'. +// Each arc has a capacity of 'max_capacity'. Using an arc incurs a +// fixed cost of 'fixed_charge_cost'. +class NetworkRoutingDataBuilder { + public: + NetworkRoutingDataBuilder() : random_(0) {} + + void BuildModelFromParameters(int num_clients, int num_backbones, + int num_demands, int traffic_min, + int traffic_max, int min_client_degree, + int max_client_degree, int min_backbone_degree, + int max_backbone_degree, int max_capacity, + int fixed_charge_cost, int seed, + NetworkRoutingData *const data) { + CHECK_GE(num_backbones, 1); + CHECK_GE(num_clients, 0); + CHECK_GE(num_demands, 1); + CHECK_LE(num_demands, num_clients == 0 ? num_backbones * num_backbones + : num_clients * num_backbones); + CHECK_GE(max_client_degree, min_client_degree); + CHECK_GE(max_backbone_degree, min_backbone_degree); + CHECK_GE(traffic_max, 1); + CHECK_GE(traffic_max, traffic_min); + CHECK_GE(traffic_min, 1); + CHECK_GE(max_backbone_degree, 2); + CHECK_GE(max_client_degree, 2); + CHECK_LE(max_client_degree, num_backbones); + CHECK_LE(max_backbone_degree, num_backbones); + CHECK_GE(max_capacity, 1); + + const int size = num_backbones + num_clients; + InitData(size, seed); + BuildGraph(num_clients, num_backbones, min_client_degree, max_client_degree, + min_backbone_degree, max_backbone_degree); + CreateDemands(num_clients, num_backbones, num_demands, traffic_min, + traffic_max, data); + FillData(num_clients, num_backbones, num_demands, traffic_min, traffic_max, + min_client_degree, max_client_degree, min_backbone_degree, + max_backbone_degree, max_capacity, fixed_charge_cost, seed, data); + } + + private: + void InitData(int size, int seed) { + network_.clear(); + network_.resize(size); + for (int i = 0; i < size; ++i) { + network_[i].resize(size, false); + } + degrees_.clear(); + degrees_.resize(size, 0); + random_.Reset(seed); + } + + void BuildGraph(int num_clients, int num_backbones, int min_client_degree, + int max_client_degree, int min_backbone_degree, + int max_backbone_degree) { + const int size = num_backbones + num_clients; + + // First we create the backbone nodes. + for (int i = 1; i < num_backbones; ++i) { + int j = random_.Uniform(i); + CHECK_LT(j, i); + AddEdge(i, j); + } + + std::unordered_set to_complete; + std::unordered_set not_full; + for (int i = 0; i < num_backbones; ++i) { + if (degrees_[i] < min_backbone_degree) { + to_complete.insert(i); + } + if (degrees_[i] < max_backbone_degree) { + not_full.insert(i); + } + } + while (!to_complete.empty() && not_full.size() > 1) { + const int node1 = *(to_complete.begin()); + int node2 = node1; + while (node2 == node1 || degrees_[node2] >= max_backbone_degree) { + node2 = random_.Uniform(num_backbones); + } + AddEdge(node1, node2); + if (degrees_[node1] >= min_backbone_degree) { + to_complete.erase(node1); + } + if (degrees_[node2] >= min_backbone_degree) { + to_complete.erase(node2); + } + if (degrees_[node1] >= max_backbone_degree) { + not_full.erase(node1); + } + if (degrees_[node2] >= max_backbone_degree) { + not_full.erase(node2); + } + } + + // Then create the client nodes connected to the backbone nodes. + // If num_client is 0, then backbone nodes are also client nodes. + for (int i = num_backbones; i < size; ++i) { + const int degree = RandomInInterval(min_client_degree, max_client_degree); + while (degrees_[i] < degree) { + const int j = random_.Uniform(num_backbones); + if (!network_[i][j]) { + AddEdge(i, j); + } + } + } + } + + void CreateDemands(int num_clients, int num_backbones, int num_demands, + int traffic_min, int traffic_max, + NetworkRoutingData *const data) { + while (data->num_demands() < num_demands) { + const int source = RandomClient(num_clients, num_backbones); + int dest = source; + while (dest == source) { + dest = RandomClient(num_clients, num_backbones); + } + const int traffic = RandomInInterval(traffic_min, traffic_max); + data->AddDemand(source, dest, traffic); + } + } + + void FillData(int num_clients, int num_backbones, int num_demands, + int traffic_min, int traffic_max, int min_client_degree, + int max_client_degree, int min_backbone_degree, + int max_backbone_degree, int max_capacity, + int fixed_charge_cost, int seed, + NetworkRoutingData *const data) { + const int size = num_backbones + num_clients; + + const std::string name = absl::StrFormat( + "mp_c%i_b%i_d%i.t%i-%i.cd%i-%i.bd%i-%i.mc%i.fc%i.s%i", num_clients, + num_backbones, num_demands, traffic_min, traffic_max, min_client_degree, + max_client_degree, min_backbone_degree, max_backbone_degree, + max_capacity, fixed_charge_cost, seed); + data->set_name(name); + + data->set_num_nodes(size); + int num_arcs = 0; + for (int i = 0; i < size - 1; ++i) { + for (int j = i + 1; j < size; ++j) { + if (network_[i][j]) { + data->AddArc(i, j, max_capacity); + num_arcs++; + } + } + } + data->set_max_capacity(max_capacity); + data->set_fixed_charge_cost(fixed_charge_cost); + } + + void AddEdge(int i, int j) { + degrees_[i]++; + degrees_[j]++; + network_[i][j] = true; + network_[j][i] = true; + } + + int RandomInInterval(int interval_min, int interval_max) { + CHECK_LE(interval_min, interval_max); + return random_.Uniform(interval_max - interval_min + 1) + interval_min; + } + + int RandomClient(int num_clients, int num_backbones) { + return (num_clients == 0) ? random_.Uniform(num_backbones) + : random_.Uniform(num_clients) + num_backbones; + } + + std::vector> network_; + std::vector degrees_; + MTRandom random_; +}; + +// ---------- Solving the Problem ---------- + +// Useful data struct to hold demands. +struct Demand { + public: + Demand(int the_source, int the_destination, int the_traffic) + : source(the_source), + destination(the_destination), + traffic(the_traffic) {} + int source; + int destination; + int traffic; +}; + +class NetworkRoutingSolver { + public: + typedef std::unordered_set OnePath; + + NetworkRoutingSolver() : num_nodes_(-1) {} + + void ComputeAllPathsForOneDemandAndOnePathLength(int demand_index, + int max_length, + int max_paths) { + // We search for paths of length exactly 'max_length'. + CpModelBuilder cp_model; + std::vector arc_vars; + std::vector node_vars; + for (int i = 0; i < max_length; ++i) { + node_vars.push_back(cp_model.NewIntVar(Domain(0, num_nodes_ - 1))); + } + for (int i = 0; i < max_length - 1; ++i) { + arc_vars.push_back(cp_model.NewIntVar(Domain(-1, count_arcs() - 1))); + } + + for (int i = 0; i < max_length - 1; ++i) { + std::vector tmp_vars; + tmp_vars.push_back(node_vars[i]); + tmp_vars.push_back(node_vars[i + 1]); + tmp_vars.push_back(arc_vars[i]); + TableConstraint table = cp_model.AddAllowedAssignments( + {node_vars[i], node_vars[i + 1], arc_vars[i]}); + for (const auto &tuple : arcs_data_) { + table.AddTuple(tuple); + } + } + + const Demand &demand = demands_array_[demand_index]; + cp_model.AddEquality(node_vars[0], demand.source); + cp_model.AddEquality(node_vars[max_length - 1], demand.destination); + cp_model.AddAllDifferent(arc_vars); + cp_model.AddAllDifferent(node_vars); + + Model model; + // Create an atomic Boolean that will be periodically checked by the limit. + std::atomic stopped(false); + model.GetOrCreate()->RegisterExternalBooleanAsLimit(&stopped); + + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse &r) { + const int path_id = all_paths_[demand_index].size(); + all_paths_[demand_index].resize(path_id + 1); + for (int arc_index = 0; arc_index < max_length - 1; ++arc_index) { + const int arc = SolutionIntegerValue(r, arc_vars[arc_index]); + all_paths_[demand_index].back().insert(arc); + } + if (all_paths_[demand_index].size() >= max_paths) { + stopped = true; + } + })); + + SatParameters parameters; + parameters.set_enumerate_all_solutions(true); + model.Add(NewSatParameters(parameters)); + + SolveCpModel(cp_model.Build(), &model); + } + + // This method will fill the all_paths_ data structure. all_paths + // contains, for each demand, a vector of possible paths, stored as + // a hash_set of arc indices. + int ComputeAllPaths(int extra_hops, int max_paths) { + int num_paths = 0; + for (int demand_index = 0; demand_index < demands_array_.size(); + ++demand_index) { + const int min_path_length = all_min_path_lengths_[demand_index]; + for (int max_length = min_path_length + 1; + max_length <= min_path_length + extra_hops + 1; ++max_length) { + ComputeAllPathsForOneDemandAndOnePathLength(demand_index, max_length, + max_paths); + if (all_paths_[demand_index].size() >= max_paths) { + break; + } + } + num_paths += all_paths_[demand_index].size(); + } + return num_paths; + } + + void AddArcData(int64 source, int64 destination, int arc_id) { + arcs_data_.push_back({source, destination, arc_id}); + } + + void InitArcInfo(const NetworkRoutingData &data) { + const int num_arcs = data.num_arcs(); + capacity_.clear(); + capacity_.resize(num_nodes_); + for (int node_index = 0; node_index < num_nodes_; ++node_index) { + capacity_[node_index].resize(num_nodes_, 0); + } + int arc_id = 0; + for (int i = 0; i < num_nodes_ - 1; ++i) { + for (int j = i + 1; j < num_nodes_; ++j) { + const int capacity = data.Capacity(i, j); + if (capacity > 0) { + AddArcData(i, j, arc_id); + AddArcData(j, i, arc_id); + arc_id++; + arc_capacity_.push_back(capacity); + capacity_[i][j] = capacity; + capacity_[j][i] = capacity; + if (FLAGS_print_model) { + LOG(INFO) << "Arc " << i << " <-> " << j << " with capacity " + << capacity; + } + } + } + } + CHECK_EQ(arc_id, num_arcs); + } + + int InitDemandInfo(const NetworkRoutingData &data) { + const int num_demands = data.num_demands(); + int total_demand = 0; + for (int i = 0; i < num_nodes_; ++i) { + for (int j = 0; j < num_nodes_; ++j) { + const int traffic = data.Demand(i, j); + if (traffic > 0) { + demands_array_.push_back(Demand(i, j, traffic)); + total_demand += traffic; + } + } + } + CHECK_EQ(num_demands, demands_array_.size()); + return total_demand; + } + + int64 InitShortestPaths(const NetworkRoutingData &data) { + const int num_demands = data.num_demands(); + int64 total_cumulated_traffic = 0; + all_min_path_lengths_.clear(); + std::vector paths; + for (int demand_index = 0; demand_index < num_demands; ++demand_index) { + paths.clear(); + const Demand &demand = demands_array_[demand_index]; + CHECK(DijkstraShortestPath(num_nodes_, demand.source, demand.destination, + [this](int x, int y) { return HasArc(x, y); }, + kDisconnectedDistance, &paths)); + all_min_path_lengths_.push_back(paths.size() - 1); + } + + for (int i = 0; i < num_demands; ++i) { + const int min_path_length = all_min_path_lengths_[i]; + total_cumulated_traffic += min_path_length * demands_array_[i].traffic; + } + return total_cumulated_traffic; + } + + int InitPaths(const NetworkRoutingData &data, int extra_hops, int max_paths) { + const int num_demands = data.num_demands(); + LOG(INFO) << "Computing all possible paths "; + LOG(INFO) << " - extra hops = " << extra_hops; + LOG(INFO) << " - max paths per demand = " << max_paths; + + all_paths_.clear(); + all_paths_.resize(num_demands); + const int num_paths = ComputeAllPaths(extra_hops, max_paths); + for (int demand_index = 0; demand_index < num_demands; ++demand_index) { + const Demand &demand = demands_array_[demand_index]; + LOG(INFO) << "Demand from " << demand.source << " to " + << demand.destination << " with traffic " << demand.traffic + << ", and " << all_paths_[demand_index].size() + << " possible paths."; + } + return num_paths; + } + + void Init(const NetworkRoutingData &data, int extra_hops, int max_paths) { + LOG(INFO) << "Model " << data.name(); + num_nodes_ = data.num_nodes(); + const int num_arcs = data.num_arcs(); + const int num_demands = data.num_demands(); + + InitArcInfo(data); + const int total_demand = InitDemandInfo(data); + const int64 total_cumulated_traffic = InitShortestPaths(data); + const int num_paths = InitPaths(data, extra_hops, max_paths); + + // ----- Report Problem Sizes ----- + + LOG(INFO) << "Model created:"; + LOG(INFO) << " - " << num_nodes_ << " nodes"; + LOG(INFO) << " - " << num_arcs << " arcs"; + LOG(INFO) << " - " << num_demands << " demands"; + LOG(INFO) << " - a total traffic of " << total_demand; + LOG(INFO) << " - a minimum cumulated traffic of " + << total_cumulated_traffic; + LOG(INFO) << " - " << num_paths << " possible paths for all demands"; + } + + // ----- Callback for Dijkstra Shortest Path ----- + + int64 HasArc(int i, int j) { + if (capacity_[i][j] > 0) { + return 1; + } else { + return kDisconnectedDistance; // disconnected distance. + } + } + + // ----- Main Solve routine ----- + + int64 Solve() { + LOG(INFO) << "Solving model"; + const int num_demands = demands_array_.size(); + const int num_arcs = count_arcs(); + + // ----- Build Model ----- + CpModelBuilder cp_model; + std::vector> path_vars(num_demands); + + // Node - Graph Constraint. + for (int demand_index = 0; demand_index < num_demands; ++demand_index) { + for (int arc = 0; arc < num_arcs; ++arc) { + path_vars[demand_index].push_back(cp_model.NewBoolVar()); + } + // Fill Tuple Set for AllowedAssignment constraint. + TableConstraint path_ct = + cp_model.AddAllowedAssignments(path_vars[demand_index]); + for (const auto &one_path : all_paths_[demand_index]) { + std::vector tuple(count_arcs(), 0); + for (const int arc : one_path) { + tuple[arc] = 1; + } + path_ct.AddTuple(tuple); + } + } + // Traffic variables and objective definition. + std::vector traffic_vars(num_arcs); + std::vector normalized_traffic_vars(num_arcs); + std::vector comfortable_traffic_vars(num_arcs); + int64 max_normalized_traffic = 0; + for (int arc_index = 0; arc_index < num_arcs; ++arc_index) { + int64 sum_of_traffic = 0; + LinearExpr traffic_expr; + for (int i = 0; i < path_vars.size(); ++i) { + sum_of_traffic += demands_array_[i].traffic; + traffic_expr.AddTerm(path_vars[i][arc_index], + demands_array_[i].traffic); + } + const IntVar traffic_var = cp_model.NewIntVar(Domain(0, sum_of_traffic)); + traffic_vars[arc_index] = traffic_var; + cp_model.AddEquality(traffic_expr, traffic_var); + + const int64 capacity = arc_capacity_[arc_index]; + IntVar scaled_traffic = + cp_model.NewIntVar(Domain(0, sum_of_traffic * 1000)); + cp_model.AddEquality(LinearExpr::ScalProd({traffic_var}, {1000}), + scaled_traffic); + IntVar normalized_traffic = + cp_model.NewIntVar(Domain(0, sum_of_traffic * 1000 / capacity)); + max_normalized_traffic = + std::max(max_normalized_traffic, sum_of_traffic * 1000 / capacity); + cp_model.AddDivisionEquality(normalized_traffic, scaled_traffic, + cp_model.NewConstant(capacity)); + normalized_traffic_vars[arc_index] = normalized_traffic; + const BoolVar comfort = cp_model.NewBoolVar(); + const int64 safe_capacity = + static_cast(capacity * FLAGS_comfort_zone); + cp_model.AddGreaterThan(traffic_var, safe_capacity) + .OnlyEnforceIf(comfort); + cp_model.AddLessOrEqual(traffic_var, safe_capacity) + .OnlyEnforceIf(Not(comfort)); + comfortable_traffic_vars[arc_index] = comfort; + } + + const IntVar max_usage_cost = + cp_model.NewIntVar(Domain(0, max_normalized_traffic)); + cp_model.AddMaxEquality(max_usage_cost, normalized_traffic_vars); + + LinearExpr objective_expr; + objective_expr.AddVar(max_usage_cost); + for (const BoolVar var : comfortable_traffic_vars) { + objective_expr.AddVar(var); + } + cp_model.Minimize(objective_expr); + + Model model; + if (!FLAGS_params.empty()) { + model.Add(NewSatParameters(FLAGS_params)); + } + int num_solutions = 0; + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse &r) { + LOG(INFO) << "Solution " << num_solutions; + const double objective_value = r.objective_value(); + const double percent = SolutionIntegerValue(r, max_usage_cost) / 10.0; + int num_non_comfortable_arcs = 0; + for (const BoolVar comfort : comfortable_traffic_vars) { + num_non_comfortable_arcs += SolutionBooleanValue(r, comfort); + } + if (num_non_comfortable_arcs > 0) { + LOG(INFO) << "*** Found a solution with a max usage of " << percent + << "%, and " << num_non_comfortable_arcs + << " links above the comfort zone"; + } else { + LOG(INFO) << "*** Found a solution with a max usage of " << percent + << "%"; + } + num_solutions++; + })); + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + return response.objective_value(); + } + + private: + int count_arcs() const { return arcs_data_.size() / 2; } + + std::vector> arcs_data_; + std::vector arc_capacity_; + std::vector demands_array_; + int num_nodes_; + std::vector all_min_path_lengths_; + std::vector> capacity_; + std::vector> all_paths_; +}; + +} // namespace sat +} // namespace operations_research + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + operations_research::sat::NetworkRoutingData data; + operations_research::sat::NetworkRoutingDataBuilder builder; + builder.BuildModelFromParameters( + FLAGS_clients, FLAGS_backbones, FLAGS_demands, FLAGS_traffic_min, + FLAGS_traffic_max, FLAGS_min_client_degree, FLAGS_max_client_degree, + FLAGS_min_backbone_degree, FLAGS_max_backbone_degree, FLAGS_max_capacity, + FLAGS_fixed_charge_cost, FLAGS_seed, &data); + operations_research::sat::NetworkRoutingSolver solver; + solver.Init(data, FLAGS_extra_hops, FLAGS_max_paths); + LOG(INFO) << "Final cost = " << solver.Solve(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/no_overlap_sample_sat.cc b/libs/or-tools-bin/examples/cpp/no_overlap_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/no_overlap_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/no_overlap_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/nqueens.cc b/libs/or-tools-bin/examples/cpp/nqueens.cc new file mode 100644 index 0000000000000000000000000000000000000000..d99a6eff17eee0a6e4fd7088d91f0f8fc18365b6 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/nqueens.cc @@ -0,0 +1,278 @@ +// 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. + +// +// N-queens problem +// +// unique solutions: http://www.research.att.com/~njas/sequences/A000170 +// distinct solutions: http://www.research.att.com/~njas/sequences/A002562 + +#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/constraint_solver/constraint_solveri.h" + +DEFINE_bool(print, false, "If true, print one of the solution."); +DEFINE_bool(print_all, false, "If true, print all the solutions."); +DEFINE_int32(nb_loops, 1, + "Number of solving loops to perform, for performance timing."); +DEFINE_int32( + size, 0, + "Size of the problem. If equal to 0, will test several increasing sizes."); +DEFINE_bool(use_symmetry, false, "Use Symmetry Breaking methods"); +DECLARE_bool(cp_disable_solve); + +static const int kNumSolutions[] = { + 1, 0, 0, 2, 10, 4, 40, 92, 352, 724, 2680, 14200, 73712, 365596, 2279184}; +static const int kKnownSolutions = 15; + +static const int kNumUniqueSolutions[] = { + 1, 0, 0, 1, 2, 1, 6, 12, 46, 92, + 341, 1787, 9233, 45752, 285053, 1846955, 11977939, 83263591, 621012754}; +static const int kKnownUniqueSolutions = 19; + +namespace operations_research { + +class NQueenSymmetry : public SymmetryBreaker { + public: + NQueenSymmetry(Solver* const s, const std::vector& vars) + : solver_(s), vars_(vars), size_(vars.size()) { + for (int i = 0; i < size_; ++i) { + indices_[vars[i]] = i; + } + } + ~NQueenSymmetry() override {} + + protected: + int Index(IntVar* const var) const { + return gtl::FindWithDefault(indices_, var, -1); + } + IntVar* Var(int index) const { + DCHECK_GE(index, 0); + DCHECK_LT(index, size_); + return vars_[index]; + } + int size() const { return size_; } + int symmetric(int index) const { return size_ - 1 - index; } + Solver* const solver() const { return solver_; } + + private: + Solver* const solver_; + const std::vector vars_; + std::map indices_; + const int size_; +}; + +// Symmetry vertical axis. +class SX : public NQueenSymmetry { + public: + SX(Solver* const s, const std::vector& vars) + : NQueenSymmetry(s, vars) {} + ~SX() override {} + + void VisitSetVariableValue(IntVar* const var, int64 value) override { + const int index = Index(var); + IntVar* const other_var = Var(symmetric(index)); + AddIntegerVariableEqualValueClause(other_var, value); + } +}; + +// Symmetry horizontal axis. +class SY : public NQueenSymmetry { + public: + SY(Solver* const s, const std::vector& vars) + : NQueenSymmetry(s, vars) {} + ~SY() override {} + + void VisitSetVariableValue(IntVar* const var, int64 value) override { + AddIntegerVariableEqualValueClause(var, symmetric(value)); + } +}; + +// Symmetry first diagonal axis. +class SD1 : public NQueenSymmetry { + public: + SD1(Solver* const s, const std::vector& vars) + : NQueenSymmetry(s, vars) {} + ~SD1() override {} + + void VisitSetVariableValue(IntVar* const var, int64 value) override { + const int index = Index(var); + IntVar* const other_var = Var(value); + AddIntegerVariableEqualValueClause(other_var, index); + } +}; + +// Symmetry second diagonal axis. +class SD2 : public NQueenSymmetry { + public: + SD2(Solver* const s, const std::vector& vars) + : NQueenSymmetry(s, vars) {} + ~SD2() override {} + + void VisitSetVariableValue(IntVar* const var, int64 value) override { + const int index = Index(var); + IntVar* const other_var = Var(symmetric(value)); + AddIntegerVariableEqualValueClause(other_var, symmetric(index)); + } +}; + +// Rotate 1/4 turn. +class R90 : public NQueenSymmetry { + public: + R90(Solver* const s, const std::vector& vars) + : NQueenSymmetry(s, vars) {} + ~R90() override {} + + void VisitSetVariableValue(IntVar* const var, int64 value) override { + const int index = Index(var); + IntVar* const other_var = Var(value); + AddIntegerVariableEqualValueClause(other_var, symmetric(index)); + } +}; + +// Rotate 1/2 turn. +class R180 : public NQueenSymmetry { + public: + R180(Solver* const s, const std::vector& vars) + : NQueenSymmetry(s, vars) {} + ~R180() override {} + + void VisitSetVariableValue(IntVar* const var, int64 value) override { + const int index = Index(var); + IntVar* const other_var = Var(symmetric(index)); + AddIntegerVariableEqualValueClause(other_var, symmetric(value)); + } +}; + +// Rotate 3/4 turn. +class R270 : public NQueenSymmetry { + public: + R270(Solver* const s, const std::vector& vars) + : NQueenSymmetry(s, vars) {} + ~R270() override {} + + void VisitSetVariableValue(IntVar* const var, int64 value) override { + const int index = Index(var); + IntVar* const other_var = Var(symmetric(value)); + AddIntegerVariableEqualValueClause(other_var, index); + } +}; + +void CheckNumberOfSolutions(int size, int num_solutions) { + if (FLAGS_use_symmetry) { + if (size - 1 < kKnownUniqueSolutions) { + CHECK_EQ(num_solutions, kNumUniqueSolutions[size - 1]); + } else if (!FLAGS_cp_disable_solve) { + CHECK_GT(num_solutions, 0); + } + } else { + if (size - 1 < kKnownSolutions) { + CHECK_EQ(num_solutions, kNumSolutions[size - 1]); + } else if (!FLAGS_cp_disable_solve) { + CHECK_GT(num_solutions, 0); + } + } +} + +void NQueens(int size) { + CHECK_GE(size, 1); + Solver s("nqueens"); + + // model + std::vector queens; + for (int i = 0; i < size; ++i) { + queens.push_back( + s.MakeIntVar(0, size - 1, absl::StrFormat("queen%04d", i))); + } + s.AddConstraint(s.MakeAllDifferent(queens)); + + std::vector vars(size); + for (int i = 0; i < size; ++i) { + vars[i] = s.MakeSum(queens[i], i)->Var(); + } + s.AddConstraint(s.MakeAllDifferent(vars)); + for (int i = 0; i < size; ++i) { + vars[i] = s.MakeSum(queens[i], -i)->Var(); + } + s.AddConstraint(s.MakeAllDifferent(vars)); + + SolutionCollector* const solution_counter = + s.MakeAllSolutionCollector(nullptr); + SolutionCollector* const collector = s.MakeAllSolutionCollector(); + collector->Add(queens); + std::vector monitors; + monitors.push_back(solution_counter); + monitors.push_back(collector); + DecisionBuilder* const db = s.MakePhase(queens, Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); + if (FLAGS_use_symmetry) { + std::vector breakers; + NQueenSymmetry* const sx = s.RevAlloc(new SX(&s, queens)); + breakers.push_back(sx); + NQueenSymmetry* const sy = s.RevAlloc(new SY(&s, queens)); + breakers.push_back(sy); + NQueenSymmetry* const sd1 = s.RevAlloc(new SD1(&s, queens)); + breakers.push_back(sd1); + NQueenSymmetry* const sd2 = s.RevAlloc(new SD2(&s, queens)); + breakers.push_back(sd2); + NQueenSymmetry* const r90 = s.RevAlloc(new R90(&s, queens)); + breakers.push_back(r90); + NQueenSymmetry* const r180 = s.RevAlloc(new R180(&s, queens)); + breakers.push_back(r180); + NQueenSymmetry* const r270 = s.RevAlloc(new R270(&s, queens)); + breakers.push_back(r270); + SearchMonitor* const symmetry_manager = s.MakeSymmetryManager(breakers); + monitors.push_back(symmetry_manager); + } + + for (int loop = 0; loop < FLAGS_nb_loops; ++loop) { + s.Solve(db, monitors); // go! + CheckNumberOfSolutions(size, solution_counter->solution_count()); + } + + const int num_solutions = solution_counter->solution_count(); + if (num_solutions > 0 && size < kKnownSolutions) { + int print_max = FLAGS_print_all ? num_solutions : FLAGS_print ? 1 : 0; + for (int n = 0; n < print_max; ++n) { + printf("--- solution #%d\n", n); + for (int i = 0; i < size; ++i) { + const int pos = static_cast(collector->Value(n, queens[i])); + for (int k = 0; k < pos; ++k) printf(" . "); + printf("%2d ", i); + for (int k = pos + 1; k < size; ++k) printf(" . "); + printf("\n"); + } + } + } + printf("========= number of solutions:%d\n", num_solutions); + absl::PrintF(" number of failures: %d\n", s.failures()); +} +} // namespace operations_research + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_size != 0) { + operations_research::NQueens(FLAGS_size); + } else { + for (int n = 1; n < 12; ++n) { + operations_research::NQueens(n); + } + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/nurses_cp.cc b/libs/or-tools-bin/examples/cpp/nurses_cp.cc similarity index 100% rename from libs/or-tools/examples/cpp/nurses_cp.cc rename to libs/or-tools-bin/examples/cpp/nurses_cp.cc diff --git a/libs/or-tools-bin/examples/cpp/opb_reader.h b/libs/or-tools-bin/examples/cpp/opb_reader.h new file mode 100644 index 0000000000000000000000000000000000000000..515a32af466e5859754f0bc33a3192280bacb6d7 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/opb_reader.h @@ -0,0 +1,138 @@ +// 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_SAT_OPB_READER_H_ +#define OR_TOOLS_SAT_OPB_READER_H_ + +#include +#include +#include +#include + +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "ortools/base/filelineiter.h" +#include "ortools/base/logging.h" +#include "ortools/base/macros.h" +#include "ortools/sat/boolean_problem.pb.h" + +namespace operations_research { +namespace sat { + +// This class loads a file in pbo file format into a LinearBooleanProblem. +// The format is described here: +// http://www.cril.univ-artois.fr/PB12/format.pdf +class OpbReader { + public: + OpbReader() {} + + // Loads the given opb filename into the given problem. + bool Load(const std::string& filename, LinearBooleanProblem* problem) { + problem->Clear(); + problem->set_name(ExtractProblemName(filename)); + + num_variables_ = 0; + int num_lines = 0; + for (const std::string& line : FileLines(filename)) { + ++num_lines; + ProcessNewLine(problem, line); + } + if (num_lines == 0) { + LOG(FATAL) << "File '" << filename << "' is empty or can't be read."; + } + problem->set_num_variables(num_variables_); + return true; + } + + private: + // Since the problem name is not stored in the cnf format, we infer it from + // the file name. + static std::string ExtractProblemName(const std::string& filename) { + const int found = filename.find_last_of("/"); + const std::string problem_name = + found != std::string::npos ? filename.substr(found + 1) : filename; + return problem_name; + } + + void ProcessNewLine(LinearBooleanProblem* problem, const std::string& line) { + const std::vector words = + absl::StrSplit(line, absl::ByAnyChar(" ;"), absl::SkipEmpty()); + if (words.empty() || words[0].empty() || words[0][0] == '*') { + return; + } + + if (words[0] == "min:") { + LinearObjective* objective = problem->mutable_objective(); + for (int i = 1; i < words.size(); ++i) { + const std::string& word = words[i]; + if (word.empty() || word[0] == ';') continue; + if (word[0] == 'x') { + int literal; + CHECK(absl::SimpleAtoi(word.substr(1), &literal)); + num_variables_ = std::max(num_variables_, literal); + objective->add_literals(literal); + } else { + int64 value; + CHECK(absl::SimpleAtoi(word, &value)); + objective->add_coefficients(value); + } + } + if (objective->literals_size() != objective->coefficients_size()) { + LOG(INFO) << "words.size() = " << words.size(); + LOG(FATAL) << "Failed to parse objective:\n " << line; + } + return; + } + LinearBooleanConstraint* constraint = problem->add_constraints(); + for (int i = 0; i < words.size(); ++i) { + const std::string& word = words[i]; + CHECK(!word.empty()); + if (word == ">=") { + CHECK_LT(i + 1, words.size()); + int64 value; + CHECK(absl::SimpleAtoi(words[i + 1], &value)); + constraint->set_lower_bound(value); + break; + } else if (word == "=") { + CHECK_LT(i + 1, words.size()); + int64 value; + CHECK(absl::SimpleAtoi(words[i + 1], &value)); + constraint->set_upper_bound(value); + constraint->set_lower_bound(value); + break; + } else { + if (word[0] == 'x') { + int literal; + CHECK(absl::SimpleAtoi(word.substr(1), &literal)); + num_variables_ = std::max(num_variables_, literal); + constraint->add_literals(literal); + } else { + int64 value; + CHECK(absl::SimpleAtoi(words[i], &value)); + constraint->add_coefficients(value); + } + } + } + if (constraint->literals_size() != constraint->coefficients_size()) { + LOG(FATAL) << "Failed to parse constraint:\n " << line; + } + } + + int num_variables_; + DISALLOW_COPY_AND_ASSIGN(OpbReader); +}; + +} // namespace sat +} // namespace operations_research + +#endif // OR_TOOLS_SAT_OPB_READER_H_ diff --git a/libs/or-tools/examples/cpp/optional_interval_sample_sat.cc b/libs/or-tools-bin/examples/cpp/optional_interval_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/optional_interval_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/optional_interval_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/parse_dimacs_assignment.h b/libs/or-tools-bin/examples/cpp/parse_dimacs_assignment.h new file mode 100644 index 0000000000000000000000000000000000000000..06b97d74b20dbbb8ae06163176a6b88e34720bd5 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/parse_dimacs_assignment.h @@ -0,0 +1,262 @@ +// 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. + +// +// Function for reading and parsing a file in DIMACS format: +// http://lpsolve.sourceforge.net/5.5/DIMACS_asn.htm +// + +#ifndef OR_TOOLS_EXAMPLES_PARSE_DIMACS_ASSIGNMENT_H_ +#define OR_TOOLS_EXAMPLES_PARSE_DIMACS_ASSIGNMENT_H_ + +#include +#include +#include +#include +#include + +#include "ortools/base/commandlineflags.h" +#include "ortools/base/filelineiter.h" +#include "ortools/base/logging.h" +#include "ortools/graph/ebert_graph.h" +#include "ortools/graph/linear_assignment.h" + +DEFINE_bool(assignment_maximize_cost, false, + "Negate costs so a max-cost assignment is found."); +DEFINE_bool(assignment_optimize_layout, true, + "Optimize graph layout for speed."); + +namespace operations_research { + +template +class LinearSumAssignment; + +template +class DimacsAssignmentParser { + public: + explicit DimacsAssignmentParser(const std::string& filename) + : filename_(filename), graph_builder_(nullptr), assignment_(nullptr) {} + + // Reads an assignment problem description from the given file in + // DIMACS format and returns a LinearSumAssignment object representing + // the problem description. For a description of the format, see + // http://lpsolve.sourceforge.net/5.5/DIMACS_asn.htm + // + // Also returns an error message (empty if no error) and a handle on + // the underlying graph representation. The error_message pointer must + // not be NULL because we insist on returning an explanatory message + // in the case of error. The graph_handle pointer must not be NULL + // because unless we pass a non-const pointer to the graph + // representation back to the caller, the caller lacks a good way to + // free the underlying graph (which isn't owned by the + // LinearAssignment instance). + LinearSumAssignment* Parse(std::string* error_message, + GraphType** graph); + + private: + void ParseProblemLine(const std::string& line); + + void ParseNodeLine(const std::string& line); + + void ParseArcLine(const std::string& line); + + void ParseOneLine(const std::string& line); + + std::string filename_; + + struct ErrorTrackingState { + ErrorTrackingState() + : bad(false), + nodes_described(false), + reason(nullptr), + num_left_nodes(0), + num_arcs(0) {} + + bool bad; + bool nodes_described; + const char* reason; + NodeIndex num_left_nodes; + ArcIndex num_arcs; + std::unique_ptr bad_line; + }; + + ErrorTrackingState state_; + + AnnotatedGraphBuildManager* graph_builder_; + + LinearSumAssignment* assignment_; +}; + +// Implementation is below here. +template +void DimacsAssignmentParser::ParseProblemLine( + const std::string& line) { + static const char* kIncorrectProblemLine = + "Incorrect assignment problem line."; + static const char* kAssignmentProblemType = "asn"; + char problem_type[4]; + NodeIndex num_nodes; + ArcIndex num_arcs; + + if ((sscanf(line.c_str(), "%*c%3s%d%d", problem_type, &num_nodes, + &num_arcs) != 3) || + (strncmp(kAssignmentProblemType, problem_type, + strlen(kAssignmentProblemType)) != 0)) { + state_.bad = true; + state_.reason = kIncorrectProblemLine; + state_.bad_line.reset(new std::string(line)); + return; + } + + state_.num_arcs = num_arcs; + graph_builder_ = new AnnotatedGraphBuildManager( + num_nodes, num_arcs, FLAGS_assignment_optimize_layout); +} + +template +void DimacsAssignmentParser::ParseNodeLine(const std::string& line) { + NodeIndex node_id; + if (sscanf(line.c_str(), "%*c%d", &node_id) != 1) { + state_.bad = true; + state_.reason = "Syntax error in node desciption."; + state_.bad_line.reset(new std::string(line)); + return; + } + if (state_.nodes_described) { + state_.bad = true; + state_.reason = "All node description must precede first arc description."; + state_.bad_line.reset(new std::string(line)); + return; + } + state_.num_left_nodes = std::max(state_.num_left_nodes, node_id); +} + +template +void DimacsAssignmentParser::ParseArcLine(const std::string& line) { + if (graph_builder_ == nullptr) { + state_.bad = true; + state_.reason = + "Problem specification line must precede any arc specification."; + state_.bad_line.reset(new std::string(line)); + return; + } + if (!state_.nodes_described) { + state_.nodes_described = true; + DCHECK(assignment_ == nullptr); + assignment_ = new LinearSumAssignment(state_.num_left_nodes, + state_.num_arcs); + } + NodeIndex tail; + NodeIndex head; + CostValue cost; + if (sscanf(line.c_str(), "%*c%d%d%lld", &tail, &head, &cost) != 3) { + state_.bad = true; + state_.reason = "Syntax error in arc descriptor."; + state_.bad_line.reset(new std::string(line)); + } + ArcIndex arc = graph_builder_->AddArc(tail - 1, head - 1); + assignment_->SetArcCost(arc, FLAGS_assignment_maximize_cost ? -cost : cost); +} + +// Parameters out of style-guide order because this function is used +// as a callback that varies the input line. +template +void DimacsAssignmentParser::ParseOneLine(const std::string& line) { + if (state_.bad) { + return; + } + switch (line[0]) { + case 'p': { + // Problem-specification line + ParseProblemLine(line); + break; + } + case 'c': { + // Comment; do nothing. + return; + } + case 'n': { + // Node line defining a node on the left side + ParseNodeLine(line); + break; + } + case 'a': { + ParseArcLine(line); + break; + } + case '0': + case '\n': + break; + default: { + state_.bad = true; + state_.reason = "Unknown line type in the input."; + state_.bad_line.reset(new std::string(line)); + break; + } + } +} + +// Reads an assignment problem description from the given file in +// DIMACS format and returns a LinearSumAssignment object representing +// the problem description. For a description of the format, see +// http://lpsolve.sourceforge.net/5.5/DIMACS_asn.htm +// +// Also returns an error message (empty if no error) and a handle on +// the underlying graph representation. The error_message pointer must +// not be NULL because we insist on returning an explanatory message +// in the case of error. The graph_handle pointer must not be NULL +// because unless we pass a non-const pointer to the graph +// representation back to the caller, the caller lacks a good way to +// free the underlying graph (which isn't owned by the +// LinearAssignment instance). +template +LinearSumAssignment* DimacsAssignmentParser::Parse( + std::string* error_message, GraphType** graph_handle) { + CHECK(error_message != nullptr); + CHECK(graph_handle != nullptr); + + for (const std::string& line : FileLines(filename_)) { + if (line.empty()) { + continue; + } + ParseOneLine(line); + } + + if (state_.bad) { + *error_message = state_.reason; + *error_message = *error_message + ": \"" + *state_.bad_line + "\""; + return nullptr; + } + if (graph_builder_ == nullptr) { + *error_message = "empty graph description"; + return nullptr; + } + std::unique_ptr > cycle_handler( + assignment_->ArcAnnotationCycleHandler()); + GraphType* graph = graph_builder_->Graph(cycle_handler.get()); + if (graph == nullptr) { + *error_message = "unable to create compact static graph"; + return nullptr; + } + assignment_->SetGraph(graph); + *error_message = ""; + // Return a handle on the graph to the caller so the caller can free + // the graph's memory, because the LinearSumAssignment object does + // not take ownership of the graph and hence will not free it. + *graph_handle = graph; + return assignment_; +} + +} // namespace operations_research + +#endif // OR_TOOLS_EXAMPLES_PARSE_DIMACS_ASSIGNMENT_H_ diff --git a/libs/or-tools-bin/examples/cpp/pdptw.cc b/libs/or-tools-bin/examples/cpp/pdptw.cc new file mode 100644 index 0000000000000000000000000000000000000000..096a9522aacc6e12bbb1c55a401d53b5106ade5f --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/pdptw.cc @@ -0,0 +1,406 @@ +// 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. + +// +// Pickup and Delivery Problem with Time Windows. +// The overall objective is to minimize the length of the routes delivering +// quantities of goods between pickup and delivery locations, taking into +// account vehicle capacities and node time windows. +// Given a set of pairs of pickup and delivery nodes, find the set of routes +// visiting all the nodes, such that +// - corresponding pickup and delivery nodes are visited on the same route, +// - the pickup node is visited before the corresponding delivery node, +// - the quantity picked up at the pickup node is the same as the quantity +// delivered at the delivery node, +// - the total quantity carried by a vehicle at any time is less than its +// capacity, +// - 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 overridden using the --pdp_force_vehicles +// flag. +// +// A further description of the problem can be found here: +// http://en.wikipedia.org/wiki/Vehicle_routing_problem +// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.123.9965&rep=rep1&type=pdf. +// 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/commandlineflags.h" +#include "ortools/base/file.h" +#include "ortools/base/mathutil.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_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."); +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 { + +// Scaling factor used to scale up distances, allowing a bit more precision +// from Euclidean distances. +const int64 kScalingFactor = 1000; + +// Vector of (x,y) node coordinates, *unscaled*, in some imaginary planar, +// metric grid. +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, + 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 = + coords->at(from.value()).second - coords->at(to.value()).second; + return static_cast(kScalingFactor * + std::sqrt(1.0L * xd * xd + yd * yd)); +} + +// Returns the scaled service time at a given node, service_times holding the +// service times. +int64 ServiceTime(const std::vector* const service_times, + RoutingIndexManager::NodeIndex node) { + return kScalingFactor * service_times->at(node.value()); +} + +// 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 RoutingIndexManager& manager, + const Coordinates* const coords, + const std::vector* const service_times, + 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 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) { + std::string output; + const RoutingDimension& time_dimension = routing.GetDimensionOrDie("time"); + const RoutingDimension& load_dimension = routing.GetDimensionOrDie("demand"); + for (int i = 0; i < routing.vehicles(); ++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)) { + absl::StrAppendFormat(&output, "%d ", + manager.IndexToNode(index).value()); + const IntVar* vehicle = routing.VehicleVar(index); + absl::StrAppendFormat(&output, "Vehicle(%d) ", + assignment.Value(vehicle)); + const IntVar* arrival = time_dimension.CumulVar(index); + absl::StrAppendFormat(&output, "Time(%d..%d) ", assignment.Min(arrival), + assignment.Max(arrival)); + const IntVar* load = load_dimension.CumulVar(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); + absl::StrAppendFormat(&output, "Vehicle(%d) ", assignment.Value(vehicle)); + const IntVar* arrival = time_dimension.CumulVar(index); + absl::StrAppendFormat(&output, "Time(%d..%d) ", assignment.Min(arrival), + assignment.Max(arrival)); + const IntVar* load = load_dimension.CumulVar(index); + absl::StrAppendFormat(&output, "Load(%d..%d) ", assignment.Min(load), + assignment.Max(load)); + } + output.append("\n"); + } + return output; +} + +namespace { +// An inefficient but convenient method to parse a whitespace-separated list +// of integers. Returns true iff the input std::string was entirely valid and +// parsed. +bool SafeParseInt64Array(const std::string& str, + std::vector* parsed_int) { + 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, + 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; + { + std::string contents; + CHECK_OK(file::GetContents(pdp_file, &contents, file::Defaults())); + const int64 kMaxInputFileSize = 1 << 30; // 1GB + if (contents.size() >= kMaxInputFileSize) { + LOG(WARNING) << "Input file '" << pdp_file << "' is too large (>" + << kMaxInputFileSize << " bytes)."; + return false; + } + lines = absl::StrSplit(contents, '\n', absl::SkipEmpty()); + } + // Reading header. + if (lines.empty()) { + LOG(WARNING) << "Empty file: " << pdp_file; + return false; + } + // Parse file header. + std::vector parsed_int; + if (!SafeParseInt64Array(lines[0], &parsed_int) || parsed_int.size() != 3 || + parsed_int[0] < 0 || parsed_int[1] < 0 || parsed_int[2] < 0) { + LOG(WARNING) << "Malformed header: " << lines[0]; + return false; + } + const int num_vehicles = + FLAGS_pdp_force_vehicles > 0 ? FLAGS_pdp_force_vehicles : parsed_int[0]; + const int64 capacity = parsed_int[1]; + // We do not care about the 'speed' field, in third position. + + // Parse order data. + std::vector customer_ids; + std::vector > coords; + std::vector demands; + std::vector open_times; + std::vector close_times; + std::vector service_times; + std::vector pickups; + std::vector deliveries; + int64 horizon = 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 || + parsed_int[5] < 0 || parsed_int[6] < 0 || parsed_int[7] < 0 || + parsed_int[8] < 0) { + LOG(WARNING) << "Malformed line #" << line_index << ": " + << lines[line_index]; + return false; + } + const int customer_id = parsed_int[0]; + const int x = parsed_int[1]; + const int y = parsed_int[2]; + const int64 demand = parsed_int[3]; + const int64 open_time = parsed_int[4]; + const int64 close_time = parsed_int[5]; + const int64 service_time = parsed_int[6]; + const int pickup = parsed_int[7]; + const int delivery = parsed_int[8]; + customer_ids.push_back(customer_id); + coords.push_back(std::make_pair(x, y)); + demands.push_back(demand); + open_times.push_back(open_time); + close_times.push_back(close_time); + service_times.push_back(service_time); + pickups.push_back(RoutingIndexManager::NodeIndex(pickup)); + deliveries.push_back(RoutingIndexManager::NodeIndex(delivery)); + if (pickup == 0 && delivery == 0) { + depot = RoutingIndexManager::NodeIndex(pickups.size() - 1); + } + horizon = std::max(horizon, close_time); + } + + // Build pickup and delivery model. + const int num_nodes = customer_ids.size(); + 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 (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(index, + manager.NodeToIndex(deliveries[node])); + } + IntVar* const cumul = time_dimension.CumulVar(index); + 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 (RoutingIndexManager::NodeIndex order(1); order < routing.nodes(); + ++order) { + std::vector orders(1, manager.NodeToIndex(order)); + routing.AddDisjunction(orders, kPenalty); + } + + // Solve pickup and delivery problem. + 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(); + 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; +} + +} // namespace operations_research + +int main(int argc, char** argv) { + absl::SetFlag(&FLAGS_logtostderr, true); + gflags::ParseCommandLineFlags(&argc, &argv, true); + 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/libs/or-tools-bin/examples/cpp/print_dimacs_assignment.h b/libs/or-tools-bin/examples/cpp/print_dimacs_assignment.h new file mode 100644 index 0000000000000000000000000000000000000000..594cee240372159003710cd39e0800ffc0c8f1d5 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/print_dimacs_assignment.h @@ -0,0 +1,72 @@ +// 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. + +// +// Function for outputting an assignment problem in DIMACS format: +// http://lpsolve.sourceforge.net/5.5/DIMACS_asn.htm +// +#ifndef OR_TOOLS_EXAMPLES_PRINT_DIMACS_ASSIGNMENT_H_ +#define OR_TOOLS_EXAMPLES_PRINT_DIMACS_ASSIGNMENT_H_ + +#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/graph/ebert_graph.h" +#include "ortools/graph/linear_assignment.h" + +namespace operations_research { + +template +class LinearSumAssignment; + +// Given a LinearSumAssigment object representing an assignment problem +// description, outputs the problem in DIMACS format in the output file. +// For a description of the format, see +// http://lpsolve.sourceforge.net/5.5/DIMACS_asn.htm +template +void PrintDimacsAssignmentProblem( + const LinearSumAssignment& assignment, + const TailArrayManager& tail_array_manager, + const std::string& output_filename) { + File* output; + CHECK_OK(file::Open(output_filename, "w", &output, file::Defaults())); + const GraphType& graph(assignment.Graph()); + std::string output_line = + 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 = absl::StrFormat("n %d\n", node_it.Index() + 1); + CHECK_OK(file::WriteString(output, output_line, file::Defaults())); + } + + tail_array_manager.BuildTailArrayFromAdjacencyListsIfForwardGraph(); + + for (typename GraphType::ArcIterator arc_it(assignment.Graph()); arc_it.Ok(); + arc_it.Next()) { + ArcIndex arc = arc_it.Index(); + 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())); + } +} + +} // namespace operations_research + +#endif // OR_TOOLS_EXAMPLES_PRINT_DIMACS_ASSIGNMENT_H_ diff --git a/libs/or-tools/examples/cpp/rabbits_and_pheasants_cp.cc b/libs/or-tools-bin/examples/cpp/rabbits_and_pheasants_cp.cc similarity index 100% rename from libs/or-tools/examples/cpp/rabbits_and_pheasants_cp.cc rename to libs/or-tools-bin/examples/cpp/rabbits_and_pheasants_cp.cc diff --git a/libs/or-tools/examples/cpp/rabbits_and_pheasants_sat.cc b/libs/or-tools-bin/examples/cpp/rabbits_and_pheasants_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/rabbits_and_pheasants_sat.cc rename to libs/or-tools-bin/examples/cpp/rabbits_and_pheasants_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/random_tsp.cc b/libs/or-tools-bin/examples/cpp/random_tsp.cc new file mode 100644 index 0000000000000000000000000000000000000000..426c93dac4ff7987e719c8af163ee059a9baab9c --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/random_tsp.cc @@ -0,0 +1,182 @@ +// 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. + +// +// Traveling Salesman Sample. +// +// This is a sample using the routing library to solve a Traveling Salesman +// Problem. +// The description of the problem can be found here: +// http://en.wikipedia.org/wiki/Travelling_salesman_problem. +// For small problems one can use the hamiltonian path library directly (cf +// graph/hamiltonian_path.h). +// The optimization engine uses local search to improve solutions, first +// solutions being generated using a cheapest addition heuristic. +// Optionally one can randomly forbid a set of random connections between nodes +// (forbidden arcs). + +#include + +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "google/protobuf/text_format.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/base/integral_types.h" +#include "ortools/base/random.h" +#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" + +DEFINE_int32(tsp_size, 10, "Size of Traveling Salesman Problem instance."); +DEFINE_bool(tsp_use_random_matrix, true, "Use random cost matrix."); +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 { + +// Random seed generator. +int32 GetSeed() { + if (FLAGS_tsp_use_deterministic_random_seed) { + return ACMRandom::DeterministicSeed(); + } else { + return ACMRandom::HostnamePidTimeSeed(); + } +} + +// Cost/distance functions. + +// Sample function. +int64 MyDistance(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) { + // Put your distance code here. + return (from + to).value(); // for instance +} + +// Random matrix. +class RandomMatrix { + public: + explicit RandomMatrix(int size) : size_(size) {} + void Initialize() { + matrix_ = absl::make_unique(size_ * size_); + const int64 kDistanceMax = 100; + ACMRandom randomizer(GetSeed()); + 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 { + matrix_[MatrixIndex(from, to)] = 0LL; + } + } + } + } + int64 Distance(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const { + return matrix_[MatrixIndex(from, to)]; + } + + private: + int64 MatrixIndex(RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) const { + return (from * size_ + to).value(); + } + std::unique_ptr matrix_; + const int size_; +}; + +void Tsp() { + if (FLAGS_tsp_size > 0) { + // TSP of size FLAGS_tsp_size. + // 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. + 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 + // has the following signature: ResultCallback2. + // The two arguments are the from and to node inidices. + RandomMatrix matrix(FLAGS_tsp_size); + if (FLAGS_tsp_use_random_matrix) { + matrix.Initialize(); + 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 { + 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()); + int64 forbidden_connections = 0; + while (forbidden_connections < FLAGS_tsp_random_forbidden_connections) { + const int64 from = randomizer.Uniform(FLAGS_tsp_size - 1); + const int64 to = randomizer.Uniform(FLAGS_tsp_size - 1) + 1; + if (routing.NextVar(from)->Contains(to)) { + LOG(INFO) << "Forbidding connection " << from << " -> " << to; + routing.NextVar(from)->RemoveValue(to); + ++forbidden_connections; + } + } + // Solve, returns a solution if any (owned by RoutingModel). + const Assignment* solution = routing.SolveWithParameters(parameters); + if (solution != nullptr) { + // Solution cost. + LOG(INFO) << "Cost " << solution->ObjectiveValue(); + // Inspect solution. + // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1 + const int route_number = 0; + std::string route; + for (int64 node = routing.Start(route_number); !routing.IsEnd(node); + node = solution->Value(routing.NextVar(node))) { + absl::StrAppend(&route, manager.IndexToNode(node).value(), " (", node, + ") -> "); + } + const int64 end = routing.End(route_number); + absl::StrAppend(&route, manager.IndexToNode(end).value(), " (", end, ")"); + LOG(INFO) << route; + } else { + LOG(INFO) << "No solution found."; + } + } else { + LOG(INFO) << "Specify an instance size greater than 0."; + } +} +} // namespace operations_research + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + operations_research::Tsp(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/ranking_sample_sat.cc b/libs/or-tools-bin/examples/cpp/ranking_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/ranking_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/ranking_sample_sat.cc diff --git a/libs/or-tools/examples/cpp/reified_sample_sat.cc b/libs/or-tools-bin/examples/cpp/reified_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/reified_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/reified_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/sat_cnf_reader.h b/libs/or-tools-bin/examples/cpp/sat_cnf_reader.h new file mode 100644 index 0000000000000000000000000000000000000000..78460acddc0fad3c7fe71bcd3ac2d1e1ae6a283f --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/sat_cnf_reader.h @@ -0,0 +1,340 @@ +// 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_SAT_SAT_CNF_READER_H_ +#define OR_TOOLS_SAT_SAT_CNF_READER_H_ + +#include +#include +#include +#include +#include + +#include "absl/strings/numbers.h" +#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/sat/boolean_problem.pb.h" +#include "ortools/sat/cp_model.pb.h" + +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."); + +namespace operations_research { +namespace sat { + +struct LinearBooleanProblemWrapper { + explicit LinearBooleanProblemWrapper(LinearBooleanProblem* p) : problem(p) {} + + void SetNumVariables(int num) { problem->set_num_variables(num); } + void SetOriginalNumVariables(int num) { + problem->set_original_num_variables(num); + } + + void AddConstraint(absl::Span clause) { + LinearBooleanConstraint* constraint = problem->add_constraints(); + constraint->mutable_literals()->Reserve(clause.size()); + constraint->mutable_coefficients()->Reserve(clause.size()); + constraint->set_lower_bound(1); + for (const int literal : clause) { + constraint->add_literals(literal); + constraint->add_coefficients(1); + } + } + + void AddObjectiveTerm(int literal, int64 value) { + CHECK_GE(literal, 0) << "Negative literal not supported."; + problem->mutable_objective()->add_literals(literal); + problem->mutable_objective()->add_coefficients(value); + } + + void SetObjectiveOffset(int64 offset) { + problem->mutable_objective()->set_offset(offset); + } + + LinearBooleanProblem* problem; +}; + +struct CpModelProtoWrapper { + explicit CpModelProtoWrapper(CpModelProto* p) : problem(p) {} + + void SetNumVariables(int num) { + for (int i = 0; i < num; ++i) { + IntegerVariableProto* variable = problem->add_variables(); + variable->add_domain(0); + variable->add_domain(1); + } + } + + // TODO(user): Not supported. This is only used for displaying a wcnf + // solution in cnf format, so it is not useful internally. Instead of adding + // another field, we could use the variables names or the search heuristics + // to encode this info. + void SetOriginalNumVariables(int num) {} + + int LiteralToRef(int signed_value) { + return signed_value > 0 ? signed_value - 1 : signed_value; + } + + void AddConstraint(absl::Span clause) { + auto* constraint = problem->add_constraints()->mutable_bool_or(); + constraint->mutable_literals()->Reserve(clause.size()); + for (const int literal : clause) { + constraint->add_literals(LiteralToRef(literal)); + } + } + + void AddObjectiveTerm(int literal, int64 value) { + CHECK_GE(literal, 0) << "Negative literal not supported."; + problem->mutable_objective()->add_vars(LiteralToRef(literal)); + problem->mutable_objective()->add_coeffs(value); + } + + void SetObjectiveOffset(int64 offset) { + problem->mutable_objective()->set_offset(offset); + } + + CpModelProto* problem; +}; + +// This class loads a file in cnf file format into a SatProblem. +// The format is described here: +// http://people.sc.fsu.edu/~jburkardt/data/cnf/cnf.html +// +// It also support the wcnf input format for partial weighted max-sat problems. +class SatCnfReader { + public: + SatCnfReader() : interpret_cnf_as_max_sat_(false) {} + + // If called with true, then a cnf file will be converted to the max-sat + // problem: Try to minimize the number of unsatisfiable clauses. + void InterpretCnfAsMaxSat(bool v) { interpret_cnf_as_max_sat_ = v; } + + // Loads the given cnf filename into the given proto. + bool Load(const std::string& filename, LinearBooleanProblem* problem) { + problem->Clear(); + problem->set_name(ExtractProblemName(filename)); + LinearBooleanProblemWrapper wrapper(problem); + return LoadInternal(filename, &wrapper); + } + bool Load(const std::string& filename, CpModelProto* problem) { + problem->Clear(); + problem->set_name(ExtractProblemName(filename)); + CpModelProtoWrapper wrapper(problem); + return LoadInternal(filename, &wrapper); + } + + private: + template + bool LoadInternal(const std::string& filename, Problem* problem) { + positive_literal_to_weight_.clear(); + objective_offset_ = 0; + is_wcnf_ = false; + end_marker_seen_ = false; + hard_weight_ = 0; + num_skipped_soft_clauses_ = 0; + num_singleton_soft_clauses_ = 0; + num_added_clauses_ = 0; + num_slack_variables_ = 0; + + int num_lines = 0; + for (const std::string& line : FileLines(filename)) { + ++num_lines; + ProcessNewLine(line, problem); + } + if (num_lines == 0) { + LOG(FATAL) << "File '" << filename << "' is empty or can't be read."; + } + problem->SetOriginalNumVariables(num_variables_); + problem->SetNumVariables(num_variables_ + num_slack_variables_); + + // Fill the objective. + if (!positive_literal_to_weight_.empty()) { + for (const std::pair p : positive_literal_to_weight_) { + if (p.second != 0) { + problem->AddObjectiveTerm(p.first, p.second); + } + } + problem->SetObjectiveOffset(objective_offset_); + } + + if (num_clauses_ != num_added_clauses_ + num_singleton_soft_clauses_ + + num_skipped_soft_clauses_) { + LOG(ERROR) << "Wrong number of clauses. " << num_clauses_ << " " + << num_added_clauses_; + return false; + } + return true; + } + + // Since the problem name is not stored in the cnf format, we infer it from + // the file name. + static std::string ExtractProblemName(const std::string& filename) { + const int found = filename.find_last_of("/"); + const std::string problem_name = + found != std::string::npos ? filename.substr(found + 1) : filename; + return problem_name; + } + + int64 StringPieceAtoi(absl::string_view input) { + int64 value; + // Hack: data() is not null terminated, but we do know that it points + // inside a string where numbers are separated by " " and since SimpleAtoi + // will stop at the first invalid char, this works. + CHECK(absl::SimpleAtoi(input, &value)); + return value; + } + + void ProcessHeader(const std::string& line) { + static const char kWordDelimiters[] = " "; + words_ = absl::StrSplit(line, kWordDelimiters, absl::SkipEmpty()); + + CHECK_EQ(words_[0], "p"); + if (words_[1] == "cnf" || words_[1] == "wcnf") { + num_variables_ = StringPieceAtoi(words_[2]); + num_clauses_ = StringPieceAtoi(words_[3]); + if (words_[1] == "wcnf") { + is_wcnf_ = true; + hard_weight_ = (words_.size() > 4) ? StringPieceAtoi(words_[4]) : 0; + } + } else { + // TODO(user): The ToString() is only required for the open source. Fix. + LOG(FATAL) << "Unknown file type: " << words_[1]; + } + } + + template + void ProcessNewLine(const std::string& line, Problem* problem) { + if (line.empty() || end_marker_seen_) return; + if (line[0] == 'c') return; + if (line[0] == '%') { + end_marker_seen_ = true; + return; + } + if (line[0] == 'p') { + ProcessHeader(line); + return; + } + + static const char kWordDelimiters[] = " "; + auto splitter = absl::StrSplit(line, kWordDelimiters, absl::SkipEmpty()); + + tmp_clause_.clear(); + int64 weight = (!is_wcnf_ && interpret_cnf_as_max_sat_) ? 1 : hard_weight_; + bool first = true; + bool end_marker_seen = false; + for (const absl::string_view word : splitter) { + const int64 signed_value = StringPieceAtoi(word); + if (first && is_wcnf_) { + // Mathematically, a soft clause of weight 0 can be removed. + if (signed_value == 0) { + ++num_skipped_soft_clauses_; + return; + } + weight = signed_value; + } else { + if (signed_value == 0) { + end_marker_seen = true; + break; // end of clause. + } + tmp_clause_.push_back(signed_value); + } + first = false; + } + if (!end_marker_seen) return; + + if (weight == hard_weight_) { + ++num_added_clauses_; + problem->AddConstraint(tmp_clause_); + } else { + if (tmp_clause_.size() == 1) { + // The max-sat formulation of an optimization sat problem with a + // linear objective introduces many singleton soft clauses. Because we + // natively work with a linear objective, we can just add the cost to + // the unique variable of such clause and remove the clause. + ++num_singleton_soft_clauses_; + const int literal = -tmp_clause_[0]; + if (literal > 0) { + positive_literal_to_weight_[literal] += weight; + } else { + positive_literal_to_weight_[-literal] -= weight; + objective_offset_ += weight; + } + } else { + // The +1 is because a positive literal is the same as the 1-based + // variable index. + const int slack_literal = num_variables_ + num_slack_variables_ + 1; + ++num_slack_variables_; + + tmp_clause_.push_back(slack_literal); + + ++num_added_clauses_; + problem->AddConstraint(tmp_clause_); + + if (slack_literal > 0) { + positive_literal_to_weight_[slack_literal] += weight; + } else { + positive_literal_to_weight_[-slack_literal] -= weight; + objective_offset_ += weight; + } + + if (FLAGS_wcnf_use_strong_slack) { + // Add the binary implications slack_literal true => all the other + // clause literals are false. + for (int i = 0; i + 1 < tmp_clause_.size(); ++i) { + problem->AddConstraint({-slack_literal, -tmp_clause_[i]}); + } + } + } + } + } + + bool interpret_cnf_as_max_sat_; + + int num_clauses_; + int num_variables_; + + // Temporary storage for ProcessNewLine(). + std::vector words_; + + // We stores the objective in a map because we want the variables to appear + // only once in the LinearObjective proto. + std::map positive_literal_to_weight_; + int64 objective_offset_; + + // Used for the wcnf format. + bool is_wcnf_; + // Some files have text after %. This indicates if we have seen the '%'. + bool end_marker_seen_; + int64 hard_weight_; + + int num_slack_variables_; + int num_skipped_soft_clauses_; + int num_singleton_soft_clauses_; + int num_added_clauses_; + + std::vector tmp_clause_; + + DISALLOW_COPY_AND_ASSIGN(SatCnfReader); +}; + +} // namespace sat +} // namespace operations_research + +#endif // OR_TOOLS_SAT_SAT_CNF_READER_H_ diff --git a/libs/or-tools-bin/examples/cpp/sat_runner.cc b/libs/or-tools-bin/examples/cpp/sat_runner.cc new file mode 100644 index 0000000000000000000000000000000000000000..3019e9893d1b19e199e855891ee59b4aa7f0dde4 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/sat_runner.cc @@ -0,0 +1,445 @@ +// 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 "absl/memory/memory.h" +#include "absl/strings/match.h" +#include "absl/strings/numbers.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/logging.h" +#include "ortools/base/status.h" +#include "ortools/base/timer.h" +#include "ortools/sat/boolean_problem.h" +#include "ortools/sat/boolean_problem.pb.h" +#include "ortools/sat/cp_model.pb.h" +#include "ortools/sat/cp_model_solver.h" +#include "ortools/sat/drat_proof_handler.h" +#include "ortools/sat/lp_utils.h" +#include "ortools/sat/model.h" +#include "ortools/sat/optimization.h" +#include "ortools/sat/pb_constraint.h" +#include "ortools/sat/sat_base.h" +#include "ortools/sat/sat_parameters.pb.h" +#include "ortools/sat/sat_solver.h" +#include "ortools/sat/simplification.h" +#include "ortools/sat/symmetry.h" +#include "ortools/util/file_util.h" +#include "ortools/util/time_limit.h" + +DEFINE_string( + input, "", + "Required: input file of the problem to solve. Many format are supported:" + ".cnf (sat, max-sat, weighted max-sat), .opb (pseudo-boolean sat/optim) " + "and by default the LinearBooleanProblem proto (binary or text)."); + +DEFINE_string( + output, "", + "If non-empty, write the input problem as a LinearBooleanProblem proto to " + "this file. By default it uses the binary format except if the file " + "extension is '.txt'. If the problem is SAT, a satisfiable assignment is " + "also written to the file."); + +DEFINE_bool(output_cnf_solution, false, + "If true and the problem was solved to optimality, this output " + "the solution to stdout in cnf form.\n"); + +DEFINE_string(params, "", + "Parameters for the sat solver in a text format of the " + "SatParameters proto, example: --params=use_conflicts:true."); + +DEFINE_bool(strict_validity, false, + "If true, stop if the given input is invalid (duplicate literals, " + "out of range, zero cofficients, etc.)"); + +DEFINE_string( + lower_bound, "", + "If not empty, look for a solution with an objective value >= this bound."); + +DEFINE_string( + upper_bound, "", + "If not empty, look for a solution with an objective value <= this bound."); + +DEFINE_bool(fu_malik, false, + "If true, search the optimal solution with the Fu & Malik algo."); + +DEFINE_bool(wpm1, false, + "If true, search the optimal solution with the WPM1 algo."); + +DEFINE_bool(qmaxsat, false, + "If true, search the optimal solution with a linear scan and " + " the cardinality encoding used in qmaxsat."); + +DEFINE_bool(core_enc, false, + "If true, search the optimal solution with the core-based " + "cardinality encoding algo."); + +DEFINE_bool(linear_scan, false, + "If true, search the optimal solution with the linear scan algo."); + +DEFINE_int32(randomize, 500, + "If positive, solve that many times the problem with a random " + "decision heuristic before trying to optimize it."); + +DEFINE_bool(use_symmetry, false, + "If true, find and exploit the eventual symmetries " + "of the problem."); + +DEFINE_bool(presolve, true, + "Only work on pure SAT problem. If true, presolve the problem."); + +DEFINE_bool(probing, false, "If true, presolve the problem using probing."); + +DEFINE_bool(use_cp_model, true, + "Whether to interpret everything as a CpModelProto or " + "to read by default a CpModelProto."); + +DEFINE_bool(reduce_memory_usage, false, + "If true, do not keep a copy of the original problem in memory." + "This reduce the memory usage, but disable the solution cheking at " + "the end."); + +namespace operations_research { +namespace sat { +namespace { + +// Returns a trivial best bound. The best bound corresponds to the lower bound +// (resp. upper bound) in case of a minimization (resp. maximization) problem. +double GetScaledTrivialBestBound(const LinearBooleanProblem& problem) { + Coefficient best_bound(0); + const LinearObjective& objective = problem.objective(); + for (const int64 value : objective.coefficients()) { + if (value < 0) best_bound += Coefficient(value); + } + return AddOffsetAndScaleObjectiveValue(problem, best_bound); +} + +bool LoadBooleanProblem(const std::string& filename, + LinearBooleanProblem* problem, CpModelProto* cp_model) { + 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 (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) { + reader.InterpretCnfAsMaxSat(true); + } + if (FLAGS_use_cp_model) { + if (!reader.Load(filename, cp_model)) { + LOG(FATAL) << "Cannot load file '" << filename << "'."; + } + } else { + if (!reader.Load(filename, problem)) { + LOG(FATAL) << "Cannot load file '" << filename << "'."; + } + } + } else if (FLAGS_use_cp_model) { + LOG(INFO) << "Reading a CpModelProto."; + *cp_model = ReadFileToProtoOrDie(filename); + } else { + LOG(INFO) << "Reading a LinearBooleanProblem."; + *problem = ReadFileToProtoOrDie(filename); + } + return true; +} + +std::string SolutionString(const LinearBooleanProblem& problem, + const std::vector& assignment) { + std::string output; + BooleanVariable limit(problem.original_num_variables()); + for (BooleanVariable index(0); index < limit; ++index) { + if (index > 0) output += " "; + absl::StrAppend(&output, + Literal(index, assignment[index.value()]).SignedValue()); + } + return output; +} + +// To benefit from the operations_research namespace, we put all the main() code +// here. +int Run() { + SatParameters parameters; + if (FLAGS_input.empty()) { + LOG(FATAL) << "Please supply a data file with --input="; + } + + // Parse the --params flag. + if (!FLAGS_params.empty()) { + CHECK(google::protobuf::TextFormat::MergeFromString(FLAGS_params, + ¶meters)) + << FLAGS_params; + } + + // Initialize the solver. + std::unique_ptr solver(new SatSolver()); + solver->SetParameters(parameters); + + // Read the problem. + LinearBooleanProblem problem; + CpModelProto cp_model; + if (!LoadBooleanProblem(FLAGS_input, &problem, &cp_model)) { + CpSolverResponse response; + response.set_status(CpSolverStatus::MODEL_INVALID); + return EXIT_SUCCESS; + } + if (FLAGS_use_cp_model && cp_model.variables_size() == 0) { + LOG(INFO) << "Converting to CpModelProto ..."; + cp_model = BooleanProblemToCpModelproto(problem); + } + + // TODO(user): clean this hack. Ideally LinearBooleanProblem should be + // completely replaced by the more general CpModelProto. + if (!cp_model.variables().empty()) { + problem.Clear(); // We no longer need it, release memory. + Model model; + model.Add(NewSatParameters(parameters)); + const CpSolverResponse response = SolveCpModel(cp_model, &model); + + if (!FLAGS_output.empty()) { + if (absl::EndsWith(FLAGS_output, ".txt")) { + CHECK_OK(file::SetTextProto(FLAGS_output, response, file::Defaults())); + } else { + CHECK_OK( + file::SetBinaryProto(FLAGS_output, response, file::Defaults())); + } + } + + // The SAT competition requires a particular exit code and since we don't + // really use it for any other purpose, we comply. + if (response.status() == CpSolverStatus::FEASIBLE) return 10; + if (response.status() == CpSolverStatus::INFEASIBLE) return 20; + return EXIT_SUCCESS; + } + + if (FLAGS_strict_validity) { + const util::Status status = ValidateBooleanProblem(problem); + if (!status.ok()) { + LOG(ERROR) << "Invalid Boolean problem: " << status.error_message(); + return EXIT_FAILURE; + } + } + + // Count the time from there. + WallTimer wall_timer; + UserTimer user_timer; + wall_timer.Start(); + user_timer.Start(); + double scaled_best_bound = GetScaledTrivialBestBound(problem); + + // Probing. + SatPostsolver probing_postsolver(problem.num_variables()); + LinearBooleanProblem original_problem; + if (FLAGS_probing) { + // TODO(user): This is nice for testing, but consumes memory. + original_problem = problem; + ProbeAndSimplifyProblem(&probing_postsolver, &problem); + } + + // Load the problem into the solver. + if (FLAGS_reduce_memory_usage) { + if (!LoadAndConsumeBooleanProblem(&problem, solver.get())) { + LOG(INFO) << "UNSAT when loading the problem."; + } + } else { + if (!LoadBooleanProblem(problem, solver.get())) { + LOG(INFO) << "UNSAT when loading the problem."; + } + } + auto strtoint64 = [](const std::string& word) { + int64 value = 0; + if (!word.empty()) CHECK(absl::SimpleAtoi(word, &value)); + return value; + }; + if (!AddObjectiveConstraint(problem, !FLAGS_lower_bound.empty(), + Coefficient(strtoint64(FLAGS_lower_bound)), + !FLAGS_upper_bound.empty(), + Coefficient(strtoint64(FLAGS_upper_bound)), + solver.get())) { + LOG(INFO) << "UNSAT when setting the objective constraint."; + } + + // Symmetries! + // + // TODO(user): To make this compatible with presolve, we just need to run + // it after the presolve step. + if (FLAGS_use_symmetry) { + CHECK(!FLAGS_reduce_memory_usage) << "incompatible"; + CHECK(!FLAGS_presolve) << "incompatible"; + LOG(INFO) << "Finding symmetries of the problem."; + std::vector> generators; + FindLinearBooleanProblemSymmetries(problem, &generators); + std::unique_ptr propagator(new SymmetryPropagator); + for (int i = 0; i < generators.size(); ++i) { + propagator->AddSymmetry(std::move(generators[i])); + } + solver->AddPropagator(propagator.get()); + solver->TakePropagatorOwnership(std::move(propagator)); + } + + // Optimize? + std::vector solution; + SatSolver::Status result = SatSolver::LIMIT_REACHED; + if (FLAGS_fu_malik || FLAGS_linear_scan || FLAGS_wpm1 || FLAGS_qmaxsat || + FLAGS_core_enc) { + if (FLAGS_randomize > 0 && (FLAGS_linear_scan || FLAGS_qmaxsat)) { + CHECK(!FLAGS_reduce_memory_usage) << "incompatible"; + result = SolveWithRandomParameters(STDOUT_LOG, problem, FLAGS_randomize, + solver.get(), &solution); + } + if (result == SatSolver::LIMIT_REACHED) { + if (FLAGS_qmaxsat) { + solver = absl::make_unique(); + solver->SetParameters(parameters); + CHECK(LoadBooleanProblem(problem, solver.get())); + result = SolveWithCardinalityEncoding(STDOUT_LOG, problem, solver.get(), + &solution); + } else if (FLAGS_core_enc) { + result = SolveWithCardinalityEncodingAndCore(STDOUT_LOG, problem, + solver.get(), &solution); + } else if (FLAGS_fu_malik) { + result = SolveWithFuMalik(STDOUT_LOG, problem, solver.get(), &solution); + } else if (FLAGS_wpm1) { + result = SolveWithWPM1(STDOUT_LOG, problem, solver.get(), &solution); + } else if (FLAGS_linear_scan) { + result = + SolveWithLinearScan(STDOUT_LOG, problem, solver.get(), &solution); + } + } + } else { + // Only solve the decision version. + parameters.set_log_search_progress(true); + solver->SetParameters(parameters); + if (FLAGS_presolve) { + std::unique_ptr time_limit = + TimeLimit::FromParameters(parameters); + result = SolveWithPresolve(&solver, time_limit.get(), &solution, nullptr); + if (result == SatSolver::FEASIBLE) { + CHECK(IsAssignmentValid(problem, solution)); + } + } else { + result = solver->Solve(); + if (result == SatSolver::FEASIBLE) { + ExtractAssignment(problem, *solver, &solution); + CHECK(IsAssignmentValid(problem, solution)); + } + } + } + + // Print the solution status. + if (result == SatSolver::FEASIBLE) { + if (FLAGS_fu_malik || FLAGS_linear_scan || FLAGS_wpm1 || FLAGS_core_enc) { + printf("s OPTIMUM FOUND\n"); + CHECK(!solution.empty()); + const Coefficient objective = ComputeObjectiveValue(problem, solution); + scaled_best_bound = AddOffsetAndScaleObjectiveValue(problem, objective); + + // Postsolve. + if (FLAGS_probing) { + solution = probing_postsolver.PostsolveSolution(solution); + problem = original_problem; + } + } else { + printf("s SATISFIABLE\n"); + } + + // Check and output the solution. + CHECK(IsAssignmentValid(problem, solution)); + if (FLAGS_output_cnf_solution) { + printf("v %s\n", SolutionString(problem, solution).c_str()); + } + if (!FLAGS_output.empty()) { + CHECK(!FLAGS_reduce_memory_usage) << "incompatible"; + if (result == SatSolver::FEASIBLE) { + StoreAssignment(solver->Assignment(), problem.mutable_assignment()); + } + 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())); + } + } + } + if (result == SatSolver::INFEASIBLE) { + printf("s UNSATISFIABLE\n"); + } + + // Print status. + printf("c status: %s\n", SatStatusString(result).c_str()); + + // Print objective value. + if (solution.empty()) { + printf("c objective: na\n"); + printf("c best bound: na\n"); + } else { + const Coefficient objective = ComputeObjectiveValue(problem, solution); + printf("c objective: %.16g\n", + AddOffsetAndScaleObjectiveValue(problem, objective)); + printf("c best bound: %.16g\n", scaled_best_bound); + } + + // Print final statistics. + printf("c booleans: %d\n", solver->NumVariables()); + 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()); + + // The SAT competition requires a particular exit code and since we don't + // really use it for any other purpose, we comply. + if (result == SatSolver::FEASIBLE) return 10; + if (result == SatSolver::INFEASIBLE) return 20; + return EXIT_SUCCESS; +} + +} // namespace +} // namespace sat +} // namespace operations_research + +static const char kUsage[] = + "Usage: see flags.\n" + "This program solves a given Boolean linear problem."; + +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. + // absl::SetFlag(&FLAGS_vmodule, "*cp_model*=1"); + gflags::SetUsageMessage(kUsage); + gflags::ParseCommandLineFlags(&argc, &argv, true); + google::InitGoogleLogging(argv[0]); + absl::SetFlag(&FLAGS_alsologtostderr, true); + return operations_research::sat::Run(); +} diff --git a/libs/or-tools/examples/cpp/search_for_all_solutions_sample_sat.cc b/libs/or-tools-bin/examples/cpp/search_for_all_solutions_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/search_for_all_solutions_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/search_for_all_solutions_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/shift_minimization_sat.cc b/libs/or-tools-bin/examples/cpp/shift_minimization_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..9cb1f7e437be565534d2117bc389e83ebbbbeb11 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/shift_minimization_sat.cc @@ -0,0 +1,312 @@ +// 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. + +// Reader and solver for the shift minimization personnel task +// scheduling problem (see +// https://publications.csiro.au/rpr/download?pid=csiro:EP104071&dsid=DS2)/ +// +// Data files are in +// examples/data/shift_scheduling/minization +// +// The problem is the following: +// - There is a list of jobs. Each job has a start date and an end date. They +// must all be performed. +// - There is a set of workers. Each worker can perform one or more jobs among +// a subset of job. One worker cannot perform two jobs that overlaps. +// - The objective it to minimize the number of active workers, while +// performing all the jobs. + +#include +#include +#include +#include + +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/base/filelineiter.h" +#include "ortools/base/logging.h" +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" + +DEFINE_string(input, "", "Input file."); +DEFINE_string(params, "", "Sat parameters in text proto format."); + +namespace operations_research { +namespace sat { +class ShiftMinimizationParser { + public: + struct Job { + int start; + int end; + }; + + struct Assignment { + int worker_id; + int job_index; + }; + + ShiftMinimizationParser() + : load_status_(NOT_STARTED), + declared_num_jobs_(0), + declared_num_workers_(0), + num_workers_read_(0) {} + + const std::vector& jobs() const { return jobs_; } + const std::vector>& possible_jobs_per_worker() const { + return possible_jobs_per_worker_; + } + const std::vector>& possible_assignments_per_job() + const { + return possible_assignments_per_job_; + } + + // The file format is the following + // # comments... + // Type = 1 + // Jobs = + // // Repeated n times + // Qualifications = + // c: job_1 .. job_c // repeated k times (a counter and job ids after). + bool LoadFile(const std::string& file_name) { + if (load_status_ != NOT_STARTED) { + return false; + } + + load_status_ = STARTED; + + for (const std::string& line : + FileLines(file_name, FileLineIterator::REMOVE_LINEFEED | + FileLineIterator::REMOVE_INLINE_CR)) { + ProcessLine(line); + } + + LOG(INFO) << "Read file " << file_name << " with " << declared_num_jobs_ + << " jobs, and " << declared_num_workers_ << " workers."; + return declared_num_jobs_ != 0 && jobs_.size() == declared_num_jobs_ && + declared_num_workers_ != 0 && + declared_num_workers_ == num_workers_read_; + } + + private: + enum LoadStatus { NOT_STARTED, STARTED, JOBS_SEEN, WORKERS_SEEN }; + + int strtoint32(const std::string& word) { + int result; + CHECK(absl::SimpleAtoi(word, &result)); + return result; + } + + void ProcessLine(const std::string& line) { + if (line.empty() || line[0] == '#') { + return; + } + + const std::vector words = + absl::StrSplit(line, absl::ByAnyChar(" :\t"), absl::SkipEmpty()); + + switch (load_status_) { + case NOT_STARTED: { + LOG(FATAL) << "Wrong status: NOT_STARTED"; + break; + } + case STARTED: { + if (words.size() == 3 && words[0] == "Type") { + CHECK_EQ(1, strtoint32(words[2])); + } else if (words.size() == 3 && words[0] == "Jobs") { + declared_num_jobs_ = strtoint32(words[2]); + possible_assignments_per_job_.resize(declared_num_jobs_); + load_status_ = JOBS_SEEN; + } else { + LOG(FATAL) << "Wrong state STARTED with line " << line; + } + break; + } + case JOBS_SEEN: { + if (words.size() == 2) { + jobs_.push_back({strtoint32(words[0]), strtoint32(words[1])}); + } else if (words.size() == 3 && words[0] == "Qualifications") { + declared_num_workers_ = strtoint32(words[2]); + possible_jobs_per_worker_.resize(declared_num_workers_); + load_status_ = WORKERS_SEEN; + } else { + LOG(FATAL) << "Wrong state JOBS_SEEN with line " << line; + } + break; + } + case WORKERS_SEEN: { + CHECK_EQ(strtoint32(words[0]), words.size() - 1); + for (int i = 1; i < words.size(); ++i) { + const int job = strtoint32(words[i]); + const int pos = possible_jobs_per_worker_[num_workers_read_].size(); + possible_jobs_per_worker_[num_workers_read_].push_back(job); + possible_assignments_per_job_[job].push_back( + {num_workers_read_, pos}); + } + num_workers_read_++; + break; + } + } + } + + std::vector jobs_; + std::vector> possible_jobs_per_worker_; + std::vector> possible_assignments_per_job_; + LoadStatus load_status_; + int declared_num_jobs_; + int declared_num_workers_; + int num_workers_read_; +}; + +bool Overlaps(const ShiftMinimizationParser::Job& j1, + const ShiftMinimizationParser::Job& j2) { + // TODO(user): Are end date inclusive or exclusive? To check. + // For now, we assume that they are exclusive. + return !(j1.start > j2.end || j2.start > j1.end); +} + +void LoadAndSolve(const std::string& file_name) { + ShiftMinimizationParser parser; + CHECK(parser.LoadFile(file_name)); + + CpModelBuilder cp_model; + + const int num_workers = parser.possible_jobs_per_worker().size(); + const std::vector& jobs = parser.jobs(); + const int num_jobs = jobs.size(); + + std::vector active_workers(num_workers); + std::vector> worker_job_vars(num_workers); + std::vector> possible_workers_per_job(num_jobs); + + for (int w = 0; w < num_workers; ++w) { + // Status variables for workers, are they active or not? + active_workers[w] = cp_model.NewBoolVar(); + + // Job-Worker variable. worker_job_vars[w][i] is true iff worker w + // performs it's ith possible job. + const std::vector& possible = parser.possible_jobs_per_worker()[w]; + for (int p : possible) { + const BoolVar var = cp_model.NewBoolVar(); + worker_job_vars[w].push_back(var); + possible_workers_per_job[p].push_back(var); + } + + // Add conflicts on overlapping jobs for the same worker. + for (int i = 0; i < possible.size() - 1; ++i) { + for (int j = i + 1; j < possible.size(); ++j) { + const int job1 = possible[i]; + const int job2 = possible[j]; + if (Overlaps(jobs[job1], jobs[job2])) { + const BoolVar v1 = worker_job_vars[w][i]; + const BoolVar v2 = worker_job_vars[w][j]; + cp_model.AddBoolOr({Not(v1), Not(v2)}); + } + } + } + + // Maintain active_workers variable. + cp_model.AddBoolOr(worker_job_vars[w]).OnlyEnforceIf(active_workers[w]); + for (const BoolVar& var : worker_job_vars[w]) { + cp_model.AddImplication(var, active_workers[w]); + } + } + + // All jobs must be performed. + for (int j = 0; j < num_jobs; ++j) { + // this does not enforce that at most one worker performs one job. + // It should not change the solution cost. + // TODO(user): Checks if sum() == 1 improves the solving speed. + cp_model.AddBoolOr(possible_workers_per_job[j]); + } + + // Redundant constraint: + // For each time point, count the number of active jobs at that time, + // then the number of active workers on these jobs is equal to the number of + // active jobs. + std::set time_points; + std::set> visited_job_lists; + + for (int j = 0; j < num_jobs; ++j) { + time_points.insert(parser.jobs()[j].start); + time_points.insert(parser.jobs()[j].end); + } + + int num_count_constraints = 0; + int max_intersection_size = 0; + + // Add one counting constraint per time point. + for (int t : time_points) { + // Collect all jobs that intersects with this time point. + std::vector intersecting_jobs; + for (int j = 0; j < num_jobs; ++j) { + const ShiftMinimizationParser::Job& job = parser.jobs()[j]; + // Assumption: End date are inclusive. + if (t >= job.start && t <= job.end) { + intersecting_jobs.push_back(j); + } + } + + // Check that we have not already visited this exact set of candidate jobs. + if (gtl::ContainsKey(visited_job_lists, intersecting_jobs)) continue; + visited_job_lists.insert(intersecting_jobs); + + // Collect the relevant worker job vars. + std::vector overlapping_worker_jobs; + for (int j : intersecting_jobs) { + for (const auto& p : parser.possible_assignments_per_job()[j]) { + const BoolVar var = worker_job_vars[p.worker_id][p.job_index]; + overlapping_worker_jobs.push_back(var); + } + } + + // Add the count constraints: We have as many active workers as jobs. + const int num_jobs = intersecting_jobs.size(); + cp_model.AddEquality(LinearExpr::BooleanSum(overlapping_worker_jobs), + num_jobs); + // Book keeping. + max_intersection_size = std::max(max_intersection_size, num_jobs); + num_count_constraints++; + } + + LOG(INFO) << "Added " << num_count_constraints + << " count constraints while processing " << time_points.size() + << " time points."; + LOG(INFO) << "Lower bound = " << max_intersection_size; + + // Objective. + const IntVar objective_var = + cp_model.NewIntVar(Domain(max_intersection_size, num_workers)); + cp_model.AddEquality(LinearExpr::BooleanSum(active_workers), objective_var); + cp_model.Minimize(objective_var); + + // Solve. + Model model; + model.Add(NewSatParameters(FLAGS_params)); + + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + LOG(INFO) << CpSolverResponseStats(response); +} +} // namespace sat +} // namespace operations_research + +int main(int argc, char** argv) { + absl::SetFlag(&FLAGS_logtostderr, true); + gflags::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_input.empty()) { + LOG(FATAL) << "Please supply a data file with --input="; + } + + operations_research::sat::LoadAndSolve(FLAGS_input); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/simple_cp_program.cc b/libs/or-tools-bin/examples/cpp/simple_cp_program.cc similarity index 100% rename from libs/or-tools/examples/cpp/simple_cp_program.cc rename to libs/or-tools-bin/examples/cpp/simple_cp_program.cc diff --git a/libs/or-tools/examples/cpp/simple_knapsack_program.cc b/libs/or-tools-bin/examples/cpp/simple_knapsack_program.cc similarity index 100% rename from libs/or-tools/examples/cpp/simple_knapsack_program.cc rename to libs/or-tools-bin/examples/cpp/simple_knapsack_program.cc diff --git a/libs/or-tools/examples/cpp/simple_lp_program.cc b/libs/or-tools-bin/examples/cpp/simple_lp_program.cc similarity index 100% rename from libs/or-tools/examples/cpp/simple_lp_program.cc rename to libs/or-tools-bin/examples/cpp/simple_lp_program.cc diff --git a/libs/or-tools/examples/cpp/simple_ls_program.cc b/libs/or-tools-bin/examples/cpp/simple_ls_program.cc similarity index 100% rename from libs/or-tools/examples/cpp/simple_ls_program.cc rename to libs/or-tools-bin/examples/cpp/simple_ls_program.cc diff --git a/libs/or-tools/examples/cpp/simple_max_flow_program.cc b/libs/or-tools-bin/examples/cpp/simple_max_flow_program.cc similarity index 100% rename from libs/or-tools/examples/cpp/simple_max_flow_program.cc rename to libs/or-tools-bin/examples/cpp/simple_max_flow_program.cc diff --git a/libs/or-tools/examples/cpp/simple_min_cost_flow_program.cc b/libs/or-tools-bin/examples/cpp/simple_min_cost_flow_program.cc similarity index 100% rename from libs/or-tools/examples/cpp/simple_min_cost_flow_program.cc rename to libs/or-tools-bin/examples/cpp/simple_min_cost_flow_program.cc diff --git a/libs/or-tools/examples/cpp/simple_mip_program.cc b/libs/or-tools-bin/examples/cpp/simple_mip_program.cc similarity index 100% rename from libs/or-tools/examples/cpp/simple_mip_program.cc rename to libs/or-tools-bin/examples/cpp/simple_mip_program.cc diff --git a/libs/or-tools/examples/cpp/simple_routing_program.cc b/libs/or-tools-bin/examples/cpp/simple_routing_program.cc similarity index 100% rename from libs/or-tools/examples/cpp/simple_routing_program.cc rename to libs/or-tools-bin/examples/cpp/simple_routing_program.cc diff --git a/libs/or-tools/examples/cpp/simple_sat_program.cc b/libs/or-tools-bin/examples/cpp/simple_sat_program.cc similarity index 100% rename from libs/or-tools/examples/cpp/simple_sat_program.cc rename to libs/or-tools-bin/examples/cpp/simple_sat_program.cc diff --git a/libs/or-tools-bin/examples/cpp/slitherlink_sat.cc b/libs/or-tools-bin/examples/cpp/slitherlink_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..f6895a261a4e934d150f5f72d86ffe5b94bd99da --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/slitherlink_sat.cc @@ -0,0 +1,259 @@ +// 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. + +// Solve the Slitherlink problem: +// see https://en.wikipedia.org/wiki/Slitherlink + +#include +#include + +#include "absl/strings/str_format.h" +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" + +const std::vector> tiny = {{3, 3, 1}}; + +const std::vector> small = { + {3, 2, -1, 3}, {-1, -1, -1, 2}, {3, -1, -1, -1}, {3, -1, 3, 1}}; + +const std::vector> 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}}; + +const std::vector> 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}}; + +namespace operations_research { +namespace sat { + +void PrintSolution(const std::vector> &data, + const std::vector> &h_arcs, + const std::vector> &v_arcs) { + const int num_rows = data.size(); + const int num_columns = data[0].size(); + + for (int i = 0; i < num_rows; ++i) { + std::string first_line; + std::string second_line; + std::string third_line; + for (int j = 0; j < num_columns; ++j) { + const bool h_arc = h_arcs[i][j]; + const bool v_arc = v_arcs[j][i]; + const int sum = data[i][j]; + first_line += h_arc ? " -----" : " "; + second_line += v_arc ? "|" : " "; + second_line += sum == -1 ? " " : absl::StrFormat(" %d ", sum).c_str(); + third_line += v_arc ? "| " : " "; + } + const bool termination = v_arcs[num_columns][i]; + second_line += termination == 1 ? "|" : " "; + third_line += termination == 1 ? "|" : " "; + std::cout << first_line << std::endl; + std::cout << third_line << std::endl; + std::cout << second_line << std::endl; + std::cout << third_line << std::endl; + } + std::string last_line; + for (int j = 0; j < num_columns; ++j) { + const bool h_arc = h_arcs[num_rows][j]; + last_line += h_arc ? " -----" : " "; + } + std::cout << last_line << std::endl; +} + +void SlitherLink(const std::vector> &data) { + const int num_rows = data.size(); + const int num_columns = data[0].size(); + + const int num_nodes = (num_rows + 1) * (num_columns + 1); + const int num_horizontal_arcs = num_columns * (num_rows + 1); + const int num_vertical_arcs = (num_rows) * (num_columns + 1); + + auto undirected_horizontal_arc = [=](int x, int y) { + CHECK_LT(x, num_columns); + CHECK_LT(y, num_rows + 1); + return x + (num_columns * y); + }; + + auto undirected_vertical_arc = [=](int x, int y) { + CHECK_LT(x, num_columns + 1); + CHECK_LT(y, num_rows); + return x + (num_columns + 1) * y; + }; + + auto node_index = [=](int x, int y) { + CHECK_LT(x, num_columns + 1); + CHECK_LT(y, num_rows + 1); + return x + y * (num_columns + 1); + }; + + CpModelBuilder builder; + + std::vector horizontal_arcs; + for (int arc = 0; arc < 2 * num_horizontal_arcs; ++arc) { + horizontal_arcs.push_back(builder.NewBoolVar()); + } + std::vector vertical_arcs; + for (int arc = 0; arc < 2 * num_vertical_arcs; ++arc) { + vertical_arcs.push_back(builder.NewBoolVar()); + } + + CircuitConstraint circuit = builder.AddCircuitConstraint(); + // Horizontal arcs. + for (int x = 0; x < num_columns; ++x) { + for (int y = 0; y < num_rows + 1; ++y) { + const int arc = undirected_horizontal_arc(x, y); + circuit.AddArc(node_index(x, y), node_index(x + 1, y), + horizontal_arcs[2 * arc]); + circuit.AddArc(node_index(x + 1, y), node_index(x, y), + horizontal_arcs[2 * arc + 1]); + } + } + // Vertical arcs. + for (int x = 0; x < num_columns + 1; ++x) { + for (int y = 0; y < num_rows; ++y) { + const int arc = undirected_vertical_arc(x, y); + circuit.AddArc(node_index(x, y), node_index(x, y + 1), + vertical_arcs[2 * arc]); + circuit.AddArc(node_index(x, y + 1), node_index(x, y), + vertical_arcs[2 * arc + 1]); + } + } + // Self loops. + std::vector self_nodes(num_nodes); + for (int x = 0; x < num_columns + 1; ++x) { + for (int y = 0; y < num_rows + 1; ++y) { + const int node = node_index(x, y); + const BoolVar self_node = builder.NewBoolVar(); + circuit.AddArc(node, node, self_node); + self_nodes[node] = self_node; + } + } + + for (int x = 0; x < num_columns; ++x) { + for (int y = 0; y < num_rows; ++y) { + if (data[y][x] == -1) + continue; + std::vector neighbors; + const int top_arc = undirected_horizontal_arc(x, y); + neighbors.push_back(horizontal_arcs[2 * top_arc]); + neighbors.push_back(horizontal_arcs[2 * top_arc + 1]); + const int bottom_arc = undirected_horizontal_arc(x, y + 1); + neighbors.push_back(horizontal_arcs[2 * bottom_arc]); + neighbors.push_back(horizontal_arcs[2 * bottom_arc + 1]); + const int left_arc = undirected_vertical_arc(x, y); + neighbors.push_back(vertical_arcs[2 * left_arc]); + neighbors.push_back(vertical_arcs[2 * left_arc + 1]); + const int right_arc = undirected_vertical_arc(x + 1, y); + neighbors.push_back(vertical_arcs[2 * right_arc]); + neighbors.push_back(vertical_arcs[2 * right_arc + 1]); + builder.AddEquality(LinearExpr::BooleanSum(neighbors), data[y][x]); + } + } + + // Special rule on corners: value == 3 implies 2 corner arcs used. + if (data[0][0] == 3) { + const int h_arc = undirected_horizontal_arc(0, 0); + builder.AddBoolOr( + {horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]}); + const int v_arc = undirected_vertical_arc(0, 0); + builder.AddBoolOr({vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]}); + } + if (data[0][num_columns - 1] == 3) { + const int h_arc = undirected_horizontal_arc(num_columns - 1, 0); + builder.AddBoolOr( + {horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]}); + const int v_arc = undirected_vertical_arc(num_columns, 0); + builder.AddBoolOr({vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]}); + } + if (data[num_rows - 1][0] == 3) { + const int h_arc = undirected_horizontal_arc(0, num_rows); + builder.AddBoolOr( + {horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]}); + const int v_arc = undirected_vertical_arc(0, num_rows - 1); + builder.AddBoolOr({vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]}); + } + if (data[num_rows - 1][num_columns - 1] == 3) { + const int h_arc = undirected_horizontal_arc(num_columns - 1, num_rows); + builder.AddBoolOr( + {horizontal_arcs[2 * h_arc], horizontal_arcs[2 * h_arc + 1]}); + const int v_arc = undirected_vertical_arc(num_columns, num_rows - 1); + builder.AddBoolOr({vertical_arcs[2 * v_arc], vertical_arcs[2 * v_arc + 1]}); + } + + // Topology rule: Border arcs are oriented in one direction. + for (int x = 0; x < num_columns; ++x) { + const int top_arc = undirected_horizontal_arc(x, 0); + builder.AddEquality(horizontal_arcs[2 * top_arc + 1], 0); + const int bottom_arc = undirected_horizontal_arc(x, num_rows); + builder.AddEquality(horizontal_arcs[2 * bottom_arc], 0); + } + + for (int y = 0; y < num_rows; ++y) { + const int left_arc = undirected_vertical_arc(0, y); + builder.AddEquality(vertical_arcs[2 * left_arc], 0); + const int right_arc = undirected_vertical_arc(num_columns, y); + builder.AddEquality(vertical_arcs[2 * right_arc + 1], 0); + } + + const CpSolverResponse response = Solve(builder.Build()); + + std::vector> h_arcs(num_rows + 1); + for (int y = 0; y < num_rows + 1; ++y) { + for (int x = 0; x < num_columns; ++x) { + const int arc = undirected_horizontal_arc(x, y); + h_arcs[y].push_back( + SolutionBooleanValue(response, horizontal_arcs[2 * arc]) || + SolutionBooleanValue(response, horizontal_arcs[2 * arc + 1])); + } + } + + std::vector> v_arcs(num_columns + 1); + for (int y = 0; y < num_rows; ++y) { + for (int x = 0; x < num_columns + 1; ++x) { + const int arc = undirected_vertical_arc(x, y); + v_arcs[x].push_back( + SolutionBooleanValue(response, vertical_arcs[2 * arc]) || + SolutionBooleanValue(response, vertical_arcs[2 * arc + 1])); + } + } + + PrintSolution(data, h_arcs, v_arcs); + LOG(INFO) << CpSolverResponseStats(response); +} + +} // namespace sat +} // namespace operations_research + +int main() { + std::cout << "Tiny problem" << std::endl; + operations_research::sat::SlitherLink(tiny); + std::cout << "Small problem" << std::endl; + operations_research::sat::SlitherLink(small); + std::cout << "Medium problem" << std::endl; + operations_research::sat::SlitherLink(medium); + std::cout << "Big problem" << std::endl; + operations_research::sat::SlitherLink(big); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/solution_hinting_sample_sat.cc b/libs/or-tools-bin/examples/cpp/solution_hinting_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/solution_hinting_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/solution_hinting_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/solve.cc b/libs/or-tools-bin/examples/cpp/solve.cc new file mode 100644 index 0000000000000000000000000000000000000000..4ade6a5e56edf292b3daa43ed5335b037c43ada4 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/solve.cc @@ -0,0 +1,259 @@ +// 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. + +// Command line interface to the MPSolver class. +// See linear_solver.h and kUsageStr below. + +#include +#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/timer.h" +#include "ortools/linear_solver/linear_solver.h" +#include "ortools/linear_solver/linear_solver.pb.h" +#include "ortools/lp_data/mps_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."); + +DEFINE_int32(num_threads, 1, + "Number of threads to use by the underlying solver."); +DEFINE_string(params_file, "", + "Solver specific parameters file. " + "If this flag is set, the --params flag is ignored."); +DEFINE_string(params, "", "Solver specific parameters"); +DEFINE_int64(time_limit_ms, 0, + "If strictly positive, specifies a limit in ms on the solving " + "time. Otherwise, no time limit will be imposed."); + +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_string(dump_format, "text", + "Format in which to dump protos (if flags --dump_model, " + "--dump_request, or --dump_response are used). Possible values: " + "'text', 'binary', 'json' which correspond to text proto format " + "binary proto format, and json. If 'binary' or 'json' are used, " + "we append '.bin' and '.json' to file names."); + +DEFINE_bool(dump_gzip, false, + "Whether to gzip dumped protos. Appends .gz to their name."); +DEFINE_string(dump_model, "", "If non-empty, dumps MPModelProto there."); +DEFINE_string(dump_request, "", "If non-empty, dumps MPModelRequest there."); +DEFINE_string(dump_response, "", "If non-empty, dumps MPModelResponse there."); + +DECLARE_bool(verify_solution); // Defined in ./linear_solver.cc + +static const char kUsageStr[] = + "Run MPSolver on the given input file. Many formats are supported: \n" + " - a .mps or .mps.gz file,\n" + " - an MPModelProto (binary or text, possibly gzipped),\n" + " - an MPModelRequest (binary or text, possibly gzipped).\n" + "MPModelProto and MPModelRequest files can comply with either the " + "linear_solver.proto or the linear_solver.proto format."; + +namespace operations_research { +namespace { + +// Returns false if an error was encountered. +// More details should be available in the logs. +bool Run() { + // Create the solver and set its parameters. + MPSolver::OptimizationProblemType type; + CHECK(MPSolver::ParseSolverType(FLAGS_solver, &type)) + << "Unsupported --solver: " << FLAGS_solver; + + // Load the problem into an MPModelProto. + MPModelProto model_proto; + MPModelRequest request_proto; + if (absl::EndsWith(FLAGS_input, ".mps") || + absl::EndsWith(FLAGS_input, ".mps.gz")) { + CHECK_OK(glop::MPSReader().ParseFile(FLAGS_input, &model_proto)) + << "Error while parsing the mps file '" << FLAGS_input << "'."; + } else { + 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. + const bool is_model_proto = model_proto.variable_size() > 0; + const bool is_request_proto = request_proto.model().variable_size() > 0; + if (!is_model_proto && !is_request_proto) { + LOG(FATAL) << "Failed to parse '" << FLAGS_input + << "' as an MPModelProto or an MPModelRequest."; + } else { + CHECK(!(is_model_proto && is_request_proto)); + if (is_request_proto) { + LOG(INFO) << "Read input proto as an MPModelRequest."; + model_proto.Swap(request_proto.mutable_model()); + } else { + LOG(INFO) << "Read input proto as an MPModelProto."; + } + } + } + printf("%-12s: '%s'\n", "File", FLAGS_input.c_str()); + + // Detect format to dump protos. + operations_research::ProtoWriteFormat write_format; + if (FLAGS_dump_format == "text") { + write_format = ProtoWriteFormat::kProtoText; + } else if (FLAGS_dump_format == "binary") { + write_format = ProtoWriteFormat::kProtoBinary; + } else if (FLAGS_dump_format == "json") { + write_format = ProtoWriteFormat::kJson; + } else { + LOG(FATAL) << "Unsupported --dump_format: " << FLAGS_dump_format; + } + + // Create the solver, we use the name of the model as the solver name. + MPSolver solver(model_proto.name(), type); + const util::Status set_num_threads_status = + solver.SetNumThreads(FLAGS_num_threads); + if (set_num_threads_status.ok()) { + LOG(INFO) << "Set number of threads to " << FLAGS_num_threads << "."; + } else { + LOG(ERROR) << "Failed to set number of threads due to: " + << set_num_threads_status.message() << ". Using 1 as default."; + } + solver.EnableOutput(); + if (!FLAGS_params_file.empty()) { + std::string file_contents; + CHECK_OK( + file::GetContents(FLAGS_params_file, &file_contents, file::Defaults())) + << "Could not read parameters file."; + CHECK(solver.SetSolverSpecificParametersAsString(file_contents)); + } else if (!FLAGS_params.empty()) { + CHECK(solver.SetSolverSpecificParametersAsString(FLAGS_params)) + << "Wrong --params format."; + } + absl::PrintF( + "%-12s: %s\n", "Solver", + MPModelRequest::SolverType_Name( + static_cast(solver.ProblemType())) + .c_str()); + + // Load the proto into the solver. + std::string error_message; + + // If requested, save the model to file. + if (!FLAGS_dump_model.empty()) { + CHECK(WriteProtoToFile(FLAGS_dump_model, model_proto, write_format, + FLAGS_dump_gzip)); + } + + const MPSolverResponseStatus status = + solver.LoadModelFromProtoWithUniqueNamesOrDie(model_proto, + &error_message); + if (request_proto.has_solver_time_limit_seconds()) { + solver.set_time_limit( + static_cast(1000.0 * request_proto.solver_time_limit_seconds())); + } + // Note, the underlying MPSolver treats time limit equal to 0 as no limit. + if (FLAGS_time_limit_ms >= 0) { + solver.set_time_limit(FLAGS_time_limit_ms); + } + if (status != MPSOLVER_MODEL_IS_VALID) { + LOG(ERROR) << MPSolverResponseStatus_Name(status) << ": " << error_message; + return false; + } + absl::PrintF("%-12s: %d x %d\n", "Dimension", solver.NumConstraints(), + solver.NumVariables()); + + // Solve. + MPSolverParameters param; + MPSolver::ResultStatus solve_status = MPSolver::NOT_SOLVED; + 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()) { + operations_research::MPModelRequest request; + request.set_solver_type( + static_cast(solver.ProblemType())); + request.set_solver_time_limit_seconds(solver.time_limit_in_secs()); + request.set_solver_specific_parameters( + solver.GetSolverSpecificParametersAsString()); + *request.mutable_model() = model_proto; + CHECK(WriteProtoToFile(FLAGS_dump_request, request, write_format, + FLAGS_dump_gzip)); + } + + const bool has_solution = + solve_status == MPSolver::OPTIMAL || solve_status == MPSolver::FEASIBLE; + + // If requested, get the MPModelResponse and save it to file. + if (!FLAGS_dump_response.empty() && has_solution) { + operations_research::MPSolutionResponse response; + solver.FillSolutionResponseProto(&response); + CHECK(WriteProtoToFile(FLAGS_dump_response, response, write_format, + FLAGS_dump_gzip)); + } + if (!FLAGS_output_csv.empty() && has_solution) { + operations_research::MPSolutionResponse result; + solver.FillSolutionResponseProto(&result); + std::string csv_file; + for (int i = 0; i < result.variable_value_size(); ++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())); + } + // If --verify_solution is true, we already verified it. If not, we add + // a verification step here. + if (has_solution && !FLAGS_verify_solution) { + LOG(INFO) << "Verifying the solution"; + solver.VerifySolution(/*tolerance=*/param.GetDoubleParam( + MPSolverParameters::PRIMAL_TOLERANCE), + /*log_errors=*/true); + } + + absl::PrintF("%-12s: %s\n", "Status", + MPSolverResponseStatus_Name( + static_cast(solve_status)) + .c_str()); + absl::PrintF("%-12s: %15.15e\n", "Objective", + has_solution ? solver.Objective().Value() : 0.0); + absl::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()) { + absl::PrintF("%-12s: %d\n", "Nodes", solver.nodes()); + } + absl::PrintF("%-12s: %-6.4g\n", "Time", absl::ToDoubleSeconds(solving_time)); + return true; +} + +} // namespace +} // namespace operations_research + +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/libs/or-tools/examples/cpp/solve_and_print_intermediate_solutions_sample_sat.cc b/libs/or-tools-bin/examples/cpp/solve_and_print_intermediate_solutions_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/solve_and_print_intermediate_solutions_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/solve_and_print_intermediate_solutions_sample_sat.cc diff --git a/libs/or-tools/examples/cpp/solve_with_time_limit_sample_sat.cc b/libs/or-tools-bin/examples/cpp/solve_with_time_limit_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/solve_with_time_limit_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/solve_with_time_limit_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/sports_scheduling_sat.cc b/libs/or-tools-bin/examples/cpp/sports_scheduling_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..eb2523b92f9a3e3950d9e9874457a86aa6b9136c --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/sports_scheduling_sat.cc @@ -0,0 +1,330 @@ +// 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. + +// Sports scheduling problem. +// +// We want to solve the problem of scheduling of team matches in a +// double round robin tournament. Given a number of teams, we want +// each team to encounter all other teams, twice, once at home, and +// once away. Furthermore, you cannot meet the same team twice in the +// same half-season. +// +// Finally, there are constraints on the sequence of home or aways: +// - You cannot have 3 consecutive homes or three consecutive aways. +// - A break is a sequence of two homes or two aways, the overall objective +// of the optimization problem is to minimize the total number of breaks. +// - If team A meets team B, the reverse match cannot happen less that 6 weeks +// after. +// +// We model this problem with three matrices of variables, each with +// num_teams rows and 2*(num_teams - 1) columns: the var at position [i][j] +// corresponds to the match of team #i at day #j. There are +// 2*(num_teams - 1) columns because each team meets num_teams - 1 +// opponents twice. +// +// - The 'opponent' var [i][j] is the index of the opposing team. +// - The 'home_away' var [i][j] is a boolean: 1 for 'playing away', +// 0 for 'playing at home'. +// - The 'signed_opponent' var [i][j] is the 'opponent' var [i][j] + +// num_teams * the 'home_away' var [i][j]. +// +// This aggregated variable will be useful to state constraints of the model +// and to do search on it. + +#include "absl/strings/str_cat.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/base/integral_types.h" +#include "ortools/base/logging.h" +#include "ortools/sat/cp_model.h" + +// Problem main flags. +DEFINE_int32(num_teams, 10, "Number of teams in the problem."); +DEFINE_string(params, "", "Sat parameters."); + +namespace operations_research { +namespace sat { + +void FirstModel(int num_teams) { + const int num_days = 2 * num_teams - 2; + const int kNoRematch = 6; + + CpModelBuilder builder; + + // Calendar variables. + std::vector> opponents(num_teams); + std::vector> home_aways(num_teams); + std::vector> signed_opponents(num_teams); + + for (int t = 0; t < num_teams; ++t) { + for (int d = 0; d < num_days; ++d) { + Domain opponent_domain(0, num_teams - 1); + Domain signed_opponent_domain(0, 2 * num_teams - 1); + IntVar opp = builder.NewIntVar(opponent_domain) + .WithName(absl::StrCat("opponent_", t, "_", d)); + BoolVar home = + builder.NewBoolVar().WithName(absl::StrCat("home_aways", t, "_", d)); + IntVar signed_opp = + builder.NewIntVar(signed_opponent_domain) + .WithName(absl::StrCat("signed_opponent_", t, "_", d)); + + opponents[t].push_back(opp); + home_aways[t].push_back(home); + signed_opponents[t].push_back(signed_opp); + + // One team cannot meet itself. + builder.AddNotEqual(opp, t); + builder.AddNotEqual(signed_opp, t); + builder.AddNotEqual(signed_opp, t + num_teams); + + // Link opponent, home_away, and signed_opponent. + builder.AddEquality(opp, signed_opp).OnlyEnforceIf(Not(home)); + builder.AddEquality(LinearExpr(opp).AddConstant(num_teams), signed_opp) + .OnlyEnforceIf(home); + } + } + + // One day constraints. + for (int d = 0; d < num_days; ++d) { + std::vector day_opponents; + std::vector day_home_aways; + for (int t = 0; t < num_teams; ++t) { + day_opponents.push_back(opponents[t][d]); + day_home_aways.push_back(home_aways[t][d]); + } + + builder.AddInverseConstraint(day_opponents, day_opponents); + + for (int first_team = 0; first_team < num_teams; ++first_team) { + IntVar first_home = day_home_aways[first_team]; + IntVar second_home = builder.NewBoolVar(); + builder.AddVariableElement(day_opponents[first_team], day_home_aways, + second_home); + builder.AddEquality(LinearExpr::Sum({first_home, second_home}), 1); + } + + builder.AddEquality(LinearExpr::Sum(day_home_aways), num_teams / 2); + } + + // One team constraints. + for (int t = 0; t < num_teams; ++t) { + builder.AddAllDifferent(signed_opponents[t]); + const std::vector first_part(opponents[t].begin(), + opponents[t].begin() + num_teams - 1); + builder.AddAllDifferent(first_part); + const std::vector second_part(opponents[t].begin() + num_teams - 1, + opponents[t].end()); + + builder.AddAllDifferent(second_part); + + for (int day = num_teams - kNoRematch; day < num_teams - 1; ++day) { + const std::vector moving(opponents[t].begin() + day, + opponents[t].begin() + day + kNoRematch); + builder.AddAllDifferent(moving); + } + + builder.AddEquality(LinearExpr::BooleanSum(home_aways[t]), num_teams - 1); + + // Forbid sequence of 3 homes or 3 aways. + for (int start = 0; start < num_days - 2; ++start) { + builder.AddBoolOr({home_aways[t][start], home_aways[t][start + 1], + home_aways[t][start + 2]}); + builder.AddBoolOr({Not(home_aways[t][start]), + Not(home_aways[t][start + 1]), + Not(home_aways[t][start + 2])}); + } + } + + // Objective. + std::vector breaks; + for (int t = 0; t < num_teams; ++t) { + for (int d = 0; d < num_days - 1; ++d) { + BoolVar break_var = + builder.NewBoolVar().WithName(absl::StrCat("break_", t, "_", d)); + builder.AddBoolOr( + {Not(home_aways[t][d]), Not(home_aways[t][d + 1]), break_var}); + builder.AddBoolOr({home_aways[t][d], home_aways[t][d + 1], break_var}); + breaks.push_back(break_var); + } + } + + builder.Minimize(LinearExpr::BooleanSum(breaks)); + + Model model; + if (!FLAGS_params.empty()) { + model.Add(NewSatParameters(FLAGS_params)); + } + + const CpSolverResponse response = SolveCpModel(builder.Build(), &model); + LOG(INFO) << CpSolverResponseStats(response); + + if (response.status() == CpSolverStatus::OPTIMAL || + response.status() == CpSolverStatus::FEASIBLE) { + for (int t = 0; t < num_teams; ++t) { + std::string output; + for (int d = 0; d < num_days; ++d) { + const int opponent = SolutionIntegerValue(response, opponents[t][d]); + const bool home = SolutionBooleanValue(response, home_aways[t][d]); + if (home) { + output += absl::StrCat(" %2d@", opponent); + } else { + output += absl::StrCat(" %2d ", opponent); + } + } + LOG(INFO) << output; + } + } +} + +void SecondModel(int num_teams) { + const int num_days = 2 * num_teams - 2; + // const int kNoRematch = 6; + const int matches_per_day = num_teams - 1; + + CpModelBuilder builder; + + // Does team i receive team j at home on day d? + std::vector>> fixtures(num_days); + for (int d = 0; d < num_days; ++d) { + fixtures[d].resize(num_teams); + for (int i = 0; i < num_teams; ++i) { + fixtures[d][i].resize(num_teams); + for (int j = 0; j < num_teams; ++j) { + if (i == j) { + fixtures[d][i][i] = builder.FalseVar(); + } else { + fixtures[d][i][j] = builder.NewBoolVar(); + } + } + } + } + + // Is team t at home on day d? + std::vector> at_home(num_days); + for (int d = 0; d < num_days; ++d) { + for (int t = 0; t < num_teams; ++t) { + at_home[d].push_back(builder.NewBoolVar()); + } + } + + // Each day, Team t plays either at home or away. + for (int d = 0; d < num_days; ++d) { + for (int team = 0; team < num_teams; ++team) { + std::vector possible_opponents; + for (int other = 0; other < num_teams; ++other) { + if (team == other) + continue; + possible_opponents.push_back(fixtures[d][team][other]); + possible_opponents.push_back(fixtures[d][other][team]); + } + builder.AddEquality(LinearExpr::BooleanSum(possible_opponents), 1); + } + } + + // Each fixture happens once per season. + for (int team = 0; team < num_teams; ++team) { + for (int other = 0; other < num_teams; ++other) { + if (team == other) + continue; + std::vector possible_days; + for (int d = 0; d < num_days; ++d) { + possible_days.push_back(fixtures[d][team][other]); + } + builder.AddEquality(LinearExpr::BooleanSum(possible_days), 1); + } + } + + // Meet each opponent once per season. + for (int team = 0; team < num_teams; ++team) { + for (int other = 0; other < num_teams; ++other) { + if (team == other) + continue; + std::vector first_half; + std::vector second_half; + for (int d = 0; d < matches_per_day; ++d) { + first_half.push_back(fixtures[d][team][other]); + first_half.push_back(fixtures[d][other][team]); + second_half.push_back(fixtures[d + matches_per_day][team][other]); + second_half.push_back(fixtures[d + matches_per_day][other][team]); + } + builder.AddEquality(LinearExpr::BooleanSum(first_half), 1); + builder.AddEquality(LinearExpr::BooleanSum(second_half), 1); + } + } + + // Maintain at_home[day][team]. + for (int d = 0; d < num_days; ++d) { + for (int team = 0; team < num_teams; ++team) { + for (int other = 0; other < num_teams; ++other) { + if (team == other) + continue; + builder.AddImplication(fixtures[d][team][other], at_home[d][team]); + builder.AddImplication(fixtures[d][team][other], + Not(at_home[d][other])); + } + } + } + + // Forbid sequence of 3 homes or 3 aways. + for (int team = 0; team < num_teams; ++team) { + for (int d = 0; d < num_days - 2; ++d) { + builder.AddBoolOr( + {at_home[d][team], at_home[d + 1][team], at_home[d + 2][team]}); + builder.AddBoolOr({Not(at_home[d][team]), Not(at_home[d + 1][team]), + Not(at_home[d + 2][team])}); + } + } + + // Objective. + std::vector breaks; + for (int t = 0; t < num_teams; ++t) { + for (int d = 0; d < num_days - 1; ++d) { + BoolVar break_var = builder.NewBoolVar(); + builder.AddBoolOr( + {Not(at_home[d][t]), Not(at_home[d + 1][t]), break_var}); + builder.AddBoolOr({at_home[d][t], at_home[d + 1][t], break_var}); + builder.AddBoolOr( + {Not(at_home[d][t]), at_home[d + 1][t], Not(break_var)}); + builder.AddBoolOr( + {at_home[d][t], Not(at_home[d + 1][t]), Not(break_var)}); + breaks.push_back(break_var); + } + } + + builder.AddGreaterOrEqual(LinearExpr::BooleanSum(breaks), 2 * num_teams - 4); + + builder.Minimize(LinearExpr::BooleanSum(breaks)); + + Model model; + if (!FLAGS_params.empty()) { + model.Add(NewSatParameters(FLAGS_params)); + } + + const CpSolverResponse response = SolveCpModel(builder.Build(), &model); + LOG(INFO) << CpSolverResponseStats(response); +} + +} // namespace sat +} // namespace operations_research + +static const char kUsage[] = + "Usage: see flags.\nThis program runs a sports scheduling problem." + "There is no output besides the debug LOGs of the solver."; + +int main(int argc, char **argv) { + gflags::SetUsageMessage(kUsage); + gflags::ParseCommandLineFlags(&argc, &argv, true); + CHECK_EQ(0, FLAGS_num_teams % 2) << "The number of teams must be even"; + CHECK_GE(FLAGS_num_teams, 2) << "At least 2 teams"; + operations_research::sat::SecondModel(FLAGS_num_teams); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/step_function_sample_sat.cc b/libs/or-tools-bin/examples/cpp/step_function_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/step_function_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/step_function_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/stigler_diet.cc b/libs/or-tools-bin/examples/cpp/stigler_diet.cc new file mode 100644 index 0000000000000000000000000000000000000000..728189251bf087e4ccc2f3ec319c7c3279a8c110 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/stigler_diet.cc @@ -0,0 +1,300 @@ +// 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/logging.h" +#include "ortools/linear_solver/linear_solver.h" +#include "ortools/linear_solver/linear_solver.pb.h" + +namespace operations_research { +void RunStiglerDietExample() { + // Nutrient minimums. + std::vector> nutrients = { + {"Calories (kcal)", 3.0}, + {"Protein (g)", 70.0}, + {"Calcium (g)", 0.8}, + {"Iron (mg)", 12.0}, + {"Vitamin A (kIU)", 5.0}, + {"Thiamine (Vitamin B1) (mg)", 1.8}, + {"Riboflavin (Vitamin B2) (mg)", 2.7}, + {"Niacin (mg)", 18.0}, + {"Ascorbic Acid (Vitamin C) (mg)", 75.0}}; + + struct Commodity { + // Commodity name + std::string name; + // Unit + std::string unit; + // 1939 price per unit (cents) + double price; + // Calories (kcal) + // Protein (g) + // Calcium (g) + // Iron (mg) + // Vitamin A (kIU) + // Vitamin B1 (mg) + // Vitamin B2 (mg) + // Niacin (mg) + // Vitamin C (mg) + std::vector nutrients; + }; + + std::vector data = { + {"Wheat Flour (Enriched)", + "10 lb.", + 36, + {44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0}}, + {"Macaroni", "1 lb.", 14.1, {11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0}}, + {"Wheat Cereal (Enriched)", + "28 oz.", + 24.2, + {11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0}}, + {"Corn Flakes", "8 oz.", 7.1, {11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0}}, + {"Corn Meal", + "1 lb.", + 4.6, + {36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0}}, + {"Hominy Grits", + "24 oz.", + 8.5, + {28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0}}, + {"Rice", "1 lb.", 7.5, {21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0}}, + {"Rolled Oats", "1 lb.", 7.1, {25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0}}, + {"White Bread (Enriched)", + "1 lb.", + 7.9, + {15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0}}, + {"Whole Wheat Bread", + "1 lb.", + 9.1, + {12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0}}, + {"Rye Bread", "1 lb.", 9.1, {12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0}}, + {"Pound Cake", "1 lb.", 24.8, {8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0}}, + {"Soda Crackers", "1 lb.", 15.1, {12.5, 288, 0.5, 50, 0, 0, 0, 0, 0}}, + {"Milk", "1 qt.", 11, {6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177}}, + {"Evaporated Milk (can)", + "14.5 oz.", + 6.7, + {8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60}}, + {"Butter", "1 lb.", 30.8, {10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0}}, + {"Oleomargarine", "1 lb.", 16.1, {20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0}}, + {"Eggs", "1 doz.", 32.6, {2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0}}, + {"Cheese (Cheddar)", + "1 lb.", + 24.2, + {7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0}}, + {"Cream", "1/2 pt.", 14.1, {3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17}}, + {"Peanut Butter", + "1 lb.", + 17.9, + {15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0}}, + {"Mayonnaise", "1/2 pt.", 16.7, {8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0}}, + {"Crisco", "1 lb.", 20.3, {20.1, 0, 0, 0, 0, 0, 0, 0, 0}}, + {"Lard", "1 lb.", 9.8, {41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0}}, + {"Sirloin Steak", + "1 lb.", + 39.6, + {2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0}}, + {"Round Steak", "1 lb.", 36.4, {2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0}}, + {"Rib Roast", "1 lb.", 29.2, {3.4, 213, 0.1, 33, 0, 0, 2, 0, 0}}, + {"Chuck Roast", "1 lb.", 22.6, {3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0}}, + {"Plate", "1 lb.", 14.6, {8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0}}, + {"Liver (Beef)", + "1 lb.", + 26.8, + {2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525}}, + {"Leg of Lamb", "1 lb.", 27.6, {3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0}}, + {"Lamb Chops (Rib)", + "1 lb.", + 36.6, + {3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0}}, + {"Pork Chops", "1 lb.", 30.7, {3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0}}, + {"Pork Loin Roast", + "1 lb.", + 24.2, + {4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0}}, + {"Bacon", "1 lb.", 25.6, {10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0}}, + {"Ham, smoked", "1 lb.", 27.4, {6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0}}, + {"Salt Pork", "1 lb.", 16, {18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0}}, + {"Roasting Chicken", + "1 lb.", + 30.3, + {1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46}}, + {"Veal Cutlets", "1 lb.", 42.3, {1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0}}, + {"Salmon, Pink (can)", + "16 oz.", + 13, + {5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0}}, + {"Apples", "1 lb.", 4.4, {5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544}}, + {"Bananas", "1 lb.", 6.1, {4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498}}, + {"Lemons", "1 doz.", 26, {1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952}}, + {"Oranges", "1 doz.", 30.9, {2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998}}, + {"Green Beans", "1 lb.", 7.1, {2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862}}, + {"Cabbage", "1 lb.", 3.7, {2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369}}, + {"Carrots", "1 bunch", 4.7, {2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608}}, + {"Celery", "1 stalk", 7.3, {0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313}}, + {"Lettuce", "1 head", 8.2, {0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449}}, + {"Onions", "1 lb.", 3.6, {5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184}}, + {"Potatoes", + "15 lb.", + 34, + {14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522}}, + {"Spinach", "1 lb.", 8.1, {1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755}}, + {"Sweet Potatoes", + "1 lb.", + 5.1, + {9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912}}, + {"Peaches (can)", + "No. 2 1/2", + 16.8, + {3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196}}, + {"Pears (can)", + "No. 2 1/2", + 20.4, + {3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81}}, + {"Pineapple (can)", + "No. 2 1/2", + 21.3, + {2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399}}, + {"Asparagus (can)", + "No. 2", + 27.7, + {0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272}}, + {"Green Beans (can)", + "No. 2", + 10, + {1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431}}, + {"Pork and Beans (can)", + "16 oz.", + 7.1, + {7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0}}, + {"Corn (can)", "No. 2", 10.4, {5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218}}, + {"Peas (can)", + "No. 2", + 13.8, + {2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370}}, + {"Tomatoes (can)", + "No. 2", + 8.6, + {1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253}}, + {"Tomato Soup (can)", + "10 1/2 oz.", + 7.6, + {1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862}}, + {"Peaches, Dried", + "1 lb.", + 15.7, + {8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57}}, + {"Prunes, Dried", + "1 lb.", + 9, + {12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257}}, + {"Raisins, Dried", + "15 oz.", + 9.4, + {13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136}}, + {"Peas, Dried", + "1 lb.", + 7.9, + {20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0}}, + {"Lima Beans, Dried", + "1 lb.", + 8.9, + {17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0}}, + {"Navy Beans, Dried", + "1 lb.", + 5.9, + {26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0}}, + {"Coffee", "1 lb.", 22.4, {0, 0, 0, 0, 0, 4, 5.1, 50, 0}}, + {"Tea", "1/4 lb.", 17.4, {0, 0, 0, 0, 0, 0, 2.3, 42, 0}}, + {"Cocoa", "8 oz.", 8.6, {8.7, 237, 3, 72, 0, 2, 11.9, 40, 0}}, + {"Chocolate", "8 oz.", 16.2, {8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0}}, + {"Sugar", "10 lb.", 51.7, {34.9, 0, 0, 0, 0, 0, 0, 0, 0}}, + {"Corn Syrup", "24 oz.", 13.7, {14.7, 0, 0.5, 74, 0, 0, 0, 5, 0}}, + {"Molasses", "18 oz.", 13.6, {9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0}}, + {"Strawberry Preserves", + "1 lb.", + 20.5, + {6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0}}}; + + // Instantiate the solver + MPSolver solver("StiglerDietExample", MPSolver::GLOP_LINEAR_PROGRAMMING); + + // Declare an array to hold our nutritional data. + std::vector food; + + // Objective: minimize the sum of (price-normalized) foods. + MPObjective* const objective = solver.MutableObjective(); + const double infinity = solver.infinity(); + for (const Commodity& commodity : data) { + food.push_back(solver.MakeNumVar(0.0, infinity, commodity.name)); + objective->SetCoefficient(food.back(), 1); + } + objective->SetMinimization(); + + // Create the constraints, one per nutrient. + std::vector constraints; + for (std::size_t i = 0; i < nutrients.size(); ++i) { + constraints.push_back( + solver.MakeRowConstraint(nutrients[i].second, infinity)); + for (std::size_t j = 0; j < data.size(); ++j) { + constraints.back()->SetCoefficient(food[j], data[j].nutrients[i]); + } + } + + LOG(INFO) << "Number of variables = " << solver.NumVariables(); + LOG(INFO) << "Number of constraints = " << solver.NumConstraints(); + + // Solve! + const MPSolver::ResultStatus result_status = solver.Solve(); + // Check that the problem has an optimal solution. + if (result_status != MPSolver::OPTIMAL) { + LOG(INFO) << "The problem does not have an optimal solution!"; + if (result_status == MPSolver::FEASIBLE) { + LOG(INFO) << "A potentially suboptimal solution was found"; + } else { + LOG(INFO) << "The solver could not solve the problem."; + } + return; + } + + std::vector nutrients_result(nutrients.size()); + LOG(INFO) << ""; + LOG(INFO) << "Annual Foods:"; + for (std::size_t i = 0; i < data.size(); ++i) { + if (food[i]->solution_value() > 0.0) { + LOG(INFO) << data[i].name << ": $" << 365. * food[i]->solution_value(); + } + for (std::size_t j = 0; j < nutrients.size(); ++j) { + nutrients_result[j] += data[i].nutrients[j] * food[i]->solution_value(); + } + } + LOG(INFO) << ""; + LOG(INFO) << "Optimal annual price: $" << 365. * objective->Value(); + LOG(INFO) << ""; + LOG(INFO) << "Nutrients per day:"; + for (std::size_t i = 0; i < nutrients.size(); ++i) { + LOG(INFO) << nutrients[i].first << ": " << nutrients_result[i] << " (min " + << nutrients[i].second << ")"; + } + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds"; + LOG(INFO) << "Problem solved in " << solver.iterations() << " iterations"; +} +} // namespace operations_research + +int main(int argc, char** argv) { + google::InitGoogleLogging(argv[0]); + FLAGS_logtostderr = 1; + operations_research::RunStiglerDietExample(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/stop_after_n_solutions_sample_sat.cc b/libs/or-tools-bin/examples/cpp/stop_after_n_solutions_sample_sat.cc similarity index 100% rename from libs/or-tools/examples/cpp/stop_after_n_solutions_sample_sat.cc rename to libs/or-tools-bin/examples/cpp/stop_after_n_solutions_sample_sat.cc diff --git a/libs/or-tools-bin/examples/cpp/strawberry_fields_with_column_generation.cc b/libs/or-tools-bin/examples/cpp/strawberry_fields_with_column_generation.cc new file mode 100644 index 0000000000000000000000000000000000000000..6b70415c2018700821dcab0770a9ec68aad83b4e --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/strawberry_fields_with_column_generation.cc @@ -0,0 +1,655 @@ +// 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. + +// Demonstration of column generation using LP toolkit. +// +// Column generation is the technique of generating columns (aka +// resource bundles aka variables) of the constraint matrix +// incrementally guided by feedback from the constraint duals +// (cost-of-resources). Frequently this lets one solve large problems +// efficiently, e.g. problems where the number of potential columns is +// exponentially large. +// +// Solves a covering problem taken from ITA Software recruiting web +// site: +// +// "Strawberries are growing in the cells of a rectangular field +// (grid). You want to build greenhouses to enclose the +// strawberries. Greenhouses are rectangular, axis-aligned with the +// field (i.e., not diagonal), and may not overlap. The cost of each +// greenhouse is $10 plus $1 per unit of area covered." +// +// Variables: +// +// for each Box (greenhouse), continuous variable b{x1,y1,x2,y2} in [0,1] +// +// Constraints: +// +// box limit: +// sum b{x1,y1,x2,y2) <= MAX_BOXES +// non-overlap (for each cell x,y): +// sum b{x1,y1,x2,y2} <= 1 (summed over containing x1<=x<=x2, y1<=y<=y2) +// coverage (for each cell x,y with a strawberry): +// sum b{x1,y1,x2,y2} = 1 (summed over containing x1<=x<=x2, y1<=y<=y2) +// +// Since the number of possible boxes is O(d^4) where d is the linear +// dimension, starts from singleton column (box) covering entire grid, +// ensuring solvability. Then iteratively the problem is solved and +// the constraint duals (aka reduced costs) used to guide the +// generation of a single new column (box), until convergence or a +// maximum number of iterations. +// +// No attempt is made to force integrality. + +#include +#include // strlen +#include +#include +#include +#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/linear_solver/linear_solver.h" + +DEFINE_bool(colgen_verbose, false, "print verbosely"); +DEFINE_bool(colgen_complete, false, "generate all columns initially"); +DEFINE_int32(colgen_max_iterations, 500, "max iterations"); +DEFINE_string(colgen_solver, "glop", "solver - glop (default) or clp"); +DEFINE_int32(colgen_instance, -1, "Which instance to solve (0 - 9)"); + +namespace operations_research { +// ---------- Data Instances ---------- +struct Instance { + int max_boxes; + int width; + int height; + const char* grid; +}; + +Instance kInstances[] = {{4, 22, 6, + "..@@@@@..............." + "..@@@@@@........@@@..." + ".....@@@@@......@@@..." + ".......@@@@@@@@@@@@..." + ".........@@@@@........" + ".........@@@@@........"}, + {3, 13, 10, + "............." + "............." + "............." + "...@@@@......" + "...@@@@......" + "...@@@@......" + ".......@@@..." + ".......@@@..." + ".......@@@..." + "............."}, + {4, 13, 9, + "............." + "..@.@.@......" + "...@.@.@....." + "..@.@.@......" + "..@.@.@......" + "...@.@.@....." + "....@.@......" + "..........@@@" + "..........@@@"}, + {4, 13, 9, + ".........@..." + ".........@..." + "@@@@@@@@@@..." + "..@......@..." + "..@......@..." + "..@......@..." + "..@@@@@@@@@@@" + "..@.........." + "..@.........."}, + {7, 25, 14, + "........................." + "..@@@@@@@@@@@@@@@@@@@@..." + "..@@@@@@@@@@@@@@@@@@@@..." + "..@@.................@..." + "..@@.................@..." + "..@@.......@@@.......@.@." + "..@@.......@@@.......@..." + "..@@...@@@@@@@@@@@@@@@..." + "..@@...@@@@@@@@@@@@@@@..." + "..@@.......@@@.......@..." + "..@@.......@@@.......@..." + "..@@.................@..." + "..@@.................@..." + "........................."}, + {6, 25, 16, + "........................." + "......@@@@@@@@@@@@@......" + "........................." + ".....@..........@........" + ".....@..........@........" + ".....@......@............" + ".....@......@.@@@@@@@...." + ".....@......@............" + ".....@......@.@@@@@@@...." + ".....@......@............" + "....@@@@....@............" + "....@@@@....@............" + "..@@@@@@....@............" + "..@@@.......@............" + "..@@@...................." + "..@@@@@@@@@@@@@@@@@@@@@@@"}, + {5, 40, 18, + "........................................" + "........................................" + "...@@@@@@..............................." + "...@@@@@@..............................." + "...@@@@@@..............................." + "...@@@@@@.........@@@@@@@@@@............" + "...@@@@@@.........@@@@@@@@@@............" + "..................@@@@@@@@@@............" + "..................@@@@@@@@@@............" + ".............@@@@@@@@@@@@@@@............" + ".............@@@@@@@@@@@@@@@............" + "........@@@@@@@@@@@@...................." + "........@@@@@@@@@@@@...................." + "........@@@@@@.........................." + "........@@@@@@.........................." + "........................................" + "........................................" + "........................................"}, + {8, 40, 18, + "........................................" + "..@@.@.@.@.............................." + "..@@.@.@.@...............@.............." + "..@@.@.@.@............@................." + "..@@.@.@.@.............................." + "..@@.@.@.@.................@............" + "..@@.@..................@..............." + "..@@.@.................................." + "..@@.@.................................." + "..@@.@................@@@@.............." + "..@@.@..............@@@@@@@@............" + "..@@.@.................................." + "..@@.@..............@@@@@@@@............" + "..@@.@.................................." + "..@@.@................@@@@.............." + "..@@.@.................................." + "..@@.@.................................." + "........................................"}, + {10, 40, 19, + "@@@@@..................................." + "@@@@@..................................." + "@@@@@..................................." + "@@@@@..................................." + "@@@@@..................................." + "@@@@@...........@@@@@@@@@@@............." + "@@@@@...........@@@@@@@@@@@............." + "....................@@@@................" + "....................@@@@................" + "....................@@@@................" + "....................@@@@................" + "....................@@@@................" + "...............@@@@@@@@@@@@@@..........." + "...............@@@@@@@@@@@@@@..........." + ".......@@@@@@@@@@@@@@@@@@@@@@..........." + ".......@@@@@@@@@........................" + "........................................" + "........................................" + "........................................"}, + {10, 40, 25, + "...................@...................." + "...............@@@@@@@@@................" + "............@@@.........@@@............." + "...........@...............@............" + "..........@.................@..........." + ".........@...................@.........." + ".........@...................@.........." + ".........@.....@@......@@....@.........." + "........@.....@@@@....@@@@....@........." + "........@.....................@........." + "........@.....................@........." + "........@..........@@.........@........." + ".......@@..........@@.........@@........" + "........@.....................@........." + "........@.....................@........." + "........@......@@@@@@@@@......@........." + "........@......@@@@@@@@@......@........." + ".........@...................@.........." + ".........@...................@.........." + ".........@...................@.........." + "..........@.................@..........." + "...........@...............@............" + "............@@@.........@@@............." + "...............@@@@@@@@@................" + "...................@...................."}}; + +const int kInstanceCount = 10; + +// ---------- Box --------- + +class Box { + public: + static const int kAreaCost = 1; + static const int kFixedCost = 10; + + Box() {} + Box(int x_min, int x_max, int y_min, int y_max) + : x_min_(x_min), x_max_(x_max), y_min_(y_min), y_max_(y_max) { + CHECK_GE(x_max, x_min); + CHECK_GE(y_max, y_min); + } + + int x_min() const { return x_min_; } + int x_max() const { return x_max_; } + int y_min() const { return y_min_; } + int y_max() const { return y_max_; } + + // Lexicographic order + int Compare(const Box& box) const { + int c; + if ((c = (x_min() - box.x_min())) != 0) return c; + if ((c = (x_max() - box.x_max())) != 0) return c; + if ((c = (y_min() - box.y_min())) != 0) return c; + return y_max() - box.y_max(); + } + + bool Contains(int x, int y) const { + return x >= x_min() && x <= x_max() && y >= y_min() && y <= y_max(); + } + + int Cost() const { + return kAreaCost * (x_max() - x_min() + 1) * (y_max() - y_min() + 1) + + kFixedCost; + } + + std::string DebugString() const { + return absl::StrFormat("[%d,%dx%d,%d]c%d", x_min(), y_min(), x_max(), + y_max(), Cost()); + } + + private: + int x_min_; + int x_max_; + int y_min_; + int y_max_; +}; + +struct BoxLessThan { + bool operator()(const Box& b1, const Box& b2) const { + return b1.Compare(b2) < 0; + } +}; + +// ---------- Covering Problem --------- + +class CoveringProblem { + public: + // Grid is a row-major string of length width*height with '@' for an + // occupied cell (strawberry) and '.' for an empty cell. Solver is + // not owned. + CoveringProblem(MPSolver* const solver, const Instance& instance) + : solver_(solver), + max_boxes_(instance.max_boxes), + width_(instance.width), + height_(instance.height), + grid_(instance.grid) {} + + // Constructs initial variables and constraints. Initial column + // (box) covers entire grid, ensuring feasibility. + bool Init() { + // Check consistency. + int size = strlen(grid_); + if (size != area()) { + return false; + } + for (int i = 0; i < size; ++i) { + char c = grid_[i]; + if ((c != '@') && (c != '.')) return false; + } + + AddCellConstraints(); // sum for every cell is <=1 or =1 + AddMaxBoxesConstraint(); // sum of box variables is <= max_boxes() + if (!FLAGS_colgen_complete) { + AddBox(Box(0, width() - 1, 0, height() - 1)); // grid-covering box + } else { + // Naive alternative to column generation - generate all boxes; + // works fine for smaller problems, too slow for big. + for (int y_min = 0; y_min < height(); ++y_min) { + for (int y_max = y_min; y_max < height(); ++y_max) { + for (int x_min = 0; x_min < width(); ++x_min) { + for (int x_max = x_min; x_max < width(); ++x_max) { + AddBox(Box(x_min, x_max, y_min, y_max)); + } + } + } + } + } + return true; + } + + int width() const { return width_; } + int height() const { return height_; } + int area() const { return width() * height(); } + int max_boxes() const { return max_boxes_; } + + bool IsCellOccupied(int x, int y) const { return grid_[index(x, y)] == '@'; } + + // Calculates reduced costs for each possible Box and if any is + // negative (improves cost), returns reduced cost and set target to + // the most-negative (steepest descent) one - otherwise returns 0.. + // + // For a problem in standard form 'minimize c*x s.t. Ax<=b, x>=0' + // the reduced cost vector is c - transp(y) * A where y is the dual + // cost column vector. + // + // For this covering problem, in which all coefficients in A are 0 + // or 1, this reduces to: + // + // reduced_cost(box) = + // + // box.Cost() - sum_{enclosed cell} cell_constraint->dual_value() + // - max_boxes_constraint_->dual_value() + // + // Since there are O(d^4) boxes, we don't also want O(d^2) sum for + // each, so pre-calculate sums of cell duals for all rectangles with + // upper-left at 0, 0, and use these to calculate the sum in + // constant time using the standard inclusion-exclusion trick. + double GetOptimalBox(Box* const target) { + // Cost change threshold for new Box + const double kCostChangeThreshold = -.01; + + // Precomputes the sum of reduced costs for every upper-left + // rectangle. + std::vector upper_left_sums(area()); + ComputeUpperLeftSums(&upper_left_sums); + + const double max_boxes_dual = max_boxes_constraint_->dual_value(); + double best_reduced_cost = kCostChangeThreshold; + Box best_box; + for (int y_min = 0; y_min < height(); ++y_min) { + for (int y_max = y_min; y_max < height(); ++y_max) { + for (int x_min = 0; x_min < width(); ++x_min) { + for (int x_max = x_min; x_max < width(); ++x_max) { + Box box(x_min, x_max, y_min, y_max); + const double cell_coverage_dual = // inclusion-exclusion + +zero_access(upper_left_sums, x_max, y_max) - + zero_access(upper_left_sums, x_max, y_min - 1) - + zero_access(upper_left_sums, x_min - 1, y_max) + + zero_access(upper_left_sums, x_min - 1, y_min - 1); + + // All coefficients for new column are 1, so no need to + // multiply constraint duals by any coefficients when + // computing the reduced cost. + const double reduced_cost = + box.Cost() - (cell_coverage_dual + max_boxes_dual); + + if (reduced_cost < best_reduced_cost) { + // Even with negative reduced cost, the box may already + // exist, and even be basic (part of solution)! This + // counterintuitive situation is due to the problem's + // many redundant linear equality constraints: many + // steepest-edge pivot moves will be of zero-length. + // Ideally one would want to check the length of the + // move but that is difficult without access to the + // internals of the solver (e.g., access to B^-1 in the + // simplex algorithm). + if (boxes_.find(box) == boxes_.end()) { + best_reduced_cost = reduced_cost; + best_box = box; + } + } + } + } + } + } + + if (best_reduced_cost < kCostChangeThreshold) { + if (target) { + *target = best_box; + } + return best_reduced_cost; + } + return 0; + } + + // Add continuous [0,1] box variable with box.Cost() as objective + // coefficient. Add to cell constraint of all enclosed cells. + MPVariable* AddBox(const Box& box) { + CHECK(boxes_.find(box) == boxes_.end()); + MPVariable* const var = solver_->MakeNumVar(0., 1., box.DebugString()); + solver_->MutableObjective()->SetCoefficient(var, box.Cost()); + max_boxes_constraint_->SetCoefficient(var, 1.0); + for (int y = box.y_min(); y <= box.y_max(); ++y) { + for (int x = box.x_min(); x <= box.x_max(); ++x) { + cell(x, y)->SetCoefficient(var, 1.0); + } + } + boxes_[box] = var; + return var; + } + + std::string PrintGrid() const { + std::string output = + absl::StrFormat("width = %d, height = %d, max_boxes = %d\n", width_, + height_, max_boxes_); + for (int y = 0; y < height_; ++y) { + absl::StrAppendFormat(&output, "%s\n", + std::string(grid_ + width_ * y, width_)); + } + return output; + } + + // Prints covering - total cost, those variables with non-zero value, + // and graphical depiction of covering using upper case letters for + // integral coverage and lower case for coverage using combination + // of fractional boxes. + std::string PrintCovering() const { + static const double kTolerance = 1e-5; + std::string output = + 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, + width_); // Copy the original line. + display[y * (width_ + 1) + width_] = '\n'; + } + display[height_ * (width_ + 1)] = '\0'; + int active_box_index = 0; + for (BoxTable::const_iterator i = boxes_.begin(); i != boxes_.end(); ++i) { + const double value = i->second->solution_value(); + if (value > kTolerance) { + const char box_character = + (i->second->solution_value() >= (1. - kTolerance) ? 'A' : 'a') + + active_box_index++; + 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) { + display[x + y * (width_ + 1)] = box_character; + } + } + } + } + output.append(display.get()); + return output; + } + + protected: + int index(int x, int y) const { return width_ * y + x; } + MPConstraint* cell(int x, int y) { return cells_[index(x, y)]; } + const MPConstraint* cell(int x, int y) const { return cells_[index(x, y)]; } + + // Adds constraints that every cell is covered at most once, exactly + // once if occupied. + void AddCellConstraints() { + cells_.resize(area()); + for (int y = 0; y < height(); ++y) { + for (int x = 0; x < width(); ++x) { + cells_[index(x, y)] = + solver_->MakeRowConstraint(IsCellOccupied(x, y) ? 1. : 0., 1.); + } + } + } + + // Adds constraint on maximum number of boxes used to cover. + void AddMaxBoxesConstraint() { + max_boxes_constraint_ = solver_->MakeRowConstraint(0., max_boxes()); + } + + // Gets 2d array element, returning 0 if out-of-bounds. + double zero_access(const std::vector& array, int x, int y) const { + if (x < 0 || y < 0) { + return 0; + } + return array[index(x, y)]; + } + + // Precomputes the sum of reduced costs for every upper-left + // rectangle. + void ComputeUpperLeftSums(std::vector* upper_left_sums) const { + for (int y = 0; y < height(); ++y) { + for (int x = 0; x < width(); ++x) { + upper_left_sums->operator[](index(x, y)) = + cell(x, y)->dual_value() + zero_access(*upper_left_sums, x - 1, y) + + zero_access(*upper_left_sums, x, y - 1) - + zero_access(*upper_left_sums, x - 1, y - 1); + } + } + } + + typedef std::map BoxTable; + MPSolver* const solver_; // not owned + const int max_boxes_; + const int width_; + const int height_; + const char* const grid_; + std::vector cells_; + BoxTable boxes_; + MPConstraint* max_boxes_constraint_; +}; + +// ---------- Main Solve Method ---------- + +// Solves iteratively using delayed column generation, up to maximum +// number of steps. +void SolveInstance(const Instance& instance, + MPSolver::OptimizationProblemType solver_type) { + // Prepares the solver. + MPSolver solver("ColumnGeneration", solver_type); + solver.SuppressOutput(); + solver.MutableObjective()->SetMinimization(); + + // Construct problem. + CoveringProblem problem(&solver, instance); + CHECK(problem.Init()); + LOG(INFO) << "Initial problem:\n" << problem.PrintGrid(); + + int step_number = 0; + while (step_number < FLAGS_colgen_max_iterations) { + if (FLAGS_colgen_verbose) { + LOG(INFO) << "Step number " << step_number; + } + + // Solve with existing columns. + CHECK_EQ(MPSolver::OPTIMAL, solver.Solve()); + if (FLAGS_colgen_verbose) { + LOG(INFO) << problem.PrintCovering(); + } + + // Find optimal new column to add, or stop if none. + Box box; + const double reduced_cost = problem.GetOptimalBox(&box); + if (reduced_cost >= 0) { + break; + } + + // Add new column to problem. + if (FLAGS_colgen_verbose) { + LOG(INFO) << "Adding " << box.DebugString() + << ", reduced_cost =" << reduced_cost; + } + problem.AddBox(box); + + ++step_number; + } + + if (step_number >= FLAGS_colgen_max_iterations) { + // Solve one last time with all generated columns. + CHECK_EQ(MPSolver::OPTIMAL, solver.Solve()); + } + + LOG(INFO) << step_number << " columns added"; + LOG(INFO) << "Final coverage: " << problem.PrintCovering(); +} +} // namespace operations_research + +int main(int argc, char** argv) { + std::string usage = "column_generation\n"; + usage += " --colgen_verbose print verbosely\n"; + usage += " --colgen_max_iterations max columns to generate\n"; + usage += " --colgen_complete generate all columns at start\n"; + + gflags::ParseCommandLineFlags(&argc, &argv, true); + + operations_research::MPSolver::OptimizationProblemType solver_type; + bool found = false; +#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 defined(USE_XPRESS) + if (FLAGS_colgen_solver == "xpress") { + solver_type = operations_research::MPSolver::XPRESS_LINEAR_PROGRAMMING; + //solver_type = operations_research::MPSolver::CPLEX_LINEAR_PROGRAMMING; + found = true; + } +#endif +#if defined(USE_CPLEX) + if (FLAGS_colgen_solver == "cplex") { + solver_type = operations_research::MPSolver::CPLEX_LINEAR_PROGRAMMING; + found = true; + } +#endif + if (!found) { + LOG(ERROR) << "Unknown solver " << FLAGS_colgen_solver; + return 1; + } + + LOG(INFO) << "Chosen solver: " << FLAGS_colgen_solver << std::endl; + + if (FLAGS_colgen_instance == -1) { + for (int i = 0; i < operations_research::kInstanceCount; ++i) { + const operations_research::Instance& instance = + operations_research::kInstances[i]; + operations_research::SolveInstance(instance, solver_type); + } + } else { + CHECK_GE(FLAGS_colgen_instance, 0); + CHECK_LT(FLAGS_colgen_instance, operations_research::kInstanceCount); + const operations_research::Instance& instance = + operations_research::kInstances[FLAGS_colgen_instance]; + operations_research::SolveInstance(instance, solver_type); + } + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/tsp.cc b/libs/or-tools-bin/examples/cpp/tsp.cc similarity index 100% rename from libs/or-tools/examples/cpp/tsp.cc rename to libs/or-tools-bin/examples/cpp/tsp.cc diff --git a/libs/or-tools/examples/cpp/tsp_circuit_board.cc b/libs/or-tools-bin/examples/cpp/tsp_circuit_board.cc similarity index 100% rename from libs/or-tools/examples/cpp/tsp_circuit_board.cc rename to libs/or-tools-bin/examples/cpp/tsp_circuit_board.cc diff --git a/libs/or-tools/examples/cpp/tsp_cities.cc b/libs/or-tools-bin/examples/cpp/tsp_cities.cc similarity index 100% rename from libs/or-tools/examples/cpp/tsp_cities.cc rename to libs/or-tools-bin/examples/cpp/tsp_cities.cc diff --git a/libs/or-tools/examples/cpp/tsp_distance_matrix.cc b/libs/or-tools-bin/examples/cpp/tsp_distance_matrix.cc similarity index 100% rename from libs/or-tools/examples/cpp/tsp_distance_matrix.cc rename to libs/or-tools-bin/examples/cpp/tsp_distance_matrix.cc diff --git a/libs/or-tools-bin/examples/cpp/uncapacitated_facility_location.cc b/libs/or-tools-bin/examples/cpp/uncapacitated_facility_location.cc new file mode 100644 index 0000000000000000000000000000000000000000..10899376a98b5ad0de0e599a4c143d4c7dd19548 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/uncapacitated_facility_location.cc @@ -0,0 +1,233 @@ +// Copyright 2020 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. + +// +// Uncapacitated Facility Location Problem. +// A description of the problem can be found here: +// https://en.wikipedia.org/wiki/Facility_location_problem. +// The variant which is tackled by this model does not consider capacities +// for facilities. Moreover, all cost are based on euclidean distance factors, +// i.e. the problem we really solve is a Metric Facility Location. For the +// sake of simplicity, facilities and demands are randomly located. Distances +// are assumed to be in meters and times in seconds. + +#include +#include + +#include "google/protobuf/text_format.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/base/random.h" +#include "ortools/base/integral_types.h" +#include "ortools/base/logging.h" +#include "ortools/linear_solver/linear_solver.h" + +DEFINE_int32(verbose, 0, "Verbosity level."); +DEFINE_int32(facilities, 20, "Candidate facilities to consider."); +DEFINE_int32(clients, 100, "Clients to serve."); +DEFINE_double(fix_cost, 5000, "Cost of opening a facility."); + +namespace operations_research { + +typedef struct { + double x{0}; + double y{0}; + } Location; + +typedef struct { + int f{-1}; + int c{-1}; + MPVariable* x{nullptr}; + } Edge; + +static double Distance(const Location& src, const Location& dst) { + return sqrt((src.x-dst.x)*(src.x-dst.x) + (src.y-dst.y)*(src.y-dst.y)); +} + +static void UncapacitatedFacilityLocation(int32 facilities, + int32 clients, double fix_cost, + MPSolver::OptimizationProblemType optimization_problem_type) { + LOG(INFO) << "Starting " << __func__; + // Local Constants + const int32 kXMax = 1000; + const int32 kYMax = 1000; + const double kMaxDistance = 6*sqrt((kXMax*kYMax))/facilities; + const int kStrLen = 1024; + // char buffer for names + char name_buffer[kStrLen+1]; + name_buffer[kStrLen] = '\0'; + LOG(INFO) << "Facilities/Clients/Fix cost/MaxDist: " << facilities << "/" + << clients << "/" << fix_cost << "/" << kMaxDistance; + // Setting up facilities and demand points + MTRandom randomizer(/*fixed seed*/20191029); + std::vector facility(facilities); + std::vector client(clients); + for (int i = 0; i < facilities; ++i) { + facility[i].x = randomizer.Uniform(kXMax + 1); + facility[i].y = randomizer.Uniform(kYMax + 1); + } + for (int i = 0; i < clients; ++i) { + client[i].x = randomizer.Uniform(kXMax + 1); + client[i].y = randomizer.Uniform(kYMax + 1); + } + + // Setup uncapacitated facility location model: + // Min sum( c_f * x_f : f in Facilities) + sum(x_{f,c} * x_{f,c} : {f,c} in E) + // s.t. (1) sum(x_{f,c} : f in Facilities) >= 1 forall c in Clients + // (2) x_f - x_{f,c} >= 0 forall {f,c} in E + // (3) x_f in {0,1} forall f in Facilities + // + // We consider E as the pairs {f,c} in Facilities x Clients such that + // Distance(f,c) <= kMaxDistance + MPSolver solver("UncapacitatedFacilityLocation", optimization_problem_type); + const double infinity = solver.infinity(); + MPObjective* objective = solver.MutableObjective(); + objective->SetMinimization(); + + // Add binary facilities variables + std::vector xf{}; + for (int f = 0; f < facilities; ++f) { + snprintf(name_buffer, kStrLen, "x[%d](%g,%g)", f, + facility[f].x, facility[f].y); + MPVariable* x = solver.MakeBoolVar(name_buffer); + xf.push_back(x); + objective->SetCoefficient(x, fix_cost); + } + + // Build edge variables + std::vector edges; + for (int c = 0; c < clients; ++c) { + snprintf(name_buffer, kStrLen, "R-Client[%d](%g,%g)", c, + client[c].x, client[c].y); + MPConstraint* client_constraint = solver.MakeRowConstraint(/* lb */1, + /* ub */infinity, name_buffer); + for (int f = 0; f < facilities; ++f) { + double distance = Distance(facility[f], client[c]); + if (distance > kMaxDistance) continue; + Edge edge{}; + snprintf(name_buffer, kStrLen, "x[%d,%d]", f, c); + edge.x = solver.MakeNumVar(/* lb */0, /*ub */1, name_buffer); + edge.f = f; + edge.c = c; + edges.push_back(edge); + objective->SetCoefficient(edge.x, distance); + // coefficient for constraint (1) + client_constraint->SetCoefficient(edge.x, 1); + // add constraint (2) + snprintf(name_buffer, kStrLen, "R-Edge[%d,%d]", f, c); + MPConstraint* edge_constraint = solver.MakeRowConstraint(/* lb */0, + /* ub */infinity, name_buffer); + edge_constraint->SetCoefficient(edge.x, -1); + edge_constraint->SetCoefficient(xf[f], 1); + } + }// End adding all edge variables + LOG(INFO) << "Number of variables = " << solver.NumVariables(); + LOG(INFO) << "Number of constraints = " << solver.NumConstraints(); + // display on screen LP if small enough + if (clients <= 10 && facilities <= 10) { + std::string lp_string{}; + solver.ExportModelAsLpFormat(/* obfuscate */false, &lp_string); + std::cout << "LP-Model:\n" << lp_string << std::endl; + } + // Set options and solve + solver.SetNumThreads(8); + solver.EnableOutput(); + 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!"; + } else { + LOG(INFO) << "Optimal objective value = " << objective->Value(); + if (FLAGS_verbose) { + std::vector> solution(facilities); + for (auto& edge : edges) { + if (edge.x->solution_value() < 0.5) continue; + solution[edge.f].push_back(edge.c); + } + std::cout << "\tSolution:\n"; + for (int f = 0; f < facilities; ++f) { + if (solution[f].size() < 1) continue; + assert(xf[f]->solution_value() > 0.5); + snprintf(name_buffer, kStrLen, "\t Facility[%d](%g,%g):", f, + facility[f].x, facility[f].y); + std::cout << name_buffer; + int i = 1; + for (auto c : solution[f]) { + snprintf(name_buffer, kStrLen, " Client[%d](%g,%g)", c, + client[c].x, client[c].y); + if(i++ >= 5) { + std::cout << "\n\t\t"; + i = 1; + } + std::cout << name_buffer; + } + std::cout << "\n"; + } + } + std::cout << "\n"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << solver.DurationSinceConstruction() + << " milliseconds"; + LOG(INFO) << "Problem solved in " << solver.iterations() << " iterations"; + LOG(INFO) << "Problem solved in " << solver.nodes() + << " branch-and-bound nodes"; + } +} + +void RunAllExamples(int32 facilities, int32 clients, double fix_cost) { +#if defined(USE_CBC) + LOG(INFO) << "---- Integer programming example with CBC ----"; + UncapacitatedFacilityLocation(facilities, clients, fix_cost, + MPSolver::CBC_MIXED_INTEGER_PROGRAMMING); +#endif +#if defined(USE_GLPK) + LOG(INFO) << "---- Integer programming example with GLPK ----"; + UncapacitatedFacilityLocation(facilities, clients, fix_cost, + MPSolver::GLPK_MIXED_INTEGER_PROGRAMMING); +#endif +#if defined(USE_SCIP) + LOG(INFO) << "---- Integer programming example with SCIP ----"; + UncapacitatedFacilityLocation(facilities, clients, fix_cost, + MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING); +#endif +#if defined(USE_GUROBI) + LOG(INFO) << "---- Integer programming example with Gurobi ----"; + UncapacitatedFacilityLocation(facilities, clients, fix_cost, + MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING); +#endif // USE_GUROBI +#if defined(USE_CPLEX) + LOG(INFO) << "---- Integer programming example with CPLEX ----"; + UncapacitatedFacilityLocation(facilities, clients, fix_cost, + MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING); +#endif // USE_CPLEX + LOG(INFO) << "---- Integer programming example with CP-SAT ----"; + UncapacitatedFacilityLocation(facilities, clients, fix_cost, + MPSolver::SAT_INTEGER_PROGRAMMING); +} + +} // namespace operations_research + +int main(int argc, char** argv) { + google::InitGoogleLogging(argv[0]); + gflags::SetUsageMessage( + std::string("This program solve a (randomly generated)\n") + + std::string("Uncapacitated Facility Location Problem. Sample Usage:\n")); + gflags::ParseCommandLineFlags(&argc, &argv, true); + CHECK_LT(0, FLAGS_facilities) << "Specify an instance size greater than 0."; + CHECK_LT(0, FLAGS_clients) << "Specify a non-null client size."; + CHECK_LT(0, FLAGS_fix_cost) << "Specify a non-null client size."; + FLAGS_logtostderr = 1; + operations_research::RunAllExamples(FLAGS_facilities, FLAGS_clients, + FLAGS_fix_cost); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-bin/examples/cpp/variable_intervals_sat.cc b/libs/or-tools-bin/examples/cpp/variable_intervals_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..1f4de6465178a480762a6b17d99a49c7e6d039d7 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/variable_intervals_sat.cc @@ -0,0 +1,72 @@ +#include "ortools/sat/cp_model.h" +#include "ortools/sat/sat_parameters.pb.h" +#include "ortools/util/time_limit.h" + +namespace operations_research { +namespace sat { + +void Solve() { + CpModelBuilder cp_model; + + const IntVar start_ins = cp_model.NewIntVar(Domain(660, 755)); + const IntVar duration_ins = cp_model.NewConstant(25); + const IntVar end_ins = cp_model.NewIntVar(Domain(685, 780)); + const IntervalVar ins = + cp_model.NewIntervalVar(start_ins, duration_ins, end_ins); + + 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 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); + + cp_model.AddEquality(LinearExpr::Sum({duration_p1, duration_p2}), 360); + cp_model.AddLessOrEqual(end_p1, start_p2); + + cp_model.AddNoOverlap({ins, p1, p2}); + + Model model; + + // Tell the solver to enumerate all solutions. + SatParameters parameters; + parameters.set_enumerate_all_solutions(true); + model.Add(NewSatParameters(parameters)); + + // Create an atomic Boolean that will be periodically checked by the limit. + std::atomic stopped(false); + model.GetOrCreate()->RegisterExternalBooleanAsLimit(&stopped); + + const int kSolutionLimit = 100; + int num_solutions = 0; + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + LOG(INFO) << "Solution " << num_solutions; + LOG(INFO) << " start_p1 = " << SolutionIntegerValue(r, start_p1); + LOG(INFO) << " duration_p1 = " << SolutionIntegerValue(r, duration_p1); + LOG(INFO) << " start_p2 = " << SolutionIntegerValue(r, start_p2); + LOG(INFO) << " duration_p2 = " << SolutionIntegerValue(r, duration_p2); + LOG(INFO) << " start_ins = " << SolutionIntegerValue(r, start_ins); + num_solutions++; + if (num_solutions >= kSolutionLimit) { + stopped = true; + LOG(INFO) << "Stop search after " << kSolutionLimit << " solutions."; + } + })); + + SolveCpModel(cp_model.Build(), &model); + LOG(INFO) << "Number of solutions found: " << num_solutions; +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::Solve(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/cpp/vrp.cc b/libs/or-tools-bin/examples/cpp/vrp.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp.cc rename to libs/or-tools-bin/examples/cpp/vrp.cc diff --git a/libs/or-tools/examples/cpp/vrp_capacity.cc b/libs/or-tools-bin/examples/cpp/vrp_capacity.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_capacity.cc rename to libs/or-tools-bin/examples/cpp/vrp_capacity.cc diff --git a/libs/or-tools/examples/cpp/vrp_drop_nodes.cc b/libs/or-tools-bin/examples/cpp/vrp_drop_nodes.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_drop_nodes.cc rename to libs/or-tools-bin/examples/cpp/vrp_drop_nodes.cc diff --git a/libs/or-tools/examples/cpp/vrp_global_span.cc b/libs/or-tools-bin/examples/cpp/vrp_global_span.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_global_span.cc rename to libs/or-tools-bin/examples/cpp/vrp_global_span.cc diff --git a/libs/or-tools/examples/cpp/vrp_initial_routes.cc b/libs/or-tools-bin/examples/cpp/vrp_initial_routes.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_initial_routes.cc rename to libs/or-tools-bin/examples/cpp/vrp_initial_routes.cc diff --git a/libs/or-tools/examples/cpp/vrp_pickup_delivery.cc b/libs/or-tools-bin/examples/cpp/vrp_pickup_delivery.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_pickup_delivery.cc rename to libs/or-tools-bin/examples/cpp/vrp_pickup_delivery.cc diff --git a/libs/or-tools/examples/cpp/vrp_pickup_delivery_fifo.cc b/libs/or-tools-bin/examples/cpp/vrp_pickup_delivery_fifo.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_pickup_delivery_fifo.cc rename to libs/or-tools-bin/examples/cpp/vrp_pickup_delivery_fifo.cc diff --git a/libs/or-tools/examples/cpp/vrp_pickup_delivery_lifo.cc b/libs/or-tools-bin/examples/cpp/vrp_pickup_delivery_lifo.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_pickup_delivery_lifo.cc rename to libs/or-tools-bin/examples/cpp/vrp_pickup_delivery_lifo.cc diff --git a/libs/or-tools/examples/cpp/vrp_resources.cc b/libs/or-tools-bin/examples/cpp/vrp_resources.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_resources.cc rename to libs/or-tools-bin/examples/cpp/vrp_resources.cc diff --git a/libs/or-tools/examples/cpp/vrp_starts_ends.cc b/libs/or-tools-bin/examples/cpp/vrp_starts_ends.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_starts_ends.cc rename to libs/or-tools-bin/examples/cpp/vrp_starts_ends.cc diff --git a/libs/or-tools/examples/cpp/vrp_time_windows.cc b/libs/or-tools-bin/examples/cpp/vrp_time_windows.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_time_windows.cc rename to libs/or-tools-bin/examples/cpp/vrp_time_windows.cc diff --git a/libs/or-tools/examples/cpp/vrp_with_time_limit.cc b/libs/or-tools-bin/examples/cpp/vrp_with_time_limit.cc similarity index 100% rename from libs/or-tools/examples/cpp/vrp_with_time_limit.cc rename to libs/or-tools-bin/examples/cpp/vrp_with_time_limit.cc diff --git a/libs/or-tools-bin/examples/cpp/weighted_tardiness_sat.cc b/libs/or-tools-bin/examples/cpp/weighted_tardiness_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..88e45996a59975b8052971048900ce8d67f1f480 --- /dev/null +++ b/libs/or-tools-bin/examples/cpp/weighted_tardiness_sat.cc @@ -0,0 +1,257 @@ +// 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 "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/logging.h" +#include "ortools/base/timer.h" +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" + +DEFINE_string(input, "examples/data/weighted_tardiness/wt40.txt", + "wt data file name."); +DEFINE_int32(size, 40, "Size of the problem in the wt file."); +DEFINE_int32(n, 28, "1-based instance number in the wt file."); +DEFINE_string(params, "", "Sat parameters in text proto format."); +DEFINE_int32(upper_bound, -1, "If positive, look for a solution <= this."); + +namespace operations_research { +namespace sat { + +// Solve a single machine problem with weighted tardiness cost. +void Solve(const std::vector& durations, + const std::vector& due_dates, + const std::vector& weights) { + const int num_tasks = durations.size(); + CHECK_EQ(due_dates.size(), num_tasks); + CHECK_EQ(weights.size(), num_tasks); + + // Display some statistics. + int horizon = 0; + for (int i = 0; i < num_tasks; ++i) { + horizon += durations[i]; + LOG(INFO) << "#" << i << " duration:" << durations[i] + << " due_date:" << due_dates[i] << " weight:" << weights[i]; + } + + // An simple heuristic solution: We choose the tasks from last to first, and + // always take the one with smallest cost. + std::vector is_taken(num_tasks, false); + int64 heuristic_bound = 0; + int64 end = horizon; + for (int i = 0; i < num_tasks; ++i) { + int next_task = -1; + int64 next_cost; + for (int j = 0; j < num_tasks; ++j) { + if (is_taken[j]) continue; + const int64 cost = weights[j] * std::max(0, end - due_dates[j]); + if (next_task == -1 || cost < next_cost) { + next_task = j; + next_cost = cost; + } + } + CHECK_NE(-1, next_task); + is_taken[next_task] = true; + end -= durations[next_task]; + heuristic_bound += next_cost; + } + LOG(INFO) << "num_tasks: " << num_tasks; + LOG(INFO) << "The time horizon is " << horizon; + LOG(INFO) << "Trival cost bound = " << heuristic_bound; + + // Create the model. + CpModelBuilder cp_model; + + std::vector task_intervals(num_tasks); + std::vector task_starts(num_tasks); + std::vector task_durations(num_tasks); + std::vector task_ends(num_tasks); + std::vector tardiness_vars(num_tasks); + + for (int i = 0; i < num_tasks; ++i) { + task_starts[i] = cp_model.NewIntVar(Domain(0, horizon - durations[i])); + task_durations[i] = cp_model.NewConstant(durations[i]); + task_ends[i] = cp_model.NewIntVar(Domain(durations[i], horizon)); + task_intervals[i] = cp_model.NewIntervalVar( + task_starts[i], task_durations[i], task_ends[i]); + if (due_dates[i] == 0) { + tardiness_vars[i] = task_ends[i]; + } else { + tardiness_vars[i] = cp_model.NewIntVar( + Domain(0, std::max(0, horizon - due_dates[i]))); + + // tardiness_vars >= end - due_date + cp_model.AddGreaterOrEqual(tardiness_vars[i], + LinearExpr(end).AddConstant(-due_dates[i])); + } + } + + // Decision heuristic. Note that we don't instantiate all the variables. As a + // consequence, in the values returned by the solution observer for the + // non-fully instantiated variable will be the variable lower bounds after + // propagation. + cp_model.AddDecisionStrategy(task_starts, + DecisionStrategyProto::CHOOSE_HIGHEST_MAX, + DecisionStrategyProto::SELECT_MAX_VALUE); + + cp_model.AddNoOverlap(task_intervals); + + // TODO(user): We can't set an objective upper bound with the current cp_model + // interface, so we can't use heuristic or FLAGS_upper_bound here. The best is + // probably to provide a "solution hint" instead. + // + // Set a known upper bound (or use the flag). This has a bigger impact than + // can be expected at first: + // - It avoid spending time finding not so good solution. + // - More importantly, because we lazily create the associated Boolean + // variables, we end up creating less of them, and that speed up the search + // for the optimal and the proof of optimality. + // + // Note however than for big problem, this will drastically augment the time + // to get a first feasible solution (but then the heuristic gave one to us). + cp_model.Minimize(LinearExpr::ScalProd(tardiness_vars, weights)); + + // Optional preprocessing: add precedences that don't change the optimal + // solution value. + // + // Proof: in any schedule, if such precedence between task A and B is not + // satisfied, then it is always better (or the same) to swap A and B. This is + // because the tasks between A and B will be completed earlier (because the + // duration of A is smaller), and the cost of the swap itself is also smaller. + int num_added_precedences = 0; + for (int i = 0; i < num_tasks; ++i) { + for (int j = 0; j < num_tasks; ++j) { + if (i == j) continue; + if (due_dates[i] <= due_dates[j] && durations[i] <= durations[j] && + weights[i] >= weights[j]) { + // If two jobs have exactly the same specs, we don't add both + // precedences! + if (due_dates[i] == due_dates[j] && durations[i] == durations[j] && + weights[i] == weights[j] && i > j) { + continue; + } + + ++num_added_precedences; + cp_model.AddLessOrEqual(task_ends[i], task_starts[j]); + } + } + } + LOG(INFO) << "Added " << num_added_precedences + << " precedences that will not affect the optimal solution value."; + + // Solve it. + // + // Note that we only fully instantiate the start/end and only look at the + // lower bound for the objective and the tardiness variables. + Model model; + model.Add(NewSatParameters(FLAGS_params)); + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + // Note that we compute the "real" cost here and do not use the tardiness + // variables. This is because in the core based approach, the tardiness + // variable might be fixed before the end date, and we just have a >= + // relation. + + int64 objective = 0; + for (int i = 0; i < num_tasks; ++i) { + const int64 end = SolutionIntegerMin(r, task_ends[i]); + CHECK_EQ(end, SolutionIntegerMax(r, task_ends[i])); + objective += weights[i] * std::max(0ll, end - due_dates[i]); + } + LOG(INFO) << "Cost " << objective; + + // Print the current solution. + std::vector sorted_tasks(num_tasks); + std::iota(sorted_tasks.begin(), sorted_tasks.end(), 0); + std::sort(sorted_tasks.begin(), sorted_tasks.end(), [&](int v1, int v2) { + CHECK_EQ(SolutionIntegerMin(r, task_starts[v1]), + SolutionIntegerMax(r, task_starts[v1])); + CHECK_EQ(SolutionIntegerMin(r, task_starts[v2]), + SolutionIntegerMax(r, task_starts[v2])); + return SolutionIntegerMin(r, task_starts[v1]) < + SolutionIntegerMin(r, task_starts[v2]); + }); + std::string solution = "0"; + int end = 0; + for (const int i : sorted_tasks) { + const int64 cost = weights[i] * SolutionIntegerMin(r, tardiness_vars[i]); + absl::StrAppend(&solution, "| #", i, " "); + if (cost > 0) { + // Display the cost in red. + absl::StrAppend(&solution, "\033[1;31m(+", cost, ") \033[0m"); + } + absl::StrAppend(&solution, "|", SolutionIntegerMin(r, task_ends[i])); + CHECK_EQ(end, SolutionIntegerMin(r, task_starts[i])); + end += durations[i]; + CHECK_EQ(end, SolutionIntegerMin(r, task_ends[i])); + } + LOG(INFO) << "solution: " << solution; + })); + + // Solve. + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + LOG(INFO) << CpSolverResponseStats(response); +} + +void ParseAndSolve() { + std::vector numbers; + std::vector entries; + for (const std::string& line : FileLines(FLAGS_input)) { + entries = absl::StrSplit(line, ' ', absl::SkipEmpty()); + for (const std::string& entry : entries) { + numbers.push_back(0); + CHECK(absl::SimpleAtoi(entry, &numbers.back())); + } + } + + const int instance_size = FLAGS_size * 3; + LOG(INFO) << numbers.size() << " numbers in '" << FLAGS_input << "'."; + LOG(INFO) << "This correspond to " << numbers.size() / instance_size + << " instances of size " << FLAGS_size; + LOG(INFO) << "Loading instance #" << FLAGS_n; + CHECK_GE(FLAGS_n, 0); + CHECK_LE(FLAGS_n * instance_size, numbers.size()); + + // The order in a wt file is: duration, tardiness weights and then due_dates. + int index = (FLAGS_n - 1) * instance_size; + std::vector durations; + for (int j = 0; j < FLAGS_size; ++j) durations.push_back(numbers[index++]); + std::vector weights; + for (int j = 0; j < FLAGS_size; ++j) weights.push_back(numbers[index++]); + std::vector due_dates; + for (int j = 0; j < FLAGS_size; ++j) due_dates.push_back(numbers[index++]); + + Solve(durations, due_dates, weights); +} + +} // namespace sat +} // namespace operations_research + +int main(int argc, char** argv) { + absl::SetFlag(&FLAGS_logtostderr, true); + gflags::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_input.empty()) { + LOG(FATAL) << "Please supply a data file with --input="; + } + operations_research::sat::ParseAndSolve(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools/examples/dotnet/3_jugs_regular.cs b/libs/or-tools-bin/examples/dotnet/3_jugs_regular.cs similarity index 100% rename from libs/or-tools/examples/dotnet/3_jugs_regular.cs rename to libs/or-tools-bin/examples/dotnet/3_jugs_regular.cs diff --git a/libs/or-tools/examples/dotnet/3_jugs_regular.csproj b/libs/or-tools-bin/examples/dotnet/3_jugs_regular.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/3_jugs_regular.csproj rename to libs/or-tools-bin/examples/dotnet/3_jugs_regular.csproj diff --git a/libs/or-tools-bin/examples/dotnet/BalanceGroupSat.cs b/libs/or-tools-bin/examples/dotnet/BalanceGroupSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..54adcc908e246678a6dc58466a6e5018b9a68b91 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/BalanceGroupSat.cs @@ -0,0 +1,209 @@ +// Copyright 2010-2019 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + +/// +/// We are trying to group items in equal sized groups. +/// Each item has a color and a value. We want the sum of values of each group to +/// be as close to the average as possible. +/// Furthermore, if one color is an a group, at least k items with this color must +/// be in that group. +/// +public class BalanceGroupSat +{ + static void Main(string[] args) + { + int numberGroups = 10; + int numberItems = 100; + int numberColors = 3; + int minItemsOfSameColorPerGroup = 4; + + var allGroups = Enumerable.Range(0, numberGroups).ToArray(); + var allItems = Enumerable.Range(0, numberItems).ToArray(); + var allColors = Enumerable.Range(0, numberColors).ToArray(); + + var values = allItems.Select(i => 1 + i + (i * i / 200)).ToArray(); + var colors = allItems.Select(i => i % numberColors).ToArray(); + + var sumOfValues = values.Sum(); + var averageSumPerGroup = sumOfValues / numberGroups; + var numItemsPerGroup = numberItems / numberGroups; + + var itemsPerColor = new Dictionary>(); + + foreach (var color in allColors) + { + itemsPerColor[color] = new List(); + foreach (var item in allItems) + { + if(colors[item] == color) + itemsPerColor[color].Add(item); + } + } + + Console.WriteLine($"Model has {numberItems}, {numberGroups} groups and {numberColors} colors"); + Console.WriteLine($" Average sum per group = {averageSumPerGroup}"); + + var model = new CpModel(); + + var itemInGroup = new IntVar[numberItems, numberGroups]; + foreach (var item in allItems) + { + foreach (var @group in allGroups) + { + itemInGroup[item, @group] = model.NewBoolVar($"item {item} in group {@group}"); + } + } + + // Each group must have the same size. + foreach (var @group in allGroups) + { + var itemsInGroup = allItems.Select(x => itemInGroup[x, @group]).ToArray(); + model.AddLinearConstraint(LinearExpr.Sum(itemsInGroup), numItemsPerGroup, numItemsPerGroup); + } + + //# One item must belong to exactly one group. + foreach (var item in allItems) + { + var groupsForItem = allGroups.Select(x => itemInGroup[item, x]).ToArray(); + model.Add(LinearExpr.Sum(groupsForItem) == 1); + } + + // The deviation of the sum of each items in a group against the average. + var e = model.NewIntVar(0, 550, "epsilon"); + + // Constrain the sum of values in one group around the average sum per group. + foreach (var @group in allGroups) + { + var itemValues = allItems.Select(x => itemInGroup[x, @group]).ToArray(); + + var sum = LinearExpr.ScalProd(itemValues, values); + model.Add(sum <= averageSumPerGroup + e); + model.Add(sum >= averageSumPerGroup - e); + } + + // colorInGroup variables. + var colorInGroup = new IntVar[numberColors, numberGroups]; + foreach (var @group in allGroups) + { + foreach (var color in allColors) + { + colorInGroup[color, @group] = model.NewBoolVar($"color {color} is in group {@group}"); + } + } + + // Item is in a group implies its color is in that group. + foreach (var item in allItems) + { + foreach (var @group in allGroups) + { + model.AddImplication(itemInGroup[item, @group], colorInGroup[colors[item], @group]); + } + } + + // If a color is in a group, it must contains at least + // min_items_of_same_color_per_group items from that color. + foreach (var color in allColors) + { + foreach (var @group in allGroups) + { + var literal = colorInGroup[color, @group]; + var items = itemsPerColor[color].Select(x => itemInGroup[x, @group]).ToArray(); + model.Add(LinearExpr.Sum(items) >= minItemsOfSameColorPerGroup).OnlyEnforceIf(literal); + } + } + + // Compute the maximum number of colors in a group. + int maxColor = numItemsPerGroup / minItemsOfSameColorPerGroup; + // Redundant contraint: The problem does not solve in reasonable time without it. + if (maxColor < numberColors) + { + foreach (var @group in allGroups) + { + var all = allColors.Select(x => colorInGroup[x, @group]).ToArray(); + model.Add(LinearExpr.Sum(all) <= maxColor); + } + } + + // Minimize epsilon + model.Minimize(e); + + var solver = new CpSolver(); + solver.StringParameters = ""; + + var solutionPrinter = new SolutionPrinter(values, colors, allGroups, allItems, itemInGroup); + + var status = solver.SolveWithSolutionCallback(model, solutionPrinter); + } + + public class SolutionPrinter : CpSolverSolutionCallback + { + private int[] _values; + private int[] _colors; + private int[] _allGroups; + private int[] _allItems; + private IntVar[,] _itemInGroup; + + private int _solutionCount; + + public SolutionPrinter(int[] values, int[] colors, int[] allGroups, int[] allItems, IntVar[,] itemInGroup) + { + this._values = values; + this._colors = colors; + this._allGroups = allGroups; + this._allItems = allItems; + this._itemInGroup = itemInGroup; + } + + public override void OnSolutionCallback() + { + Console.WriteLine($"Solution {_solutionCount}"); + _solutionCount++; + + Console.WriteLine($" objective value = {this.ObjectiveValue()}"); + + Dictionary> groups = new Dictionary>(); + int[] sum = new int[_allGroups.Length]; + + foreach (var @group in _allGroups) + { + groups[@group] = new List(); + foreach (var item in _allItems) + { + if (BooleanValue(_itemInGroup[item, @group])) + { + groups[@group].Add(item); + sum[@group] += _values[item]; + } + } + } + + foreach (var g in _allGroups) + { + var group = groups[g]; + Console.Write($"Group {g}: sum = {sum[g]} ["); + foreach (var item in group) + { + Console.Write($"({item}, {_values[item]}, {_colors[item]})"); + } + + Console.WriteLine("]"); + } + } + } +} + diff --git a/libs/or-tools-bin/examples/dotnet/BalanceGroupSat.csproj b/libs/or-tools-bin/examples/dotnet/BalanceGroupSat.csproj new file mode 100644 index 0000000000000000000000000000000000000000..6257256be515918a766fc179043f26216510ddb3 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/BalanceGroupSat.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.BalanceGroupSat + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools/examples/dotnet/BinPackingProblemSat.cs b/libs/or-tools-bin/examples/dotnet/BinPackingProblemSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/BinPackingProblemSat.cs rename to libs/or-tools-bin/examples/dotnet/BinPackingProblemSat.cs diff --git a/libs/or-tools/examples/dotnet/BinPackingProblemSat.csproj b/libs/or-tools-bin/examples/dotnet/BinPackingProblemSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/BinPackingProblemSat.csproj rename to libs/or-tools-bin/examples/dotnet/BinPackingProblemSat.csproj diff --git a/libs/or-tools/examples/dotnet/BoolOrSampleSat.cs b/libs/or-tools-bin/examples/dotnet/BoolOrSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/BoolOrSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/BoolOrSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/BoolOrSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/BoolOrSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/BoolOrSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/BoolOrSampleSat.csproj diff --git a/libs/or-tools/examples/dotnet/ChannelingSampleSat.cs b/libs/or-tools-bin/examples/dotnet/ChannelingSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/ChannelingSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/ChannelingSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/ChannelingSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/ChannelingSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/ChannelingSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/ChannelingSampleSat.csproj diff --git a/libs/or-tools/examples/dotnet/CpIsFunSat.cs b/libs/or-tools-bin/examples/dotnet/CpIsFunSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/CpIsFunSat.cs rename to libs/or-tools-bin/examples/dotnet/CpIsFunSat.cs diff --git a/libs/or-tools/examples/dotnet/CpIsFunSat.csproj b/libs/or-tools-bin/examples/dotnet/CpIsFunSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/CpIsFunSat.csproj rename to libs/or-tools-bin/examples/dotnet/CpIsFunSat.csproj diff --git a/libs/or-tools/examples/dotnet/EarlinessTardinessCostSampleSat.cs b/libs/or-tools-bin/examples/dotnet/EarlinessTardinessCostSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/EarlinessTardinessCostSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/EarlinessTardinessCostSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/EarlinessTardinessCostSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/EarlinessTardinessCostSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/EarlinessTardinessCostSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/EarlinessTardinessCostSampleSat.csproj diff --git a/libs/or-tools-bin/examples/dotnet/GateSchedulingSat.cs b/libs/or-tools-bin/examples/dotnet/GateSchedulingSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..3f7d90190e86aa39d8e8a9e49f83345fdf07e057 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/GateSchedulingSat.cs @@ -0,0 +1,149 @@ +// 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. + +// Gate Scheduling problem. +// +// We have a set of jobs to perform (duration, width). +// We have two parallel machines that can perform this job. +// One machine can only perform one job at a time. +// At any point in time, the sum of the width of the two active jobs does not +// exceed a max_length. +// +//The objective is to minimize the max end time of all jobs. + +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + +public class GateSchedulingSat +{ + static void Main() + { + CpModel model = new CpModel(); + + int[,] jobs = new [,] {{3, 3}, + {2, 5}, + {1, 3}, + {3, 7}, + {7, 3}, + {2, 2}, + {2, 2}, + {5, 5}, + {10, 2}, + {4, 3}, + {2, 6}, + {1, 2}, + {6, 8}, + {4, 5}, + {3, 7}}; + + int max_length = 10; + int num_jobs = jobs.GetLength(0); + var all_jobs = Enumerable.Range(0, num_jobs); + + int horizon = 0; + foreach (int j in all_jobs) + { + horizon += jobs[j, 0]; + } + + + List intervals = new List(); + List intervals0 = new List(); + List intervals1 = new List(); + List performed = new List(); + List starts = new List(); + List ends = new List(); + List demands = new List(); + + foreach (int i in all_jobs) + { + // Create main interval. + IntVar start = model.NewIntVar(0, horizon, String.Format("start_{0}", i)); + int duration = jobs[i, 0]; + IntVar end = model.NewIntVar(0, horizon, String.Format("end_{0}", i)); + IntervalVar interval = model.NewIntervalVar( + start, duration, end, String.Format("interval_{0}", i)); + starts.Add(start); + intervals.Add(interval); + ends.Add(end); + demands.Add(jobs[i, 1]); + + IntVar performed_on_m0 = + model.NewBoolVar(String.Format("perform_{0}_on_m0", i)); + performed.Add(performed_on_m0); + + // Create an optional copy of interval to be executed on machine 0. + IntVar start0 = model.NewIntVar( + 0, horizon, String.Format("start_{0}_on_m0", i)); + IntVar end0 = model.NewIntVar( + 0, horizon, String.Format("end_{0}_on_m0", i)); + IntervalVar interval0 = model.NewOptionalIntervalVar( + start0, duration, end0, performed_on_m0, + String.Format("interval_{0}_on_m0", i)); + intervals0.Add(interval0); + + // Create an optional copy of interval to be executed on machine 1. + IntVar start1 = model.NewIntVar( + 0, horizon, String.Format("start_{0}_on_m1", i)); + IntVar end1 = model.NewIntVar( + 0, horizon, String.Format("end_{0}_on_m1", i)); + IntervalVar interval1 = model.NewOptionalIntervalVar( + start1, duration, end1, performed_on_m0.Not(), + String.Format("interval_{0}_on_m1", i)); + intervals1.Add(interval1); + + // We only propagate the constraint if the tasks is performed on the + // machine. + model.Add(start0 == start).OnlyEnforceIf(performed_on_m0); + model.Add(start1 == start).OnlyEnforceIf(performed_on_m0.Not()); + } + + // Max Length constraint (modeled as a cumulative) + model.AddCumulative(intervals, demands, max_length); + + // Choose which machine to perform the jobs on. + model.AddNoOverlap(intervals0); + model.AddNoOverlap(intervals1); + + // Objective variable. + IntVar makespan = model.NewIntVar(0, horizon, "makespan"); + model.AddMaxEquality(makespan, ends); + model.Minimize(makespan); + + // Symmetry breaking. + model.Add(performed[0] == 0); + + // Creates the solver and solve. + CpSolver solver = new CpSolver(); + solver.Solve(model); + + + // Output solution. + Console.WriteLine("Solution"); + Console.WriteLine(" - makespan = " + solver.ObjectiveValue); + foreach (int i in all_jobs) + { + long performed_machine = 1 - solver.Value(performed[i]); + long start = solver.Value(starts[i]); + Console.WriteLine( + String.Format(" - Job {0} starts at {1} on machine {2}", + i, start, performed_machine)); + } + Console.WriteLine("Statistics"); + Console.WriteLine(" - conflicts : " + solver.NumConflicts()); + Console.WriteLine(" - branches : " + solver.NumBranches()); + Console.WriteLine(" - wall time : " + solver.WallTime() + " ms"); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/GateSchedulingSat.csproj b/libs/or-tools-bin/examples/dotnet/GateSchedulingSat.csproj new file mode 100644 index 0000000000000000000000000000000000000000..a97f031965f1da5b2845e9abb2d60d380d964d09 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/GateSchedulingSat.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.GateSchedulingSat + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools/examples/dotnet/IntervalSampleSat.cs b/libs/or-tools-bin/examples/dotnet/IntervalSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/IntervalSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/IntervalSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/IntervalSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/IntervalSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/IntervalSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/IntervalSampleSat.csproj diff --git a/libs/or-tools-bin/examples/dotnet/JobshopFt06Sat.cs b/libs/or-tools-bin/examples/dotnet/JobshopFt06Sat.cs new file mode 100644 index 0000000000000000000000000000000000000000..6acfc7acc205d600f186514c6656aa9082498301 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/JobshopFt06Sat.cs @@ -0,0 +1,134 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + +public class JobshopFt06Sat +{ + + public struct Task + { + public Task(IntVar s, IntVar e, IntervalVar i) + { + start = s; + end = e; + interval = i; + } + + public IntVar start; + public IntVar end; + public IntervalVar interval; + } + + static void Main() + { + int[,] durations = new int[,] { {1, 3, 6, 7, 3, 6}, + {8, 5, 10, 10, 10, 4}, + {5, 4, 8, 9, 1, 7}, + {5, 5, 5, 3, 8, 9}, + {9, 3, 5, 4, 3, 1}, + {3, 3, 9, 10, 4, 1} }; + int[,] machines = new int[,] { {2, 0, 1, 3, 5, 4}, + {1, 2, 4, 5, 0, 3}, + {2, 3, 5, 0, 1, 4}, + {1, 0, 2, 3, 4, 5}, + {2, 1, 4, 5, 0, 3}, + {1, 3, 5, 0, 4, 2} }; + + int num_jobs = durations.GetLength(0); + int num_machines = durations.GetLength(1); + var all_jobs = Enumerable.Range(0, num_jobs); + var all_machines = Enumerable.Range(0, num_machines); + + int horizon = 0; + foreach (int j in all_jobs) + { + foreach (int m in all_machines) + { + horizon += durations[j, m]; + } + } + + + // Creates the model. + CpModel model = new CpModel(); + + // Creates jobs. + Task[,] all_tasks = new Task[num_jobs, num_machines]; + foreach (int j in all_jobs) + { + foreach (int m in all_machines) + { + IntVar start_var = model.NewIntVar( + 0, horizon, String.Format("start_{0}_{1}", j, m)); + int duration = durations[j, m]; + IntVar end_var = model.NewIntVar( + 0, horizon, String.Format("end_{0}_{1}", j, m)); + IntervalVar interval_var = model.NewIntervalVar( + start_var, duration, end_var, + String.Format("interval_{0}_{1}", j, m)); + all_tasks[j, m] = new Task(start_var, end_var, interval_var); + } + } + + // Create disjuctive constraints. + List[] machine_to_jobs = new List[num_machines]; + foreach (int m in all_machines) + { + machine_to_jobs[m] = new List(); + } + foreach (int j in all_jobs) + { + foreach (int m in all_machines) + { + machine_to_jobs[machines[j, m]].Add(all_tasks[j, m].interval); + } + } + foreach (int m in all_machines) + { + model.AddNoOverlap(machine_to_jobs[m]); + } + + // Precedences inside a job. + foreach (int j in all_jobs) + { + for (int k = 0; k < num_machines - 1; ++k) + { + model.Add(all_tasks[j, k + 1].start >= all_tasks[j, k].end); + } + } + + // Makespan objective. + IntVar[] all_ends = new IntVar[num_jobs]; + foreach (int j in all_jobs) + { + all_ends[j] = all_tasks[j, num_machines - 1].end; + } + IntVar makespan = model.NewIntVar(0, horizon, "makespan"); + model.AddMaxEquality(makespan, all_ends); + model.Minimize(makespan); + + Console.WriteLine(model.ModelStats()); + + // Creates the solver and solve. + CpSolver solver = new CpSolver(); + // Display a few solutions picked at random. + solver.Solve(model); + + // Statistics. + Console.WriteLine(solver.ResponseStats()); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/JobshopFt06Sat.csproj b/libs/or-tools-bin/examples/dotnet/JobshopFt06Sat.csproj new file mode 100644 index 0000000000000000000000000000000000000000..95e2473ac6cfed143efc1815502ecbb1496153ac --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/JobshopFt06Sat.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.JobshopFt06Sat + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools-bin/examples/dotnet/JobshopSat.cs b/libs/or-tools-bin/examples/dotnet/JobshopSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..395096446c77e7da087deeacbd5fdecf02d4f4dd --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/JobshopSat.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + + +class Task +{ + public Task(int taskId, int jobId, int duration, int machine) + { + TaskId = taskId; + JobId = jobId; + Duration = duration; + Machine = machine; + Name = "T" + taskId + "J" + jobId + "M" + + machine + "D" + duration; + } + public int TaskId { get; set; } + public int JobId { get; set; } + public int Machine { get; set; } + public int Duration { get; set; } + public string Name { get; } +} + + + +class JobshopSat +{ + //Number of machines. + public const int machinesCount = 3; + //horizon is the upper bound of the start time of all tasks. + public const int horizon = 300; + //this will be set to the size of myJobList variable. + public static int jobsCount; + /*Search time limit in milliseconds. if it's equal to 0, + then no time limit will be used.*/ + public const int timeLimitInSeconds = 0; + public static List> myJobList = new List>(); + public static void InitTaskList() + { + List taskList = new List(); + taskList.Add(new Task(0, 0, 65, 0)); + taskList.Add(new Task(1, 0, 5, 1)); + taskList.Add(new Task(2, 0, 15, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 1, 15, 0)); + taskList.Add(new Task(1, 1, 25, 1)); + taskList.Add(new Task(2, 1, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 2, 25, 0)); + taskList.Add(new Task(1, 2, 30, 1)); + taskList.Add(new Task(2, 2, 40, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 3, 20, 0)); + taskList.Add(new Task(1, 3, 35, 1)); + taskList.Add(new Task(2, 3, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 4, 15, 0)); + taskList.Add(new Task(1, 4, 25, 1)); + taskList.Add(new Task(2, 4, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 5, 25, 0)); + taskList.Add(new Task(1, 5, 30, 1)); + taskList.Add(new Task(2, 5, 40, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 6, 20, 0)); + taskList.Add(new Task(1, 6, 35, 1)); + taskList.Add(new Task(2, 6, 10, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 7, 10, 0)); + taskList.Add(new Task(1, 7, 15, 1)); + taskList.Add(new Task(2, 7, 50, 2)); + myJobList.Add(taskList); + + taskList = new List(); + taskList.Add(new Task(0, 8, 50, 0)); + taskList.Add(new Task(1, 8, 10, 1)); + taskList.Add(new Task(2, 8, 20, 2)); + myJobList.Add(taskList); + + jobsCount = myJobList.Count; + } + + public static void Main(String[] args) + { + InitTaskList(); + CpModel model = new CpModel(); + + // ----- Creates all intervals and integer variables ----- + + // Stores all tasks attached interval variables per job. + List> + jobsToTasks = new List>(jobsCount); + List> + jobsToStarts = new List>(jobsCount); + List> + jobsToEnds = new List>(jobsCount); + + // machinesToTasks stores the same interval variables as above, but + // grouped my machines instead of grouped by jobs. + List> + machinesToTasks = new List>(machinesCount); + List> + machinesToStarts = new List>(machinesCount); + for (int i = 0; i < machinesCount; i++) + { + machinesToTasks.Add(new List()); + machinesToStarts.Add(new List()); + } + + // Creates all individual interval variables. + foreach (List job in myJobList) + { + jobsToTasks.Add(new List()); + jobsToStarts.Add(new List()); + jobsToEnds.Add(new List()); + foreach (Task task in job) + { + IntVar start = model.NewIntVar(0, horizon, task.Name); + IntVar end = model.NewIntVar(0, horizon, task.Name); + IntervalVar oneTask = + model.NewIntervalVar(start, task.Duration, end, task.Name); + jobsToTasks[task.JobId].Add(oneTask); + jobsToStarts[task.JobId].Add(start); + jobsToEnds[task.JobId].Add(end); + machinesToTasks[task.Machine].Add(oneTask); + machinesToStarts[task.Machine].Add(start); + } + } + + // ----- Creates model ----- + + // Creates precedences inside jobs. + for (int j = 0; j < jobsToTasks.Count; ++j) + { + for (int t = 0; t < jobsToTasks[j].Count - 1; ++t) + { + model.Add(jobsToEnds[j][t] <= jobsToStarts[j][t + 1]); + } + } + + // Adds no_overkap constraints on unary resources. + for (int machineId = 0; machineId < machinesCount; ++machineId) + { + model.AddNoOverlap(machinesToTasks[machineId]); + } + + // Creates array of end_times of jobs. + IntVar[] allEnds = new IntVar[jobsCount]; + for (int i = 0; i < jobsCount; i++) + { + allEnds[i] = jobsToEnds[i].Last(); + } + + // Objective: minimize the makespan (maximum end times of all tasks) + // of the problem. + IntVar makespan = model.NewIntVar(0, horizon, "makespan"); + model.AddMaxEquality(makespan, allEnds); + model.Minimize(makespan); + + + // Create the solver. + CpSolver solver = new CpSolver(); + // Set the time limit. + if (timeLimitInSeconds > 0) + { + solver.StringParameters = "max_time_in_seconds:" + timeLimitInSeconds; + } + // Solve the problem. + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Optimal) + { + Console.WriteLine("Makespan = " + solver.ObjectiveValue); + for (int m = 0; m < machinesCount; ++m) + { + Console.WriteLine($"Machine {m}:"); + SortedDictionary starts = new SortedDictionary(); + foreach (IntVar var in machinesToStarts[m]) + { + starts[solver.Value(var)] = var.Name(); + } + foreach (KeyValuePair p in starts) + { + Console.WriteLine($" Task {p.Value} starts at {p.Key}"); + } + } + } + else + { + Console.WriteLine("No solution found!"); + } + } +} diff --git a/libs/or-tools-bin/examples/dotnet/JobshopSat.csproj b/libs/or-tools-bin/examples/dotnet/JobshopSat.csproj new file mode 100644 index 0000000000000000000000000000000000000000..71b16c71968fa8cb16082e9458dfeb7804af95c6 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/JobshopSat.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.JobshopSat + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools/examples/dotnet/Knapsack.cs b/libs/or-tools-bin/examples/dotnet/Knapsack.cs similarity index 100% rename from libs/or-tools/examples/dotnet/Knapsack.cs rename to libs/or-tools-bin/examples/dotnet/Knapsack.cs diff --git a/libs/or-tools/examples/dotnet/Knapsack.csproj b/libs/or-tools-bin/examples/dotnet/Knapsack.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/Knapsack.csproj rename to libs/or-tools-bin/examples/dotnet/Knapsack.csproj diff --git a/libs/or-tools/examples/dotnet/LinearProgrammingExample.cs b/libs/or-tools-bin/examples/dotnet/LinearProgrammingExample.cs similarity index 100% rename from libs/or-tools/examples/dotnet/LinearProgrammingExample.cs rename to libs/or-tools-bin/examples/dotnet/LinearProgrammingExample.cs diff --git a/libs/or-tools/examples/dotnet/LinearProgrammingExample.csproj b/libs/or-tools-bin/examples/dotnet/LinearProgrammingExample.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/LinearProgrammingExample.csproj rename to libs/or-tools-bin/examples/dotnet/LinearProgrammingExample.csproj diff --git a/libs/or-tools/examples/dotnet/LiteralSampleSat.cs b/libs/or-tools-bin/examples/dotnet/LiteralSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/LiteralSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/LiteralSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/LiteralSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/LiteralSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/LiteralSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/LiteralSampleSat.csproj diff --git a/libs/or-tools/examples/dotnet/MipVarArray.cs b/libs/or-tools-bin/examples/dotnet/MipVarArray.cs similarity index 100% rename from libs/or-tools/examples/dotnet/MipVarArray.cs rename to libs/or-tools-bin/examples/dotnet/MipVarArray.cs diff --git a/libs/or-tools/examples/dotnet/MipVarArray.csproj b/libs/or-tools-bin/examples/dotnet/MipVarArray.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/MipVarArray.csproj rename to libs/or-tools-bin/examples/dotnet/MipVarArray.csproj diff --git a/libs/or-tools-bin/examples/dotnet/NetworkRoutingSat.cs b/libs/or-tools-bin/examples/dotnet/NetworkRoutingSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..48a4a1a64b1ee97eb9436d7f293e2211f9584887 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/NetworkRoutingSat.cs @@ -0,0 +1,1148 @@ +// Copyright 2010-2019 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +using Google.OrTools.Sat; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + + +/// +/// This model solves a multicommodity mono-routing problem with +/// capacity constraints and a max usage cost structure. This means +/// that given a graph with capacity on edges, and a set of demands +/// (source, destination, traffic), the goal is to assign one unique +/// path for each demand such that the cost is minimized. The cost is +/// defined by the maximum ratio utilization (traffic/capacity) for all +/// arcs. There is also a penalty associated with an traffic of an arc +/// being above the comfort zone, 85% of the capacity by default. +/// Please note that constraint programming is well suited here because +/// we cannot have multiple active paths for a single demand. +/// Otherwise, a approach based on a linear solver is a better match. +/// A random problem generator is also included. +/// +public class NetworkRoutingSat +{ + private static int clients = 0; // Number of network clients nodes. If equal to zero, then all backbones nodes are also client nodes. + private static int backbones = 0; // "Number of backbone nodes" + private static int demands = 0; // "Number of network demands." + private static int trafficMin = 0; // "Min traffic of a demand." + private static int trafficMax = 0; // "Max traffic of a demand." + private static int minClientDegree = 0; //"Min number of connections from a client to the backbone." + private static int maxClientDegree = 0; //"Max number of connections from a client to the backbone." + private static int minBackboneDegree = 0; //"Min number of connections from a backbone node to the rest of the backbone nodes." + private static int maxBackboneDegree = 0; // "Max number of connections from a backbone node to the rest of the backbone nodes." + private static int maxCapacity = 0; //"Max traffic on any arc." + private static int fixedChargeCost = 0; //"Fixed charged cost when using an arc." + private static int seed = 0; //"Random seed" + private static double comfortZone = 0.85; // "Above this limit in 1/1000th, the link is said to be congested." + private static int extraHops = 6; // "When creating all paths for a demand, we look at paths with maximum length 'shortest path + extra_hops'" + private static int maxPaths = 1200; //"Max number of possible paths for a demand." + private static bool printModel = false; //"Print details of the model." + private static string parameters = ""; // "Sat parameters." + + private const long kDisconnectedDistance = -1L; + + static void Main(string[] args) + { + readArgs(args); + var builder = new NetworkRoutingDataBuilder(); + var data = builder.BuildModelFromParameters(clients, backbones, demands, trafficMin, trafficMax, + minClientDegree, maxClientDegree, minBackboneDegree, maxBackboneDegree, maxCapacity, fixedChargeCost, seed); + var solver = new NetworkRoutingSolver(); + solver.Init(data, extraHops, maxPaths); + var cost = solver.Solve(); + Console.WriteLine($"Final cost = {cost}"); + } + + private static void readArgs(string[] args) + { + readInt(args, ref clients, nameof(clients)); + readInt(args, ref backbones, nameof(backbones)); + readInt(args, ref demands, nameof(demands)); + readInt(args, ref trafficMin, nameof(trafficMin)); + readInt(args, ref trafficMax, nameof(trafficMax)); + readInt(args, ref minClientDegree, nameof(minClientDegree)); + readInt(args, ref maxClientDegree, nameof(maxClientDegree)); + readInt(args, ref minBackboneDegree, nameof(minBackboneDegree)); + readInt(args, ref maxBackboneDegree, nameof(maxBackboneDegree)); + readInt(args, ref maxCapacity, nameof(maxCapacity)); + readInt(args, ref fixedChargeCost, nameof(fixedChargeCost)); + readInt(args, ref seed, nameof(seed)); + readDouble(args, ref comfortZone, nameof(comfortZone)); + readInt(args, ref extraHops, nameof(extraHops)); + readInt(args, ref maxPaths, nameof(maxPaths)); + readBoolean(args, ref printModel, nameof(printModel)); + readString(args, ref parameters, nameof(parameters)); + } + + private static void readDouble(string[] args, ref double setting, string arg) + { + var v = getArgValue(args, arg); + if (v.IsSet) + { + setting = Convert.ToDouble(v.Value); + } + } + + private static void readInt(string[] args, ref int setting, string arg) + { + var v = getArgValue(args, arg); + if (v.IsSet) + { + setting = Convert.ToInt32(v.Value); + } + } + + private static void readBoolean(string[] args, ref bool setting, string arg) + { + var v = getArgValue(args, arg); + if (v.IsSet) + { + setting = Convert.ToBoolean(v.Value); + } + } + + private static void readString(string[] args, ref string setting, string arg) + { + var v = getArgValue(args, arg); + if (v.IsSet) + { + setting = v.Value; + } + } + + private static (bool IsSet, string Value) getArgValue(string[] args, string arg) + { + string lookup = $"--{arg}="; + + var item = args.FirstOrDefault(x => x.StartsWith(lookup)); + + if (string.IsNullOrEmpty(item)) + { + return (false, string.Empty); + } + + return (true, item.Replace(lookup, string.Empty)); + } + + /// + /// Contains problem data. It assumes capacities are symmetrical: + /// (capacity(i->j) == capacity(j->i)). + /// Demands are not symmetrical. + /// + public class NetworkRoutingData + { + private Dictionary<(int source, int destination), int> _arcs = new Dictionary<(int source, int destination), int>(); + private Dictionary<(int node1, int node2), int> _demands = new Dictionary<(int node1, int node2), int>(); + + public int NumberOfNodes { get; set; } = -1; + + public int NumberOfArcs + { + get { return _arcs.Count(); } + } + + public int NumberOfDemands + { + get { return _demands.Count(); } + } + + public int MaximumCapacity { get; set; } = -1; + public int FixedChargeCost { get; set; } = -1; + public string Name { get; set; } = string.Empty; + + public void AddDemand(int source, int destination, int traffic) + { + var pair = (source, destination); + if(!_demands.ContainsKey(pair)) + _demands.Add(pair, traffic); + } + + public void AddArc(int node1, int node2, int capacity) + { + _arcs.Add((Math.Min(node1, node2), Math.Max(node1, node2)), capacity); + } + + public int Demand(int source, int destination) + { + var pair = (source, destination); + if (_demands.TryGetValue(pair, out var demand)) + return demand; + + return 0; + } + + public int Capacity(int node1, int node2) + { + var pair = (Math.Min(node1, node2), Math.Max(node1, node2)); + if (_arcs.TryGetValue(pair, out var capacity)) + return capacity; + + return 0; + } + } + + /// + /// Random generator of problem. This generator creates a random + /// problem. This problem uses a special topology. There are + /// 'numBackbones' nodes and 'numClients' nodes. if 'numClients' is + /// null, then all backbones nodes are also client nodes. All traffic + /// originates and terminates in client nodes. Each client node is + /// connected to 'minClientDegree' - 'maxClientDegree' backbone + /// nodes. Each backbone node is connected to 'minBackboneDegree' - + /// 'maxBackboneDegree' other backbone nodes. There are 'numDemands' + /// demands, with a traffic between 'trafficMin' and 'trafficMax'. + /// Each arc has a capacity of 'maxCapacity'. Using an arc incurs a + /// fixed cost of 'fixedChargeCost'. + /// + public class NetworkRoutingDataBuilder + { + private List> _network; + private List _degrees; + private Random _random; + + public NetworkRoutingData BuildModelFromParameters(int numClients, int numBackbones, + int numDemands, int trafficMin, + int trafficMax, int minClientDegree, + int maxClientDegree, int minBackboneDegree, + int maxBackboneDegree, int maxCapacity, + int fixedChargeCost, int seed) + { + Debug.Assert(numBackbones >= 1); + Debug.Assert(numClients >= 0); + Debug.Assert(numDemands >= 1); + Debug.Assert(numDemands <= (numClients == 0 ? numBackbones * numBackbones : numClients * numBackbones)); + Debug.Assert(maxClientDegree >= minClientDegree); + Debug.Assert(maxBackboneDegree >= minBackboneDegree); + Debug.Assert(trafficMax >= 1); + Debug.Assert(trafficMax >= trafficMin); + Debug.Assert(trafficMin >= 1); + Debug.Assert(maxBackboneDegree >= 2); + Debug.Assert(maxClientDegree >= 2); + Debug.Assert(maxClientDegree <= numBackbones); + Debug.Assert(maxBackboneDegree <= numBackbones); + Debug.Assert(maxCapacity >= 1); + + int size = numBackbones + numClients; + initData(size, seed); + buildGraph(numClients, numBackbones, minClientDegree, maxClientDegree, minBackboneDegree, maxBackboneDegree); + NetworkRoutingData data = new NetworkRoutingData(); + createDemands(numClients, numBackbones, numDemands, trafficMin, trafficMax, data); + fillData(numClients, numBackbones, numDemands, trafficMin, trafficMax, minClientDegree, maxClientDegree, + minBackboneDegree, maxBackboneDegree, maxCapacity, fixedChargeCost, seed, data); + + return data; + } + + private void initData(int size, int seed) + { + _network = new List>(size); + for (int i = 0; i < size; i++) + { + _network.Add(new List(size)); + for (int j = 0; j < size; j++) + { + _network[i].Add(false); + } + } + + _degrees = new List(size); + for (int i = 0; i < size; i++) + { + _degrees.Add(0); + } + + _random = new Random(seed); + } + + private void buildGraph(int numClients, int numBackbones, int minClientDegree, + int maxClientDegree, int minBackboneDegree, int maxBackboneDegree) + { + int size = numBackbones + numClients; + for (int i = 1; i < numBackbones; i++) + { + int j = randomUniform(i); + addEdge(i, j); + } + + List notFull = new List(); + HashSet toComplete = new HashSet(); + + for (int i = 0; i < numBackbones; i++) + { + if (_degrees[i] < minBackboneDegree) + { + toComplete.Add(i); + } + + if (_degrees[i] < maxBackboneDegree) + { + notFull.Add(i); + } + } + + while (toComplete.Any() && notFull.Count > 1) + { + int node1 = getNextToComplete(toComplete); + int node2 = node1; + while (node2 == node1 || _degrees[node2] >= maxBackboneDegree) + { + node2 = randomUniform(numBackbones); + } + + addEdge(node1, node2); + + if (_degrees[node1] >= minBackboneDegree) + { + toComplete.Remove(node1); + } + + if (_degrees[node2] >= minBackboneDegree) + { + toComplete.Remove(node2); + } + + if (_degrees[node1] >= maxBackboneDegree) + { + notFull.Remove(node1); + } + + if (_degrees[node2] >= maxBackboneDegree) + { + notFull.Remove(node2); + } + } + + // Then create the client nodes connected to the backbone nodes. + // If numClient is 0, then backbone nodes are also client nodes. + + for (int i = numBackbones; i < size; i++) + { + int degree = randomInInterval(minClientDegree, maxClientDegree); + + while (_degrees[i] < degree) + { + int j = randomUniform(numBackbones); + if (!_network[i][j]) + { + addEdge(i, j); + } + } + } + } + + private int getNextToComplete(HashSet toComplete) + { + return toComplete.Last(); + } + + private void createDemands(int numClients, int numBackbones, int numDemands, + int trafficMin, int trafficMax, NetworkRoutingData data) + { + while (data.NumberOfDemands < numDemands) + { + int source = randomClient(numClients, numBackbones); + int dest = source; + while (dest == source) + { + dest = randomClient(numClients, numBackbones); + } + + int traffic = randomInInterval(trafficMin, trafficMax); + data.AddDemand(source, dest, traffic); + } + } + + private void fillData(int numClients, int numBackbones, int numDemands, + int trafficMin, int trafficMax, int minClientDegree, + int maxClientDegree, int minBackboneDegree, + int maxBackboneDegree, int maxCapacity, + int fixedChargeCost, int seed, + NetworkRoutingData data) + { + int size = numBackbones + numClients; + string name = $"mp_c{numClients}_b{numBackbones}_d{numDemands}.t{trafficMin}-{trafficMax}.cd{minClientDegree}-{maxClientDegree}.bd{minBackboneDegree}-{maxBackboneDegree}.mc{maxCapacity}.fc{fixedChargeCost}.s{seed}"; + data.Name = name; + + data.NumberOfNodes = size; + int numArcs = 0; + for (int i = 0; i < size - 1; i++) + { + for (int j = i + 1; j < size; j++) + { + if (_network[i][j]) + { + data.AddArc(i, j, maxCapacity); + numArcs++; + } + } + } + + data.MaximumCapacity = maxCapacity; + data.FixedChargeCost = fixedChargeCost; + } + + private void addEdge(int i, int j) + { + _degrees[i]++; + _degrees[j]++; + _network[i][j] = true; + _network[j][i] = true; + } + + private int randomInInterval(int intervalMin, int intervalMax) + { + var p = randomUniform(intervalMax - intervalMin + 1) + intervalMin; + return p; + } + + private int randomClient(int numClients, int numBackbones) + { + var p = (numClients == 0) + ? randomUniform(numBackbones) + : randomUniform(numClients) + numBackbones; + return p; + } + + private int randomUniform(int max) + { + var r = _random.Next(max); + return r; + } + } + + [DebuggerDisplay("Source {Source} Destination {Destination} Traffic {Traffic}")] + public struct Demand + { + public Demand(int source, int destination, int traffic) + { + Source = source; + Destination = destination; + Traffic = traffic; + } + + public int Source { get; } + public int Destination { get; } + public int Traffic { get; } + } + + public class NetworkRoutingSolver + { + private List<(long source, long destination, int arcId)> _arcsData = new List<(long source, long destination, int arcId)>(); + private List _arcCapacity = new List(); + private List _demands = new List(); + private List _allMinPathLengths = new List(); + private List> _capacity; + private List>> _allPaths; + + public int NumberOfNodes { get; private set; } = -1; + + private int countArcs + { + get { return _arcsData.Count / 2; } + } + + public void ComputeAllPathsForOneDemandAndOnePathLength(int demandIndex, int maxLength, int maxPaths) + { + // We search for paths of length exactly 'maxLength'. + CpModel cpModel = new CpModel(); + var arcVars = new List(); + var nodeVars = new List(); + + for (int i = 0; i < maxLength; i++) + { + nodeVars.Add(cpModel.NewIntVar(0, NumberOfNodes - 1, string.Empty)); + } + + for (int i = 0; i < maxLength - 1; i++) + { + arcVars.Add(cpModel.NewIntVar(-1, countArcs - 1, string.Empty)); + } + + var arcs = getArcsData(); + + for (int i = 0; i < maxLength - 1; i++) + { + var tmpVars = new List(); + tmpVars.Add(nodeVars[i]); + tmpVars.Add(nodeVars[i + 1]); + tmpVars.Add(arcVars[i]); + var table = cpModel.AddAllowedAssignments(tmpVars, arcs); + } + + var demand = _demands[demandIndex]; + cpModel.Add(nodeVars[0] == demand.Source); + cpModel.Add(nodeVars[maxLength - 1] == demand.Destination); + cpModel.AddAllDifferent(arcVars); + cpModel.AddAllDifferent(nodeVars); + + var solver = new CpSolver(); + + var solutionPrinter = new FeasibleSolutionChecker(demandIndex, ref _allPaths, maxLength, arcVars, maxPaths, nodeVars); + var status = solver.SearchAllSolutions(cpModel, solutionPrinter); + } + + private long[,] getArcsData() + { + long[,] arcs = new long[_arcsData.Count, 3]; + + for (int i = 0; i < _arcsData.Count; i++) + { + var data = _arcsData[i]; + arcs[i, 0] = data.source; + arcs[i, 1] = data.destination; + arcs[i, 2] = data.arcId; + } + + return arcs; + } + + public int ComputeAllPaths(int extraHops, int maxPaths) + { + int numPaths = 0; + for (int demandIndex = 0; demandIndex < _demands.Count; demandIndex++) + { + int minPathLength = _allMinPathLengths[demandIndex]; + + for (int maxLength = minPathLength + 1; maxLength <= minPathLength + extraHops+1; maxLength++) + { + ComputeAllPathsForOneDemandAndOnePathLength(demandIndex, maxLength, maxPaths); + + if (_allPaths[demandIndex].Count >= maxPaths) + break; + } + + numPaths += _allPaths[demandIndex].Count; + } + + return numPaths; + } + + public void AddArcData(long source, long destination, int arcId) + { + _arcsData.Add((source, destination, arcId)); + } + + public void InitArcInfo(NetworkRoutingData data) + { + int numArcs = data.NumberOfArcs; + _capacity = new List>(NumberOfNodes); + + for (int nodeIndex = 0; nodeIndex < NumberOfNodes; nodeIndex++) + { + _capacity.Add(new List(NumberOfNodes)); + for (int i = 0; i < NumberOfNodes; i++) + { + _capacity[nodeIndex].Add(0); + } + } + + int arcId = 0; + for (int i = 0; i < NumberOfNodes - 1; i++) + { + for (int j = i + 1; j < NumberOfNodes; j++) + { + int capacity = data.Capacity(i, j); + if (capacity > 0) + { + AddArcData(i, j, arcId); + AddArcData(j, i, arcId); + arcId++; + _arcCapacity.Add(capacity); + _capacity[i][j] = capacity; + _capacity[j][i] = capacity; + + if (printModel) + { + Console.WriteLine($"Arc {i} <-> {j} with capacity {capacity}"); + } + } + } + } + + Debug.Assert(arcId == numArcs); + } + + public int InitDemandInfo(NetworkRoutingData data) + { + int numDemands = data.NumberOfDemands; + int totalDemand = 0; + for (int i = 0; i < NumberOfNodes; i++) + { + for (int j = 0; j < NumberOfNodes; j++) + { + int traffic = data.Demand(i, j); + if (traffic > 0) + { + _demands.Add(new Demand(i, j, traffic)); + totalDemand += traffic; + } + } + } + + Debug.Assert(numDemands == _demands.Count); + + return totalDemand; + } + + public long InitShortestPaths(NetworkRoutingData data) + { + int numDemands = data.NumberOfDemands; + long totalCumulatedTraffic = 0L; + _allMinPathLengths.Clear(); + var paths = new List(); + + for (int demandIndex = 0; demandIndex < numDemands; demandIndex++) + { + paths.Clear(); + var demand = _demands[demandIndex]; + var r = DijkstraShortestPath(NumberOfNodes, demand.Source, demand.Destination, + ((int x, int y) p) => hasArc(p.x, p.y), kDisconnectedDistance, paths); + + _allMinPathLengths.Add(paths.Count - 1); + var minPathLength = _allMinPathLengths[demandIndex]; + totalCumulatedTraffic += minPathLength * demand.Traffic; + } + + return totalCumulatedTraffic; + } + + public int InitPaths(NetworkRoutingData data, int extraHops, int maxPaths) + { + var numDemands = data.NumberOfDemands; + Console.WriteLine("Computing all possible paths "); + Console.WriteLine($" - extra hops = {extraHops}"); + Console.WriteLine($" - max paths per demand = {maxPaths}"); + + _allPaths =new List>>(numDemands); + + var numPaths = ComputeAllPaths(extraHops, maxPaths); + + for (int demandIndex = 0; demandIndex < numDemands; demandIndex++) + { + var demand = _demands[demandIndex]; + Console.WriteLine($"Demand from {demand.Source} to {demand.Destination} with traffic {demand.Traffic}, amd {_allPaths[demandIndex].Count} possible paths."); + } + + return numPaths; + } + + public void Init(NetworkRoutingData data, int extraHops, int maxPaths) + { + Console.WriteLine($"Model {data.Name}"); + NumberOfNodes = data.NumberOfNodes; + var numArcs = data.NumberOfArcs; + var numDemands = data.NumberOfDemands; + + InitArcInfo(data); + var totalDemand = InitDemandInfo(data); + var totalAccumulatedTraffic = InitShortestPaths(data); + var numPaths = InitPaths(data, extraHops, maxPaths); + + Console.WriteLine("Model created:"); + Console.WriteLine($" - {NumberOfNodes} nodes"); + Console.WriteLine($" - {numArcs} arcs"); + Console.WriteLine($" - {numDemands} demands"); + Console.WriteLine($" - a total traffic of {totalDemand}"); + Console.WriteLine($" - a minimum cumulated traffic of {totalAccumulatedTraffic}"); + Console.WriteLine($" - {numPaths} possible paths for all demands"); + } + + private long hasArc(int i, int j) + { + if (_capacity[i][j] > 0) + return 1; + else + return kDisconnectedDistance; + } + + public long Solve() + { + Console.WriteLine("Solving model"); + var numDemands = _demands.Count; + var numArcs = countArcs; + + CpModel cpModel = new CpModel(); + + var pathVars = new List>(numDemands); + + for (int demandIndex = 0; demandIndex < numDemands; demandIndex++) + { + pathVars.Add(new List()); + + for (int arc = 0; arc < numArcs; arc++) + { + pathVars[demandIndex].Add(cpModel.NewBoolVar("")); + } + + long[,] tuples = new long[_allPaths[demandIndex].Count, numArcs]; + + int pathCount = 0; + foreach (var set in _allPaths[demandIndex]) + { + foreach (var arc in set) + { + tuples[pathCount, arc] = 1; + } + + pathCount++; + } + + var pathCt = cpModel.AddAllowedAssignments(pathVars[demandIndex], tuples); + } + + var trafficVars = new List(numArcs); + var normalizedTrafficVars = new List(numArcs); + var comfortableTrafficVars = new List(numArcs); + + long maxNormalizedTraffic = 0; + + for (int arcIndex = 0; arcIndex < numArcs; arcIndex++) + { + long sumOfTraffic = 0; + + var vars = new List(); + var traffics = new List(); + + for (int i = 0; i < pathVars.Count; i++) + { + sumOfTraffic += _demands[i].Traffic; + vars.Add(pathVars[i][arcIndex]); + traffics.Add(_demands[i].Traffic); + } + + var sum = LinearExpr.ScalProd(vars, traffics); + var trafficVar = cpModel.NewIntVar(0, sumOfTraffic, $"trafficVar{arcIndex}"); + trafficVars.Add(trafficVar); + cpModel.Add(sum == trafficVar); + + var capacity = _arcCapacity[arcIndex]; + var scaledTraffic = cpModel.NewIntVar(0, sumOfTraffic * 1000, $"scaledTrafficVar{arcIndex}"); + var scaledTrafficVar = trafficVar * 1000; + cpModel.Add(scaledTrafficVar == scaledTraffic); + + var normalizedTraffic = + cpModel.NewIntVar(0, sumOfTraffic * 1000 / capacity, $"normalizedTraffic{arcIndex}"); + + maxNormalizedTraffic = Math.Max(maxNormalizedTraffic, sumOfTraffic * 1000 / capacity); + cpModel.AddDivisionEquality(normalizedTraffic, scaledTraffic, cpModel.NewConstant(capacity)); + normalizedTrafficVars.Add(normalizedTraffic); + var comfort = cpModel.NewBoolVar($"comfort{arcIndex}"); + var safeCapacity = (long)(capacity * comfortZone); + cpModel.Add(trafficVar > safeCapacity).OnlyEnforceIf(comfort); + cpModel.Add(trafficVar <=safeCapacity).OnlyEnforceIf(comfort.Not()); + comfortableTrafficVars.Add(comfort); + } + + var maxUsageCost = cpModel.NewIntVar(0, maxNormalizedTraffic, "maxUsageCost"); + cpModel.AddMaxEquality(maxUsageCost, normalizedTrafficVars); + + var obj = new List() {maxUsageCost}; + obj.AddRange(comfortableTrafficVars); + cpModel.Minimize(LinearExpr.Sum(obj)); + + CpSolver solver = new CpSolver(); + solver.StringParameters = parameters; + + CpSolverStatus status = solver.SearchAllSolutions(cpModel, + new FeasibleSolutionChecker2(maxUsageCost, comfortableTrafficVars, trafficVars)); + + return (long)solver.ObjectiveValue; + + } + } + + private class DijkstraSP + { + private const long kInfinity = long.MaxValue / 2; + + private readonly Func<(int, int), long> _graph; + private readonly int[] _predecessor; + private readonly List _elements; + private readonly AdjustablePriorityQueue _frontier; + private readonly List _notVisited = new List(); + private readonly List _addedToFrontier = new List(); + + public DijkstraSP(int nodeCount, int startNode, Func<(int,int), long> graph, long disconnectedDistance) + { + NodeCount = nodeCount; + StartNode = startNode; + this._graph = graph; + DisconnectedDistance = disconnectedDistance; + _predecessor = new int[nodeCount]; + _elements = new List(nodeCount); + _frontier = new AdjustablePriorityQueue(); + } + + public int NodeCount { get; } + public int StartNode { get; } + public long DisconnectedDistance { get; } + + public bool ShortestPath(int endNode, List nodes) + { + initialize(); + bool found = false; + while (!_frontier.IsEmpty) + { + long distance; + int node = selectClosestNode(out distance); + if (distance == kInfinity) + { + found = false; + break; + } + else if (node == endNode) + { + found = true; + break; + } + update(node); + } + + if (found) + { + findPath(endNode, nodes); + } + + return found; + + } + + private void initialize() + { + for (int i = 0; i < NodeCount; i++) + { + _elements.Add(new Element {Node = i}); + + if (i == StartNode) + { + _predecessor[i] = -1; + _elements[i].Distance = 0; + _frontier.Add(_elements[i]); + } + else + { + _elements[i].Distance = kInfinity; + _predecessor[i] = StartNode; + _notVisited.Add(i); + } + } + } + + private int selectClosestNode(out long distance) + { + var node = _frontier.Top().Node; + distance = _frontier.Top().Distance; + _frontier.Pop(); + _notVisited.Remove(node); + _addedToFrontier.Remove(node); + return node; + } + + private void update(int node) + { + foreach (var otherNode in _notVisited) + { + var graphNode = _graph((node, otherNode)); + + if (graphNode != DisconnectedDistance) + { + if (!_addedToFrontier.Contains(otherNode)) + { + _frontier.Add(_elements[otherNode]); + _addedToFrontier.Add(otherNode); + } + + var otherDistance = _elements[node].Distance + graphNode; + + if (_elements[otherNode].Distance > otherDistance) + { + _elements[otherNode].Distance = otherDistance; + _frontier.NoteChangedPriority(_elements[otherNode]); + _predecessor[otherNode] = node; + } + } + } + } + + private void findPath(int dest, List nodes) + { + var j = dest; + nodes.Add(j); + while (_predecessor[j] != -1) + { + nodes.Add(_predecessor[j]); + j = _predecessor[j]; + } + } + } + + public static bool DijkstraShortestPath(int nodeCount, int startNode, int endNode, Func<(int, int), long> graph, + long disconnectedDistance, List nodes) + { + DijkstraSP bf = new DijkstraSP(nodeCount, startNode, graph, disconnectedDistance); + return bf.ShortestPath(endNode, nodes); + } + + [DebuggerDisplay("Node = {Node}, HeapIndex = {HeapIndex}, Distance = {Distance}")] + private class Element : IHasHeapIndex, IComparable + { + public int HeapIndex { get; set; } = -1; + public long Distance { get; set; } = 0; + public int Node { get; set; } = -1; + + public int CompareTo(Element other) + { + if (this.Distance > other.Distance) + return -1; + + if (this.Distance < other.Distance) + return 1; + + return 0; + } + } + + private class AdjustablePriorityQueue where T: class, IHasHeapIndex, IComparable + { + private readonly List _elems = new List(); + + public void Add(T val) + { + _elems.Add(val); + adjustUpwards(_elems.Count - 1); + } + + public void Remove(T val) + { + var i = val.HeapIndex; + if (i == _elems.Count - 1) + { + _elems.RemoveAt(_elems.Count - 1); + return; + } + + _elems[i] = _elems.Last(); + _elems[i].HeapIndex = i; + _elems.RemoveAt(_elems.Count - 1); + NoteChangedPriority(_elems[i]); + } + + public bool Contains(T val) + { + var i = val.HeapIndex; + if (i < 0 || i >= _elems.Count || _elems[i].CompareTo(val) != 0) + return false; + + return true; + } + + public T Top() + { + return _elems[0]; + } + + public void Pop() + { + Remove(Top()); + } + + public int Size() + { + return _elems.Count; + } + + public bool IsEmpty + { + get { return !_elems.Any(); } + } + + public void Clear() + { + _elems.Clear(); + } + + public void CheckValid() + { + for (int i = 0; i < _elems.Count; i++) + { + var leftChild = 1 + 2 * i; + if (leftChild < _elems.Count) + { + var compare = _elems[i].CompareTo(_elems[leftChild]); + Debug.Assert(compare >=0); + } + + int rightChild = leftChild + 1; + if (rightChild < _elems.Count) + { + var compare = _elems[i].CompareTo(_elems[rightChild]); + Debug.Assert(compare >= 0); + } + } + } + + public void NoteChangedPriority(T val) + { + if (_elems.Count == 0) + return; + + var i = val.HeapIndex; + var parent = (i - 1) / 2; + if (_elems[parent].CompareTo(val) == -1) + { + adjustUpwards(i); + } + else + { + adjustDownwards(i); + } + } + + private void adjustUpwards(int i) + { + var t = _elems[i]; + while (i > 0) + { + var parent = (i - 1) / 2; + if (_elems[parent].CompareTo(t) != -1) + { + break; + } + + _elems[i] = _elems[parent]; + _elems[i].HeapIndex = i; + i = parent; + } + + _elems[i] = t; + t.HeapIndex = i; + } + + private void adjustDownwards(int i) + { + var t = _elems[i]; + while (true) + { + var leftChild = 1 + 2 * i; + if (leftChild >= _elems.Count) + { + break; + } + + var rightChild = leftChild + 1; + var next = (rightChild < _elems.Count && _elems[leftChild].CompareTo(_elems[rightChild]) == -1) + ? rightChild + : leftChild; + + if (t.CompareTo(_elems[next]) != -1) + { + break; + } + + _elems[i] = _elems[next]; + _elems[i].HeapIndex = i; + i = next; + } + + _elems[i] = t; + t.HeapIndex = i; + } + } + + public interface IHasHeapIndex + { + int HeapIndex { get; set; } + } + + private class FeasibleSolutionChecker : CpSolverSolutionCallback + { + public FeasibleSolutionChecker(int demandIndex, ref List>> allPaths, int maxLength, List arcVars, int maxPaths, List nodeVars) + { + DemandIndex = demandIndex; + AllPaths = allPaths; + MaxLength = maxLength; + ArcVars = arcVars; + MaxPaths = maxPaths; + NodeVars = nodeVars; + } + + public int DemandIndex { get; } + public List>> AllPaths { get; } + public int MaxLength { get; } + public List ArcVars { get; } + public int MaxPaths { get; } + public List NodeVars { get; } + + public override void OnSolutionCallback() + { + if(AllPaths.Count < DemandIndex + 1) + AllPaths.Add(new List>()); + + int pathId = AllPaths[DemandIndex].Count; + AllPaths[DemandIndex].Add(new HashSet()); + + for (int i = 0; i < MaxLength - 1; i++) + { + int arc = (int) this.SolutionIntegerValue(ArcVars[i].GetIndex()); + AllPaths[DemandIndex][pathId].Add(arc); + } + + if (AllPaths[DemandIndex].Count() >= MaxPaths) + { + StopSearch(); + } + } + } + + private class FeasibleSolutionChecker2 : CpSolverSolutionCallback + { + public IntVar MaxUsageCost { get; } + public List ComfortableTrafficVars { get; } + public List TrafficVars { get; } + private int _numSolutions = 0; + + public FeasibleSolutionChecker2(IntVar maxUsageCost, List comfortableTrafficVars, List trafficVars) + { + MaxUsageCost = maxUsageCost; + ComfortableTrafficVars = comfortableTrafficVars; + TrafficVars = trafficVars; + } + + public override void OnSolutionCallback() + { + Console.WriteLine($"Solution {_numSolutions}"); + var percent = SolutionIntegerValue(MaxUsageCost.GetIndex()) / 10.0; + int numNonComfortableArcs = 0; + + foreach (var comfort in ComfortableTrafficVars) + { + numNonComfortableArcs += SolutionBooleanValue(comfort.GetIndex()) ? 1 : 0; + } + + if (numNonComfortableArcs > 0) + { + Console.WriteLine($"*** Found a solution with a max usage of {percent}%, and {numNonComfortableArcs} links above the comfort zone"); + } + else + { + Console.WriteLine($"*** Found a solution with a max usage of {percent}%"); + } + + _numSolutions++; + } + } +} diff --git a/libs/or-tools-bin/examples/dotnet/NetworkRoutingSat.csproj b/libs/or-tools-bin/examples/dotnet/NetworkRoutingSat.csproj new file mode 100644 index 0000000000000000000000000000000000000000..58b1834c58552d47c2ed300bbeefe84bae020e07 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/NetworkRoutingSat.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.NetworkRoutingSat + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools/examples/dotnet/NoOverlapSampleSat.cs b/libs/or-tools-bin/examples/dotnet/NoOverlapSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/NoOverlapSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/NoOverlapSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/NoOverlapSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/NoOverlapSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/NoOverlapSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/NoOverlapSampleSat.csproj diff --git a/libs/or-tools-bin/examples/dotnet/NursesSat.cs b/libs/or-tools-bin/examples/dotnet/NursesSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..727ba7eeb71b9c6786b63c06b2be046a5bdeba6a --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/NursesSat.cs @@ -0,0 +1,215 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + +public class NurseSolutionObserver : CpSolverSolutionCallback +{ + public NurseSolutionObserver(IntVar[,,] shifts, int num_nurses, int num_days, + int num_shifts, HashSet to_print) { + shifts_ = shifts; + num_nurses_ = num_nurses; + num_days_ = num_days; + num_shifts_ = num_shifts; + to_print_ = to_print; + } + + public override void OnSolutionCallback() + { + solution_count_++; + if (to_print_.Contains(solution_count_)) + { + Console.WriteLine( + String.Format("Solution #{0}: time = {1:.02} s", + solution_count_, WallTime())); + for (int d = 0; d < num_days_; ++d) + { + Console.WriteLine(String.Format("Day #{0}", d)); + for (int n = 0; n < num_nurses_; ++n) + { + for (int s = 0; s < num_shifts_; ++s) + { + if (BooleanValue(shifts_[n, d, s])) + { + Console.WriteLine( + String.Format(" Nurse #{0} is working shift #{1}", n, s)); + } + } + } + } + } + } + + public int SolutionCount() + { + return solution_count_; + } + + private int solution_count_; + private IntVar[,,] shifts_; + private int num_nurses_; + private int num_days_; + private int num_shifts_; + private HashSet to_print_; +} + +public class NursesSat +{ + static void Main() + { + // Data. + int num_nurses = 4; + // Nurse assigned to shift 0 means not working that day. + int num_shifts = 4; + int num_days = 7; + + var all_nurses = Enumerable.Range(0, num_nurses); + var all_shifts = Enumerable.Range(0, num_shifts); + var all_working_shifts = Enumerable.Range(1, num_shifts - 1); + var all_days = Enumerable.Range(0, num_days); + + // Creates the model. + CpModel model = new CpModel(); + + // Creates shift variables. + // shift[n, d, s]: nurse "n" works shift "s" on day "d". + IntVar[,,] shift = new IntVar[num_nurses, num_days, num_shifts]; + foreach (int n in all_nurses) + { + foreach (int d in all_days) + { + foreach (int s in all_shifts) + { + shift[n, d, s] = + model.NewBoolVar(String.Format("shift_n{0}d{1}s{2}", n, d, s)); + } + } + } + + // Makes assignments different on each day, that is each shift is + // assigned at most one nurse. As we have the same number of + // nurses and shifts, then each day, each shift is assigned to + // exactly one nurse. + foreach (int d in all_days) + { + foreach (int s in all_shifts) + { + IntVar[] tmp = new IntVar[num_nurses]; + foreach (int n in all_nurses) + { + tmp[n] = shift[n, d, s]; + } + model.Add(LinearExpr.Sum(tmp) == 1); + } + } + + // Nurses do 1 shift per day. + foreach (int n in all_nurses) + { + foreach (int d in all_days) + { + IntVar[] tmp = new IntVar[num_shifts]; + foreach (int s in all_shifts) + { + tmp[s] = shift[n, d, s]; + } + model.Add(LinearExpr.Sum(tmp) == 1); + } + } + + // Each nurse works 5 or 6 days in a week. + // That is each nurse works shift 0 at most 2 times. + foreach (int n in all_nurses) + { + IntVar[] tmp = new IntVar[num_days]; + foreach (int d in all_days) + { + tmp[d] = shift[n, d, 0]; + } + model.AddLinearConstraint(LinearExpr.Sum(tmp), 1, 2); + } + + // works_shift[(n, s)] is 1 if nurse n works shift s at least one day in + // the week. + IntVar[,] works_shift = new IntVar[num_nurses, num_shifts]; + foreach (int n in all_nurses) + { + foreach (int s in all_shifts) + { + works_shift[n, s] = + model.NewBoolVar(String.Format("works_shift_n{0}s{1}", n, s)); + IntVar[] tmp = new IntVar[num_days]; + foreach (int d in all_days) + { + tmp[d] = shift[n, d, s]; + } + model.AddMaxEquality(works_shift[n, s], tmp); + } + } + + // For each working shift, at most 2 nurses are assigned to that shift + // during the week. + foreach (int s in all_working_shifts) + { + IntVar[] tmp = new IntVar[num_nurses]; + foreach (int n in all_nurses) + { + tmp[n] = works_shift[n, s]; + } + model.Add(LinearExpr.Sum(tmp) <= 2); + } + + // If a nurse works shifts 2 or 3 on, she must also work that + // shift the previous day or the following day. This means that + // on a given day and shift, either she does not work that shift + // on that day, or she works that shift on the day before, or the + // day after. + foreach (int n in all_nurses) + { + for (int s = 2; s <= 3; ++s) + { + foreach (int d in all_days) + { + int yesterday = d == 0 ? num_days - 1 : d - 1; + int tomorrow = d == num_days - 1 ? 0 : d + 1; + model.AddBoolOr(new ILiteral[] { shift[n, yesterday, s], + shift[n, d, s].Not(), + shift[n, tomorrow, s] } ); + } + } + } + + // Creates the solver and solve. + CpSolver solver = new CpSolver(); + // Display a few solutions picked at random. + HashSet to_print = new HashSet(); + to_print.Add(859); + to_print.Add(2034); + to_print.Add(5091); + to_print.Add(7003); + NurseSolutionObserver cb = new NurseSolutionObserver( + shift, num_nurses, num_days, num_shifts, to_print); + CpSolverStatus status = solver.SearchAllSolutions(model, cb); + + // Statistics. + Console.WriteLine("Statistics"); + Console.WriteLine(String.Format(" - solve status : {0}", status)); + Console.WriteLine(" - conflicts : " + solver.NumConflicts()); + Console.WriteLine(" - branches : " + solver.NumBranches()); + Console.WriteLine(" - wall time : " + solver.WallTime() + " ms"); + Console.WriteLine(" - #solutions : " + cb.SolutionCount()); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/NursesSat.csproj b/libs/or-tools-bin/examples/dotnet/NursesSat.csproj new file mode 100644 index 0000000000000000000000000000000000000000..12126c722c9ab450b83f2d3439a77d9efa66c13d --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/NursesSat.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.NursesSat + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools/examples/dotnet/OptionalIntervalSampleSat.cs b/libs/or-tools-bin/examples/dotnet/OptionalIntervalSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/OptionalIntervalSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/OptionalIntervalSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/OptionalIntervalSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/OptionalIntervalSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/OptionalIntervalSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/OptionalIntervalSampleSat.csproj diff --git a/libs/or-tools-bin/examples/dotnet/README.md b/libs/or-tools-bin/examples/dotnet/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2e341c0caf79bdf5c622e38ecbaab53274b749b7 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/README.md @@ -0,0 +1,40 @@ +# .NetCoreApp examples +The following examples showcase how to use OrTools. +The project solution has examples for both C# and F#. + +We recommend that all projects you create target `netcoreapp2.1`, +as this allows you to compile for various frameworks and +keep up-to-date with the latest frameworks. + +Wherever you have or-tools installed, be sure to `PackageReference` the `Google.OrTools` +from the project file. + +# Execution +Running the examples will involve building them, then running them. +You can run the following command: +```shell +dotnet build .csproj +dotnet run --no-build --project .csproj +``` + +# Note on Google.OrTools.FSharp +This part describes how to use Google.OrTools.FSharp nuget package in F#. + +## SolverOptions and lpSolve +This function and parameter object are a wrapper around the standard Google.OrTools functions. +It is designed to enter the Linear/Integer program as *matrices* and *vectors*. + +Two input formats are allowed: +* Canonical Form; +* Standard Form. + +**ALL Matrices & Vectors are entered as columns** + +## Execution +Running the examples will involve building them, then running them. +You can run the following command: +```shell +dotnet build .fsproj +dotnet run --no-build --project .fsproj +``` + diff --git a/libs/or-tools/examples/dotnet/RabbitsAndPheasantsSat.cs b/libs/or-tools-bin/examples/dotnet/RabbitsAndPheasantsSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/RabbitsAndPheasantsSat.cs rename to libs/or-tools-bin/examples/dotnet/RabbitsAndPheasantsSat.cs diff --git a/libs/or-tools/examples/dotnet/RabbitsAndPheasantsSat.csproj b/libs/or-tools-bin/examples/dotnet/RabbitsAndPheasantsSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/RabbitsAndPheasantsSat.csproj rename to libs/or-tools-bin/examples/dotnet/RabbitsAndPheasantsSat.csproj diff --git a/libs/or-tools/examples/dotnet/RankingSampleSat.cs b/libs/or-tools-bin/examples/dotnet/RankingSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/RankingSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/RankingSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/RankingSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/RankingSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/RankingSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/RankingSampleSat.csproj diff --git a/libs/or-tools/examples/dotnet/ReifiedSampleSat.cs b/libs/or-tools-bin/examples/dotnet/ReifiedSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/ReifiedSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/ReifiedSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/ReifiedSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/ReifiedSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/ReifiedSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/ReifiedSampleSat.csproj diff --git a/libs/or-tools/examples/dotnet/SearchForAllSolutionsSampleSat.cs b/libs/or-tools-bin/examples/dotnet/SearchForAllSolutionsSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SearchForAllSolutionsSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/SearchForAllSolutionsSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/SearchForAllSolutionsSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/SearchForAllSolutionsSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SearchForAllSolutionsSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/SearchForAllSolutionsSampleSat.csproj diff --git a/libs/or-tools-bin/examples/dotnet/ShiftSchedulingSat.cs b/libs/or-tools-bin/examples/dotnet/ShiftSchedulingSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..3f11d18bd6ce5f0d98f61091865deb268f2603ac --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/ShiftSchedulingSat.cs @@ -0,0 +1,548 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.Sat; + + +/// +/// Creates a shift scheduling problem and solves it +/// +public class ShiftSchedulingSat +{ + static void Main(string[] args) + { + SolveShiftScheduling(); + } + + static void SolveShiftScheduling() + { + int numEmployees = 8; + int numWeeks = 3; + var shifts = new[] { "O", "M", "A", "N" }; + + // Fixed assignment: (employee, shift, day). + // This fixes the first 2 days of the schedule. + var fixedAssignments = new (int Employee, int Shift, int Day)[] + { + (0, 0, 0), + (1, 0, 0), + (2, 1, 0), + (3, 1, 0), + (4, 2, 0), + (5, 2, 0), + (6, 2, 3), + (7, 3, 0), + (0, 1, 1), + (1, 1, 1), + (2, 2, 1), + (3, 2, 1), + (4, 2, 1), + (5, 0, 1), + (6, 0, 1), + (7, 3, 1), + }; + + // Request: (employee, shift, day, weight) + // A negative weight indicates that the employee desire this assignment. + var requests = new (int Employee, int Shift, int Day, int Weight)[] + { + // Employee 3 wants the first Saturday off. + (3, 0, 5, -2), + // Employee 5 wants a night shift on the second Thursday. + (5, 3, 10, -2), + // Employee 2 does not want a night shift on the third Friday. + (2, 3, 4, 4) + }; + + // Shift constraints on continuous sequence : + // (shift, hard_min, soft_min, min_penalty, + // soft_max, hard_max, max_penalty) + var shiftConstraints = new (int Shift, int HardMin, int SoftMin, int MinPenalty, int SoftMax, int HardMax, int MaxPenalty)[] + { + // One or two consecutive days of rest, this is a hard constraint. + (0, 1, 1, 0, 2, 2, 0), + // Between 2 and 3 consecutive days of night shifts, 1 and 4 are + // possible but penalized. + (3, 1, 2, 20, 3, 4, 5), + }; + + // Weekly sum constraints on shifts days: + // (shift, hardMin, softMin, minPenalty, + // softMax, hardMax, maxPenalty) + var weeklySumConstraints = new (int Shift, int HardMin, int SoftMin, int MinPenalty, int SoftMax, int HardMax, int MaxPenalty)[] + { + // Constraints on rests per week. + (0, 1, 2, 7, 2, 3, 4), + // At least 1 night shift per week (penalized). At most 4 (hard). + (3, 0, 1, 3, 4, 4, 0), + }; + + // Penalized transitions: + // (previous_shift, next_shift, penalty (0 means forbidden)) + var penalizedTransitions = new (int PreviousShift, int NextShift, int Penalty)[] + { + // Afternoon to night has a penalty of 4. + (2, 3, 4), + // Night to morning is forbidden. + (3, 1, 0), + }; + + // daily demands for work shifts (morning, afternon, night) for each day + // of the week starting on Monday. + var weeklyCoverDemands = new int[][] + { + new [] {2, 3, 1}, // Monday + new [] {2, 3, 1}, // Tuesday + new [] {2, 2, 2}, // Wednesday + new [] {2, 3, 1}, // Thursday + new [] {2, 2, 2}, // Friday + new [] {1, 2, 3}, // Saturday + new [] {1, 3, 1}, // Sunday + }; + + // Penalty for exceeding the cover constraint per shift type. + var excessCoverPenalties = new[] { 2, 2, 5 }; + + var numDays = numWeeks * 7; + var numShifts = shifts.Length; + + var model = new CpModel(); + + IntVar[,,] work = new IntVar[numEmployees, numShifts, numDays]; + + foreach (int e in Range(numEmployees)) + { + foreach (int s in Range(numShifts)) + { + foreach (int d in Range(numDays)) + { + work[e, s, d] = model.NewBoolVar($"work{e}_{s}_{d}"); + } + } + } + + // Linear terms of the objective in a minimization context. + var objIntVars = new List(); + var objIntCoeffs = new List(); + var objBoolVars = new List(); + var objBoolCoeffs = new List(); + + // Exactly one shift per day. + foreach (int e in Range(numEmployees)) + { + foreach (int d in Range(numDays)) + { + var temp = new IntVar[numShifts]; + foreach (int s in Range(numShifts)) + { + temp[s] = work[e, s, d]; + } + + model.Add(LinearExpr.Sum(temp) == 1); + } + } + + // Fixed assignments. + foreach (var (e, s, d) in fixedAssignments) + { + model.Add(work[e, s, d] == 1); + } + + // Employee requests + foreach (var (e, s, d, w) in requests) + { + objBoolVars.Add(work[e, s, d]); + objBoolCoeffs.Add(w); + } + + // Shift constraints + foreach (var constraint in shiftConstraints) + { + foreach (int e in Range(numEmployees)) + { + var works = new IntVar[numDays]; + foreach (int d in Range(numDays)) + { + works[d] = work[e, constraint.Shift, d]; + } + + var (variables, coeffs) = AddSoftSequenceConstraint( + model, works, + constraint.HardMin, constraint.SoftMin, constraint.MinPenalty, + constraint.SoftMax, constraint.HardMax, constraint.MaxPenalty, + $"shift_constraint(employee {e}, shift {constraint.Shift}"); + + objBoolVars.AddRange(variables); + objBoolCoeffs.AddRange(coeffs); + } + } + + // Weekly sum constraints + foreach (var constraint in weeklySumConstraints) + { + foreach (int e in Range(numEmployees)) + { + foreach (int w in Range(numWeeks)) + { + var works = new IntVar[7]; + + foreach (int d in Range(7)) + { + works[d] = work[e, constraint.Shift, d + w * 7]; + } + + var (variables, coeffs) = AddSoftSumConstraint( + model, works, + constraint.HardMin, constraint.SoftMin, constraint.MinPenalty, + constraint.SoftMax, constraint.HardMax, constraint.MaxPenalty, + $"weekly_sum_constraint(employee {e}, shift {constraint.Shift}, week {w}"); + + objBoolVars.AddRange(variables); + objBoolCoeffs.AddRange(coeffs); + } + } + } + + // Penalized transitions + foreach (var penalizedTransition in penalizedTransitions) + { + foreach (int e in Range(numEmployees)) + { + foreach (int d in Range(numDays - 1)) + { + var transition = new List() + { + work[e, penalizedTransition.PreviousShift, d].Not(), + work[e, penalizedTransition.NextShift, d + 1].Not() + }; + + if (penalizedTransition.Penalty == 0) + { + model.AddBoolOr(transition); + } + else + { + var transVar = model.NewBoolVar($"transition (employee {e}, day={d}"); + transition.Add(transVar); + model.AddBoolOr(transition); + objBoolVars.Add(transVar); + objBoolCoeffs.Add(penalizedTransition.Penalty); + } + } + } + } + + // Cover constraints + foreach (int s in Range(1, numShifts)) + { + foreach (int w in Range(numWeeks)) + { + foreach (int d in Range(7)) + { + var works = new IntVar[numEmployees]; + foreach (int e in Range(numEmployees)) + { + works[e] = work[e, s, w * 7 + d]; + } + + // Ignore off shift + var minDemand = weeklyCoverDemands[d][s - 1]; + var worked = model.NewIntVar(minDemand, numEmployees, ""); + model.Add(LinearExpr.Sum(works) == worked); + + var overPenalty = excessCoverPenalties[s - 1]; + if (overPenalty > 0) + { + var name = $"excess_demand(shift={s}, week={w}, day={d}"; + var excess = model.NewIntVar(0, numEmployees - minDemand, name); + model.Add(excess == worked - minDemand); + objIntVars.Add(excess); + objIntCoeffs.Add(overPenalty); + } + } + } + } + + // Objective + var objBoolSum = LinearExpr.ScalProd(objBoolVars, objBoolCoeffs); + var objIntSum = LinearExpr.ScalProd(objIntVars, objIntCoeffs); + + model.Minimize(objBoolSum + objIntSum); + + // Solve model + var solver = new CpSolver(); + solver.StringParameters = + "num_search_workers:8, log_search_progress: true, max_time_in_seconds:30"; + + var status = solver.Solve(model); + + // Print solution + if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) + { + Console.WriteLine(); + var header = " "; + for (int w = 0; w < numWeeks; w++) + { + header += "M T W T F S S "; + } + + Console.WriteLine(header); + + foreach (int e in Range(numEmployees)) + { + var schedule = ""; + foreach (int d in Range(numDays)) + { + foreach (int s in Range(numShifts)) + { + if (solver.BooleanValue(work[e, s, d])) + { + schedule += shifts[s] + " "; + } + } + } + + Console.WriteLine($"worker {e}: {schedule}"); + } + + Console.WriteLine(); + Console.WriteLine("Penalties:"); + + foreach (var (i, var) in objBoolVars.Select((x, i) => (i, x))) + { + if (solver.BooleanValue(var)) + { + var penalty = objBoolCoeffs[i]; + if (penalty > 0) + { + Console.WriteLine($" {var.Name()} violated, penalty={penalty}"); + } + else + { + Console.WriteLine($" {var.Name()} fulfilled, gain={-penalty}"); + } + } + } + + foreach (var (i, var) in objIntVars.Select((x, i) => (i, x))) + { + if (solver.Value(var) > 0) + { + Console.WriteLine($" {var.Name()} violated by {solver.Value(var)}, linear penalty={objIntCoeffs[i]}"); + } + } + + Console.WriteLine(); + Console.WriteLine("Statistics"); + Console.WriteLine($" - status : {status}"); + Console.WriteLine($" - conflicts : {solver.NumConflicts()}"); + Console.WriteLine($" - branches : {solver.NumBranches()}"); + Console.WriteLine($" - wall time : {solver.WallTime()}"); + } + } + + /// + /// Filters an isolated sub-sequence of variables assigned to True. + /// Extract the span of Boolean variables[start, start + length), negate them, + /// and if there is variables to the left / right of this span, surround the span by + /// them in non negated form. + /// + /// A list of variables to extract the span from. + /// The start to the span. + /// The length of the span. + /// An array of variables which conjunction will be false if the sub-list is + /// assigned to True, and correctly bounded by variables assigned to False, + /// or by the start or end of works. + static ILiteral[] NegatedBoundedSpan(IntVar[] works, int start, int length) + { + var sequence = new List(); + + if (start > 0) + sequence.Add(works[start - 1]); + + foreach (var i in Range(length)) + sequence.Add(works[start + i].Not()); + + if (start + length < works.Length) + sequence.Add(works[start + length]); + + return sequence.ToArray(); + } + + /// + /// Sequence constraint on true variables with soft and hard bounds. + /// This constraint look at every maximal contiguous sequence of variables + /// assigned to true. If forbids sequence of length < hardMin or > hardMax. + /// Then it creates penalty terms if the length is < softMin or > softMax. + /// + /// The sequence constraint is built on this model. + /// A list of Boolean variables. + /// Any sequence of true variables must have a length of at least hardMin. + /// Any sequence should have a length of at least softMin, or a linear penalty on the delta will be added to the objective. + /// The coefficient of the linear penalty if the length is less than softMin. + /// Any sequence should have a length of at most softMax, or a linear penalty on the delta will be added to the objective. + /// Any sequence of true variables must have a length of at most hardMax. + /// The coefficient of the linear penalty if the length is more than softMax. + /// A base name for penalty literals. + /// A tuple (costLiterals, costCoefficients) containing the different penalties created by the sequence constraint. + static (IntVar[] costLiterals, int[] costCoefficients) AddSoftSequenceConstraint(CpModel model, IntVar[] works, int hardMin, int softMin, int minCost, + int softMax, int hardMax, int maxCost, string prefix) + { + var costLiterals = new List(); + var costCoefficients = new List(); + + // Forbid sequences that are too short. + foreach (var length in Range(1, hardMin)) + { + foreach (var start in Range(works.Length - length + 1)) + { + model.AddBoolOr(NegatedBoundedSpan(works, start, length)); + } + } + + // Penalize sequences that are below the soft limit. + + if (minCost > 0) + { + foreach (var length in Range(hardMin, softMin)) + { + foreach (var start in Range(works.Length - length + 1)) + { + var span = NegatedBoundedSpan(works, start, length).ToList(); + var name = $": under_span(start={start}, length={length})"; + var lit = model.NewBoolVar(prefix + name); + span.Add(lit); + model.AddBoolOr(span); + costLiterals.Add(lit); + // We filter exactly the sequence with a short length. + // The penalty is proportional to the delta with softMin. + costCoefficients.Add(minCost * (softMin - length)); + } + } + } + + // Penalize sequences that are above the soft limit. + if (maxCost > 0) + { + foreach (var length in Range(softMax + 1, hardMax + 1)) + { + foreach (var start in Range(works.Length - length + 1)) + { + var span = NegatedBoundedSpan(works, start, length).ToList(); + var name = $": over_span(start={start}, length={length})"; + var lit = model.NewBoolVar(prefix + name); + span.Add(lit); + model.AddBoolOr(span); + costLiterals.Add(lit); + // Cost paid is max_cost * excess length. + costCoefficients.Add(maxCost * (length - softMax)); + } + } + } + + // Just forbid any sequence of true variables with length hardMax + 1 + foreach (var start in Range(works.Length - hardMax)) + { + var temp = new List(); + + foreach (var i in Range(start, start + hardMax + 1)) + { + temp.Add(works[i].Not()); + } + + model.AddBoolOr(temp); + } + + return (costLiterals.ToArray(), costCoefficients.ToArray()); + } + + /// + /// Sum constraint with soft and hard bounds. + /// This constraint counts the variables assigned to true from works. + /// If forbids sum < hardMin or > hardMax. + /// Then it creates penalty terms if the sum is < softMin or > softMax. + /// + /// The sequence constraint is built on this model. + /// A list of Boolean variables. + /// Any sequence of true variables must have a length of at least hardMin. + /// Any sequence should have a length of at least softMin, or a linear penalty on the delta will be added to the objective. + /// The coefficient of the linear penalty if the length is less than softMin. + /// Any sequence should have a length of at most softMax, or a linear penalty on the delta will be added to the objective. + /// Any sequence of true variables must have a length of at most hardMax. + /// The coefficient of the linear penalty if the length is more than softMax. + /// A base name for penalty literals. + /// A tuple (costVariables, costCoefficients) containing the different + /// penalties created by the sequence constraint. + static (IntVar[] costVariables, int[] costCoefficients) AddSoftSumConstraint(CpModel model, IntVar[] works, + int hardMin, int softMin, int minCost, + int softMax, int hardMax, int maxCost, string prefix) + { + var costVariables = new List(); + var costCoefficients = new List(); + var sumVar = model.NewIntVar(hardMin, hardMax, ""); + // This adds the hard constraints on the sum. + model.Add(sumVar == LinearExpr.Sum(works)); + + var zero = model.NewConstant(0); + + // Penalize sums below the soft_min target. + + if (softMin > hardMin && minCost > 0) + { + var delta = model.NewIntVar(-works.Length, works.Length, ""); + model.Add(delta == (softMin - sumVar)); + var excess = model.NewIntVar(0, works.Length, prefix + ": under_sum"); + model.AddMaxEquality(excess, new[] { delta, zero }); + costVariables.Add(excess); + costCoefficients.Add(minCost); + } + + // Penalize sums above the soft_max target. + if (softMax < hardMax && maxCost > 0) + { + var delta = model.NewIntVar(-works.Length, works.Length, ""); + model.Add(delta == sumVar - softMax); + var excess = model.NewIntVar(0, works.Length, prefix + ": over_sum"); + model.AddMaxEquality(excess, new[] { delta, zero }); + costVariables.Add(excess); + costCoefficients.Add(maxCost); + } + + return (costVariables.ToArray(), costCoefficients.ToArray()); + } + + /// + /// C# equivalent of Python range (start, stop) + /// + /// The inclusive start. + /// The exclusive stop. + /// A sequence of integers. + static IEnumerable Range(int start, int stop) + { + foreach (var i in Enumerable.Range(start, stop - start)) + yield return i; + } + + /// + /// C# equivalent of Python range (stop) + /// + /// The exclusive stop. + /// A sequence of integers. + static IEnumerable Range(int stop) + { + return Range(0, stop); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/ShiftSchedulingSat.csproj b/libs/or-tools-bin/examples/dotnet/ShiftSchedulingSat.csproj new file mode 100644 index 0000000000000000000000000000000000000000..74cf3a778b15d9e4136dd895ea73c56a0e4cb78a --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/ShiftSchedulingSat.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.ShiftSchedulingSat + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools/examples/dotnet/SimpleCpProgram.cs b/libs/or-tools-bin/examples/dotnet/SimpleCpProgram.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleCpProgram.cs rename to libs/or-tools-bin/examples/dotnet/SimpleCpProgram.cs diff --git a/libs/or-tools/examples/dotnet/SimpleCpProgram.csproj b/libs/or-tools-bin/examples/dotnet/SimpleCpProgram.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleCpProgram.csproj rename to libs/or-tools-bin/examples/dotnet/SimpleCpProgram.csproj diff --git a/libs/or-tools/examples/dotnet/SimpleLpProgram.cs b/libs/or-tools-bin/examples/dotnet/SimpleLpProgram.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleLpProgram.cs rename to libs/or-tools-bin/examples/dotnet/SimpleLpProgram.cs diff --git a/libs/or-tools/examples/dotnet/SimpleLpProgram.csproj b/libs/or-tools-bin/examples/dotnet/SimpleLpProgram.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleLpProgram.csproj rename to libs/or-tools-bin/examples/dotnet/SimpleLpProgram.csproj diff --git a/libs/or-tools/examples/dotnet/SimpleMaxFlowProgram.cs b/libs/or-tools-bin/examples/dotnet/SimpleMaxFlowProgram.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleMaxFlowProgram.cs rename to libs/or-tools-bin/examples/dotnet/SimpleMaxFlowProgram.cs diff --git a/libs/or-tools/examples/dotnet/SimpleMaxFlowProgram.csproj b/libs/or-tools-bin/examples/dotnet/SimpleMaxFlowProgram.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleMaxFlowProgram.csproj rename to libs/or-tools-bin/examples/dotnet/SimpleMaxFlowProgram.csproj diff --git a/libs/or-tools/examples/dotnet/SimpleMinCostFlowProgram.cs b/libs/or-tools-bin/examples/dotnet/SimpleMinCostFlowProgram.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleMinCostFlowProgram.cs rename to libs/or-tools-bin/examples/dotnet/SimpleMinCostFlowProgram.cs diff --git a/libs/or-tools/examples/dotnet/SimpleMinCostFlowProgram.csproj b/libs/or-tools-bin/examples/dotnet/SimpleMinCostFlowProgram.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleMinCostFlowProgram.csproj rename to libs/or-tools-bin/examples/dotnet/SimpleMinCostFlowProgram.csproj diff --git a/libs/or-tools/examples/dotnet/SimpleMipProgram.cs b/libs/or-tools-bin/examples/dotnet/SimpleMipProgram.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleMipProgram.cs rename to libs/or-tools-bin/examples/dotnet/SimpleMipProgram.cs diff --git a/libs/or-tools/examples/dotnet/SimpleMipProgram.csproj b/libs/or-tools-bin/examples/dotnet/SimpleMipProgram.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleMipProgram.csproj rename to libs/or-tools-bin/examples/dotnet/SimpleMipProgram.csproj diff --git a/libs/or-tools/examples/dotnet/SimpleProgramFSharp.fs b/libs/or-tools-bin/examples/dotnet/SimpleProgramFSharp.fs similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleProgramFSharp.fs rename to libs/or-tools-bin/examples/dotnet/SimpleProgramFSharp.fs diff --git a/libs/or-tools/examples/dotnet/SimpleProgramFSharp.fsproj b/libs/or-tools-bin/examples/dotnet/SimpleProgramFSharp.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleProgramFSharp.fsproj rename to libs/or-tools-bin/examples/dotnet/SimpleProgramFSharp.fsproj diff --git a/libs/or-tools/examples/dotnet/SimpleRoutingProgram.cs b/libs/or-tools-bin/examples/dotnet/SimpleRoutingProgram.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleRoutingProgram.cs rename to libs/or-tools-bin/examples/dotnet/SimpleRoutingProgram.cs diff --git a/libs/or-tools/examples/dotnet/SimpleRoutingProgram.csproj b/libs/or-tools-bin/examples/dotnet/SimpleRoutingProgram.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleRoutingProgram.csproj rename to libs/or-tools-bin/examples/dotnet/SimpleRoutingProgram.csproj diff --git a/libs/or-tools/examples/dotnet/SimpleSatProgram.cs b/libs/or-tools-bin/examples/dotnet/SimpleSatProgram.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleSatProgram.cs rename to libs/or-tools-bin/examples/dotnet/SimpleSatProgram.cs diff --git a/libs/or-tools/examples/dotnet/SimpleSatProgram.csproj b/libs/or-tools-bin/examples/dotnet/SimpleSatProgram.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SimpleSatProgram.csproj rename to libs/or-tools-bin/examples/dotnet/SimpleSatProgram.csproj diff --git a/libs/or-tools/examples/dotnet/SolutionHintingSampleSat.cs b/libs/or-tools-bin/examples/dotnet/SolutionHintingSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SolutionHintingSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/SolutionHintingSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/SolutionHintingSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/SolutionHintingSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SolutionHintingSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/SolutionHintingSampleSat.csproj diff --git a/libs/or-tools/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.cs b/libs/or-tools-bin/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.csproj diff --git a/libs/or-tools/examples/dotnet/SolveWithTimeLimitSampleSat.cs b/libs/or-tools-bin/examples/dotnet/SolveWithTimeLimitSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/SolveWithTimeLimitSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/SolveWithTimeLimitSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/SolveWithTimeLimitSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/SolveWithTimeLimitSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/SolveWithTimeLimitSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/SolveWithTimeLimitSampleSat.csproj diff --git a/libs/or-tools-bin/examples/dotnet/SpeakerSchedulingSat.cs b/libs/or-tools-bin/examples/dotnet/SpeakerSchedulingSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..8359ad0cc958cd27718cb1a01efab18e2e303ba4 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/SpeakerSchedulingSat.cs @@ -0,0 +1,191 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Google.OrTools.Sat; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System; + +class SpeakerScheduling +{ + struct Entry + { + public IntVar var; + public int start; + + public Entry(IntVar v, int s) + { + var = v; + start = s; + } + } + + static void Solve(int first_slot) + { + Console.WriteLine("----------------------------------------------------"); + + // the slots each speaker is available + int[][] speaker_availability = { + new int[] {1,3,4,6,7,10,12,13,14,15,16,18,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,54,55,56,57,58,59}, + new int[] {1,2,7,8,10,11,16,17,18,21,22,23,24,25,33,34,35,36,37,38,39,40,42,43,44,45,46,47,48,49,52,53,54,55,56,57,58,59,60}, + new int[] {5,6,7,10,12,14,16,17,21,22,23,24,33,35,37,38,39,40,41,42,43,44,45,46,51,53,55,56,57,58,59}, + new int[] {1,2,3,4,5,6,7,11,13,14,15,16,20,24,25,33,34,35,37,38,39,40,41,43,44,45,46,47,48,49,50,51,52,53,54,55,56,58,59,60}, + new int[] {4,7,8,9,16,17,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,49,50,51,53,55,56,57,58,59,60}, + new int[] {4,7,9,11,12,13,14,15,16,17,18,22,23,24,33,34,35,36,38,39,42,44,46,48,49,51,53,54,55,56,57}, + new int[] {1,2,3,4,5,6,7,33,34,35,36,37,38,39,40,41,42,54,55,56,57,58,59,60}, + new int[] {1,3,11,14,15,16,17,21,22,23,24,25,33,35,36,37,39,40,41,42,43,44,45,47,48,49,51,52,53,54,55,56,57,58,59,60}, + new int[] {1,2,3,4,5,7,8,9,10,11,13,18,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,44,45,46,50,51,52,53,54,55,56,57,59,60}, + new int[] {24,33,34,35,36,37,38,39,40,41,42,43,45,49,50,51,52,53,54,55,56,57,58,59,60}, + new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,18,19,20,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,51,52,53,55,56,57,58,59,60}, + new int[] {3,4,5,6,13,15,16,17,18,19,21,22,24,25,33,34,35,36,37,39,40,41,42,43,44,45,47,52,53,55,57,58,59,60}, + new int[] {4,5,6,8,11,12,13,14,17,19,20,22,23,24,25,33,34,35,36,37,39,40,41,42,43,47,48,49,50,51,52,55,56,57}, + new int[] {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60}, + new int[] {12,25,33,35,36,37,39,41,42,43,48,51,52,53,54,57,59,60}, + new int[] {4,8,16,17,19,23,25,33,34,35,37,41,44,45,47,48,49,50}, + new int[] {3,23,24,25,33,35,36,37,38,39,40,42,43,44,49,50,53,54,55,56,57,58,60}, + new int[] {7,13,19,20,22,23,24,25,33,34,35,38,40,41,42,44,45,46,47,48,49,52,53,54,58,59,60} + }; + // how long each talk lasts for each speaker + int[] durations = { 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + int sum_of_durations = durations.Sum(); + int number_of_speakers = durations.Length; + + // calculate the total number of slots (maximum in the availability array) + // (and add the max durations) + int last_slot = + (from s in Enumerable.Range(0, number_of_speakers) + select speaker_availability[s].Max()).Max(); + Console.WriteLine( + $"Scheduling {number_of_speakers} speakers, for a total of " + + $"{sum_of_durations} slots, during [{first_slot}..{last_slot}]"); + + CpModel model = new CpModel(); + + // We store the possible entries (var, start) for all talks filtered + // from the duration and the speaker availability. + List[] entries = new List[number_of_speakers]; + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + entries[speaker] = new List(); + } + + List[] contributions_per_slot = new List[last_slot + 1]; + for (int slot = 1; slot <= last_slot; ++slot) + { + contributions_per_slot[slot] = new List(); + } + + for (int speaker = 0; speaker < number_of_speakers; ++speaker) + { + List all_vars = new List(); + int duration = durations[speaker]; + // Let's filter the possible starts. + int availability = speaker_availability[speaker].Length; + for (int index = 0; index < availability; ++index) + { + bool ok = true; + int slot = speaker_availability[speaker][index]; + if (slot < first_slot) + { + continue; + } + for (int offset = 1; offset < duration; ++offset) + { + if (index + offset >= availability || + speaker_availability[speaker][index + offset] != slot + offset) + { + // discontinuity. + ok = false; + break; + } + } + if (ok) + { + IntVar var = model.NewBoolVar("speaker " + (speaker + 1) + " starts at " + slot); + entries[speaker].Add(new Entry(var, slot)); + all_vars.Add(var); + for (int offset = 0; offset < duration; ++offset) + { + contributions_per_slot[slot + offset].Add(var); + } + } + } + model.Add(LinearExpr.Sum(all_vars) == 1); + } + // Force the schedule to be consistent. + for (int slot = first_slot; slot <= last_slot; ++slot) + { + model.Add(LinearExpr.Sum(contributions_per_slot[slot]) <= 1); + } + + // Creates last_slot. + IntVar last_slot_var = model.NewIntVar( + first_slot + sum_of_durations - 1, last_slot, "last_slot"); + for (int speaker = 0; speaker < number_of_speakers; speaker++) + { + int duration = durations[speaker]; + foreach (Entry e in entries[speaker]) + { + model.Add(last_slot_var >= e.start + duration - 1).OnlyEnforceIf(e.var); + } + } + + model.Minimize(last_slot_var); + + // Creates the solver and solve. + CpSolver solver = new CpSolver(); + solver.StringParameters = "num_search_workers:8"; + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Optimal) + { + Console.WriteLine("\nLast used slot: " + solver.Value(last_slot_var)); + Console.WriteLine("Speakers (start..end):"); + for (int speaker = 0; speaker < number_of_speakers; speaker++) + { + foreach (Entry e in entries[speaker]) + { + if (solver.BooleanValue(e.var)) + { + Console.WriteLine( + " - speaker {0,2}: {1,2}..{2,2}", (speaker + 1), + e.start, (e.start + durations[speaker] - 1)); + + } + } + } + } + + // Statistics. + Console.WriteLine(solver.ResponseStats()); + } + + public static void Main(String[] args) + { + int start = 1; + if (args.Length == 1) + { + start = int.Parse(args[0]); + } + Stopwatch s = new Stopwatch(); + s.Start(); + for (int i = start; i < 40; i++) + { + Solve(i); + } + + s.Stop(); + Console.WriteLine("Finished in " + s.ElapsedMilliseconds + " ms"); + } +} \ No newline at end of file diff --git a/libs/or-tools-bin/examples/dotnet/SpeakerSchedulingSat.csproj b/libs/or-tools-bin/examples/dotnet/SpeakerSchedulingSat.csproj new file mode 100644 index 0000000000000000000000000000000000000000..08be7e2685ddced4762d185ed5056b8c6de91b46 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/SpeakerSchedulingSat.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.SpeakerSchedulingSat + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools/examples/dotnet/StepFunctionSampleSat.cs b/libs/or-tools-bin/examples/dotnet/StepFunctionSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/StepFunctionSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/StepFunctionSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/StepFunctionSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/StepFunctionSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/StepFunctionSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/StepFunctionSampleSat.csproj diff --git a/libs/or-tools/examples/dotnet/StopAfterNSolutionsSampleSat.cs b/libs/or-tools-bin/examples/dotnet/StopAfterNSolutionsSampleSat.cs similarity index 100% rename from libs/or-tools/examples/dotnet/StopAfterNSolutionsSampleSat.cs rename to libs/or-tools-bin/examples/dotnet/StopAfterNSolutionsSampleSat.cs diff --git a/libs/or-tools/examples/dotnet/StopAfterNSolutionsSampleSat.csproj b/libs/or-tools-bin/examples/dotnet/StopAfterNSolutionsSampleSat.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/StopAfterNSolutionsSampleSat.csproj rename to libs/or-tools-bin/examples/dotnet/StopAfterNSolutionsSampleSat.csproj diff --git a/libs/or-tools-bin/examples/dotnet/TaskSchedulingSat.cs b/libs/or-tools-bin/examples/dotnet/TaskSchedulingSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..72a29f02ad2e3c89885989c557b90cb397d7be19 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/TaskSchedulingSat.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using Google.OrTools.Sat; + +class Job { + public Job(List tasks) { + AlternativeTasks = tasks; + } + public Job Successor { get; set; } + public List AlternativeTasks { get; set; } +} + +class Task { + public Task(string name, long duration, long equipment) { + Name = name; + Duration = duration; + Equipment = equipment; + } + + public string Name {get; set;} + public long StartTime {get; set;} + public long EndTime { + get { + return StartTime + Duration; + } + } + public long Duration {get; set;} + public long Equipment { get; set; } + + public override string ToString() { + return Name + " [ " + Equipment + " ]\tstarts: " + StartTime + " ends:" + + EndTime + ", duration: " + Duration; + } +} + +class TaskSchedulingSat { + public static List myJobList = new List(); + public static Dictionary> tasksToEquipment = + new Dictionary>(); + public static Dictionary taskIndexes = + new Dictionary(); + + public static void InitTaskList() { + List taskList = new List(); + taskList.Add(new Task("Job1Task0a", 15, 0)); + taskList.Add(new Task("Job1Task0b", 25, 1)); + taskList.Add(new Task("Job1Task0c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job1Task1a", 25, 0)); + taskList.Add(new Task("Job1Task1b", 30, 1)); + taskList.Add(new Task("Job1Task1c", 40, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job1Task2a", 20, 0)); + taskList.Add(new Task("Job1Task2b", 35, 1)); + taskList.Add(new Task("Job1Task2c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job2Task0a", 15, 0)); + taskList.Add(new Task("Job2Task0b", 25, 1)); + taskList.Add(new Task("Job2Task0c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job2Task1a", 25, 0)); + taskList.Add(new Task("Job2Task1b", 30, 1)); + taskList.Add(new Task("Job2Task1c", 40, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job2Task2a", 20, 0)); + taskList.Add(new Task("Job2Task2b", 35, 1)); + taskList.Add(new Task("Job2Task2c", 10, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job3Task0a", 10, 0)); + taskList.Add(new Task("Job3Task0b", 15, 1)); + taskList.Add(new Task("Job3Task0c", 50, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job3Task1a", 50, 0)); + taskList.Add(new Task("Job3Task1b", 10, 1)); + taskList.Add(new Task("Job3Task1c", 20, 2)); + myJobList.Add(new Job(taskList)); + + taskList = new List(); + taskList.Add(new Task("Job3Task2a", 65, 0)); + taskList.Add(new Task("Job3Task2b", 5, 1)); + taskList.Add(new Task("Job3Task2c", 15, 2)); + myJobList.Add(new Job(taskList)); + + myJobList[0].Successor = myJobList[1]; + myJobList[1].Successor = myJobList[2]; + myJobList[2].Successor = null; + + myJobList[3].Successor = myJobList[4]; + myJobList[4].Successor = myJobList[5]; + myJobList[5].Successor = null; + + myJobList[6].Successor = myJobList[7]; + myJobList[7].Successor = myJobList[8]; + myJobList[8].Successor = null; + } + + public static int GetTaskCount() { + int c = 0; + foreach (Job j in myJobList) + foreach (Task t in j.AlternativeTasks) { + taskIndexes[t.Name] = c; + c++; + } + + return c; + } + + public static int GetEndTaskCount() { + int c = 0; + foreach (Job j in myJobList) + if (j.Successor == null) + c += j.AlternativeTasks.Count; + return c; + } + + static void Main() { + InitTaskList(); + int taskCount = GetTaskCount(); + + CpModel model = new CpModel(); + + IntervalVar[] tasks = new IntervalVar[taskCount]; + IntVar[] taskChoosed = new IntVar[taskCount]; + IntVar[] allEnds = new IntVar[GetEndTaskCount()]; + + int endJobCounter = 0; + foreach (Job j in myJobList) { + IntVar[] tmp = new IntVar[j.AlternativeTasks.Count]; + int i = 0; + foreach (Task t in j.AlternativeTasks) { + long ti = taskIndexes[t.Name]; + taskChoosed[ti] = model.NewBoolVar(t.Name + "_choose"); + tmp[i++] = taskChoosed[ti]; + IntVar start = model.NewIntVar(0, 10000, t.Name + "_start"); + IntVar end = model.NewIntVar(0, 10000, t.Name + "_end"); + tasks[ti] = model.NewIntervalVar(start, t.Duration, end, t.Name + "_interval"); + if (j.Successor == null) + allEnds[endJobCounter++] = end; + if (!tasksToEquipment.ContainsKey(t.Equipment)) + tasksToEquipment[t.Equipment] = new List(); + tasksToEquipment[t.Equipment].Add(tasks[ti]); + } + model.Add(LinearExpr.Sum(tmp) == 1); + } + + foreach (KeyValuePair> pair in tasksToEquipment) { + model.AddNoOverlap(pair.Value); + } + + IntVar makespan = model.NewIntVar(0, 100000, "makespan"); + model.AddMaxEquality(makespan, allEnds); + model.Minimize(makespan); + + // Create the solver. + CpSolver solver = new CpSolver(); + // Solve the problem. + solver.Solve(model); + Console.WriteLine(solver.ResponseStats()); + } +} + diff --git a/libs/or-tools-bin/examples/dotnet/TaskSchedulingSat.csproj b/libs/or-tools-bin/examples/dotnet/TaskSchedulingSat.csproj new file mode 100644 index 0000000000000000000000000000000000000000..87799b6bc0540198b07d75f3c9eda9379b1df9fd --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/TaskSchedulingSat.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.TaskSchedulingSat + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools/examples/dotnet/Tsp.cs b/libs/or-tools-bin/examples/dotnet/Tsp.cs similarity index 100% rename from libs/or-tools/examples/dotnet/Tsp.cs rename to libs/or-tools-bin/examples/dotnet/Tsp.cs diff --git a/libs/or-tools/examples/dotnet/Tsp.csproj b/libs/or-tools-bin/examples/dotnet/Tsp.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/Tsp.csproj rename to libs/or-tools-bin/examples/dotnet/Tsp.csproj diff --git a/libs/or-tools/examples/dotnet/TspCircuitBoard.cs b/libs/or-tools-bin/examples/dotnet/TspCircuitBoard.cs similarity index 100% rename from libs/or-tools/examples/dotnet/TspCircuitBoard.cs rename to libs/or-tools-bin/examples/dotnet/TspCircuitBoard.cs diff --git a/libs/or-tools/examples/dotnet/TspCircuitBoard.csproj b/libs/or-tools-bin/examples/dotnet/TspCircuitBoard.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/TspCircuitBoard.csproj rename to libs/or-tools-bin/examples/dotnet/TspCircuitBoard.csproj diff --git a/libs/or-tools/examples/dotnet/TspCities.cs b/libs/or-tools-bin/examples/dotnet/TspCities.cs similarity index 100% rename from libs/or-tools/examples/dotnet/TspCities.cs rename to libs/or-tools-bin/examples/dotnet/TspCities.cs diff --git a/libs/or-tools/examples/dotnet/TspCities.csproj b/libs/or-tools-bin/examples/dotnet/TspCities.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/TspCities.csproj rename to libs/or-tools-bin/examples/dotnet/TspCities.csproj diff --git a/libs/or-tools/examples/dotnet/TspDistanceMatrix.cs b/libs/or-tools-bin/examples/dotnet/TspDistanceMatrix.cs similarity index 100% rename from libs/or-tools/examples/dotnet/TspDistanceMatrix.cs rename to libs/or-tools-bin/examples/dotnet/TspDistanceMatrix.cs diff --git a/libs/or-tools/examples/dotnet/TspDistanceMatrix.csproj b/libs/or-tools-bin/examples/dotnet/TspDistanceMatrix.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/TspDistanceMatrix.csproj rename to libs/or-tools-bin/examples/dotnet/TspDistanceMatrix.csproj diff --git a/libs/or-tools/examples/dotnet/Vrp.cs b/libs/or-tools-bin/examples/dotnet/Vrp.cs similarity index 100% rename from libs/or-tools/examples/dotnet/Vrp.cs rename to libs/or-tools-bin/examples/dotnet/Vrp.cs diff --git a/libs/or-tools/examples/dotnet/Vrp.csproj b/libs/or-tools-bin/examples/dotnet/Vrp.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/Vrp.csproj rename to libs/or-tools-bin/examples/dotnet/Vrp.csproj diff --git a/libs/or-tools/examples/dotnet/VrpCapacity.cs b/libs/or-tools-bin/examples/dotnet/VrpCapacity.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpCapacity.cs rename to libs/or-tools-bin/examples/dotnet/VrpCapacity.cs diff --git a/libs/or-tools/examples/dotnet/VrpCapacity.csproj b/libs/or-tools-bin/examples/dotnet/VrpCapacity.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpCapacity.csproj rename to libs/or-tools-bin/examples/dotnet/VrpCapacity.csproj diff --git a/libs/or-tools/examples/dotnet/VrpDropNodes.cs b/libs/or-tools-bin/examples/dotnet/VrpDropNodes.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpDropNodes.cs rename to libs/or-tools-bin/examples/dotnet/VrpDropNodes.cs diff --git a/libs/or-tools/examples/dotnet/VrpDropNodes.csproj b/libs/or-tools-bin/examples/dotnet/VrpDropNodes.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpDropNodes.csproj rename to libs/or-tools-bin/examples/dotnet/VrpDropNodes.csproj diff --git a/libs/or-tools/examples/dotnet/VrpGlobalSpan.cs b/libs/or-tools-bin/examples/dotnet/VrpGlobalSpan.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpGlobalSpan.cs rename to libs/or-tools-bin/examples/dotnet/VrpGlobalSpan.cs diff --git a/libs/or-tools/examples/dotnet/VrpGlobalSpan.csproj b/libs/or-tools-bin/examples/dotnet/VrpGlobalSpan.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpGlobalSpan.csproj rename to libs/or-tools-bin/examples/dotnet/VrpGlobalSpan.csproj diff --git a/libs/or-tools/examples/dotnet/VrpInitialRoutes.cs b/libs/or-tools-bin/examples/dotnet/VrpInitialRoutes.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpInitialRoutes.cs rename to libs/or-tools-bin/examples/dotnet/VrpInitialRoutes.cs diff --git a/libs/or-tools/examples/dotnet/VrpInitialRoutes.csproj b/libs/or-tools-bin/examples/dotnet/VrpInitialRoutes.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpInitialRoutes.csproj rename to libs/or-tools-bin/examples/dotnet/VrpInitialRoutes.csproj diff --git a/libs/or-tools/examples/dotnet/VrpPickupDelivery.cs b/libs/or-tools-bin/examples/dotnet/VrpPickupDelivery.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpPickupDelivery.cs rename to libs/or-tools-bin/examples/dotnet/VrpPickupDelivery.cs diff --git a/libs/or-tools/examples/dotnet/VrpPickupDelivery.csproj b/libs/or-tools-bin/examples/dotnet/VrpPickupDelivery.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpPickupDelivery.csproj rename to libs/or-tools-bin/examples/dotnet/VrpPickupDelivery.csproj diff --git a/libs/or-tools/examples/dotnet/VrpPickupDeliveryFifo.cs b/libs/or-tools-bin/examples/dotnet/VrpPickupDeliveryFifo.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpPickupDeliveryFifo.cs rename to libs/or-tools-bin/examples/dotnet/VrpPickupDeliveryFifo.cs diff --git a/libs/or-tools/examples/dotnet/VrpPickupDeliveryFifo.csproj b/libs/or-tools-bin/examples/dotnet/VrpPickupDeliveryFifo.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpPickupDeliveryFifo.csproj rename to libs/or-tools-bin/examples/dotnet/VrpPickupDeliveryFifo.csproj diff --git a/libs/or-tools/examples/dotnet/VrpPickupDeliveryLifo.cs b/libs/or-tools-bin/examples/dotnet/VrpPickupDeliveryLifo.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpPickupDeliveryLifo.cs rename to libs/or-tools-bin/examples/dotnet/VrpPickupDeliveryLifo.cs diff --git a/libs/or-tools/examples/dotnet/VrpPickupDeliveryLifo.csproj b/libs/or-tools-bin/examples/dotnet/VrpPickupDeliveryLifo.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpPickupDeliveryLifo.csproj rename to libs/or-tools-bin/examples/dotnet/VrpPickupDeliveryLifo.csproj diff --git a/libs/or-tools/examples/dotnet/VrpResources.cs b/libs/or-tools-bin/examples/dotnet/VrpResources.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpResources.cs rename to libs/or-tools-bin/examples/dotnet/VrpResources.cs diff --git a/libs/or-tools/examples/dotnet/VrpResources.csproj b/libs/or-tools-bin/examples/dotnet/VrpResources.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpResources.csproj rename to libs/or-tools-bin/examples/dotnet/VrpResources.csproj diff --git a/libs/or-tools/examples/dotnet/VrpStartsEnds.cs b/libs/or-tools-bin/examples/dotnet/VrpStartsEnds.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpStartsEnds.cs rename to libs/or-tools-bin/examples/dotnet/VrpStartsEnds.cs diff --git a/libs/or-tools/examples/dotnet/VrpStartsEnds.csproj b/libs/or-tools-bin/examples/dotnet/VrpStartsEnds.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpStartsEnds.csproj rename to libs/or-tools-bin/examples/dotnet/VrpStartsEnds.csproj diff --git a/libs/or-tools/examples/dotnet/VrpTimeWindows.cs b/libs/or-tools-bin/examples/dotnet/VrpTimeWindows.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpTimeWindows.cs rename to libs/or-tools-bin/examples/dotnet/VrpTimeWindows.cs diff --git a/libs/or-tools/examples/dotnet/VrpTimeWindows.csproj b/libs/or-tools-bin/examples/dotnet/VrpTimeWindows.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpTimeWindows.csproj rename to libs/or-tools-bin/examples/dotnet/VrpTimeWindows.csproj diff --git a/libs/or-tools/examples/dotnet/VrpWithTimeLimit.cs b/libs/or-tools-bin/examples/dotnet/VrpWithTimeLimit.cs similarity index 100% rename from libs/or-tools/examples/dotnet/VrpWithTimeLimit.cs rename to libs/or-tools-bin/examples/dotnet/VrpWithTimeLimit.cs diff --git a/libs/or-tools/examples/dotnet/VrpWithTimeLimit.csproj b/libs/or-tools-bin/examples/dotnet/VrpWithTimeLimit.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/VrpWithTimeLimit.csproj rename to libs/or-tools-bin/examples/dotnet/VrpWithTimeLimit.csproj diff --git a/libs/or-tools/examples/dotnet/a_puzzle.cs b/libs/or-tools-bin/examples/dotnet/a_puzzle.cs similarity index 100% rename from libs/or-tools/examples/dotnet/a_puzzle.cs rename to libs/or-tools-bin/examples/dotnet/a_puzzle.cs diff --git a/libs/or-tools/examples/dotnet/a_puzzle.csproj b/libs/or-tools-bin/examples/dotnet/a_puzzle.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/a_puzzle.csproj rename to libs/or-tools-bin/examples/dotnet/a_puzzle.csproj diff --git a/libs/or-tools/examples/dotnet/a_round_of_golf.cs b/libs/or-tools-bin/examples/dotnet/a_round_of_golf.cs similarity index 100% rename from libs/or-tools/examples/dotnet/a_round_of_golf.cs rename to libs/or-tools-bin/examples/dotnet/a_round_of_golf.cs diff --git a/libs/or-tools/examples/dotnet/a_round_of_golf.csproj b/libs/or-tools-bin/examples/dotnet/a_round_of_golf.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/a_round_of_golf.csproj rename to libs/or-tools-bin/examples/dotnet/a_round_of_golf.csproj diff --git a/libs/or-tools/examples/dotnet/all_interval.cs b/libs/or-tools-bin/examples/dotnet/all_interval.cs similarity index 100% rename from libs/or-tools/examples/dotnet/all_interval.cs rename to libs/or-tools-bin/examples/dotnet/all_interval.cs diff --git a/libs/or-tools/examples/dotnet/all_interval.csproj b/libs/or-tools-bin/examples/dotnet/all_interval.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/all_interval.csproj rename to libs/or-tools-bin/examples/dotnet/all_interval.csproj diff --git a/libs/or-tools/examples/dotnet/alldifferent_except_0.cs b/libs/or-tools-bin/examples/dotnet/alldifferent_except_0.cs similarity index 100% rename from libs/or-tools/examples/dotnet/alldifferent_except_0.cs rename to libs/or-tools-bin/examples/dotnet/alldifferent_except_0.cs diff --git a/libs/or-tools/examples/dotnet/alldifferent_except_0.csproj b/libs/or-tools-bin/examples/dotnet/alldifferent_except_0.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/alldifferent_except_0.csproj rename to libs/or-tools-bin/examples/dotnet/alldifferent_except_0.csproj diff --git a/libs/or-tools/examples/dotnet/assignment.cs b/libs/or-tools-bin/examples/dotnet/assignment.cs similarity index 100% rename from libs/or-tools/examples/dotnet/assignment.cs rename to libs/or-tools-bin/examples/dotnet/assignment.cs diff --git a/libs/or-tools/examples/dotnet/assignment.csproj b/libs/or-tools-bin/examples/dotnet/assignment.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/assignment.csproj rename to libs/or-tools-bin/examples/dotnet/assignment.csproj diff --git a/libs/or-tools/examples/dotnet/broken_weights.cs b/libs/or-tools-bin/examples/dotnet/broken_weights.cs similarity index 100% rename from libs/or-tools/examples/dotnet/broken_weights.cs rename to libs/or-tools-bin/examples/dotnet/broken_weights.cs diff --git a/libs/or-tools/examples/dotnet/broken_weights.csproj b/libs/or-tools-bin/examples/dotnet/broken_weights.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/broken_weights.csproj rename to libs/or-tools-bin/examples/dotnet/broken_weights.csproj diff --git a/libs/or-tools/examples/dotnet/bus_schedule.cs b/libs/or-tools-bin/examples/dotnet/bus_schedule.cs similarity index 100% rename from libs/or-tools/examples/dotnet/bus_schedule.cs rename to libs/or-tools-bin/examples/dotnet/bus_schedule.cs diff --git a/libs/or-tools/examples/dotnet/bus_schedule.csproj b/libs/or-tools-bin/examples/dotnet/bus_schedule.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/bus_schedule.csproj rename to libs/or-tools-bin/examples/dotnet/bus_schedule.csproj diff --git a/libs/or-tools/examples/dotnet/circuit.cs b/libs/or-tools-bin/examples/dotnet/circuit.cs similarity index 100% rename from libs/or-tools/examples/dotnet/circuit.cs rename to libs/or-tools-bin/examples/dotnet/circuit.cs diff --git a/libs/or-tools/examples/dotnet/circuit.csproj b/libs/or-tools-bin/examples/dotnet/circuit.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/circuit.csproj rename to libs/or-tools-bin/examples/dotnet/circuit.csproj diff --git a/libs/or-tools/examples/dotnet/circuit2.cs b/libs/or-tools-bin/examples/dotnet/circuit2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/circuit2.cs rename to libs/or-tools-bin/examples/dotnet/circuit2.cs diff --git a/libs/or-tools/examples/dotnet/circuit2.csproj b/libs/or-tools-bin/examples/dotnet/circuit2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/circuit2.csproj rename to libs/or-tools-bin/examples/dotnet/circuit2.csproj diff --git a/libs/or-tools/examples/dotnet/coins3.cs b/libs/or-tools-bin/examples/dotnet/coins3.cs similarity index 100% rename from libs/or-tools/examples/dotnet/coins3.cs rename to libs/or-tools-bin/examples/dotnet/coins3.cs diff --git a/libs/or-tools/examples/dotnet/coins3.csproj b/libs/or-tools-bin/examples/dotnet/coins3.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/coins3.csproj rename to libs/or-tools-bin/examples/dotnet/coins3.csproj diff --git a/libs/or-tools/examples/dotnet/coins_grid.cs b/libs/or-tools-bin/examples/dotnet/coins_grid.cs similarity index 100% rename from libs/or-tools/examples/dotnet/coins_grid.cs rename to libs/or-tools-bin/examples/dotnet/coins_grid.cs diff --git a/libs/or-tools/examples/dotnet/coins_grid.csproj b/libs/or-tools-bin/examples/dotnet/coins_grid.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/coins_grid.csproj rename to libs/or-tools-bin/examples/dotnet/coins_grid.csproj diff --git a/libs/or-tools/examples/dotnet/combinatorial_auction2.cs b/libs/or-tools-bin/examples/dotnet/combinatorial_auction2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/combinatorial_auction2.cs rename to libs/or-tools-bin/examples/dotnet/combinatorial_auction2.cs diff --git a/libs/or-tools/examples/dotnet/combinatorial_auction2.csproj b/libs/or-tools-bin/examples/dotnet/combinatorial_auction2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/combinatorial_auction2.csproj rename to libs/or-tools-bin/examples/dotnet/combinatorial_auction2.csproj diff --git a/libs/or-tools/examples/dotnet/contiguity_regular.cs b/libs/or-tools-bin/examples/dotnet/contiguity_regular.cs similarity index 100% rename from libs/or-tools/examples/dotnet/contiguity_regular.cs rename to libs/or-tools-bin/examples/dotnet/contiguity_regular.cs diff --git a/libs/or-tools/examples/dotnet/contiguity_regular.csproj b/libs/or-tools-bin/examples/dotnet/contiguity_regular.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/contiguity_regular.csproj rename to libs/or-tools-bin/examples/dotnet/contiguity_regular.csproj diff --git a/libs/or-tools/examples/dotnet/contiguity_transition.cs b/libs/or-tools-bin/examples/dotnet/contiguity_transition.cs similarity index 100% rename from libs/or-tools/examples/dotnet/contiguity_transition.cs rename to libs/or-tools-bin/examples/dotnet/contiguity_transition.cs diff --git a/libs/or-tools/examples/dotnet/contiguity_transition.csproj b/libs/or-tools-bin/examples/dotnet/contiguity_transition.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/contiguity_transition.csproj rename to libs/or-tools-bin/examples/dotnet/contiguity_transition.csproj diff --git a/libs/or-tools/examples/dotnet/costas_array.cs b/libs/or-tools-bin/examples/dotnet/costas_array.cs similarity index 100% rename from libs/or-tools/examples/dotnet/costas_array.cs rename to libs/or-tools-bin/examples/dotnet/costas_array.cs diff --git a/libs/or-tools/examples/dotnet/costas_array.csproj b/libs/or-tools-bin/examples/dotnet/costas_array.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/costas_array.csproj rename to libs/or-tools-bin/examples/dotnet/costas_array.csproj diff --git a/libs/or-tools/examples/dotnet/covering_opl.cs b/libs/or-tools-bin/examples/dotnet/covering_opl.cs similarity index 100% rename from libs/or-tools/examples/dotnet/covering_opl.cs rename to libs/or-tools-bin/examples/dotnet/covering_opl.cs diff --git a/libs/or-tools/examples/dotnet/covering_opl.csproj b/libs/or-tools-bin/examples/dotnet/covering_opl.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/covering_opl.csproj rename to libs/or-tools-bin/examples/dotnet/covering_opl.csproj diff --git a/libs/or-tools/examples/dotnet/crew.cs b/libs/or-tools-bin/examples/dotnet/crew.cs similarity index 100% rename from libs/or-tools/examples/dotnet/crew.cs rename to libs/or-tools-bin/examples/dotnet/crew.cs diff --git a/libs/or-tools/examples/dotnet/crew.csproj b/libs/or-tools-bin/examples/dotnet/crew.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/crew.csproj rename to libs/or-tools-bin/examples/dotnet/crew.csproj diff --git a/libs/or-tools/examples/dotnet/crossword.cs b/libs/or-tools-bin/examples/dotnet/crossword.cs similarity index 100% rename from libs/or-tools/examples/dotnet/crossword.cs rename to libs/or-tools-bin/examples/dotnet/crossword.cs diff --git a/libs/or-tools/examples/dotnet/crossword.csproj b/libs/or-tools-bin/examples/dotnet/crossword.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/crossword.csproj rename to libs/or-tools-bin/examples/dotnet/crossword.csproj diff --git a/libs/or-tools/examples/dotnet/crypta.cs b/libs/or-tools-bin/examples/dotnet/crypta.cs similarity index 100% rename from libs/or-tools/examples/dotnet/crypta.cs rename to libs/or-tools-bin/examples/dotnet/crypta.cs diff --git a/libs/or-tools/examples/dotnet/crypta.csproj b/libs/or-tools-bin/examples/dotnet/crypta.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/crypta.csproj rename to libs/or-tools-bin/examples/dotnet/crypta.csproj diff --git a/libs/or-tools/examples/dotnet/crypto.cs b/libs/or-tools-bin/examples/dotnet/crypto.cs similarity index 100% rename from libs/or-tools/examples/dotnet/crypto.cs rename to libs/or-tools-bin/examples/dotnet/crypto.cs diff --git a/libs/or-tools/examples/dotnet/crypto.csproj b/libs/or-tools-bin/examples/dotnet/crypto.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/crypto.csproj rename to libs/or-tools-bin/examples/dotnet/crypto.csproj diff --git a/libs/or-tools-bin/examples/dotnet/cscvrptw.cs b/libs/or-tools-bin/examples/dotnet/cscvrptw.cs new file mode 100644 index 0000000000000000000000000000000000000000..55be6c2dd6f5f61620acc7f7dd7aebe954662da3 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/cscvrptw.cs @@ -0,0 +1,368 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +/// +/// 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 { + + /// + /// A position on the map with (x, y) coordinates. + /// + class Position { + public Position() { + this.x_ = 0; + this.y_ = 0; + } + + public Position(int x, int y) { + this.x_ = x; + this.y_ = y; + } + + public int x_; + public int y_; + } + + /// + /// A time window with start/end data. + /// + class TimeWindow { + public TimeWindow() { + this.start_ = -1; + this.end_ = -1; + } + + public TimeWindow(int start, int end) { + this.start_ = start; + this.end_ = end; + } + + public int start_; + public int end_; + } + + /// + /// Manhattan distance implemented as a callback. It uses an array of + /// positions and computes the Manhattan distance between the two + /// positions of two different indices. + /// + class Manhattan { + public Manhattan( + RoutingIndexManager manager, + Position[] locations, + int coefficient) { + this.manager_ = manager; + this.locations_ = locations; + this.coefficient_ = coefficient; + } + + public long Call(long first_index, long second_index) { + if (first_index >= locations_.Length || + second_index >= locations_.Length) { + return 0; + } + 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 readonly RoutingIndexManager manager_; + private readonly Position[] locations_; + private readonly int coefficient_; + }; + + /// + /// A callback that computes the volume of a demand stored in an + /// integer array. + /// + class Demand { + public Demand( + RoutingIndexManager manager, + int[] order_demands) { + this.manager_ = manager; + this.order_demands_ = order_demands; + } + + public long Call(long index) { + if (index < order_demands_.Length) { + int node = manager_.IndexToNode(index); + return order_demands_[node]; + } + return 0; + } + + private readonly RoutingIndexManager manager_; + private readonly int[] order_demands_; + }; + + /// Locations representing either an order location or a vehicle route + /// start/end. + private Position[] locations_; + /// Quantity to be picked up for each order. + private int[] order_demands_; + /// Time window in which each order must be performed. + private TimeWindow[] order_time_windows_; + /// Penalty cost "paid" for dropping an order. + private int[] order_penalties_; + /// Capacity of the vehicles. + private int vehicle_capacity_ = 0; + /// Latest time at which each vehicle must end its tour. + private int[] vehicle_end_time_; + /// Cost per unit of distance of each vehicle. + private int[] vehicle_cost_coefficients_; + /// Vehicle start and end indices. They have to be implemented as int[] due + /// to the available SWIG-ed interface. + private int[] vehicle_starts_; + private int[] vehicle_ends_; + + /// Random number generator to produce data. + private Random random_generator = new Random(0xBEEF); + + /// + /// Constructs a capacitated vehicle routing problem with time windows. + /// + private CapacitatedVehicleRoutingProblemWithTimeWindows() {} + + /// + /// Creates order data. Location of the order is random, as well + /// as its demand (quantity), time window and penalty. /// + /// + /// number of orders to build. + /// maximum x coordinate in which orders are located. + /// + /// maximum y coordinate in which orders are located. + /// + /// maximum quantity of a demand. + /// maximum starting time of the order time + /// window. + /// duration of the order time window. + /// + /// minimum pernalty cost if order is dropped. + /// + /// maximum pernalty cost if order is dropped. + /// + private void BuildOrders(int number_of_orders, + int number_of_vehicles, + int x_max, int y_max, + int demand_max, + int time_window_max, + int time_window_width, + int penalty_min, + int penalty_max) { + Console.WriteLine("Building orders."); + locations_ = new Position[number_of_orders + 2 * number_of_vehicles]; + order_demands_ = new int[number_of_orders]; + order_time_windows_ = new TimeWindow[number_of_orders]; + order_penalties_ = new int[number_of_orders]; + for (int order = 0; order < number_of_orders; ++order) { + locations_[order] = + new Position(random_generator.Next(x_max + 1), + random_generator.Next(y_max + 1)); + order_demands_[order] = random_generator.Next(demand_max + 1); + int time_window_start = random_generator.Next(time_window_max + 1); + order_time_windows_[order] = + new TimeWindow(time_window_start, + time_window_start + time_window_width); + order_penalties_[order] = + random_generator.Next(penalty_max - penalty_min + 1) + penalty_min; + } + } + + /// + /// Creates fleet data. Vehicle starting and ending locations are + /// random, as well as vehicle costs per distance unit. + /// + /// + /// number of orders + /// number of vehicles + /// maximum x coordinate in which orders are located. + /// + /// maximum y coordinate in which orders are located. + /// + /// latest end time of a tour of a vehicle. + /// capacity of a vehicle. + /// maximum cost per distance unit of a + /// vehicle (minimum is 1) + private void BuildFleet(int number_of_orders, + int number_of_vehicles, + int x_max, int y_max, + int end_time, + int capacity, + int cost_coefficient_max) { + Console.WriteLine("Building fleet."); + vehicle_capacity_ = capacity; + vehicle_starts_ = new int[number_of_vehicles]; + vehicle_ends_ = new int[number_of_vehicles]; + vehicle_end_time_ = new int[number_of_vehicles]; + vehicle_cost_coefficients_ = new int[number_of_vehicles]; + for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { + int index = 2 * vehicle + number_of_orders; + vehicle_starts_[vehicle] = index; + locations_[index] = + new Position(random_generator.Next(x_max + 1), + random_generator.Next(y_max + 1)); + vehicle_ends_[vehicle] = index + 1; + locations_[index + 1] = + new Position(random_generator.Next(x_max + 1), + random_generator.Next(y_max + 1)); + vehicle_end_time_[vehicle] = end_time; + vehicle_cost_coefficients_[vehicle] = + random_generator.Next(cost_coefficient_max) + 1; + } + } + + /// + /// Solves the current routing problem. + /// + private void Solve(int number_of_orders, int number_of_vehicles) { + Console.WriteLine("Creating model with " + number_of_orders + + " orders and " + number_of_vehicles + " vehicles."); + // Finalizing model + int number_of_locations = locations_.Length; + + 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; + Manhattan manhattan_callback = new Manhattan(manager, locations_, 1); + model.AddDimension( + model.RegisterTransitCallback(manhattan_callback.Call), + big_number, big_number, false, "time"); + RoutingDimension time_dimension = model.GetDimensionOrDie("time"); + + Demand demand_callback = new Demand(manager, order_demands_); + model.AddDimension(model.RegisterUnaryTransitCallback(demand_callback.Call), + 0, vehicle_capacity_, true, "capacity"); + RoutingDimension capacity_dimension = model.GetDimensionOrDie("capacity"); + + // Setting up vehicles + Manhattan[] cost_callbacks = new Manhattan[number_of_vehicles]; + for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { + int cost_coefficient = vehicle_cost_coefficients_[vehicle]; + Manhattan manhattan_cost_callback = + new Manhattan(manager, locations_, cost_coefficient); + cost_callbacks[vehicle] = manhattan_cost_callback; + int manhattan_cost_index = + model.RegisterTransitCallback(manhattan_cost_callback.Call); + 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) { + time_dimension.CumulVar(order).SetRange(order_time_windows_[order].start_, + order_time_windows_[order].end_); + long[] orders = {manager.NodeToIndex(order)}; + model.AddDisjunction(orders, order_penalties_[order]); + } + + // Solving + RoutingSearchParameters search_parameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + search_parameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.AllUnperformed; + + Console.WriteLine("Search..."); + Assignment solution = model.SolveWithParameters(search_parameters); + + if (solution != null) { + String output = "Total cost: " + solution.ObjectiveValue() + "\n"; + // Dropped orders + String dropped = ""; + for (int order = 0; order < number_of_orders; ++order) { + if (solution.Value(model.NextVar(order)) == order) { + dropped += " " + order; + } + } + if (dropped.Length > 0) { + output += "Dropped orders:" + dropped + "\n"; + } + // Routes + for (int vehicle = 0; vehicle < number_of_vehicles; ++vehicle) { + String route = "Vehicle " + vehicle + ": "; + long order = model.Start(vehicle); + if (model.IsEnd(solution.Value(model.NextVar(order)))) { + route += "Empty"; + } else { + for (; + !model.IsEnd(order); + order = solution.Value(model.NextVar(order))) { + 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 = capacity_dimension.CumulVar(order); + IntVar time = time_dimension.CumulVar(order); + route += order + " Load(" + solution.Value(load) + ") " + + "Time(" + solution.Min(time) + ", " + solution.Max(time) + ")"; + } + output += route + "\n"; + } + Console.WriteLine(output); + } + } + + + public static void Main(String[] args) + { + CapacitatedVehicleRoutingProblemWithTimeWindows problem = + new CapacitatedVehicleRoutingProblemWithTimeWindows(); + int x_max = 20; + int y_max = 20; + int demand_max = 3; + int time_window_max = 24 * 60; + int time_window_width = 4 * 60; + int penalty_min = 50; + int penalty_max = 100; + int end_time = 24 * 60; + int cost_coefficient_max = 3; + + int orders = 100; + int vehicles = 20; + int capacity = 50; + + problem.BuildOrders(orders, + vehicles, + x_max, + y_max, + demand_max, + time_window_max, + time_window_width, + penalty_min, + penalty_max); + problem.BuildFleet(orders, + vehicles, + x_max, + y_max, + end_time, + capacity, + cost_coefficient_max); + problem.Solve(orders, vehicles); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/cscvrptw.csproj b/libs/or-tools-bin/examples/dotnet/cscvrptw.csproj new file mode 100644 index 0000000000000000000000000000000000000000..060eeb062e4885913c9c6f8d60751b617313118e --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/cscvrptw.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.cscvrptw + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools/examples/dotnet/csdiet.cs b/libs/or-tools-bin/examples/dotnet/csdiet.cs similarity index 100% rename from libs/or-tools/examples/dotnet/csdiet.cs rename to libs/or-tools-bin/examples/dotnet/csdiet.cs diff --git a/libs/or-tools/examples/dotnet/csdiet.csproj b/libs/or-tools-bin/examples/dotnet/csdiet.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/csdiet.csproj rename to libs/or-tools-bin/examples/dotnet/csdiet.csproj diff --git a/libs/or-tools-bin/examples/dotnet/csflow.cs b/libs/or-tools-bin/examples/dotnet/csflow.cs new file mode 100644 index 0000000000000000000000000000000000000000..d327f639dfe182ee47ac2fc971d3f5c5f7ff2ccd --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/csflow.cs @@ -0,0 +1,108 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using Google.OrTools.Graph; + +public class CsFlow +{ + private static void SolveMaxFlow() + { + Console.WriteLine("Max Flow Problem"); + int numNodes = 6; + int numArcs = 9; + int[] tails = {0, 0, 0, 0, 1, 2, 3, 3, 4}; + int[] heads = {1, 2, 3, 4, 3, 4, 4, 5, 5}; + int[] capacities = {5, 8, 5, 3, 4, 5, 6, 6, 4}; + int[] expectedFlows = {4, 4, 2, 0, 4, 4, 0, 6, 4}; + int expectedTotalFlow = 10; + MaxFlow maxFlow = new MaxFlow(); + for (int i = 0; i < numArcs; ++i) + { + int arc = maxFlow.AddArcWithCapacity(tails[i], heads[i], capacities[i]); + if (arc != i) throw new Exception("Internal error"); + } + int source = 0; + int sink = numNodes - 1; + Console.WriteLine("Solving max flow with " + numNodes + " nodes, and " + + numArcs + " arcs, source=" + source + ", sink=" + sink); + MaxFlow.Status solveStatus = maxFlow.Solve(source, sink); + if (solveStatus == MaxFlow.Status.OPTIMAL) + { + long totalFlow = maxFlow.OptimalFlow(); + Console.WriteLine("total computed flow " + totalFlow + + ", expected = " + expectedTotalFlow); + for (int i = 0; i < numArcs; ++i) + { + Console.WriteLine("Arc " + i + " (" + maxFlow.Head(i) + " -> " + + maxFlow.Tail(i) + "), capacity = " + + maxFlow.Capacity(i) + ") computed = " + + maxFlow.Flow(i) + ", expected = " + expectedFlows[i]); + } + } + else + { + Console.WriteLine("Solving the max flow problem failed. Solver status: " + + solveStatus); + } + } + + private static void SolveMinCostFlow() + { + Console.WriteLine("Min Cost Flow Problem"); + int numSources = 4; + int numTargets = 4; + int[,] costs = { {90, 75, 75, 80}, + {35, 85, 55, 65}, + {125, 95, 90, 105}, + {45, 110, 95, 115} }; + int expectedCost = 275; + MinCostFlow minCostFlow = new MinCostFlow(); + for (int source = 0; source < numSources; ++source) + { + for (int target = 0; target < numTargets; ++target) { + minCostFlow.AddArcWithCapacityAndUnitCost( + source, /*target=*/numSources + target, /*capacity=*/1, + /*flow unit cost=*/costs[source, target]); + } + } + for (int source = 0; source < numSources; ++source) + { + minCostFlow.SetNodeSupply(source, 1); + } + for (int target = 0; target < numTargets; ++target) + { + minCostFlow.SetNodeSupply(numSources + target, -1); + } + Console.WriteLine("Solving min cost flow with " + numSources + + " sources, and " + numTargets + " targets."); + MinCostFlow.Status solveStatus = minCostFlow.Solve(); + if (solveStatus == MinCostFlow.Status.OPTIMAL) + { + Console.WriteLine("total computed flow cost = " + + minCostFlow.OptimalCost() + + ", expected = " + expectedCost); + } + else + { + Console.WriteLine("Solving the min cost flow problem failed." + + " Solver status: " + solveStatus); + } + } + + static void Main() + { + SolveMaxFlow(); + SolveMinCostFlow(); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/csflow.csproj b/libs/or-tools-bin/examples/dotnet/csflow.csproj new file mode 100644 index 0000000000000000000000000000000000000000..778848fe3057085b79609d6bafa03c9abfaf0f29 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/csflow.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.csflow + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools-bin/examples/dotnet/csintegerprogramming.cs b/libs/or-tools-bin/examples/dotnet/csintegerprogramming.cs new file mode 100644 index 0000000000000000000000000000000000000000..c8e2181f39bd76409f674f37ea4d5a38c503464c --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/csintegerprogramming.cs @@ -0,0 +1,124 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using Google.OrTools.LinearSolver; + +public class CsIntegerProgramming +{ + private static void RunIntegerProgrammingExample(String solverType) + { + Solver solver = Solver.CreateSolver("IntegerProgramming", solverType); + if (solver == null) + { + Console.WriteLine("Could not create solver " + solverType); + return; + } + // x1 and x2 are integer non-negative variables. + Variable x1 = solver.MakeIntVar(0.0, double.PositiveInfinity, "x1"); + Variable x2 = solver.MakeIntVar(0.0, double.PositiveInfinity, "x2"); + + // Minimize x1 + 2 * x2. + Objective objective = solver.Objective(); + objective.SetMinimization(); + objective.SetCoefficient(x1, 1); + objective.SetCoefficient(x2, 2); + + // 2 * x2 + 3 * x1 >= 17. + Constraint ct = solver.MakeConstraint(17, double.PositiveInfinity); + ct.SetCoefficient(x1, 3); + ct.SetCoefficient(x2, 2); + + Solver.ResultStatus resultStatus = solver.Solve(); + + // Check that the problem has an optimal solution. + if (resultStatus != Solver.ResultStatus.OPTIMAL) + { + Console.WriteLine("The problem does not have an optimal solution!"); + return; + } + + Console.WriteLine("Problem solved in " + solver.WallTime() + + " milliseconds"); + + // The objective value of the solution. + Console.WriteLine("Optimal objective value = " + objective.Value()); + + // The value of each variable in the solution. + Console.WriteLine("x1 = " + x1.SolutionValue()); + Console.WriteLine("x2 = " + x2.SolutionValue()); + + Console.WriteLine("Advanced usage:"); + Console.WriteLine("Problem solved in " + solver.Nodes() + + " branch-and-bound nodes"); + } + + private static void RunIntegerProgrammingExampleNaturalApi(String solverType) + { + Solver solver = Solver.CreateSolver("IntegerProgramming", solverType); + if (solver == null) + { + Console.WriteLine("Could not create solver " + solverType); + return; + } + // x1 and x2 are integer non-negative variables. + Variable x1 = solver.MakeIntVar(0.0, double.PositiveInfinity, "x1"); + Variable x2 = solver.MakeIntVar(0.0, double.PositiveInfinity, "x2"); + + solver.Minimize(x1 + 2 * x2); + solver.Add(2 * x2 + 3 * x1 >= 17); + + Solver.ResultStatus resultStatus = solver.Solve(); + + // Check that the problem has an optimal solution. + if (resultStatus != Solver.ResultStatus.OPTIMAL) + { + Console.WriteLine("The problem does not have an optimal solution!"); + return; + } + + Console.WriteLine("Problem solved in " + solver.WallTime() + + " milliseconds"); + + // The objective value of the solution. + Console.WriteLine("Optimal objective value = " + + solver.Objective().Value()); + + // The value of each variable in the solution. + Console.WriteLine("x1 = " + x1.SolutionValue()); + Console.WriteLine("x2 = " + x2.SolutionValue()); + + Console.WriteLine("Advanced usage:"); + Console.WriteLine("Problem solved in " + solver.Nodes() + + " branch-and-bound nodes"); + } + + static void Main() + { + Console.WriteLine("---- Integer programming example with GLPK ----"); + RunIntegerProgrammingExample("GLPK_MIXED_INTEGER_PROGRAMMING"); + Console.WriteLine("---- Linear programming example with CBC ----"); + RunIntegerProgrammingExample("CBC_MIXED_INTEGER_PROGRAMMING"); + Console.WriteLine("---- Linear programming example with SCIP ----"); + RunIntegerProgrammingExample("SCIP_MIXED_INTEGER_PROGRAMMING"); + Console.WriteLine( + "---- Integer programming example (Natural API) with GLPK ----"); + RunIntegerProgrammingExampleNaturalApi("GLPK_MIXED_INTEGER_PROGRAMMING"); + Console.WriteLine( + "---- Linear programming example (Natural API) with CBC ----"); + RunIntegerProgrammingExampleNaturalApi("CBC_MIXED_INTEGER_PROGRAMMING"); + Console.WriteLine( + "---- Linear programming example (Natural API) with SCIP ----"); + RunIntegerProgrammingExampleNaturalApi("SCIP_MIXED_INTEGER_PROGRAMMING"); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/csintegerprogramming.csproj b/libs/or-tools-bin/examples/dotnet/csintegerprogramming.csproj new file mode 100644 index 0000000000000000000000000000000000000000..1d6c7c26d8b910dd50753b50b573f5fab7eac9d9 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/csintegerprogramming.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.csintegerprogramming + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools-bin/examples/dotnet/csknapsack.cs b/libs/or-tools-bin/examples/dotnet/csknapsack.cs new file mode 100644 index 0000000000000000000000000000000000000000..445aa24dda49b0808c54025ffb4c9a89f926512e --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/csknapsack.cs @@ -0,0 +1,49 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using Google.OrTools.Algorithms; + +public class CsKnapsack +{ + static void Main() + { + KnapsackSolver solver = new KnapsackSolver( + KnapsackSolver.SolverType.KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, "test"); + 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 }; + + 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 } }; + + long[] capacities = { 850 }; + + long optimalProfit = 7534; + + Console.WriteLine("Solving knapsack with " + profits.Length + + " items, and " + weights.GetLength(0) + " dimension"); + solver.Init(profits, weights, capacities); + long computedProfit = solver.Solve(); + + Console.WriteLine("Optimal Profit = " + computedProfit + ", expected = " + + optimalProfit); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/csknapsack.csproj b/libs/or-tools-bin/examples/dotnet/csknapsack.csproj new file mode 100644 index 0000000000000000000000000000000000000000..55f90001e1a867eaf2c8f26c8b98954066a7bc23 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/csknapsack.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.csknapsack + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools-bin/examples/dotnet/cslinearprogramming.cs b/libs/or-tools-bin/examples/dotnet/cslinearprogramming.cs new file mode 100644 index 0000000000000000000000000000000000000000..28b2128d6840425c7c7f6056420777fb452d6e24 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/cslinearprogramming.cs @@ -0,0 +1,176 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using Google.OrTools.LinearSolver; + +public class CsLinearProgramming +{ + private static void RunLinearProgrammingExample(String solverType) + { + Solver solver = Solver.CreateSolver("IntegerProgramming", solverType); + if (solver == null) + { + Console.WriteLine("Could not create solver " + solverType); + return; + } + // x1, x2 and x3 are continuous non-negative variables. + Variable x1 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x1"); + Variable x2 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x2"); + Variable x3 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x3"); + + // Maximize 10 * x1 + 6 * x2 + 4 * x3. + Objective objective = solver.Objective(); + objective.SetCoefficient(x1, 10); + objective.SetCoefficient(x2, 6); + objective.SetCoefficient(x3, 4); + objective.SetMaximization(); + + // x1 + x2 + x3 <= 100. + Constraint c0 = solver.MakeConstraint(double.NegativeInfinity, 100.0); + c0.SetCoefficient(x1, 1); + c0.SetCoefficient(x2, 1); + c0.SetCoefficient(x3, 1); + + // 10 * x1 + 4 * x2 + 5 * x3 <= 600. + Constraint c1 = solver.MakeConstraint(double.NegativeInfinity, 600.0); + c1.SetCoefficient(x1, 10); + c1.SetCoefficient(x2, 4); + c1.SetCoefficient(x3, 5); + + // 2 * x1 + 2 * x2 + 6 * x3 <= 300. + Constraint c2 = solver.MakeConstraint(double.NegativeInfinity, 300.0); + c2.SetCoefficient(x1, 2); + c2.SetCoefficient(x2, 2); + c2.SetCoefficient(x3, 6); + + Console.WriteLine("Number of variables = " + solver.NumVariables()); + Console.WriteLine("Number of constraints = " + solver.NumConstraints()); + + Solver.ResultStatus resultStatus = solver.Solve(); + + // Check that the problem has an optimal solution. + if (resultStatus != Solver.ResultStatus.OPTIMAL) { + Console.WriteLine("The problem does not have an optimal solution!"); + return; + } + + Console.WriteLine("Problem solved in " + solver.WallTime() + + " milliseconds"); + + // The objective value of the solution. + Console.WriteLine("Optimal objective value = " + + solver.Objective().Value()); + + // The value of each variable in the solution. + Console.WriteLine("x1 = " + x1.SolutionValue()); + Console.WriteLine("x2 = " + x2.SolutionValue()); + Console.WriteLine("x3 = " + x3.SolutionValue()); + + Console.WriteLine("Advanced usage:"); + double[] activities = solver.ComputeConstraintActivities(); + + Console.WriteLine("Problem solved in " + solver.Iterations() + + " iterations"); + Console.WriteLine("x1: reduced cost = " + x1.ReducedCost()); + Console.WriteLine("x2: reduced cost = " + x2.ReducedCost()); + Console.WriteLine("x3: reduced cost = " + x3.ReducedCost()); + Console.WriteLine("c0: dual value = " + c0.DualValue()); + Console.WriteLine(" activity = " + activities[c0.Index()]); + Console.WriteLine("c1: dual value = " + c1.DualValue()); + Console.WriteLine(" activity = " + activities[c1.Index()]); + Console.WriteLine("c2: dual value = " + c2.DualValue()); + Console.WriteLine(" activity = " + activities[c2.Index()]); + } + + private static void RunLinearProgrammingExampleNaturalApi( + String solverType, bool printModel) + { + Solver solver = Solver.CreateSolver("IntegerProgramming", solverType); + if (solver == null) + { + Console.WriteLine("Could not create solver " + solverType); + return; + } + // x1, x2 and x3 are continuous non-negative variables. + Variable x1 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x1"); + Variable x2 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x2"); + Variable x3 = solver.MakeNumVar(0.0, double.PositiveInfinity, "x3"); + + solver.Maximize(10 * x1 + 6 * x2 + 4 * x3); + Constraint c0 = solver.Add(x1 + x2 + x3 <= 100); + Constraint c1 = solver.Add(10 * x1 + x2 * 4 + 5 * x3 <= 600); + Constraint c2 = solver.Add(2 * x1 + 2 * x2 + 6 * x3 <= 300); + + Console.WriteLine("Number of variables = " + solver.NumVariables()); + Console.WriteLine("Number of constraints = " + solver.NumConstraints()); + + if (printModel) { + string model = solver.ExportModelAsLpFormat(false); + Console.WriteLine(model); + } + + Solver.ResultStatus resultStatus = solver.Solve(); + + // Check that the problem has an optimal solution. + if (resultStatus != Solver.ResultStatus.OPTIMAL) { + Console.WriteLine("The problem does not have an optimal solution!"); + return; + } + + Console.WriteLine("Problem solved in " + solver.WallTime() + + " milliseconds"); + + // The objective value of the solution. + Console.WriteLine("Optimal objective value = " + + solver.Objective().Value()); + + // The value of each variable in the solution. + Console.WriteLine("x1 = " + x1.SolutionValue()); + Console.WriteLine("x2 = " + x2.SolutionValue()); + Console.WriteLine("x3 = " + x3.SolutionValue()); + + Console.WriteLine("Advanced usage:"); + double[] activities = solver.ComputeConstraintActivities(); + Console.WriteLine("Problem solved in " + solver.Iterations() + + " iterations"); + Console.WriteLine("x1: reduced cost = " + x1.ReducedCost()); + Console.WriteLine("x2: reduced cost = " + x2.ReducedCost()); + Console.WriteLine("x3: reduced cost = " + x3.ReducedCost()); + Console.WriteLine("c0: dual value = " + c0.DualValue()); + Console.WriteLine(" activity = " + activities[c0.Index()]); + Console.WriteLine("c1: dual value = " + c1.DualValue()); + Console.WriteLine(" activity = " + activities[c1.Index()]); + Console.WriteLine("c2: dual value = " + c2.DualValue()); + Console.WriteLine(" activity = " + activities[c2.Index()]); + } + + static void Main() + { + Console.WriteLine("---- Linear programming example with GLOP ----"); + RunLinearProgrammingExample("GLOP_LINEAR_PROGRAMMING"); + Console.WriteLine("---- Linear programming example with GLPK ----"); + RunLinearProgrammingExample("GLPK_LINEAR_PROGRAMMING"); + Console.WriteLine("---- Linear programming example with CLP ----"); + RunLinearProgrammingExample("CLP_LINEAR_PROGRAMMING"); + Console.WriteLine( + "---- Linear programming example (Natural API) with GLOP ----"); + RunLinearProgrammingExampleNaturalApi("GLOP_LINEAR_PROGRAMMING", true); + Console.WriteLine( + "---- Linear programming example (Natural API) with GLPK ----"); + RunLinearProgrammingExampleNaturalApi("GLPK_LINEAR_PROGRAMMING", false); + Console.WriteLine( + "---- Linear programming example (Natural API) with CLP ----"); + RunLinearProgrammingExampleNaturalApi("CLP_LINEAR_PROGRAMMING", false); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/cslinearprogramming.csproj b/libs/or-tools-bin/examples/dotnet/cslinearprogramming.csproj new file mode 100644 index 0000000000000000000000000000000000000000..6a37b7322a4cdc5bbec65cd36d0f7c3d986b800f --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/cslinearprogramming.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.cslinearprogramming + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools-bin/examples/dotnet/csls_api.cs b/libs/or-tools-bin/examples/dotnet/csls_api.cs new file mode 100644 index 0000000000000000000000000000000000000000..41e2a4bc58dd4d403923bc7c8030b4871d1f218f --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/csls_api.cs @@ -0,0 +1,195 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using Google.OrTools.ConstraintSolver; + +/** + * Shows how to write a custom lns operator. + */ + +public class OneVarLns : BaseLns +{ + public OneVarLns(IntVar[] vars) : base(vars) {} + + public override void InitFragments() + { + index_ = 0; + } + + public override bool NextFragment() + { + int size = Size(); + if (index_ < size) + { + AppendToFragment(index_); + ++index_; + return true; + } + else + { + return false; + } + } + + private int index_; +} + +class MoveOneVar : IntVarLocalSearchOperator { + public MoveOneVar(IntVar[] variables) : base(variables) + { + variable_index_ = 0; + move_up_ = false; + } + + protected override bool MakeOneNeighbor() + { + long current_value = OldValue(variable_index_); + if (move_up_) + { + SetValue(variable_index_, current_value + 1); + variable_index_ = (variable_index_ + 1) % Size(); + } + else + { + SetValue(variable_index_, current_value - 1); + } + move_up_ = !move_up_; + return true; + } + + // Index of the next variable to try to restore + private long variable_index_; + // Direction of the modification. + private bool move_up_; +}; + +public class SumFilter : IntVarLocalSearchFilter { + public SumFilter(IntVar[] vars) : base(vars) + { + sum_ = 0; + } + + protected override void OnSynchronize(Assignment delta) + { + sum_ = 0; + for (int index = 0; index < Size(); ++index) + { + sum_ += Value(index); + } + } + + public override bool Accept(Assignment delta, Assignment unused_deltadelta, + long unused_objective_min, + long unused_objective_max) { + AssignmentIntContainer solution_delta = delta.IntVarContainer(); + int solution_delta_size = solution_delta.Size(); + + for (int i = 0; i < solution_delta_size; ++i) + { + if (!solution_delta.Element(i).Activated()) + { + return true; + } + } + long new_sum = sum_; + for (int index = 0; index < solution_delta_size; ++index) + { + int touched_var = Index(solution_delta.Element(index).Var()); + long old_value = Value(touched_var); + long new_value = solution_delta.Element(index).Value(); + new_sum += new_value - old_value; + } + return new_sum < sum_; + } + + private long sum_; +}; + +public class CsLsApi +{ + private static void BasicLns() + { + Console.WriteLine("BasicLns"); + Solver solver = new Solver("BasicLns"); + IntVar[] vars = solver.MakeIntVarArray(4, 0, 4, "vars"); + IntVar sum_var = vars.Sum().Var(); + OptimizeVar obj = sum_var.Minimize(1); + DecisionBuilder db = solver.MakePhase(vars, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + OneVarLns one_var_lns = new OneVarLns(vars); + LocalSearchPhaseParameters ls_params = + solver.MakeLocalSearchPhaseParameters(sum_var, one_var_lns, db); + DecisionBuilder ls = solver.MakeLocalSearchPhase(vars, db, ls_params); + SolutionCollector collector = solver.MakeLastSolutionCollector(); + collector.AddObjective(sum_var); + SearchMonitor log = solver.MakeSearchLog(1000, obj); + solver.Solve(ls, collector, obj, log); + Console.WriteLine("Objective value = {0}", collector.ObjectiveValue(0)); + } + + private static void BasicLs() + { + Console.WriteLine("BasicLs"); + Solver solver = new Solver("BasicLs"); + IntVar[] vars = solver.MakeIntVarArray(4, 0, 4, "vars"); + IntVar sum_var = vars.Sum().Var(); + OptimizeVar obj = sum_var.Minimize(1); + DecisionBuilder db = solver.MakePhase(vars, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + MoveOneVar move_one_var = new MoveOneVar(vars); + LocalSearchPhaseParameters ls_params = + solver.MakeLocalSearchPhaseParameters(sum_var, move_one_var, db); + DecisionBuilder ls = solver.MakeLocalSearchPhase(vars, db, ls_params); + SolutionCollector collector = solver.MakeLastSolutionCollector(); + collector.AddObjective(sum_var); + SearchMonitor log = solver.MakeSearchLog(1000, obj); + solver.Solve(ls, collector, obj, log); + Console.WriteLine("Objective value = {0}", collector.ObjectiveValue(0)); + } + + private static void BasicLsWithFilter() + { + Console.WriteLine("BasicLsWithFilter"); + Solver solver = new Solver("BasicLs"); + IntVar[] vars = solver.MakeIntVarArray(4, 0, 4, "vars"); + IntVar sum_var = vars.Sum().Var(); + OptimizeVar obj = sum_var.Minimize(1); + DecisionBuilder db = solver.MakePhase(vars, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + MoveOneVar move_one_var = new MoveOneVar(vars); + SumFilter filter = new SumFilter(vars); + IntVarLocalSearchFilter[] filters = + new IntVarLocalSearchFilter[] { filter }; + LocalSearchPhaseParameters ls_params = + solver.MakeLocalSearchPhaseParameters(sum_var, move_one_var, db, + null, filters); + DecisionBuilder ls = solver.MakeLocalSearchPhase(vars, db, ls_params); + SolutionCollector collector = solver.MakeLastSolutionCollector(); + collector.AddObjective(sum_var); + SearchMonitor log = solver.MakeSearchLog(1000, obj); + solver.Solve(ls, collector, obj, log); + Console.WriteLine("Objective value = {0}", collector.ObjectiveValue(0)); + } + + + public static void Main(String[] args) + { + BasicLns(); + BasicLs(); + BasicLsWithFilter(); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/csls_api.csproj b/libs/or-tools-bin/examples/dotnet/csls_api.csproj new file mode 100644 index 0000000000000000000000000000000000000000..9b2f06817075177c632d4e7e65ef46e832fb11e3 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/csls_api.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.csls_api + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools-bin/examples/dotnet/csrabbitspheasants.cs b/libs/or-tools-bin/examples/dotnet/csrabbitspheasants.cs new file mode 100644 index 0000000000000000000000000000000000000000..536a0b0d26fc101fdb9a7e58a23cfab521d03151 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/csrabbitspheasants.cs @@ -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. + +using System; +using Google.OrTools.ConstraintSolver; + +/** + * Shows how to write a custom decision builder. + */ +public class AssignFirstUnboundToMin : NetDecisionBuilder +{ + public AssignFirstUnboundToMin(IntVar[] vars) + { + vars_ = vars; + } + + public override Decision Next(Solver solver) + { + foreach (IntVar var in vars_) + { + if (!var.Bound()) + { + return solver.MakeAssignVariableValue(var, var.Min()); + } + } + return null; + } + + private IntVar[] vars_; +} + + +public class CsRabbitsPheasants +{ + /** + * 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() + { + Solver solver = new Solver("RabbitsPheasants"); + IntVar rabbits = solver.MakeIntVar(0, 100, "rabbits"); + IntVar pheasants = solver.MakeIntVar(0, 100, "pheasants"); + solver.Add(rabbits + pheasants == 20); + solver.Add(rabbits * 4 + pheasants * 2 == 56); + DecisionBuilder db = + new AssignFirstUnboundToMin(new IntVar[] {rabbits, pheasants}); + solver.NewSearch(db); + solver.NextSolution(); + Console.WriteLine( + "Solved Rabbits + Pheasants in {0} ms, and {1} search tree branches.", + solver.WallTime(), solver.Branches()); + Console.WriteLine(rabbits.ToString()); + Console.WriteLine(pheasants.ToString()); + solver.EndSearch(); + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/csrabbitspheasants.csproj b/libs/or-tools-bin/examples/dotnet/csrabbitspheasants.csproj new file mode 100644 index 0000000000000000000000000000000000000000..4344b7218c6518951887006070fd5726736567e6 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/csrabbitspheasants.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.csrabbitspheasants + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools-bin/examples/dotnet/cstsp.cs b/libs/or-tools-bin/examples/dotnet/cstsp.cs new file mode 100644 index 0000000000000000000000000000000000000000..76cf52cf7646eab0c791ee09b6e8df3423f80d16 --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/cstsp.cs @@ -0,0 +1,121 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +class Tsp +{ + class RandomManhattan { + 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) + { + xs_[i] = generator.Next(1000); + ys_[i] = generator.Next(1000); + } + } + + public long Call(long first_index, long 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 readonly int[] xs_; + private readonly int[] ys_; + private readonly RoutingIndexManager manager_; + }; + + static void Solve(int size, int forbidden, int seed) + { + 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(manager, size, seed); + routing.SetArcCostEvaluatorOfAllVehicles( + routing.RegisterTransitCallback(distances.Call)); + + // Forbid node connections (randomly). + Random randomizer = new Random(); + long forbidden_connections = 0; + while (forbidden_connections < forbidden) { + long from = randomizer.Next(size - 1); + long to = randomizer.Next(size - 1) + 1; + if (routing.NextVar(from).Contains(to)) { + Console.WriteLine("Forbidding connection {0} -> {1}", from, to); + routing.NextVar(from).RemoveValue(to); + ++forbidden_connections; + } + } + + // Add dummy dimension to test API. + routing.AddDimension( + routing.RegisterUnaryTransitCallback((long index) => {return 1;}), + size + 1, + size + 1, + true, + "dummy"); + + // Solve, returns a solution if any (owned by RoutingModel). + RoutingSearchParameters search_parameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + // Setting first solution heuristic (cheapest addition). + search_parameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + + Assignment solution = routing.SolveWithParameters(search_parameters); + Console.WriteLine("Status = {0}", routing.GetStatus()); + if (solution != null) { + // Solution cost. + Console.WriteLine("Cost = {0}", solution.ObjectiveValue()); + // 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))) { + Console.Write("{0} -> ", node); + } + Console.WriteLine("0"); + } + } + + public static void Main(String[] args) + { + int size = 10; + if (args.Length > 0) { + size = Convert.ToInt32(args[0]); + } + int forbidden = 0; + if (args.Length > 1) { + forbidden = Convert.ToInt32(args[1]); + } + int seed = 0; + if (args.Length > 2) { + seed = Convert.ToInt32(args[2]); + } + + Solve(size, forbidden, seed); + } +} diff --git a/libs/or-tools-bin/examples/dotnet/cstsp.csproj b/libs/or-tools-bin/examples/dotnet/cstsp.csproj new file mode 100644 index 0000000000000000000000000000000000000000..5883791e8c4123fa83dc951763c7cb3b6a0ea24b --- /dev/null +++ b/libs/or-tools-bin/examples/dotnet/cstsp.csproj @@ -0,0 +1,24 @@ + + + Exe + 7.3 + netcoreapp2.1 + false + + LatestMajor + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + Google.OrTools.cstsp + true + + + + full + true + true + + + + + + + diff --git a/libs/or-tools/examples/dotnet/curious_set_of_integers.cs b/libs/or-tools-bin/examples/dotnet/curious_set_of_integers.cs similarity index 100% rename from libs/or-tools/examples/dotnet/curious_set_of_integers.cs rename to libs/or-tools-bin/examples/dotnet/curious_set_of_integers.cs diff --git a/libs/or-tools/examples/dotnet/curious_set_of_integers.csproj b/libs/or-tools-bin/examples/dotnet/curious_set_of_integers.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/curious_set_of_integers.csproj rename to libs/or-tools-bin/examples/dotnet/curious_set_of_integers.csproj diff --git a/libs/or-tools/examples/dotnet/debruijn.cs b/libs/or-tools-bin/examples/dotnet/debruijn.cs similarity index 100% rename from libs/or-tools/examples/dotnet/debruijn.cs rename to libs/or-tools-bin/examples/dotnet/debruijn.cs diff --git a/libs/or-tools/examples/dotnet/debruijn.csproj b/libs/or-tools-bin/examples/dotnet/debruijn.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/debruijn.csproj rename to libs/or-tools-bin/examples/dotnet/debruijn.csproj diff --git a/libs/or-tools/examples/dotnet/discrete_tomography.cs b/libs/or-tools-bin/examples/dotnet/discrete_tomography.cs similarity index 100% rename from libs/or-tools/examples/dotnet/discrete_tomography.cs rename to libs/or-tools-bin/examples/dotnet/discrete_tomography.cs diff --git a/libs/or-tools/examples/dotnet/discrete_tomography.csproj b/libs/or-tools-bin/examples/dotnet/discrete_tomography.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/discrete_tomography.csproj rename to libs/or-tools-bin/examples/dotnet/discrete_tomography.csproj diff --git a/libs/or-tools/examples/dotnet/divisible_by_9_through_1.cs b/libs/or-tools-bin/examples/dotnet/divisible_by_9_through_1.cs similarity index 100% rename from libs/or-tools/examples/dotnet/divisible_by_9_through_1.cs rename to libs/or-tools-bin/examples/dotnet/divisible_by_9_through_1.cs diff --git a/libs/or-tools/examples/dotnet/divisible_by_9_through_1.csproj b/libs/or-tools-bin/examples/dotnet/divisible_by_9_through_1.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/divisible_by_9_through_1.csproj rename to libs/or-tools-bin/examples/dotnet/divisible_by_9_through_1.csproj diff --git a/libs/or-tools/examples/dotnet/dudeney.cs b/libs/or-tools-bin/examples/dotnet/dudeney.cs similarity index 100% rename from libs/or-tools/examples/dotnet/dudeney.cs rename to libs/or-tools-bin/examples/dotnet/dudeney.cs diff --git a/libs/or-tools/examples/dotnet/dudeney.csproj b/libs/or-tools-bin/examples/dotnet/dudeney.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/dudeney.csproj rename to libs/or-tools-bin/examples/dotnet/dudeney.csproj diff --git a/libs/or-tools/examples/dotnet/einav_puzzle2.cs b/libs/or-tools-bin/examples/dotnet/einav_puzzle2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/einav_puzzle2.cs rename to libs/or-tools-bin/examples/dotnet/einav_puzzle2.cs diff --git a/libs/or-tools/examples/dotnet/einav_puzzle2.csproj b/libs/or-tools-bin/examples/dotnet/einav_puzzle2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/einav_puzzle2.csproj rename to libs/or-tools-bin/examples/dotnet/einav_puzzle2.csproj diff --git a/libs/or-tools/examples/dotnet/eq10.cs b/libs/or-tools-bin/examples/dotnet/eq10.cs similarity index 100% rename from libs/or-tools/examples/dotnet/eq10.cs rename to libs/or-tools-bin/examples/dotnet/eq10.cs diff --git a/libs/or-tools/examples/dotnet/eq10.csproj b/libs/or-tools-bin/examples/dotnet/eq10.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/eq10.csproj rename to libs/or-tools-bin/examples/dotnet/eq10.csproj diff --git a/libs/or-tools/examples/dotnet/eq20.cs b/libs/or-tools-bin/examples/dotnet/eq20.cs similarity index 100% rename from libs/or-tools/examples/dotnet/eq20.cs rename to libs/or-tools-bin/examples/dotnet/eq20.cs diff --git a/libs/or-tools/examples/dotnet/eq20.csproj b/libs/or-tools-bin/examples/dotnet/eq20.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/eq20.csproj rename to libs/or-tools-bin/examples/dotnet/eq20.csproj diff --git a/libs/or-tools/examples/dotnet/fill_a_pix.cs b/libs/or-tools-bin/examples/dotnet/fill_a_pix.cs similarity index 100% rename from libs/or-tools/examples/dotnet/fill_a_pix.cs rename to libs/or-tools-bin/examples/dotnet/fill_a_pix.cs diff --git a/libs/or-tools/examples/dotnet/fill_a_pix.csproj b/libs/or-tools-bin/examples/dotnet/fill_a_pix.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/fill_a_pix.csproj rename to libs/or-tools-bin/examples/dotnet/fill_a_pix.csproj diff --git a/libs/or-tools/examples/dotnet/fsProgram.fs b/libs/or-tools-bin/examples/dotnet/fsProgram.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsProgram.fs rename to libs/or-tools-bin/examples/dotnet/fsProgram.fs diff --git a/libs/or-tools/examples/dotnet/fsProgram.fsproj b/libs/or-tools-bin/examples/dotnet/fsProgram.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsProgram.fsproj rename to libs/or-tools-bin/examples/dotnet/fsProgram.fsproj diff --git a/libs/or-tools/examples/dotnet/fsdiet.fs b/libs/or-tools-bin/examples/dotnet/fsdiet.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsdiet.fs rename to libs/or-tools-bin/examples/dotnet/fsdiet.fs diff --git a/libs/or-tools/examples/dotnet/fsdiet.fsproj b/libs/or-tools-bin/examples/dotnet/fsdiet.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsdiet.fsproj rename to libs/or-tools-bin/examples/dotnet/fsdiet.fsproj diff --git a/libs/or-tools/examples/dotnet/fsequality-inequality.fs b/libs/or-tools-bin/examples/dotnet/fsequality-inequality.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsequality-inequality.fs rename to libs/or-tools-bin/examples/dotnet/fsequality-inequality.fs diff --git a/libs/or-tools/examples/dotnet/fsequality-inequality.fsproj b/libs/or-tools-bin/examples/dotnet/fsequality-inequality.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsequality-inequality.fsproj rename to libs/or-tools-bin/examples/dotnet/fsequality-inequality.fsproj diff --git a/libs/or-tools/examples/dotnet/fsequality.fs b/libs/or-tools-bin/examples/dotnet/fsequality.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsequality.fs rename to libs/or-tools-bin/examples/dotnet/fsequality.fs diff --git a/libs/or-tools/examples/dotnet/fsequality.fsproj b/libs/or-tools-bin/examples/dotnet/fsequality.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsequality.fsproj rename to libs/or-tools-bin/examples/dotnet/fsequality.fsproj diff --git a/libs/or-tools/examples/dotnet/fsinteger-linear-program.fs b/libs/or-tools-bin/examples/dotnet/fsinteger-linear-program.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsinteger-linear-program.fs rename to libs/or-tools-bin/examples/dotnet/fsinteger-linear-program.fs diff --git a/libs/or-tools/examples/dotnet/fsinteger-linear-program.fsproj b/libs/or-tools-bin/examples/dotnet/fsinteger-linear-program.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsinteger-linear-program.fsproj rename to libs/or-tools-bin/examples/dotnet/fsinteger-linear-program.fsproj diff --git a/libs/or-tools/examples/dotnet/fsintegerprogramming.fs b/libs/or-tools-bin/examples/dotnet/fsintegerprogramming.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsintegerprogramming.fs rename to libs/or-tools-bin/examples/dotnet/fsintegerprogramming.fs diff --git a/libs/or-tools/examples/dotnet/fsintegerprogramming.fsproj b/libs/or-tools-bin/examples/dotnet/fsintegerprogramming.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsintegerprogramming.fsproj rename to libs/or-tools-bin/examples/dotnet/fsintegerprogramming.fsproj diff --git a/libs/or-tools/examples/dotnet/fsknapsack.fs b/libs/or-tools-bin/examples/dotnet/fsknapsack.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsknapsack.fs rename to libs/or-tools-bin/examples/dotnet/fsknapsack.fs diff --git a/libs/or-tools/examples/dotnet/fsknapsack.fsproj b/libs/or-tools-bin/examples/dotnet/fsknapsack.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsknapsack.fsproj rename to libs/or-tools-bin/examples/dotnet/fsknapsack.fsproj diff --git a/libs/or-tools/examples/dotnet/fslinearprogramming.fs b/libs/or-tools-bin/examples/dotnet/fslinearprogramming.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fslinearprogramming.fs rename to libs/or-tools-bin/examples/dotnet/fslinearprogramming.fs diff --git a/libs/or-tools/examples/dotnet/fslinearprogramming.fsproj b/libs/or-tools-bin/examples/dotnet/fslinearprogramming.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fslinearprogramming.fsproj rename to libs/or-tools-bin/examples/dotnet/fslinearprogramming.fsproj diff --git a/libs/or-tools/examples/dotnet/fsnetwork-max-flow-lpSolve.fs b/libs/or-tools-bin/examples/dotnet/fsnetwork-max-flow-lpSolve.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsnetwork-max-flow-lpSolve.fs rename to libs/or-tools-bin/examples/dotnet/fsnetwork-max-flow-lpSolve.fs diff --git a/libs/or-tools/examples/dotnet/fsnetwork-max-flow-lpSolve.fsproj b/libs/or-tools-bin/examples/dotnet/fsnetwork-max-flow-lpSolve.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsnetwork-max-flow-lpSolve.fsproj rename to libs/or-tools-bin/examples/dotnet/fsnetwork-max-flow-lpSolve.fsproj diff --git a/libs/or-tools/examples/dotnet/fsnetwork-max-flow.fs b/libs/or-tools-bin/examples/dotnet/fsnetwork-max-flow.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsnetwork-max-flow.fs rename to libs/or-tools-bin/examples/dotnet/fsnetwork-max-flow.fs diff --git a/libs/or-tools/examples/dotnet/fsnetwork-max-flow.fsproj b/libs/or-tools-bin/examples/dotnet/fsnetwork-max-flow.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsnetwork-max-flow.fsproj rename to libs/or-tools-bin/examples/dotnet/fsnetwork-max-flow.fsproj diff --git a/libs/or-tools/examples/dotnet/fsnetwork-min-cost-flow.fs b/libs/or-tools-bin/examples/dotnet/fsnetwork-min-cost-flow.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsnetwork-min-cost-flow.fs rename to libs/or-tools-bin/examples/dotnet/fsnetwork-min-cost-flow.fs diff --git a/libs/or-tools/examples/dotnet/fsnetwork-min-cost-flow.fsproj b/libs/or-tools-bin/examples/dotnet/fsnetwork-min-cost-flow.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsnetwork-min-cost-flow.fsproj rename to libs/or-tools-bin/examples/dotnet/fsnetwork-min-cost-flow.fsproj diff --git a/libs/or-tools/examples/dotnet/fsrabbit-pheasant.fs b/libs/or-tools-bin/examples/dotnet/fsrabbit-pheasant.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsrabbit-pheasant.fs rename to libs/or-tools-bin/examples/dotnet/fsrabbit-pheasant.fs diff --git a/libs/or-tools/examples/dotnet/fsrabbit-pheasant.fsproj b/libs/or-tools-bin/examples/dotnet/fsrabbit-pheasant.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsrabbit-pheasant.fsproj rename to libs/or-tools-bin/examples/dotnet/fsrabbit-pheasant.fsproj diff --git a/libs/or-tools/examples/dotnet/fsvolsay.fs b/libs/or-tools-bin/examples/dotnet/fsvolsay.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsvolsay.fs rename to libs/or-tools-bin/examples/dotnet/fsvolsay.fs diff --git a/libs/or-tools/examples/dotnet/fsvolsay.fsproj b/libs/or-tools-bin/examples/dotnet/fsvolsay.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsvolsay.fsproj rename to libs/or-tools-bin/examples/dotnet/fsvolsay.fsproj diff --git a/libs/or-tools/examples/dotnet/fsvolsay3-lpSolve.fs b/libs/or-tools-bin/examples/dotnet/fsvolsay3-lpSolve.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsvolsay3-lpSolve.fs rename to libs/or-tools-bin/examples/dotnet/fsvolsay3-lpSolve.fs diff --git a/libs/or-tools/examples/dotnet/fsvolsay3-lpSolve.fsproj b/libs/or-tools-bin/examples/dotnet/fsvolsay3-lpSolve.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsvolsay3-lpSolve.fsproj rename to libs/or-tools-bin/examples/dotnet/fsvolsay3-lpSolve.fsproj diff --git a/libs/or-tools/examples/dotnet/fsvolsay3.fs b/libs/or-tools-bin/examples/dotnet/fsvolsay3.fs similarity index 100% rename from libs/or-tools/examples/dotnet/fsvolsay3.fs rename to libs/or-tools-bin/examples/dotnet/fsvolsay3.fs diff --git a/libs/or-tools/examples/dotnet/fsvolsay3.fsproj b/libs/or-tools-bin/examples/dotnet/fsvolsay3.fsproj similarity index 100% rename from libs/or-tools/examples/dotnet/fsvolsay3.fsproj rename to libs/or-tools-bin/examples/dotnet/fsvolsay3.fsproj diff --git a/libs/or-tools/examples/dotnet/furniture_moving.cs b/libs/or-tools-bin/examples/dotnet/furniture_moving.cs similarity index 100% rename from libs/or-tools/examples/dotnet/furniture_moving.cs rename to libs/or-tools-bin/examples/dotnet/furniture_moving.cs diff --git a/libs/or-tools/examples/dotnet/furniture_moving.csproj b/libs/or-tools-bin/examples/dotnet/furniture_moving.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/furniture_moving.csproj rename to libs/or-tools-bin/examples/dotnet/furniture_moving.csproj diff --git a/libs/or-tools/examples/dotnet/furniture_moving_intervals.cs b/libs/or-tools-bin/examples/dotnet/furniture_moving_intervals.cs similarity index 100% rename from libs/or-tools/examples/dotnet/furniture_moving_intervals.cs rename to libs/or-tools-bin/examples/dotnet/furniture_moving_intervals.cs diff --git a/libs/or-tools/examples/dotnet/furniture_moving_intervals.csproj b/libs/or-tools-bin/examples/dotnet/furniture_moving_intervals.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/furniture_moving_intervals.csproj rename to libs/or-tools-bin/examples/dotnet/furniture_moving_intervals.csproj diff --git a/libs/or-tools/examples/dotnet/futoshiki.cs b/libs/or-tools-bin/examples/dotnet/futoshiki.cs similarity index 100% rename from libs/or-tools/examples/dotnet/futoshiki.cs rename to libs/or-tools-bin/examples/dotnet/futoshiki.cs diff --git a/libs/or-tools/examples/dotnet/futoshiki.csproj b/libs/or-tools-bin/examples/dotnet/futoshiki.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/futoshiki.csproj rename to libs/or-tools-bin/examples/dotnet/futoshiki.csproj diff --git a/libs/or-tools/examples/dotnet/golomb_ruler.cs b/libs/or-tools-bin/examples/dotnet/golomb_ruler.cs similarity index 100% rename from libs/or-tools/examples/dotnet/golomb_ruler.cs rename to libs/or-tools-bin/examples/dotnet/golomb_ruler.cs diff --git a/libs/or-tools/examples/dotnet/golomb_ruler.csproj b/libs/or-tools-bin/examples/dotnet/golomb_ruler.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/golomb_ruler.csproj rename to libs/or-tools-bin/examples/dotnet/golomb_ruler.csproj diff --git a/libs/or-tools/examples/dotnet/grocery.cs b/libs/or-tools-bin/examples/dotnet/grocery.cs similarity index 100% rename from libs/or-tools/examples/dotnet/grocery.cs rename to libs/or-tools-bin/examples/dotnet/grocery.cs diff --git a/libs/or-tools/examples/dotnet/grocery.csproj b/libs/or-tools-bin/examples/dotnet/grocery.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/grocery.csproj rename to libs/or-tools-bin/examples/dotnet/grocery.csproj diff --git a/libs/or-tools/examples/dotnet/hidato_table.cs b/libs/or-tools-bin/examples/dotnet/hidato_table.cs similarity index 100% rename from libs/or-tools/examples/dotnet/hidato_table.cs rename to libs/or-tools-bin/examples/dotnet/hidato_table.cs diff --git a/libs/or-tools/examples/dotnet/hidato_table.csproj b/libs/or-tools-bin/examples/dotnet/hidato_table.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/hidato_table.csproj rename to libs/or-tools-bin/examples/dotnet/hidato_table.csproj diff --git a/libs/or-tools/examples/dotnet/just_forgotten.cs b/libs/or-tools-bin/examples/dotnet/just_forgotten.cs similarity index 100% rename from libs/or-tools/examples/dotnet/just_forgotten.cs rename to libs/or-tools-bin/examples/dotnet/just_forgotten.cs diff --git a/libs/or-tools/examples/dotnet/just_forgotten.csproj b/libs/or-tools-bin/examples/dotnet/just_forgotten.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/just_forgotten.csproj rename to libs/or-tools-bin/examples/dotnet/just_forgotten.csproj diff --git a/libs/or-tools/examples/dotnet/kakuro.cs b/libs/or-tools-bin/examples/dotnet/kakuro.cs similarity index 100% rename from libs/or-tools/examples/dotnet/kakuro.cs rename to libs/or-tools-bin/examples/dotnet/kakuro.cs diff --git a/libs/or-tools/examples/dotnet/kakuro.csproj b/libs/or-tools-bin/examples/dotnet/kakuro.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/kakuro.csproj rename to libs/or-tools-bin/examples/dotnet/kakuro.csproj diff --git a/libs/or-tools/examples/dotnet/kenken2.cs b/libs/or-tools-bin/examples/dotnet/kenken2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/kenken2.cs rename to libs/or-tools-bin/examples/dotnet/kenken2.cs diff --git a/libs/or-tools/examples/dotnet/kenken2.csproj b/libs/or-tools-bin/examples/dotnet/kenken2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/kenken2.csproj rename to libs/or-tools-bin/examples/dotnet/kenken2.csproj diff --git a/libs/or-tools/examples/dotnet/killer_sudoku.cs b/libs/or-tools-bin/examples/dotnet/killer_sudoku.cs similarity index 100% rename from libs/or-tools/examples/dotnet/killer_sudoku.cs rename to libs/or-tools-bin/examples/dotnet/killer_sudoku.cs diff --git a/libs/or-tools/examples/dotnet/killer_sudoku.csproj b/libs/or-tools-bin/examples/dotnet/killer_sudoku.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/killer_sudoku.csproj rename to libs/or-tools-bin/examples/dotnet/killer_sudoku.csproj diff --git a/libs/or-tools/examples/dotnet/labeled_dice.cs b/libs/or-tools-bin/examples/dotnet/labeled_dice.cs similarity index 100% rename from libs/or-tools/examples/dotnet/labeled_dice.cs rename to libs/or-tools-bin/examples/dotnet/labeled_dice.cs diff --git a/libs/or-tools/examples/dotnet/labeled_dice.csproj b/libs/or-tools-bin/examples/dotnet/labeled_dice.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/labeled_dice.csproj rename to libs/or-tools-bin/examples/dotnet/labeled_dice.csproj diff --git a/libs/or-tools/examples/dotnet/langford.cs b/libs/or-tools-bin/examples/dotnet/langford.cs similarity index 100% rename from libs/or-tools/examples/dotnet/langford.cs rename to libs/or-tools-bin/examples/dotnet/langford.cs diff --git a/libs/or-tools/examples/dotnet/langford.csproj b/libs/or-tools-bin/examples/dotnet/langford.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/langford.csproj rename to libs/or-tools-bin/examples/dotnet/langford.csproj diff --git a/libs/or-tools/examples/dotnet/least_diff.cs b/libs/or-tools-bin/examples/dotnet/least_diff.cs similarity index 100% rename from libs/or-tools/examples/dotnet/least_diff.cs rename to libs/or-tools-bin/examples/dotnet/least_diff.cs diff --git a/libs/or-tools/examples/dotnet/least_diff.csproj b/libs/or-tools-bin/examples/dotnet/least_diff.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/least_diff.csproj rename to libs/or-tools-bin/examples/dotnet/least_diff.csproj diff --git a/libs/or-tools/examples/dotnet/lectures.cs b/libs/or-tools-bin/examples/dotnet/lectures.cs similarity index 100% rename from libs/or-tools/examples/dotnet/lectures.cs rename to libs/or-tools-bin/examples/dotnet/lectures.cs diff --git a/libs/or-tools/examples/dotnet/lectures.csproj b/libs/or-tools-bin/examples/dotnet/lectures.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/lectures.csproj rename to libs/or-tools-bin/examples/dotnet/lectures.csproj diff --git a/libs/or-tools/examples/dotnet/magic_sequence.cs b/libs/or-tools-bin/examples/dotnet/magic_sequence.cs similarity index 100% rename from libs/or-tools/examples/dotnet/magic_sequence.cs rename to libs/or-tools-bin/examples/dotnet/magic_sequence.cs diff --git a/libs/or-tools/examples/dotnet/magic_sequence.csproj b/libs/or-tools-bin/examples/dotnet/magic_sequence.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/magic_sequence.csproj rename to libs/or-tools-bin/examples/dotnet/magic_sequence.csproj diff --git a/libs/or-tools/examples/dotnet/magic_square.cs b/libs/or-tools-bin/examples/dotnet/magic_square.cs similarity index 100% rename from libs/or-tools/examples/dotnet/magic_square.cs rename to libs/or-tools-bin/examples/dotnet/magic_square.cs diff --git a/libs/or-tools/examples/dotnet/magic_square.csproj b/libs/or-tools-bin/examples/dotnet/magic_square.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/magic_square.csproj rename to libs/or-tools-bin/examples/dotnet/magic_square.csproj diff --git a/libs/or-tools/examples/dotnet/magic_square_and_cards.cs b/libs/or-tools-bin/examples/dotnet/magic_square_and_cards.cs similarity index 100% rename from libs/or-tools/examples/dotnet/magic_square_and_cards.cs rename to libs/or-tools-bin/examples/dotnet/magic_square_and_cards.cs diff --git a/libs/or-tools/examples/dotnet/magic_square_and_cards.csproj b/libs/or-tools-bin/examples/dotnet/magic_square_and_cards.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/magic_square_and_cards.csproj rename to libs/or-tools-bin/examples/dotnet/magic_square_and_cards.csproj diff --git a/libs/or-tools/examples/dotnet/map.cs b/libs/or-tools-bin/examples/dotnet/map.cs similarity index 100% rename from libs/or-tools/examples/dotnet/map.cs rename to libs/or-tools-bin/examples/dotnet/map.cs diff --git a/libs/or-tools/examples/dotnet/map.csproj b/libs/or-tools-bin/examples/dotnet/map.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/map.csproj rename to libs/or-tools-bin/examples/dotnet/map.csproj diff --git a/libs/or-tools/examples/dotnet/map2.cs b/libs/or-tools-bin/examples/dotnet/map2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/map2.cs rename to libs/or-tools-bin/examples/dotnet/map2.cs diff --git a/libs/or-tools/examples/dotnet/map2.csproj b/libs/or-tools-bin/examples/dotnet/map2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/map2.csproj rename to libs/or-tools-bin/examples/dotnet/map2.csproj diff --git a/libs/or-tools/examples/dotnet/marathon2.cs b/libs/or-tools-bin/examples/dotnet/marathon2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/marathon2.cs rename to libs/or-tools-bin/examples/dotnet/marathon2.cs diff --git a/libs/or-tools/examples/dotnet/marathon2.csproj b/libs/or-tools-bin/examples/dotnet/marathon2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/marathon2.csproj rename to libs/or-tools-bin/examples/dotnet/marathon2.csproj diff --git a/libs/or-tools/examples/dotnet/max_flow_taha.cs b/libs/or-tools-bin/examples/dotnet/max_flow_taha.cs similarity index 100% rename from libs/or-tools/examples/dotnet/max_flow_taha.cs rename to libs/or-tools-bin/examples/dotnet/max_flow_taha.cs diff --git a/libs/or-tools/examples/dotnet/max_flow_taha.csproj b/libs/or-tools-bin/examples/dotnet/max_flow_taha.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/max_flow_taha.csproj rename to libs/or-tools-bin/examples/dotnet/max_flow_taha.csproj diff --git a/libs/or-tools/examples/dotnet/max_flow_winston1.cs b/libs/or-tools-bin/examples/dotnet/max_flow_winston1.cs similarity index 100% rename from libs/or-tools/examples/dotnet/max_flow_winston1.cs rename to libs/or-tools-bin/examples/dotnet/max_flow_winston1.cs diff --git a/libs/or-tools/examples/dotnet/max_flow_winston1.csproj b/libs/or-tools-bin/examples/dotnet/max_flow_winston1.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/max_flow_winston1.csproj rename to libs/or-tools-bin/examples/dotnet/max_flow_winston1.csproj diff --git a/libs/or-tools/examples/dotnet/minesweeper.cs b/libs/or-tools-bin/examples/dotnet/minesweeper.cs similarity index 100% rename from libs/or-tools/examples/dotnet/minesweeper.cs rename to libs/or-tools-bin/examples/dotnet/minesweeper.cs diff --git a/libs/or-tools/examples/dotnet/minesweeper.csproj b/libs/or-tools-bin/examples/dotnet/minesweeper.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/minesweeper.csproj rename to libs/or-tools-bin/examples/dotnet/minesweeper.csproj diff --git a/libs/or-tools/examples/dotnet/mr_smith.cs b/libs/or-tools-bin/examples/dotnet/mr_smith.cs similarity index 100% rename from libs/or-tools/examples/dotnet/mr_smith.cs rename to libs/or-tools-bin/examples/dotnet/mr_smith.cs diff --git a/libs/or-tools/examples/dotnet/mr_smith.csproj b/libs/or-tools-bin/examples/dotnet/mr_smith.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/mr_smith.csproj rename to libs/or-tools-bin/examples/dotnet/mr_smith.csproj diff --git a/libs/or-tools/examples/dotnet/nontransitive_dice.cs b/libs/or-tools-bin/examples/dotnet/nontransitive_dice.cs similarity index 100% rename from libs/or-tools/examples/dotnet/nontransitive_dice.cs rename to libs/or-tools-bin/examples/dotnet/nontransitive_dice.cs diff --git a/libs/or-tools/examples/dotnet/nontransitive_dice.csproj b/libs/or-tools-bin/examples/dotnet/nontransitive_dice.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/nontransitive_dice.csproj rename to libs/or-tools-bin/examples/dotnet/nontransitive_dice.csproj diff --git a/libs/or-tools/examples/dotnet/nqueens.cs b/libs/or-tools-bin/examples/dotnet/nqueens.cs similarity index 100% rename from libs/or-tools/examples/dotnet/nqueens.cs rename to libs/or-tools-bin/examples/dotnet/nqueens.cs diff --git a/libs/or-tools/examples/dotnet/nqueens.csproj b/libs/or-tools-bin/examples/dotnet/nqueens.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/nqueens.csproj rename to libs/or-tools-bin/examples/dotnet/nqueens.csproj diff --git a/libs/or-tools/examples/dotnet/nurse_rostering_regular.cs b/libs/or-tools-bin/examples/dotnet/nurse_rostering_regular.cs similarity index 100% rename from libs/or-tools/examples/dotnet/nurse_rostering_regular.cs rename to libs/or-tools-bin/examples/dotnet/nurse_rostering_regular.cs diff --git a/libs/or-tools/examples/dotnet/nurse_rostering_regular.csproj b/libs/or-tools-bin/examples/dotnet/nurse_rostering_regular.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/nurse_rostering_regular.csproj rename to libs/or-tools-bin/examples/dotnet/nurse_rostering_regular.csproj diff --git a/libs/or-tools/examples/dotnet/nurse_rostering_transition.cs b/libs/or-tools-bin/examples/dotnet/nurse_rostering_transition.cs similarity index 100% rename from libs/or-tools/examples/dotnet/nurse_rostering_transition.cs rename to libs/or-tools-bin/examples/dotnet/nurse_rostering_transition.cs diff --git a/libs/or-tools/examples/dotnet/nurse_rostering_transition.csproj b/libs/or-tools-bin/examples/dotnet/nurse_rostering_transition.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/nurse_rostering_transition.csproj rename to libs/or-tools-bin/examples/dotnet/nurse_rostering_transition.csproj diff --git a/libs/or-tools/examples/dotnet/olympic.cs b/libs/or-tools-bin/examples/dotnet/olympic.cs similarity index 100% rename from libs/or-tools/examples/dotnet/olympic.cs rename to libs/or-tools-bin/examples/dotnet/olympic.cs diff --git a/libs/or-tools/examples/dotnet/olympic.csproj b/libs/or-tools-bin/examples/dotnet/olympic.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/olympic.csproj rename to libs/or-tools-bin/examples/dotnet/olympic.csproj diff --git a/libs/or-tools/examples/dotnet/organize_day.cs b/libs/or-tools-bin/examples/dotnet/organize_day.cs similarity index 100% rename from libs/or-tools/examples/dotnet/organize_day.cs rename to libs/or-tools-bin/examples/dotnet/organize_day.cs diff --git a/libs/or-tools/examples/dotnet/organize_day.csproj b/libs/or-tools-bin/examples/dotnet/organize_day.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/organize_day.csproj rename to libs/or-tools-bin/examples/dotnet/organize_day.csproj diff --git a/libs/or-tools/examples/dotnet/organize_day_intervals.cs b/libs/or-tools-bin/examples/dotnet/organize_day_intervals.cs similarity index 100% rename from libs/or-tools/examples/dotnet/organize_day_intervals.cs rename to libs/or-tools-bin/examples/dotnet/organize_day_intervals.cs diff --git a/libs/or-tools/examples/dotnet/organize_day_intervals.csproj b/libs/or-tools-bin/examples/dotnet/organize_day_intervals.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/organize_day_intervals.csproj rename to libs/or-tools-bin/examples/dotnet/organize_day_intervals.csproj diff --git a/libs/or-tools/examples/dotnet/p_median.cs b/libs/or-tools-bin/examples/dotnet/p_median.cs similarity index 100% rename from libs/or-tools/examples/dotnet/p_median.cs rename to libs/or-tools-bin/examples/dotnet/p_median.cs diff --git a/libs/or-tools/examples/dotnet/p_median.csproj b/libs/or-tools-bin/examples/dotnet/p_median.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/p_median.csproj rename to libs/or-tools-bin/examples/dotnet/p_median.csproj diff --git a/libs/or-tools/examples/dotnet/pandigital_numbers.cs b/libs/or-tools-bin/examples/dotnet/pandigital_numbers.cs similarity index 100% rename from libs/or-tools/examples/dotnet/pandigital_numbers.cs rename to libs/or-tools-bin/examples/dotnet/pandigital_numbers.cs diff --git a/libs/or-tools/examples/dotnet/pandigital_numbers.csproj b/libs/or-tools-bin/examples/dotnet/pandigital_numbers.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/pandigital_numbers.csproj rename to libs/or-tools-bin/examples/dotnet/pandigital_numbers.csproj diff --git a/libs/or-tools/examples/dotnet/partition.cs b/libs/or-tools-bin/examples/dotnet/partition.cs similarity index 100% rename from libs/or-tools/examples/dotnet/partition.cs rename to libs/or-tools-bin/examples/dotnet/partition.cs diff --git a/libs/or-tools/examples/dotnet/partition.csproj b/libs/or-tools-bin/examples/dotnet/partition.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/partition.csproj rename to libs/or-tools-bin/examples/dotnet/partition.csproj diff --git a/libs/or-tools/examples/dotnet/perfect_square_sequence.cs b/libs/or-tools-bin/examples/dotnet/perfect_square_sequence.cs similarity index 100% rename from libs/or-tools/examples/dotnet/perfect_square_sequence.cs rename to libs/or-tools-bin/examples/dotnet/perfect_square_sequence.cs diff --git a/libs/or-tools/examples/dotnet/perfect_square_sequence.csproj b/libs/or-tools-bin/examples/dotnet/perfect_square_sequence.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/perfect_square_sequence.csproj rename to libs/or-tools-bin/examples/dotnet/perfect_square_sequence.csproj diff --git a/libs/or-tools/examples/dotnet/photo_problem.cs b/libs/or-tools-bin/examples/dotnet/photo_problem.cs similarity index 100% rename from libs/or-tools/examples/dotnet/photo_problem.cs rename to libs/or-tools-bin/examples/dotnet/photo_problem.cs diff --git a/libs/or-tools/examples/dotnet/photo_problem.csproj b/libs/or-tools-bin/examples/dotnet/photo_problem.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/photo_problem.csproj rename to libs/or-tools-bin/examples/dotnet/photo_problem.csproj diff --git a/libs/or-tools/examples/dotnet/place_number_puzzle.cs b/libs/or-tools-bin/examples/dotnet/place_number_puzzle.cs similarity index 100% rename from libs/or-tools/examples/dotnet/place_number_puzzle.cs rename to libs/or-tools-bin/examples/dotnet/place_number_puzzle.cs diff --git a/libs/or-tools/examples/dotnet/place_number_puzzle.csproj b/libs/or-tools-bin/examples/dotnet/place_number_puzzle.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/place_number_puzzle.csproj rename to libs/or-tools-bin/examples/dotnet/place_number_puzzle.csproj diff --git a/libs/or-tools/examples/dotnet/post_office_problem2.cs b/libs/or-tools-bin/examples/dotnet/post_office_problem2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/post_office_problem2.cs rename to libs/or-tools-bin/examples/dotnet/post_office_problem2.cs diff --git a/libs/or-tools/examples/dotnet/post_office_problem2.csproj b/libs/or-tools-bin/examples/dotnet/post_office_problem2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/post_office_problem2.csproj rename to libs/or-tools-bin/examples/dotnet/post_office_problem2.csproj diff --git a/libs/or-tools/examples/dotnet/quasigroup_completion.cs b/libs/or-tools-bin/examples/dotnet/quasigroup_completion.cs similarity index 100% rename from libs/or-tools/examples/dotnet/quasigroup_completion.cs rename to libs/or-tools-bin/examples/dotnet/quasigroup_completion.cs diff --git a/libs/or-tools/examples/dotnet/quasigroup_completion.csproj b/libs/or-tools-bin/examples/dotnet/quasigroup_completion.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/quasigroup_completion.csproj rename to libs/or-tools-bin/examples/dotnet/quasigroup_completion.csproj diff --git a/libs/or-tools/examples/dotnet/regex.cs b/libs/or-tools-bin/examples/dotnet/regex.cs similarity index 100% rename from libs/or-tools/examples/dotnet/regex.cs rename to libs/or-tools-bin/examples/dotnet/regex.cs diff --git a/libs/or-tools/examples/dotnet/regex.csproj b/libs/or-tools-bin/examples/dotnet/regex.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/regex.csproj rename to libs/or-tools-bin/examples/dotnet/regex.csproj diff --git a/libs/or-tools/examples/dotnet/rogo2.cs b/libs/or-tools-bin/examples/dotnet/rogo2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/rogo2.cs rename to libs/or-tools-bin/examples/dotnet/rogo2.cs diff --git a/libs/or-tools/examples/dotnet/rogo2.csproj b/libs/or-tools-bin/examples/dotnet/rogo2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/rogo2.csproj rename to libs/or-tools-bin/examples/dotnet/rogo2.csproj diff --git a/libs/or-tools/examples/dotnet/scheduling_speakers.cs b/libs/or-tools-bin/examples/dotnet/scheduling_speakers.cs similarity index 100% rename from libs/or-tools/examples/dotnet/scheduling_speakers.cs rename to libs/or-tools-bin/examples/dotnet/scheduling_speakers.cs diff --git a/libs/or-tools/examples/dotnet/scheduling_speakers.csproj b/libs/or-tools-bin/examples/dotnet/scheduling_speakers.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/scheduling_speakers.csproj rename to libs/or-tools-bin/examples/dotnet/scheduling_speakers.csproj diff --git a/libs/or-tools/examples/dotnet/secret_santa.cs b/libs/or-tools-bin/examples/dotnet/secret_santa.cs similarity index 100% rename from libs/or-tools/examples/dotnet/secret_santa.cs rename to libs/or-tools-bin/examples/dotnet/secret_santa.cs diff --git a/libs/or-tools/examples/dotnet/secret_santa.csproj b/libs/or-tools-bin/examples/dotnet/secret_santa.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/secret_santa.csproj rename to libs/or-tools-bin/examples/dotnet/secret_santa.csproj diff --git a/libs/or-tools/examples/dotnet/secret_santa2.cs b/libs/or-tools-bin/examples/dotnet/secret_santa2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/secret_santa2.cs rename to libs/or-tools-bin/examples/dotnet/secret_santa2.cs diff --git a/libs/or-tools/examples/dotnet/secret_santa2.csproj b/libs/or-tools-bin/examples/dotnet/secret_santa2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/secret_santa2.csproj rename to libs/or-tools-bin/examples/dotnet/secret_santa2.csproj diff --git a/libs/or-tools/examples/dotnet/send_more_money.cs b/libs/or-tools-bin/examples/dotnet/send_more_money.cs similarity index 100% rename from libs/or-tools/examples/dotnet/send_more_money.cs rename to libs/or-tools-bin/examples/dotnet/send_more_money.cs diff --git a/libs/or-tools/examples/dotnet/send_more_money.csproj b/libs/or-tools-bin/examples/dotnet/send_more_money.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/send_more_money.csproj rename to libs/or-tools-bin/examples/dotnet/send_more_money.csproj diff --git a/libs/or-tools/examples/dotnet/send_more_money2.cs b/libs/or-tools-bin/examples/dotnet/send_more_money2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/send_more_money2.cs rename to libs/or-tools-bin/examples/dotnet/send_more_money2.cs diff --git a/libs/or-tools/examples/dotnet/send_more_money2.csproj b/libs/or-tools-bin/examples/dotnet/send_more_money2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/send_more_money2.csproj rename to libs/or-tools-bin/examples/dotnet/send_more_money2.csproj diff --git a/libs/or-tools/examples/dotnet/send_most_money.cs b/libs/or-tools-bin/examples/dotnet/send_most_money.cs similarity index 100% rename from libs/or-tools/examples/dotnet/send_most_money.cs rename to libs/or-tools-bin/examples/dotnet/send_most_money.cs diff --git a/libs/or-tools/examples/dotnet/send_most_money.csproj b/libs/or-tools-bin/examples/dotnet/send_most_money.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/send_most_money.csproj rename to libs/or-tools-bin/examples/dotnet/send_most_money.csproj diff --git a/libs/or-tools/examples/dotnet/seseman.cs b/libs/or-tools-bin/examples/dotnet/seseman.cs similarity index 100% rename from libs/or-tools/examples/dotnet/seseman.cs rename to libs/or-tools-bin/examples/dotnet/seseman.cs diff --git a/libs/or-tools/examples/dotnet/seseman.csproj b/libs/or-tools-bin/examples/dotnet/seseman.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/seseman.csproj rename to libs/or-tools-bin/examples/dotnet/seseman.csproj diff --git a/libs/or-tools/examples/dotnet/set_covering.cs b/libs/or-tools-bin/examples/dotnet/set_covering.cs similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering.cs rename to libs/or-tools-bin/examples/dotnet/set_covering.cs diff --git a/libs/or-tools/examples/dotnet/set_covering.csproj b/libs/or-tools-bin/examples/dotnet/set_covering.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering.csproj rename to libs/or-tools-bin/examples/dotnet/set_covering.csproj diff --git a/libs/or-tools/examples/dotnet/set_covering2.cs b/libs/or-tools-bin/examples/dotnet/set_covering2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering2.cs rename to libs/or-tools-bin/examples/dotnet/set_covering2.cs diff --git a/libs/or-tools/examples/dotnet/set_covering2.csproj b/libs/or-tools-bin/examples/dotnet/set_covering2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering2.csproj rename to libs/or-tools-bin/examples/dotnet/set_covering2.csproj diff --git a/libs/or-tools/examples/dotnet/set_covering3.cs b/libs/or-tools-bin/examples/dotnet/set_covering3.cs similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering3.cs rename to libs/or-tools-bin/examples/dotnet/set_covering3.cs diff --git a/libs/or-tools/examples/dotnet/set_covering3.csproj b/libs/or-tools-bin/examples/dotnet/set_covering3.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering3.csproj rename to libs/or-tools-bin/examples/dotnet/set_covering3.csproj diff --git a/libs/or-tools/examples/dotnet/set_covering4.cs b/libs/or-tools-bin/examples/dotnet/set_covering4.cs similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering4.cs rename to libs/or-tools-bin/examples/dotnet/set_covering4.cs diff --git a/libs/or-tools/examples/dotnet/set_covering4.csproj b/libs/or-tools-bin/examples/dotnet/set_covering4.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering4.csproj rename to libs/or-tools-bin/examples/dotnet/set_covering4.csproj diff --git a/libs/or-tools/examples/dotnet/set_covering_deployment.cs b/libs/or-tools-bin/examples/dotnet/set_covering_deployment.cs similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering_deployment.cs rename to libs/or-tools-bin/examples/dotnet/set_covering_deployment.cs diff --git a/libs/or-tools/examples/dotnet/set_covering_deployment.csproj b/libs/or-tools-bin/examples/dotnet/set_covering_deployment.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering_deployment.csproj rename to libs/or-tools-bin/examples/dotnet/set_covering_deployment.csproj diff --git a/libs/or-tools/examples/dotnet/set_covering_skiena.cs b/libs/or-tools-bin/examples/dotnet/set_covering_skiena.cs similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering_skiena.cs rename to libs/or-tools-bin/examples/dotnet/set_covering_skiena.cs diff --git a/libs/or-tools/examples/dotnet/set_covering_skiena.csproj b/libs/or-tools-bin/examples/dotnet/set_covering_skiena.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/set_covering_skiena.csproj rename to libs/or-tools-bin/examples/dotnet/set_covering_skiena.csproj diff --git a/libs/or-tools/examples/dotnet/set_partition.cs b/libs/or-tools-bin/examples/dotnet/set_partition.cs similarity index 100% rename from libs/or-tools/examples/dotnet/set_partition.cs rename to libs/or-tools-bin/examples/dotnet/set_partition.cs diff --git a/libs/or-tools/examples/dotnet/set_partition.csproj b/libs/or-tools-bin/examples/dotnet/set_partition.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/set_partition.csproj rename to libs/or-tools-bin/examples/dotnet/set_partition.csproj diff --git a/libs/or-tools/examples/dotnet/sicherman_dice.cs b/libs/or-tools-bin/examples/dotnet/sicherman_dice.cs similarity index 100% rename from libs/or-tools/examples/dotnet/sicherman_dice.cs rename to libs/or-tools-bin/examples/dotnet/sicherman_dice.cs diff --git a/libs/or-tools/examples/dotnet/sicherman_dice.csproj b/libs/or-tools-bin/examples/dotnet/sicherman_dice.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/sicherman_dice.csproj rename to libs/or-tools-bin/examples/dotnet/sicherman_dice.csproj diff --git a/libs/or-tools/examples/dotnet/ski_assignment.cs b/libs/or-tools-bin/examples/dotnet/ski_assignment.cs similarity index 100% rename from libs/or-tools/examples/dotnet/ski_assignment.cs rename to libs/or-tools-bin/examples/dotnet/ski_assignment.cs diff --git a/libs/or-tools/examples/dotnet/ski_assignment.csproj b/libs/or-tools-bin/examples/dotnet/ski_assignment.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/ski_assignment.csproj rename to libs/or-tools-bin/examples/dotnet/ski_assignment.csproj diff --git a/libs/or-tools/examples/dotnet/stable_marriage.cs b/libs/or-tools-bin/examples/dotnet/stable_marriage.cs similarity index 100% rename from libs/or-tools/examples/dotnet/stable_marriage.cs rename to libs/or-tools-bin/examples/dotnet/stable_marriage.cs diff --git a/libs/or-tools/examples/dotnet/stable_marriage.csproj b/libs/or-tools-bin/examples/dotnet/stable_marriage.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/stable_marriage.csproj rename to libs/or-tools-bin/examples/dotnet/stable_marriage.csproj diff --git a/libs/or-tools/examples/dotnet/strimko2.cs b/libs/or-tools-bin/examples/dotnet/strimko2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/strimko2.cs rename to libs/or-tools-bin/examples/dotnet/strimko2.cs diff --git a/libs/or-tools/examples/dotnet/strimko2.csproj b/libs/or-tools-bin/examples/dotnet/strimko2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/strimko2.csproj rename to libs/or-tools-bin/examples/dotnet/strimko2.csproj diff --git a/libs/or-tools/examples/dotnet/subset_sum.cs b/libs/or-tools-bin/examples/dotnet/subset_sum.cs similarity index 100% rename from libs/or-tools/examples/dotnet/subset_sum.cs rename to libs/or-tools-bin/examples/dotnet/subset_sum.cs diff --git a/libs/or-tools/examples/dotnet/subset_sum.csproj b/libs/or-tools-bin/examples/dotnet/subset_sum.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/subset_sum.csproj rename to libs/or-tools-bin/examples/dotnet/subset_sum.csproj diff --git a/libs/or-tools/examples/dotnet/sudoku.cs b/libs/or-tools-bin/examples/dotnet/sudoku.cs similarity index 100% rename from libs/or-tools/examples/dotnet/sudoku.cs rename to libs/or-tools-bin/examples/dotnet/sudoku.cs diff --git a/libs/or-tools/examples/dotnet/sudoku.csproj b/libs/or-tools-bin/examples/dotnet/sudoku.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/sudoku.csproj rename to libs/or-tools-bin/examples/dotnet/sudoku.csproj diff --git a/libs/or-tools/examples/dotnet/survo_puzzle.cs b/libs/or-tools-bin/examples/dotnet/survo_puzzle.cs similarity index 100% rename from libs/or-tools/examples/dotnet/survo_puzzle.cs rename to libs/or-tools-bin/examples/dotnet/survo_puzzle.cs diff --git a/libs/or-tools/examples/dotnet/survo_puzzle.csproj b/libs/or-tools-bin/examples/dotnet/survo_puzzle.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/survo_puzzle.csproj rename to libs/or-tools-bin/examples/dotnet/survo_puzzle.csproj diff --git a/libs/or-tools/examples/dotnet/to_num.cs b/libs/or-tools-bin/examples/dotnet/to_num.cs similarity index 100% rename from libs/or-tools/examples/dotnet/to_num.cs rename to libs/or-tools-bin/examples/dotnet/to_num.cs diff --git a/libs/or-tools/examples/dotnet/to_num.csproj b/libs/or-tools-bin/examples/dotnet/to_num.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/to_num.csproj rename to libs/or-tools-bin/examples/dotnet/to_num.csproj diff --git a/libs/or-tools/examples/dotnet/traffic_lights.cs b/libs/or-tools-bin/examples/dotnet/traffic_lights.cs similarity index 100% rename from libs/or-tools/examples/dotnet/traffic_lights.cs rename to libs/or-tools-bin/examples/dotnet/traffic_lights.cs diff --git a/libs/or-tools/examples/dotnet/traffic_lights.csproj b/libs/or-tools-bin/examples/dotnet/traffic_lights.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/traffic_lights.csproj rename to libs/or-tools-bin/examples/dotnet/traffic_lights.csproj diff --git a/libs/or-tools/examples/dotnet/volsay.cs b/libs/or-tools-bin/examples/dotnet/volsay.cs similarity index 100% rename from libs/or-tools/examples/dotnet/volsay.cs rename to libs/or-tools-bin/examples/dotnet/volsay.cs diff --git a/libs/or-tools/examples/dotnet/volsay.csproj b/libs/or-tools-bin/examples/dotnet/volsay.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/volsay.csproj rename to libs/or-tools-bin/examples/dotnet/volsay.csproj diff --git a/libs/or-tools/examples/dotnet/volsay2.cs b/libs/or-tools-bin/examples/dotnet/volsay2.cs similarity index 100% rename from libs/or-tools/examples/dotnet/volsay2.cs rename to libs/or-tools-bin/examples/dotnet/volsay2.cs diff --git a/libs/or-tools/examples/dotnet/volsay2.csproj b/libs/or-tools-bin/examples/dotnet/volsay2.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/volsay2.csproj rename to libs/or-tools-bin/examples/dotnet/volsay2.csproj diff --git a/libs/or-tools/examples/dotnet/volsay3.cs b/libs/or-tools-bin/examples/dotnet/volsay3.cs similarity index 100% rename from libs/or-tools/examples/dotnet/volsay3.cs rename to libs/or-tools-bin/examples/dotnet/volsay3.cs diff --git a/libs/or-tools/examples/dotnet/volsay3.csproj b/libs/or-tools-bin/examples/dotnet/volsay3.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/volsay3.csproj rename to libs/or-tools-bin/examples/dotnet/volsay3.csproj diff --git a/libs/or-tools/examples/dotnet/wedding_optimal_chart.cs b/libs/or-tools-bin/examples/dotnet/wedding_optimal_chart.cs similarity index 100% rename from libs/or-tools/examples/dotnet/wedding_optimal_chart.cs rename to libs/or-tools-bin/examples/dotnet/wedding_optimal_chart.cs diff --git a/libs/or-tools/examples/dotnet/wedding_optimal_chart.csproj b/libs/or-tools-bin/examples/dotnet/wedding_optimal_chart.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/wedding_optimal_chart.csproj rename to libs/or-tools-bin/examples/dotnet/wedding_optimal_chart.csproj diff --git a/libs/or-tools/examples/dotnet/who_killed_agatha.cs b/libs/or-tools-bin/examples/dotnet/who_killed_agatha.cs similarity index 100% rename from libs/or-tools/examples/dotnet/who_killed_agatha.cs rename to libs/or-tools-bin/examples/dotnet/who_killed_agatha.cs diff --git a/libs/or-tools/examples/dotnet/who_killed_agatha.csproj b/libs/or-tools-bin/examples/dotnet/who_killed_agatha.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/who_killed_agatha.csproj rename to libs/or-tools-bin/examples/dotnet/who_killed_agatha.csproj diff --git a/libs/or-tools/examples/dotnet/word_square.cs b/libs/or-tools-bin/examples/dotnet/word_square.cs similarity index 100% rename from libs/or-tools/examples/dotnet/word_square.cs rename to libs/or-tools-bin/examples/dotnet/word_square.cs diff --git a/libs/or-tools/examples/dotnet/word_square.csproj b/libs/or-tools-bin/examples/dotnet/word_square.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/word_square.csproj rename to libs/or-tools-bin/examples/dotnet/word_square.csproj diff --git a/libs/or-tools/examples/dotnet/xkcd.cs b/libs/or-tools-bin/examples/dotnet/xkcd.cs similarity index 100% rename from libs/or-tools/examples/dotnet/xkcd.cs rename to libs/or-tools-bin/examples/dotnet/xkcd.cs diff --git a/libs/or-tools/examples/dotnet/xkcd.csproj b/libs/or-tools-bin/examples/dotnet/xkcd.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/xkcd.csproj rename to libs/or-tools-bin/examples/dotnet/xkcd.csproj diff --git a/libs/or-tools/examples/dotnet/young_tableaux.cs b/libs/or-tools-bin/examples/dotnet/young_tableaux.cs similarity index 100% rename from libs/or-tools/examples/dotnet/young_tableaux.cs rename to libs/or-tools-bin/examples/dotnet/young_tableaux.cs diff --git a/libs/or-tools/examples/dotnet/young_tableaux.csproj b/libs/or-tools-bin/examples/dotnet/young_tableaux.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/young_tableaux.csproj rename to libs/or-tools-bin/examples/dotnet/young_tableaux.csproj diff --git a/libs/or-tools/examples/dotnet/zebra.cs b/libs/or-tools-bin/examples/dotnet/zebra.cs similarity index 100% rename from libs/or-tools/examples/dotnet/zebra.cs rename to libs/or-tools-bin/examples/dotnet/zebra.cs diff --git a/libs/or-tools/examples/dotnet/zebra.csproj b/libs/or-tools-bin/examples/dotnet/zebra.csproj similarity index 100% rename from libs/or-tools/examples/dotnet/zebra.csproj rename to libs/or-tools-bin/examples/dotnet/zebra.csproj diff --git a/libs/or-tools/examples/java/AllDifferentExcept0.java b/libs/or-tools-bin/examples/java/AllDifferentExcept0.java similarity index 100% rename from libs/or-tools/examples/java/AllDifferentExcept0.java rename to libs/or-tools-bin/examples/java/AllDifferentExcept0.java diff --git a/libs/or-tools/examples/java/AllInterval.java b/libs/or-tools-bin/examples/java/AllInterval.java similarity index 100% rename from libs/or-tools/examples/java/AllInterval.java rename to libs/or-tools-bin/examples/java/AllInterval.java diff --git a/libs/or-tools/examples/java/BinPackingProblemSat.java b/libs/or-tools-bin/examples/java/BinPackingProblemSat.java similarity index 100% rename from libs/or-tools/examples/java/BinPackingProblemSat.java rename to libs/or-tools-bin/examples/java/BinPackingProblemSat.java diff --git a/libs/or-tools/examples/java/BoolOrSampleSat.java b/libs/or-tools-bin/examples/java/BoolOrSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/BoolOrSampleSat.java rename to libs/or-tools-bin/examples/java/BoolOrSampleSat.java diff --git a/libs/or-tools-bin/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java b/libs/or-tools-bin/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java new file mode 100644 index 0000000000000000000000000000000000000000..1a8be2b87d0b04a6943654460ae4b0e578aaab81 --- /dev/null +++ b/libs/or-tools-bin/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java @@ -0,0 +1,311 @@ +// +// Copyright 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. +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.RoutingDimension; +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; +import java.util.List; +import java.util.Random; +import java.util.function.LongBinaryOperator; +import java.util.function.LongUnaryOperator; +import java.util.logging.Logger; + +// A pair class + +class Pair { + final K first; + final V second; + + public static Pair of(K element0, V element1) { + return new Pair(element0, element1); + } + + public Pair(K element0, V element1) { + this.first = element0; + this.second = 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. + */ +public class CapacitatedVehicleRoutingProblemWithTimeWindows { + + static { + System.loadLibrary("jniortools"); + } + + private static Logger logger = + Logger.getLogger(CapacitatedVehicleRoutingProblemWithTimeWindows.class.getName()); + + // Locations representing either an order location or a vehicle route + // start/end. + private List> locations = new ArrayList(); + + // Quantity to be picked up for each order. + private List orderDemands = new ArrayList(); + // Time window in which each order must be performed. + private List> orderTimeWindows = new ArrayList(); + // Penalty cost "paid" for dropping an order. + private List orderPenalties = new ArrayList(); + + // Capacity of the vehicles. + private int vehicleCapacity = 0; + // Latest time at which each vehicle must end its tour. + private List vehicleEndTime = new ArrayList(); + // Cost per unit of distance of each vehicle. + private List vehicleCostCoefficients = new ArrayList(); + // Vehicle start and end indices. They have to be implemented as int[] due + // to the available SWIG-ed interface. + private int vehicleStarts[]; + private int vehicleEnds[]; + + // Random number generator to produce data. + private final Random randomGenerator = new Random(0xBEEF); + + /** + * Creates a Manhattan Distance evaluator with 'costCoefficient'. + * + * @param manager Node Index Manager. + * @param costCoefficient The coefficient to apply to the evaluator. + */ + private LongBinaryOperator buildManhattanCallback(RoutingIndexManager manager, int costCoefficient) { + return new LongBinaryOperator() { + public long applyAsLong(long firstIndex, long 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. + * + * @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 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 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)); + 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. + * + * @param numberOfVehicles + * @param xMax maximum x coordinate in which orders are located. + * @param yMax maximum y coordinate in which orders are located. + * @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), + */ + 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]; + vehicleEnds = new int[numberOfVehicles]; + for (int vehicle = 0; vehicle < numberOfVehicles; ++vehicle) { + vehicleStarts[vehicle] = locations.size(); + 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))); + vehicleEndTime.add(endTime); + vehicleCostCoefficients.add(randomGenerator.nextInt(costCoefficientMax) + 1); + } + } + + /** 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(); + + RoutingIndexManager manager = + new RoutingIndexManager(numberOfLocations, numberOfVehicles, vehicleStarts, vehicleEnds); + RoutingModel model = new RoutingModel(manager); + + // Setting up dimensions + final int bigNumber = 100000; + final LongBinaryOperator callback = buildManhattanCallback(manager, 1); + final String timeStr = "time"; + model.addDimension( + model.registerTransitCallback(callback), bigNumber, bigNumber, false, timeStr); + RoutingDimension timeDimension = model.getMutableDimension(timeStr); + + LongUnaryOperator demandCallback = + new LongUnaryOperator() { + public long applyAsLong(long 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; + } + } + }; + final String capacityStr = "capacity"; + model.addDimension( + model.registerUnaryTransitCallback(demandCallback), 0, vehicleCapacity, true, capacityStr); + RoutingDimension capacityDimension = model.getMutableDimension(capacityStr); + + // Setting up vehicles + LongBinaryOperator[] callbacks = new LongBinaryOperator[numberOfVehicles]; + for (int vehicle = 0; vehicle < numberOfVehicles; ++vehicle) { + final int costCoefficient = vehicleCostCoefficients.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) { + timeDimension + .cumulVar(order) + .setRange(orderTimeWindows.get(order).first, orderTimeWindows.get(order).second); + long[] orderIndices = {manager.nodeToIndex(order)}; + model.addDisjunction(orderIndices, orderPenalties.get(order)); + } + + // Solving + RoutingSearchParameters parameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.ALL_UNPERFORMED) + .build(); + + logger.info("Search"); + Assignment solution = model.solveWithParameters(parameters); + + if (solution != null) { + String output = "Total cost: " + solution.objectiveValue() + "\n"; + // Dropped orders + String dropped = ""; + for (int order = 0; order < numberOfOrders; ++order) { + if (solution.value(model.nextVar(order)) == order) { + dropped += " " + order; + } + } + if (dropped.length() > 0) { + output += "Dropped orders:" + dropped + "\n"; + } + // Routes + for (int vehicle = 0; vehicle < numberOfVehicles; ++vehicle) { + String route = "Vehicle " + vehicle + ": "; + 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"; + } else { + for (; !model.isEnd(order); order = solution.value(model.nextVar(order))) { + 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 = 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); + } + } + + public static void main(String[] args) throws Exception { + CapacitatedVehicleRoutingProblemWithTimeWindows problem = + new CapacitatedVehicleRoutingProblemWithTimeWindows(); + final int xMax = 20; + final int yMax = 20; + final int demandMax = 3; + final int timeWindowMax = 24 * 60; + final int timeWindowWidth = 4 * 60; + final int penaltyMin = 50; + final int penaltyMax = 100; + 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, timeWindowMax, timeWindowWidth, penaltyMin, penaltyMax); + problem.buildFleet(vehicles, xMax, yMax, endTime, capacity, costCoefficientMax); + problem.solve(orders, vehicles); + } +} diff --git a/libs/or-tools/examples/java/ChannelingSampleSat.java b/libs/or-tools-bin/examples/java/ChannelingSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/ChannelingSampleSat.java rename to libs/or-tools-bin/examples/java/ChannelingSampleSat.java diff --git a/libs/or-tools/examples/java/Circuit.java b/libs/or-tools-bin/examples/java/Circuit.java similarity index 100% rename from libs/or-tools/examples/java/Circuit.java rename to libs/or-tools-bin/examples/java/Circuit.java diff --git a/libs/or-tools/examples/java/CoinsGrid.java b/libs/or-tools-bin/examples/java/CoinsGrid.java similarity index 100% rename from libs/or-tools/examples/java/CoinsGrid.java rename to libs/or-tools-bin/examples/java/CoinsGrid.java diff --git a/libs/or-tools/examples/java/CoinsGridMIP.java b/libs/or-tools-bin/examples/java/CoinsGridMIP.java similarity index 100% rename from libs/or-tools/examples/java/CoinsGridMIP.java rename to libs/or-tools-bin/examples/java/CoinsGridMIP.java diff --git a/libs/or-tools/examples/java/ColoringMIP.java b/libs/or-tools-bin/examples/java/ColoringMIP.java similarity index 100% rename from libs/or-tools/examples/java/ColoringMIP.java rename to libs/or-tools-bin/examples/java/ColoringMIP.java diff --git a/libs/or-tools/examples/java/CoveringOpl.java b/libs/or-tools-bin/examples/java/CoveringOpl.java similarity index 100% rename from libs/or-tools/examples/java/CoveringOpl.java rename to libs/or-tools-bin/examples/java/CoveringOpl.java diff --git a/libs/or-tools/examples/java/CpIsFunSat.java b/libs/or-tools-bin/examples/java/CpIsFunSat.java similarity index 100% rename from libs/or-tools/examples/java/CpIsFunSat.java rename to libs/or-tools-bin/examples/java/CpIsFunSat.java diff --git a/libs/or-tools/examples/java/Crossword.java b/libs/or-tools-bin/examples/java/Crossword.java similarity index 100% rename from libs/or-tools/examples/java/Crossword.java rename to libs/or-tools-bin/examples/java/Crossword.java diff --git a/libs/or-tools/examples/java/DeBruijn.java b/libs/or-tools-bin/examples/java/DeBruijn.java similarity index 100% rename from libs/or-tools/examples/java/DeBruijn.java rename to libs/or-tools-bin/examples/java/DeBruijn.java diff --git a/libs/or-tools/examples/java/Diet.java b/libs/or-tools-bin/examples/java/Diet.java similarity index 100% rename from libs/or-tools/examples/java/Diet.java rename to libs/or-tools-bin/examples/java/Diet.java diff --git a/libs/or-tools/examples/java/DietMIP.java b/libs/or-tools-bin/examples/java/DietMIP.java similarity index 100% rename from libs/or-tools/examples/java/DietMIP.java rename to libs/or-tools-bin/examples/java/DietMIP.java diff --git a/libs/or-tools/examples/java/DivisibleBy9Through1.java b/libs/or-tools-bin/examples/java/DivisibleBy9Through1.java similarity index 100% rename from libs/or-tools/examples/java/DivisibleBy9Through1.java rename to libs/or-tools-bin/examples/java/DivisibleBy9Through1.java diff --git a/libs/or-tools/examples/java/EarlinessTardinessCostSampleSat.java b/libs/or-tools-bin/examples/java/EarlinessTardinessCostSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/EarlinessTardinessCostSampleSat.java rename to libs/or-tools-bin/examples/java/EarlinessTardinessCostSampleSat.java diff --git a/libs/or-tools-bin/examples/java/FlowExample.java b/libs/or-tools-bin/examples/java/FlowExample.java new file mode 100644 index 0000000000000000000000000000000000000000..15e78723735d522b24ceed2872fd762e2c52fcd6 --- /dev/null +++ b/libs/or-tools-bin/examples/java/FlowExample.java @@ -0,0 +1,88 @@ +// 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. + +import com.google.ortools.graph.MaxFlow; +import com.google.ortools.graph.MinCostFlow; + +/** + * Sample showing how to model using the flow solver. + * + */ + +public class FlowExample { + static { + System.loadLibrary("jniortools"); + } + + private static void solveMinCostFlow() { + System.out.println("Min Cost Flow Problem - Simple interface"); + 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}}; + final int expectedCost = 275; + MinCostFlow minCostFlow = new MinCostFlow(); + for (int source = 0; source < numSources; ++source) { + for (int target = 0; target < numTargets; ++target) { + minCostFlow.addArcWithCapacityAndUnitCost( + source, numSources + target, 1, costs[source][target]); + } + } + for (int node = 0; node < numSources; ++node) { + minCostFlow.setNodeSupply(node, 1); + minCostFlow.setNodeSupply(numSources + node, -1); + } + if (minCostFlow.solve() == MinCostFlow.Status.OPTIMAL) { + final long totalFlowCost = minCostFlow.getOptimalCost(); + 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)); + } + } + } else { + System.out.println("No solution found"); + } + } + + private static void solveMaxFlow() { + System.out.println("Max Flow Problem - Simple interface"); + final int[] tails = {0, 0, 0, 0, 1, 2, 3, 3, 4}; + final int[] heads = {1, 2, 3, 4, 3, 4, 4, 5, 5}; + final int[] capacities = {5, 8, 5, 3, 4, 5, 6, 6, 4}; + final int expectedTotalFlow = 10; + MaxFlow maxFlow = new MaxFlow(); + for (int i = 0; i < tails.length; ++i) { + maxFlow.addArcWithCapacity(tails[i], heads[i], capacities[i]); + } + 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)); + } + // TODO(user): Our SWIG configuration does not currently handle these + // functions correctly in Java: + // maxFlow.getSourceSideMinCut(...); + // maxFlow.getSinkSideMinCut(...); + } else { + System.out.println("There was an issue with the input."); + } + } + + public static void main(String[] args) throws Exception { + FlowExample.solveMinCostFlow(); + FlowExample.solveMaxFlow(); + } +} diff --git a/libs/or-tools/examples/java/GolombRuler.java b/libs/or-tools-bin/examples/java/GolombRuler.java similarity index 100% rename from libs/or-tools/examples/java/GolombRuler.java rename to libs/or-tools-bin/examples/java/GolombRuler.java diff --git a/libs/or-tools-bin/examples/java/IntegerProgramming.java b/libs/or-tools-bin/examples/java/IntegerProgramming.java new file mode 100644 index 0000000000000000000000000000000000000000..29bf93b4eb6c61d038c067ad6f847be642176b0d --- /dev/null +++ b/libs/or-tools-bin/examples/java/IntegerProgramming.java @@ -0,0 +1,96 @@ +// 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. + +import com.google.ortools.linearsolver.MPConstraint; +import com.google.ortools.linearsolver.MPObjective; +import com.google.ortools.linearsolver.MPSolver; +import com.google.ortools.linearsolver.MPVariable; + +/** + * Integer programming example that shows how to use the API. + * + */ + +public class IntegerProgramming { + static { + System.loadLibrary("jniortools"); + } + + private static MPSolver createSolver(String solverType) { + try { + return new MPSolver( + "IntegerProgrammingExample", MPSolver.OptimizationProblemType.valueOf(solverType)); + } catch (java.lang.IllegalArgumentException e) { + return null; + } + } + + private static void runIntegerProgrammingExample(String solverType) { + MPSolver solver = createSolver(solverType); + if (solver == null) { + System.out.println("Could not create solver " + solverType); + return; + } + double infinity = java.lang.Double.POSITIVE_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); + + final MPSolver.ResultStatus resultStatus = solver.solve(); + + // Check that the problem has an optimal solution. + if (resultStatus != MPSolver.ResultStatus.OPTIMAL) { + System.err.println("The problem does not have an optimal solution!"); + return; + } + + // 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, /* log_errors= */ true)) { + System.err.println("The solution returned by the solver violated the" + + " problem constraints by at least 1e-7"); + return; + } + + System.out.println("Problem solved in " + solver.wallTime() + " milliseconds"); + + // The objective value of the solution. + System.out.println("Optimal objective value = " + solver.objective().value()); + + // The value of each variable in the solution. + System.out.println("x1 = " + x1.solutionValue()); + System.out.println("x2 = " + x2.solutionValue()); + + System.out.println("Advanced usage:"); + System.out.println("Problem solved in " + solver.nodes() + " branch-and-bound nodes"); + } + + public static void main(String[] args) throws Exception { + System.out.println("---- Integer programming example with SCIP (recommended) ----"); + runIntegerProgrammingExample("SCIP_MIXED_INTEGER_PROGRAMMING"); + System.out.println("---- Integer programming example with CBC ----"); + runIntegerProgrammingExample("CBC_MIXED_INTEGER_PROGRAMMING"); + System.out.println("---- Integer programming example with GLPK ----"); + runIntegerProgrammingExample("GLPK_MIXED_INTEGER_PROGRAMMING"); + } +} diff --git a/libs/or-tools/examples/java/IntervalSampleSat.java b/libs/or-tools-bin/examples/java/IntervalSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/IntervalSampleSat.java rename to libs/or-tools-bin/examples/java/IntervalSampleSat.java diff --git a/libs/or-tools/examples/java/Issue173.java b/libs/or-tools-bin/examples/java/Issue173.java similarity index 100% rename from libs/or-tools/examples/java/Issue173.java rename to libs/or-tools-bin/examples/java/Issue173.java diff --git a/libs/or-tools/examples/java/Knapsack.java b/libs/or-tools-bin/examples/java/Knapsack.java similarity index 100% rename from libs/or-tools/examples/java/Knapsack.java rename to libs/or-tools-bin/examples/java/Knapsack.java diff --git a/libs/or-tools/examples/java/KnapsackMIP.java b/libs/or-tools-bin/examples/java/KnapsackMIP.java similarity index 100% rename from libs/or-tools/examples/java/KnapsackMIP.java rename to libs/or-tools-bin/examples/java/KnapsackMIP.java diff --git a/libs/or-tools/examples/java/LeastDiff.java b/libs/or-tools-bin/examples/java/LeastDiff.java similarity index 100% rename from libs/or-tools/examples/java/LeastDiff.java rename to libs/or-tools-bin/examples/java/LeastDiff.java diff --git a/libs/or-tools-bin/examples/java/LinearAssignmentAPI.java b/libs/or-tools-bin/examples/java/LinearAssignmentAPI.java new file mode 100644 index 0000000000000000000000000000000000000000..7effad6beb95e7d54f078befd2d9968fe54489cd --- /dev/null +++ b/libs/or-tools-bin/examples/java/LinearAssignmentAPI.java @@ -0,0 +1,56 @@ +// 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 +// +// 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. + +import com.google.ortools.graph.LinearSumAssignment; + +/** + * 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. + * + */ + +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}}; + final int expectedCost = cost[0][3] + cost[1][2] + cost[2][1] + cost[3][0]; + + LinearSumAssignment assignment = new LinearSumAssignment(); + for (int source = 0; source < numSources; ++source) { + for (int target = 0; target < numTargets; ++target) { + assignment.addArcWithCost(source, target, cost[source][target]); + } + } + + 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)); + } + } else { + System.out.println("No solution found."); + } + } + + public static void main(String[] args) throws Exception { + LinearAssignmentAPI.runAssignmentOn4x4Matrix(); + } +} diff --git a/libs/or-tools-bin/examples/java/LinearProgramming.java b/libs/or-tools-bin/examples/java/LinearProgramming.java new file mode 100644 index 0000000000000000000000000000000000000000..4baeea3f5689644554b9795c3d179f0d2aad3043 --- /dev/null +++ b/libs/or-tools-bin/examples/java/LinearProgramming.java @@ -0,0 +1,132 @@ +// 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. + +import com.google.ortools.linearsolver.MPConstraint; +import com.google.ortools.linearsolver.MPObjective; +import com.google.ortools.linearsolver.MPSolver; +import com.google.ortools.linearsolver.MPVariable; + +/** + * Linear programming example that shows how to use the API. + * + */ + +public class LinearProgramming { + static { + System.loadLibrary("jniortools"); + } + + private static MPSolver createSolver(String solverType) { + try { + return new MPSolver( + "LinearProgrammingExample", MPSolver.OptimizationProblemType.valueOf(solverType)); + } catch (java.lang.IllegalArgumentException e) { + return null; + } + } + + private static void runLinearProgrammingExample(String solverType, boolean printModel) { + MPSolver solver = createSolver(solverType); + if (solver == null) { + System.out.println("Could not create solver " + solverType); + return; + } + double infinity = java.lang.Double.POSITIVE_INFINITY; + // x1, x2 and x3 are continuous non-negative variables. + MPVariable x1 = solver.makeNumVar(0.0, infinity, "x1"); + MPVariable x2 = solver.makeNumVar(0.0, infinity, "x2"); + MPVariable x3 = solver.makeNumVar(0.0, infinity, "x3"); + + // Maximize 10 * x1 + 6 * x2 + 4 * x3. + MPObjective objective = solver.objective(); + objective.setCoefficient(x1, 10); + objective.setCoefficient(x2, 6); + objective.setCoefficient(x3, 4); + objective.setMaximization(); + + // x1 + x2 + x3 <= 100. + MPConstraint c0 = solver.makeConstraint(-infinity, 100.0); + c0.setCoefficient(x1, 1); + c0.setCoefficient(x2, 1); + c0.setCoefficient(x3, 1); + + // 10 * x1 + 4 * x2 + 5 * x3 <= 600. + MPConstraint c1 = solver.makeConstraint(-infinity, 600.0); + c1.setCoefficient(x1, 10); + c1.setCoefficient(x2, 4); + c1.setCoefficient(x3, 5); + + // 2 * x1 + 2 * x2 + 6 * x3 <= 300. + MPConstraint c2 = solver.makeConstraint(-infinity, 300.0); + c2.setCoefficient(x1, 2); + c2.setCoefficient(x2, 2); + c2.setCoefficient(x3, 6); + + System.out.println("Number of variables = " + solver.numVariables()); + System.out.println("Number of constraints = " + solver.numConstraints()); + + if (printModel) { + String model = solver.exportModelAsLpFormat(); + System.out.println(model); + } + + final MPSolver.ResultStatus resultStatus = solver.solve(); + + // Check that the problem has an optimal solution. + if (resultStatus != MPSolver.ResultStatus.OPTIMAL) { + System.err.println("The problem does not have an optimal solution!"); + return; + } + + // 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, /* log_errors= */ true)) { + System.err.println("The solution returned by the solver violated the" + + " problem constraints by at least 1e-7"); + return; + } + + System.out.println("Problem solved in " + solver.wallTime() + " milliseconds"); + + // The objective value of the solution. + System.out.println("Optimal objective value = " + solver.objective().value()); + + // The value of each variable in the solution. + System.out.println("x1 = " + x1.solutionValue()); + System.out.println("x2 = " + x2.solutionValue()); + System.out.println("x3 = " + x3.solutionValue()); + + final double[] activities = solver.computeConstraintActivities(); + + System.out.println("Advanced usage:"); + System.out.println("Problem solved in " + solver.iterations() + " iterations"); + System.out.println("x1: reduced cost = " + x1.reducedCost()); + System.out.println("x2: reduced cost = " + x2.reducedCost()); + System.out.println("x3: reduced cost = " + x3.reducedCost()); + System.out.println("c0: dual value = " + c0.dualValue()); + System.out.println(" activity = " + activities[c0.index()]); + System.out.println("c1: dual value = " + c1.dualValue()); + System.out.println(" activity = " + activities[c1.index()]); + System.out.println("c2: dual value = " + c2.dualValue()); + System.out.println(" activity = " + activities[c2.index()]); + } + + public static void main(String[] args) throws Exception { + System.out.println("---- Linear programming example with GLOP (recommended) ----"); + runLinearProgrammingExample("GLOP_LINEAR_PROGRAMMING", true); + System.out.println("---- Linear programming example with CLP ----"); + runLinearProgrammingExample("CLP_LINEAR_PROGRAMMING", false); + System.out.println("---- Linear programming example with GLPK ----"); + runLinearProgrammingExample("GLPK_LINEAR_PROGRAMMING", false); + } +} diff --git a/libs/or-tools/examples/java/LinearProgrammingExample.java b/libs/or-tools-bin/examples/java/LinearProgrammingExample.java similarity index 100% rename from libs/or-tools/examples/java/LinearProgrammingExample.java rename to libs/or-tools-bin/examples/java/LinearProgrammingExample.java diff --git a/libs/or-tools/examples/java/LiteralSampleSat.java b/libs/or-tools-bin/examples/java/LiteralSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/LiteralSampleSat.java rename to libs/or-tools-bin/examples/java/LiteralSampleSat.java diff --git a/libs/or-tools/examples/java/MagicSquare.java b/libs/or-tools-bin/examples/java/MagicSquare.java similarity index 100% rename from libs/or-tools/examples/java/MagicSquare.java rename to libs/or-tools-bin/examples/java/MagicSquare.java diff --git a/libs/or-tools/examples/java/Map.java b/libs/or-tools-bin/examples/java/Map.java similarity index 100% rename from libs/or-tools/examples/java/Map.java rename to libs/or-tools-bin/examples/java/Map.java diff --git a/libs/or-tools/examples/java/Map2.java b/libs/or-tools-bin/examples/java/Map2.java similarity index 100% rename from libs/or-tools/examples/java/Map2.java rename to libs/or-tools-bin/examples/java/Map2.java diff --git a/libs/or-tools/examples/java/Minesweeper.java b/libs/or-tools-bin/examples/java/Minesweeper.java similarity index 100% rename from libs/or-tools/examples/java/Minesweeper.java rename to libs/or-tools-bin/examples/java/Minesweeper.java diff --git a/libs/or-tools/examples/java/MultiThreadTest.java b/libs/or-tools-bin/examples/java/MultiThreadTest.java similarity index 100% rename from libs/or-tools/examples/java/MultiThreadTest.java rename to libs/or-tools-bin/examples/java/MultiThreadTest.java diff --git a/libs/or-tools/examples/java/MultipleKnapsackSat.java b/libs/or-tools-bin/examples/java/MultipleKnapsackSat.java similarity index 100% rename from libs/or-tools/examples/java/MultipleKnapsackSat.java rename to libs/or-tools-bin/examples/java/MultipleKnapsackSat.java diff --git a/libs/or-tools/examples/java/NQueens.java b/libs/or-tools-bin/examples/java/NQueens.java similarity index 100% rename from libs/or-tools/examples/java/NQueens.java rename to libs/or-tools-bin/examples/java/NQueens.java diff --git a/libs/or-tools/examples/java/NQueens2.java b/libs/or-tools-bin/examples/java/NQueens2.java similarity index 100% rename from libs/or-tools/examples/java/NQueens2.java rename to libs/or-tools-bin/examples/java/NQueens2.java diff --git a/libs/or-tools/examples/java/NoOverlapSampleSat.java b/libs/or-tools-bin/examples/java/NoOverlapSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/NoOverlapSampleSat.java rename to libs/or-tools-bin/examples/java/NoOverlapSampleSat.java diff --git a/libs/or-tools/examples/java/OptionalIntervalSampleSat.java b/libs/or-tools-bin/examples/java/OptionalIntervalSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/OptionalIntervalSampleSat.java rename to libs/or-tools-bin/examples/java/OptionalIntervalSampleSat.java diff --git a/libs/or-tools/examples/java/Partition.java b/libs/or-tools-bin/examples/java/Partition.java similarity index 100% rename from libs/or-tools/examples/java/Partition.java rename to libs/or-tools-bin/examples/java/Partition.java diff --git a/libs/or-tools/examples/java/QuasigroupCompletion.java b/libs/or-tools-bin/examples/java/QuasigroupCompletion.java similarity index 100% rename from libs/or-tools/examples/java/QuasigroupCompletion.java rename to libs/or-tools-bin/examples/java/QuasigroupCompletion.java diff --git a/libs/or-tools-bin/examples/java/README.md b/libs/or-tools-bin/examples/java/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4fff7a60a854fd1df79b9585ed65d120092b3c9d --- /dev/null +++ b/libs/or-tools-bin/examples/java/README.md @@ -0,0 +1,10 @@ +# Java examples +The following examples showcase how to use the different Operations Research libraries. + +# Execution +Running the examples will involve building them, then running them. +You can run the following command from the **top** directory: +```shell +make build SOURCE=examples/java/.java +make run SOURCE=examples/java/.java +``` diff --git a/libs/or-tools/examples/java/RabbitsAndPheasantsSat.java b/libs/or-tools-bin/examples/java/RabbitsAndPheasantsSat.java similarity index 100% rename from libs/or-tools/examples/java/RabbitsAndPheasantsSat.java rename to libs/or-tools-bin/examples/java/RabbitsAndPheasantsSat.java diff --git a/libs/or-tools-bin/examples/java/RabbitsPheasants.java b/libs/or-tools-bin/examples/java/RabbitsPheasants.java new file mode 100644 index 0000000000000000000000000000000000000000..466ff5aee304bb7de6664f1f1ddd46638f6127ce --- /dev/null +++ b/libs/or-tools-bin/examples/java/RabbitsPheasants.java @@ -0,0 +1,59 @@ +// 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. +import com.google.ortools.constraintsolver.ConstraintSolverParameters; +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. + * + */ +public class RabbitsPheasants { + private static Logger logger = Logger.getLogger(RabbitsPheasants.class.getName()); + + static { + System.loadLibrary("jniortools"); + } + + /** + * 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() + .mergeFrom(Solver.defaultSolverParameters()) + .setTraceSearch(traceSearch) + .build(); + Solver solver = new Solver("RabbitsPheasants", parameters); + IntVar rabbits = solver.makeIntVar(0, 100, "rabbits"); + IntVar pheasants = solver.makeIntVar(0, 100, "pheasants"); + solver.addConstraint(solver.makeEquality(solver.makeSum(rabbits, pheasants), 20)); + solver.addConstraint(solver.makeEquality( + solver.makeSum(solver.makeProd(rabbits, 4), solver.makeProd(pheasants, 2)), 56)); + DecisionBuilder db = + solver.makePhase(rabbits, pheasants, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); + solver.newSearch(db); + solver.nextSolution(); + logger.info(rabbits.toString()); + logger.info(pheasants.toString()); + solver.endSearch(); + } + + public static void main(String[] args) throws Exception { + boolean traceSearch = args.length > 0 && args[1].equals("--trace"); + RabbitsPheasants.solve(traceSearch); + } +} diff --git a/libs/or-tools-bin/examples/java/RandomTsp.java b/libs/or-tools-bin/examples/java/RandomTsp.java new file mode 100644 index 0000000000000000000000000000000000000000..deca08fb9e3ddb111d3c7765cbb00c15575996b5 --- /dev/null +++ b/libs/or-tools-bin/examples/java/RandomTsp.java @@ -0,0 +1,140 @@ +// +// 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. +// 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. +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.main; +//import java.io.*; +//import java.text.*; +//import java.util.*; +import java.util.Random; +import java.util.function.LongBinaryOperator; +import java.util.function.LongUnaryOperator; +import java.util.logging.Logger; + +class RandomTsp { + static { + System.loadLibrary("jniortools"); + } + + private static Logger logger = + Logger.getLogger(RandomTsp.class.getName()); + + static class RandomManhattan implements LongBinaryOperator { + 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); + ys[i] = generator.nextInt(1000); + } + } + + public long applyAsLong(long firstIndex, long 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 implements LongUnaryOperator { + public long applyAsLong(long index) { + return 1; + } + } + + static void solve(int size, int forbidden, int seed) { + 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. + LongBinaryOperator distances = new RandomManhattan(manager, size, seed); + routing.setArcCostEvaluatorOfAllVehicles(routing.registerTransitCallback(distances)); + + // Forbid node connections (randomly). + Random randomizer = new Random(); + long forbidden_connections = 0; + while (forbidden_connections < forbidden) { + long from = randomizer.nextInt(size - 1); + long to = randomizer.nextInt(size - 1) + 1; + if (routing.nextVar(from).contains(to)) { + logger.info("Forbidding connection " + from + " -> " + to); + routing.nextVar(from).removeValue(to); + ++forbidden_connections; + } + } + + // Add dummy dimension to test API. + 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(main.defaultRoutingSearchParameters()) + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + + Assignment solution = routing.solveWithParameters(search_parameters); + if (solution != null) { + // Solution cost. + logger.info("Objective : " + solution.objectiveValue()); + // Inspect solution. + // Only one route here; otherwise iterate from 0 to routing.vehicles() - 1 + int route_number = 0; + String route = ""; + for (long node = routing.start(route_number); + !routing.isEnd(node); + node = solution.value(routing.nextVar(node))) { + route += "" + node + " -> "; + } + logger.info(route + "0"); + } + } + + public static void main(String[] args) throws Exception { + int size = 10; + if (args.length > 0) { + size = Integer.parseInt(args[0]); + } + + int forbidden = 0; + if (args.length > 1) { + forbidden = Integer.parseInt(args[1]); + } + + int seed = 0; + if (args.length > 2) { + seed = Integer.parseInt(args[2]); + } + + solve(size, forbidden, seed); + } +} diff --git a/libs/or-tools/examples/java/RankingSampleSat.java b/libs/or-tools-bin/examples/java/RankingSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/RankingSampleSat.java rename to libs/or-tools-bin/examples/java/RankingSampleSat.java diff --git a/libs/or-tools/examples/java/ReifiedSampleSat.java b/libs/or-tools-bin/examples/java/ReifiedSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/ReifiedSampleSat.java rename to libs/or-tools-bin/examples/java/ReifiedSampleSat.java diff --git a/libs/or-tools/examples/java/SearchForAllSolutionsSampleSat.java b/libs/or-tools-bin/examples/java/SearchForAllSolutionsSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/SearchForAllSolutionsSampleSat.java rename to libs/or-tools-bin/examples/java/SearchForAllSolutionsSampleSat.java diff --git a/libs/or-tools/examples/java/SendMoreMoney.java b/libs/or-tools-bin/examples/java/SendMoreMoney.java similarity index 100% rename from libs/or-tools/examples/java/SendMoreMoney.java rename to libs/or-tools-bin/examples/java/SendMoreMoney.java diff --git a/libs/or-tools/examples/java/SendMoreMoney2.java b/libs/or-tools-bin/examples/java/SendMoreMoney2.java similarity index 100% rename from libs/or-tools/examples/java/SendMoreMoney2.java rename to libs/or-tools-bin/examples/java/SendMoreMoney2.java diff --git a/libs/or-tools/examples/java/SendMostMoney.java b/libs/or-tools-bin/examples/java/SendMostMoney.java similarity index 100% rename from libs/or-tools/examples/java/SendMostMoney.java rename to libs/or-tools-bin/examples/java/SendMostMoney.java diff --git a/libs/or-tools/examples/java/Seseman.java b/libs/or-tools-bin/examples/java/Seseman.java similarity index 100% rename from libs/or-tools/examples/java/Seseman.java rename to libs/or-tools-bin/examples/java/Seseman.java diff --git a/libs/or-tools/examples/java/SetCovering.java b/libs/or-tools-bin/examples/java/SetCovering.java similarity index 100% rename from libs/or-tools/examples/java/SetCovering.java rename to libs/or-tools-bin/examples/java/SetCovering.java diff --git a/libs/or-tools/examples/java/SetCovering2.java b/libs/or-tools-bin/examples/java/SetCovering2.java similarity index 100% rename from libs/or-tools/examples/java/SetCovering2.java rename to libs/or-tools-bin/examples/java/SetCovering2.java diff --git a/libs/or-tools/examples/java/SetCovering3.java b/libs/or-tools-bin/examples/java/SetCovering3.java similarity index 100% rename from libs/or-tools/examples/java/SetCovering3.java rename to libs/or-tools-bin/examples/java/SetCovering3.java diff --git a/libs/or-tools/examples/java/SetCovering4.java b/libs/or-tools-bin/examples/java/SetCovering4.java similarity index 100% rename from libs/or-tools/examples/java/SetCovering4.java rename to libs/or-tools-bin/examples/java/SetCovering4.java diff --git a/libs/or-tools/examples/java/SetCoveringDeployment.java b/libs/or-tools-bin/examples/java/SetCoveringDeployment.java similarity index 100% rename from libs/or-tools/examples/java/SetCoveringDeployment.java rename to libs/or-tools-bin/examples/java/SetCoveringDeployment.java diff --git a/libs/or-tools/examples/java/SimpleCpProgram.java b/libs/or-tools-bin/examples/java/SimpleCpProgram.java similarity index 100% rename from libs/or-tools/examples/java/SimpleCpProgram.java rename to libs/or-tools-bin/examples/java/SimpleCpProgram.java diff --git a/libs/or-tools/examples/java/SimpleLpProgram.java b/libs/or-tools-bin/examples/java/SimpleLpProgram.java similarity index 100% rename from libs/or-tools/examples/java/SimpleLpProgram.java rename to libs/or-tools-bin/examples/java/SimpleLpProgram.java diff --git a/libs/or-tools/examples/java/SimpleMipProgram.java b/libs/or-tools-bin/examples/java/SimpleMipProgram.java similarity index 100% rename from libs/or-tools/examples/java/SimpleMipProgram.java rename to libs/or-tools-bin/examples/java/SimpleMipProgram.java diff --git a/libs/or-tools/examples/java/SimpleRoutingProgram.java b/libs/or-tools-bin/examples/java/SimpleRoutingProgram.java similarity index 100% rename from libs/or-tools/examples/java/SimpleRoutingProgram.java rename to libs/or-tools-bin/examples/java/SimpleRoutingProgram.java diff --git a/libs/or-tools/examples/java/SimpleRoutingTest.java b/libs/or-tools-bin/examples/java/SimpleRoutingTest.java similarity index 100% rename from libs/or-tools/examples/java/SimpleRoutingTest.java rename to libs/or-tools-bin/examples/java/SimpleRoutingTest.java diff --git a/libs/or-tools/examples/java/SimpleSatProgram.java b/libs/or-tools-bin/examples/java/SimpleSatProgram.java similarity index 100% rename from libs/or-tools/examples/java/SimpleSatProgram.java rename to libs/or-tools-bin/examples/java/SimpleSatProgram.java diff --git a/libs/or-tools/examples/java/SolutionHintingSampleSat.java b/libs/or-tools-bin/examples/java/SolutionHintingSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/SolutionHintingSampleSat.java rename to libs/or-tools-bin/examples/java/SolutionHintingSampleSat.java diff --git a/libs/or-tools/examples/java/SolveAndPrintIntermediateSolutionsSampleSat.java b/libs/or-tools-bin/examples/java/SolveAndPrintIntermediateSolutionsSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/SolveAndPrintIntermediateSolutionsSampleSat.java rename to libs/or-tools-bin/examples/java/SolveAndPrintIntermediateSolutionsSampleSat.java diff --git a/libs/or-tools/examples/java/SolveWithTimeLimitSampleSat.java b/libs/or-tools-bin/examples/java/SolveWithTimeLimitSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/SolveWithTimeLimitSampleSat.java rename to libs/or-tools-bin/examples/java/SolveWithTimeLimitSampleSat.java diff --git a/libs/or-tools/examples/java/StableMarriage.java b/libs/or-tools-bin/examples/java/StableMarriage.java similarity index 100% rename from libs/or-tools/examples/java/StableMarriage.java rename to libs/or-tools-bin/examples/java/StableMarriage.java diff --git a/libs/or-tools/examples/java/StepFunctionSampleSat.java b/libs/or-tools-bin/examples/java/StepFunctionSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/StepFunctionSampleSat.java rename to libs/or-tools-bin/examples/java/StepFunctionSampleSat.java diff --git a/libs/or-tools/examples/java/StiglerMIP.java b/libs/or-tools-bin/examples/java/StiglerMIP.java similarity index 100% rename from libs/or-tools/examples/java/StiglerMIP.java rename to libs/or-tools-bin/examples/java/StiglerMIP.java diff --git a/libs/or-tools/examples/java/StopAfterNSolutionsSampleSat.java b/libs/or-tools-bin/examples/java/StopAfterNSolutionsSampleSat.java similarity index 100% rename from libs/or-tools/examples/java/StopAfterNSolutionsSampleSat.java rename to libs/or-tools-bin/examples/java/StopAfterNSolutionsSampleSat.java diff --git a/libs/or-tools/examples/java/Strimko2.java b/libs/or-tools-bin/examples/java/Strimko2.java similarity index 100% rename from libs/or-tools/examples/java/Strimko2.java rename to libs/or-tools-bin/examples/java/Strimko2.java diff --git a/libs/or-tools/examples/java/Sudoku.java b/libs/or-tools-bin/examples/java/Sudoku.java similarity index 100% rename from libs/or-tools/examples/java/Sudoku.java rename to libs/or-tools-bin/examples/java/Sudoku.java diff --git a/libs/or-tools/examples/java/SurvoPuzzle.java b/libs/or-tools-bin/examples/java/SurvoPuzzle.java similarity index 100% rename from libs/or-tools/examples/java/SurvoPuzzle.java rename to libs/or-tools-bin/examples/java/SurvoPuzzle.java diff --git a/libs/or-tools/examples/java/ToNum.java b/libs/or-tools-bin/examples/java/ToNum.java similarity index 100% rename from libs/or-tools/examples/java/ToNum.java rename to libs/or-tools-bin/examples/java/ToNum.java diff --git a/libs/or-tools/examples/java/Tsp.java b/libs/or-tools-bin/examples/java/Tsp.java similarity index 100% rename from libs/or-tools/examples/java/Tsp.java rename to libs/or-tools-bin/examples/java/Tsp.java diff --git a/libs/or-tools/examples/java/TspCircuitBoard.java b/libs/or-tools-bin/examples/java/TspCircuitBoard.java similarity index 100% rename from libs/or-tools/examples/java/TspCircuitBoard.java rename to libs/or-tools-bin/examples/java/TspCircuitBoard.java diff --git a/libs/or-tools/examples/java/TspCities.java b/libs/or-tools-bin/examples/java/TspCities.java similarity index 100% rename from libs/or-tools/examples/java/TspCities.java rename to libs/or-tools-bin/examples/java/TspCities.java diff --git a/libs/or-tools/examples/java/TspDistanceMatrix.java b/libs/or-tools-bin/examples/java/TspDistanceMatrix.java similarity index 100% rename from libs/or-tools/examples/java/TspDistanceMatrix.java rename to libs/or-tools-bin/examples/java/TspDistanceMatrix.java diff --git a/libs/or-tools/examples/java/Vrp.java b/libs/or-tools-bin/examples/java/Vrp.java similarity index 100% rename from libs/or-tools/examples/java/Vrp.java rename to libs/or-tools-bin/examples/java/Vrp.java diff --git a/libs/or-tools/examples/java/VrpCapacity.java b/libs/or-tools-bin/examples/java/VrpCapacity.java similarity index 100% rename from libs/or-tools/examples/java/VrpCapacity.java rename to libs/or-tools-bin/examples/java/VrpCapacity.java diff --git a/libs/or-tools/examples/java/VrpDropNodes.java b/libs/or-tools-bin/examples/java/VrpDropNodes.java similarity index 100% rename from libs/or-tools/examples/java/VrpDropNodes.java rename to libs/or-tools-bin/examples/java/VrpDropNodes.java diff --git a/libs/or-tools/examples/java/VrpGlobalSpan.java b/libs/or-tools-bin/examples/java/VrpGlobalSpan.java similarity index 100% rename from libs/or-tools/examples/java/VrpGlobalSpan.java rename to libs/or-tools-bin/examples/java/VrpGlobalSpan.java diff --git a/libs/or-tools/examples/java/VrpInitialRoutes.java b/libs/or-tools-bin/examples/java/VrpInitialRoutes.java similarity index 100% rename from libs/or-tools/examples/java/VrpInitialRoutes.java rename to libs/or-tools-bin/examples/java/VrpInitialRoutes.java diff --git a/libs/or-tools/examples/java/VrpPickupDelivery.java b/libs/or-tools-bin/examples/java/VrpPickupDelivery.java similarity index 100% rename from libs/or-tools/examples/java/VrpPickupDelivery.java rename to libs/or-tools-bin/examples/java/VrpPickupDelivery.java diff --git a/libs/or-tools/examples/java/VrpPickupDeliveryFifo.java b/libs/or-tools-bin/examples/java/VrpPickupDeliveryFifo.java similarity index 100% rename from libs/or-tools/examples/java/VrpPickupDeliveryFifo.java rename to libs/or-tools-bin/examples/java/VrpPickupDeliveryFifo.java diff --git a/libs/or-tools/examples/java/VrpPickupDeliveryLifo.java b/libs/or-tools-bin/examples/java/VrpPickupDeliveryLifo.java similarity index 100% rename from libs/or-tools/examples/java/VrpPickupDeliveryLifo.java rename to libs/or-tools-bin/examples/java/VrpPickupDeliveryLifo.java diff --git a/libs/or-tools/examples/java/VrpResources.java b/libs/or-tools-bin/examples/java/VrpResources.java similarity index 100% rename from libs/or-tools/examples/java/VrpResources.java rename to libs/or-tools-bin/examples/java/VrpResources.java diff --git a/libs/or-tools/examples/java/VrpStartsEnds.java b/libs/or-tools-bin/examples/java/VrpStartsEnds.java similarity index 100% rename from libs/or-tools/examples/java/VrpStartsEnds.java rename to libs/or-tools-bin/examples/java/VrpStartsEnds.java diff --git a/libs/or-tools/examples/java/VrpTimeWindows.java b/libs/or-tools-bin/examples/java/VrpTimeWindows.java similarity index 100% rename from libs/or-tools/examples/java/VrpTimeWindows.java rename to libs/or-tools-bin/examples/java/VrpTimeWindows.java diff --git a/libs/or-tools/examples/java/VrpWithTimeLimit.java b/libs/or-tools-bin/examples/java/VrpWithTimeLimit.java similarity index 100% rename from libs/or-tools/examples/java/VrpWithTimeLimit.java rename to libs/or-tools-bin/examples/java/VrpWithTimeLimit.java diff --git a/libs/or-tools/examples/java/WhoKilledAgatha.java b/libs/or-tools-bin/examples/java/WhoKilledAgatha.java similarity index 100% rename from libs/or-tools/examples/java/WhoKilledAgatha.java rename to libs/or-tools-bin/examples/java/WhoKilledAgatha.java diff --git a/libs/or-tools/examples/java/Xkcd.java b/libs/or-tools-bin/examples/java/Xkcd.java similarity index 100% rename from libs/or-tools/examples/java/Xkcd.java rename to libs/or-tools-bin/examples/java/Xkcd.java diff --git a/libs/or-tools/examples/java/YoungTableaux.java b/libs/or-tools-bin/examples/java/YoungTableaux.java similarity index 100% rename from libs/or-tools/examples/java/YoungTableaux.java rename to libs/or-tools-bin/examples/java/YoungTableaux.java diff --git a/libs/or-tools/include/absl/algorithm/algorithm.h b/libs/or-tools-bin/include/absl/algorithm/algorithm.h similarity index 100% rename from libs/or-tools/include/absl/algorithm/algorithm.h rename to libs/or-tools-bin/include/absl/algorithm/algorithm.h diff --git a/libs/or-tools/include/absl/algorithm/container.h b/libs/or-tools-bin/include/absl/algorithm/container.h similarity index 100% rename from libs/or-tools/include/absl/algorithm/container.h rename to libs/or-tools-bin/include/absl/algorithm/container.h diff --git a/libs/or-tools/include/absl/base/attributes.h b/libs/or-tools-bin/include/absl/base/attributes.h similarity index 100% rename from libs/or-tools/include/absl/base/attributes.h rename to libs/or-tools-bin/include/absl/base/attributes.h diff --git a/libs/or-tools/include/absl/base/call_once.h b/libs/or-tools-bin/include/absl/base/call_once.h similarity index 100% rename from libs/or-tools/include/absl/base/call_once.h rename to libs/or-tools-bin/include/absl/base/call_once.h diff --git a/libs/or-tools/include/absl/base/casts.h b/libs/or-tools-bin/include/absl/base/casts.h similarity index 100% rename from libs/or-tools/include/absl/base/casts.h rename to libs/or-tools-bin/include/absl/base/casts.h diff --git a/libs/or-tools/include/absl/base/config.h b/libs/or-tools-bin/include/absl/base/config.h similarity index 100% rename from libs/or-tools/include/absl/base/config.h rename to libs/or-tools-bin/include/absl/base/config.h diff --git a/libs/or-tools/include/absl/base/const_init.h b/libs/or-tools-bin/include/absl/base/const_init.h similarity index 100% rename from libs/or-tools/include/absl/base/const_init.h rename to libs/or-tools-bin/include/absl/base/const_init.h diff --git a/libs/or-tools/include/absl/base/dynamic_annotations.h b/libs/or-tools-bin/include/absl/base/dynamic_annotations.h similarity index 100% rename from libs/or-tools/include/absl/base/dynamic_annotations.h rename to libs/or-tools-bin/include/absl/base/dynamic_annotations.h diff --git a/libs/or-tools/include/absl/base/internal/atomic_hook.h b/libs/or-tools-bin/include/absl/base/internal/atomic_hook.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/atomic_hook.h rename to libs/or-tools-bin/include/absl/base/internal/atomic_hook.h diff --git a/libs/or-tools/include/absl/base/internal/atomic_hook_test_helper.h b/libs/or-tools-bin/include/absl/base/internal/atomic_hook_test_helper.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/atomic_hook_test_helper.h rename to libs/or-tools-bin/include/absl/base/internal/atomic_hook_test_helper.h diff --git a/libs/or-tools/include/absl/base/internal/bits.h b/libs/or-tools-bin/include/absl/base/internal/bits.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/bits.h rename to libs/or-tools-bin/include/absl/base/internal/bits.h diff --git a/libs/or-tools/include/absl/base/internal/cycleclock.h b/libs/or-tools-bin/include/absl/base/internal/cycleclock.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/cycleclock.h rename to libs/or-tools-bin/include/absl/base/internal/cycleclock.h diff --git a/libs/or-tools/include/absl/base/internal/direct_mmap.h b/libs/or-tools-bin/include/absl/base/internal/direct_mmap.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/direct_mmap.h rename to libs/or-tools-bin/include/absl/base/internal/direct_mmap.h diff --git a/libs/or-tools/include/absl/base/internal/endian.h b/libs/or-tools-bin/include/absl/base/internal/endian.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/endian.h rename to libs/or-tools-bin/include/absl/base/internal/endian.h diff --git a/libs/or-tools/include/absl/base/internal/errno_saver.h b/libs/or-tools-bin/include/absl/base/internal/errno_saver.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/errno_saver.h rename to libs/or-tools-bin/include/absl/base/internal/errno_saver.h diff --git a/libs/or-tools/include/absl/base/internal/exception_safety_testing.h b/libs/or-tools-bin/include/absl/base/internal/exception_safety_testing.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/exception_safety_testing.h rename to libs/or-tools-bin/include/absl/base/internal/exception_safety_testing.h diff --git a/libs/or-tools/include/absl/base/internal/exception_testing.h b/libs/or-tools-bin/include/absl/base/internal/exception_testing.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/exception_testing.h rename to libs/or-tools-bin/include/absl/base/internal/exception_testing.h diff --git a/libs/or-tools/include/absl/base/internal/exponential_biased.h b/libs/or-tools-bin/include/absl/base/internal/exponential_biased.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/exponential_biased.h rename to libs/or-tools-bin/include/absl/base/internal/exponential_biased.h diff --git a/libs/or-tools/include/absl/base/internal/hide_ptr.h b/libs/or-tools-bin/include/absl/base/internal/hide_ptr.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/hide_ptr.h rename to libs/or-tools-bin/include/absl/base/internal/hide_ptr.h diff --git a/libs/or-tools/include/absl/base/internal/identity.h b/libs/or-tools-bin/include/absl/base/internal/identity.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/identity.h rename to libs/or-tools-bin/include/absl/base/internal/identity.h diff --git a/libs/or-tools/include/absl/base/internal/inline_variable.h b/libs/or-tools-bin/include/absl/base/internal/inline_variable.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/inline_variable.h rename to libs/or-tools-bin/include/absl/base/internal/inline_variable.h diff --git a/libs/or-tools/include/absl/base/internal/inline_variable_testing.h b/libs/or-tools-bin/include/absl/base/internal/inline_variable_testing.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/inline_variable_testing.h rename to libs/or-tools-bin/include/absl/base/internal/inline_variable_testing.h diff --git a/libs/or-tools/include/absl/base/internal/invoke.h b/libs/or-tools-bin/include/absl/base/internal/invoke.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/invoke.h rename to libs/or-tools-bin/include/absl/base/internal/invoke.h diff --git a/libs/or-tools/include/absl/base/internal/low_level_alloc.h b/libs/or-tools-bin/include/absl/base/internal/low_level_alloc.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/low_level_alloc.h rename to libs/or-tools-bin/include/absl/base/internal/low_level_alloc.h diff --git a/libs/or-tools/include/absl/base/internal/low_level_scheduling.h b/libs/or-tools-bin/include/absl/base/internal/low_level_scheduling.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/low_level_scheduling.h rename to libs/or-tools-bin/include/absl/base/internal/low_level_scheduling.h diff --git a/libs/or-tools/include/absl/base/internal/per_thread_tls.h b/libs/or-tools-bin/include/absl/base/internal/per_thread_tls.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/per_thread_tls.h rename to libs/or-tools-bin/include/absl/base/internal/per_thread_tls.h diff --git a/libs/or-tools/include/absl/base/internal/periodic_sampler.h b/libs/or-tools-bin/include/absl/base/internal/periodic_sampler.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/periodic_sampler.h rename to libs/or-tools-bin/include/absl/base/internal/periodic_sampler.h diff --git a/libs/or-tools/include/absl/base/internal/pretty_function.h b/libs/or-tools-bin/include/absl/base/internal/pretty_function.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/pretty_function.h rename to libs/or-tools-bin/include/absl/base/internal/pretty_function.h diff --git a/libs/or-tools/include/absl/base/internal/raw_logging.h b/libs/or-tools-bin/include/absl/base/internal/raw_logging.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/raw_logging.h rename to libs/or-tools-bin/include/absl/base/internal/raw_logging.h diff --git a/libs/or-tools/include/absl/base/internal/scheduling_mode.h b/libs/or-tools-bin/include/absl/base/internal/scheduling_mode.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/scheduling_mode.h rename to libs/or-tools-bin/include/absl/base/internal/scheduling_mode.h diff --git a/libs/or-tools/include/absl/base/internal/scoped_set_env.h b/libs/or-tools-bin/include/absl/base/internal/scoped_set_env.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/scoped_set_env.h rename to libs/or-tools-bin/include/absl/base/internal/scoped_set_env.h diff --git a/libs/or-tools/include/absl/base/internal/spinlock.h b/libs/or-tools-bin/include/absl/base/internal/spinlock.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/spinlock.h rename to libs/or-tools-bin/include/absl/base/internal/spinlock.h diff --git a/libs/or-tools/include/absl/base/internal/spinlock_akaros.inc b/libs/or-tools-bin/include/absl/base/internal/spinlock_akaros.inc similarity index 100% rename from libs/or-tools/include/absl/base/internal/spinlock_akaros.inc rename to libs/or-tools-bin/include/absl/base/internal/spinlock_akaros.inc diff --git a/libs/or-tools/include/absl/base/internal/spinlock_linux.inc b/libs/or-tools-bin/include/absl/base/internal/spinlock_linux.inc similarity index 100% rename from libs/or-tools/include/absl/base/internal/spinlock_linux.inc rename to libs/or-tools-bin/include/absl/base/internal/spinlock_linux.inc diff --git a/libs/or-tools/include/absl/base/internal/spinlock_posix.inc b/libs/or-tools-bin/include/absl/base/internal/spinlock_posix.inc similarity index 100% rename from libs/or-tools/include/absl/base/internal/spinlock_posix.inc rename to libs/or-tools-bin/include/absl/base/internal/spinlock_posix.inc diff --git a/libs/or-tools/include/absl/base/internal/spinlock_wait.h b/libs/or-tools-bin/include/absl/base/internal/spinlock_wait.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/spinlock_wait.h rename to libs/or-tools-bin/include/absl/base/internal/spinlock_wait.h diff --git a/libs/or-tools/include/absl/base/internal/spinlock_win32.inc b/libs/or-tools-bin/include/absl/base/internal/spinlock_win32.inc similarity index 100% rename from libs/or-tools/include/absl/base/internal/spinlock_win32.inc rename to libs/or-tools-bin/include/absl/base/internal/spinlock_win32.inc diff --git a/libs/or-tools/include/absl/base/internal/sysinfo.h b/libs/or-tools-bin/include/absl/base/internal/sysinfo.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/sysinfo.h rename to libs/or-tools-bin/include/absl/base/internal/sysinfo.h diff --git a/libs/or-tools/include/absl/base/internal/thread_annotations.h b/libs/or-tools-bin/include/absl/base/internal/thread_annotations.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/thread_annotations.h rename to libs/or-tools-bin/include/absl/base/internal/thread_annotations.h diff --git a/libs/or-tools/include/absl/base/internal/thread_identity.h b/libs/or-tools-bin/include/absl/base/internal/thread_identity.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/thread_identity.h rename to libs/or-tools-bin/include/absl/base/internal/thread_identity.h diff --git a/libs/or-tools/include/absl/base/internal/throw_delegate.h b/libs/or-tools-bin/include/absl/base/internal/throw_delegate.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/throw_delegate.h rename to libs/or-tools-bin/include/absl/base/internal/throw_delegate.h diff --git a/libs/or-tools/include/absl/base/internal/tsan_mutex_interface.h b/libs/or-tools-bin/include/absl/base/internal/tsan_mutex_interface.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/tsan_mutex_interface.h rename to libs/or-tools-bin/include/absl/base/internal/tsan_mutex_interface.h diff --git a/libs/or-tools/include/absl/base/internal/unaligned_access.h b/libs/or-tools-bin/include/absl/base/internal/unaligned_access.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/unaligned_access.h rename to libs/or-tools-bin/include/absl/base/internal/unaligned_access.h diff --git a/libs/or-tools/include/absl/base/internal/unscaledcycleclock.h b/libs/or-tools-bin/include/absl/base/internal/unscaledcycleclock.h similarity index 100% rename from libs/or-tools/include/absl/base/internal/unscaledcycleclock.h rename to libs/or-tools-bin/include/absl/base/internal/unscaledcycleclock.h diff --git a/libs/or-tools/include/absl/base/log_severity.h b/libs/or-tools-bin/include/absl/base/log_severity.h similarity index 100% rename from libs/or-tools/include/absl/base/log_severity.h rename to libs/or-tools-bin/include/absl/base/log_severity.h diff --git a/libs/or-tools/include/absl/base/macros.h b/libs/or-tools-bin/include/absl/base/macros.h similarity index 100% rename from libs/or-tools/include/absl/base/macros.h rename to libs/or-tools-bin/include/absl/base/macros.h diff --git a/libs/or-tools/include/absl/base/optimization.h b/libs/or-tools-bin/include/absl/base/optimization.h similarity index 100% rename from libs/or-tools/include/absl/base/optimization.h rename to libs/or-tools-bin/include/absl/base/optimization.h diff --git a/libs/or-tools/include/absl/base/options.h b/libs/or-tools-bin/include/absl/base/options.h similarity index 100% rename from libs/or-tools/include/absl/base/options.h rename to libs/or-tools-bin/include/absl/base/options.h diff --git a/libs/or-tools/include/absl/base/policy_checks.h b/libs/or-tools-bin/include/absl/base/policy_checks.h similarity index 100% rename from libs/or-tools/include/absl/base/policy_checks.h rename to libs/or-tools-bin/include/absl/base/policy_checks.h diff --git a/libs/or-tools/include/absl/base/port.h b/libs/or-tools-bin/include/absl/base/port.h similarity index 100% rename from libs/or-tools/include/absl/base/port.h rename to libs/or-tools-bin/include/absl/base/port.h diff --git a/libs/or-tools/include/absl/base/thread_annotations.h b/libs/or-tools-bin/include/absl/base/thread_annotations.h similarity index 100% rename from libs/or-tools/include/absl/base/thread_annotations.h rename to libs/or-tools-bin/include/absl/base/thread_annotations.h diff --git a/libs/or-tools/include/absl/container/btree_map.h b/libs/or-tools-bin/include/absl/container/btree_map.h similarity index 100% rename from libs/or-tools/include/absl/container/btree_map.h rename to libs/or-tools-bin/include/absl/container/btree_map.h diff --git a/libs/or-tools/include/absl/container/btree_set.h b/libs/or-tools-bin/include/absl/container/btree_set.h similarity index 100% rename from libs/or-tools/include/absl/container/btree_set.h rename to libs/or-tools-bin/include/absl/container/btree_set.h diff --git a/libs/or-tools/include/absl/container/btree_test.h b/libs/or-tools-bin/include/absl/container/btree_test.h similarity index 100% rename from libs/or-tools/include/absl/container/btree_test.h rename to libs/or-tools-bin/include/absl/container/btree_test.h diff --git a/libs/or-tools/include/absl/container/fixed_array.h b/libs/or-tools-bin/include/absl/container/fixed_array.h similarity index 100% rename from libs/or-tools/include/absl/container/fixed_array.h rename to libs/or-tools-bin/include/absl/container/fixed_array.h diff --git a/libs/or-tools/include/absl/container/flat_hash_map.h b/libs/or-tools-bin/include/absl/container/flat_hash_map.h similarity index 100% rename from libs/or-tools/include/absl/container/flat_hash_map.h rename to libs/or-tools-bin/include/absl/container/flat_hash_map.h diff --git a/libs/or-tools/include/absl/container/flat_hash_set.h b/libs/or-tools-bin/include/absl/container/flat_hash_set.h similarity index 100% rename from libs/or-tools/include/absl/container/flat_hash_set.h rename to libs/or-tools-bin/include/absl/container/flat_hash_set.h diff --git a/libs/or-tools/include/absl/container/inlined_vector.h b/libs/or-tools-bin/include/absl/container/inlined_vector.h similarity index 100% rename from libs/or-tools/include/absl/container/inlined_vector.h rename to libs/or-tools-bin/include/absl/container/inlined_vector.h diff --git a/libs/or-tools/include/absl/container/internal/btree.h b/libs/or-tools-bin/include/absl/container/internal/btree.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/btree.h rename to libs/or-tools-bin/include/absl/container/internal/btree.h diff --git a/libs/or-tools/include/absl/container/internal/btree_container.h b/libs/or-tools-bin/include/absl/container/internal/btree_container.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/btree_container.h rename to libs/or-tools-bin/include/absl/container/internal/btree_container.h diff --git a/libs/or-tools/include/absl/container/internal/common.h b/libs/or-tools-bin/include/absl/container/internal/common.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/common.h rename to libs/or-tools-bin/include/absl/container/internal/common.h diff --git a/libs/or-tools/include/absl/container/internal/compressed_tuple.h b/libs/or-tools-bin/include/absl/container/internal/compressed_tuple.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/compressed_tuple.h rename to libs/or-tools-bin/include/absl/container/internal/compressed_tuple.h diff --git a/libs/or-tools/include/absl/container/internal/container_memory.h b/libs/or-tools-bin/include/absl/container/internal/container_memory.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/container_memory.h rename to libs/or-tools-bin/include/absl/container/internal/container_memory.h diff --git a/libs/or-tools/include/absl/container/internal/counting_allocator.h b/libs/or-tools-bin/include/absl/container/internal/counting_allocator.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/counting_allocator.h rename to libs/or-tools-bin/include/absl/container/internal/counting_allocator.h diff --git a/libs/or-tools/include/absl/container/internal/hash_function_defaults.h b/libs/or-tools-bin/include/absl/container/internal/hash_function_defaults.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/hash_function_defaults.h rename to libs/or-tools-bin/include/absl/container/internal/hash_function_defaults.h diff --git a/libs/or-tools/include/absl/container/internal/hash_generator_testing.h b/libs/or-tools-bin/include/absl/container/internal/hash_generator_testing.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/hash_generator_testing.h rename to libs/or-tools-bin/include/absl/container/internal/hash_generator_testing.h diff --git a/libs/or-tools/include/absl/container/internal/hash_policy_testing.h b/libs/or-tools-bin/include/absl/container/internal/hash_policy_testing.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/hash_policy_testing.h rename to libs/or-tools-bin/include/absl/container/internal/hash_policy_testing.h diff --git a/libs/or-tools/include/absl/container/internal/hash_policy_traits.h b/libs/or-tools-bin/include/absl/container/internal/hash_policy_traits.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/hash_policy_traits.h rename to libs/or-tools-bin/include/absl/container/internal/hash_policy_traits.h diff --git a/libs/or-tools/include/absl/container/internal/hashtable_debug.h b/libs/or-tools-bin/include/absl/container/internal/hashtable_debug.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/hashtable_debug.h rename to libs/or-tools-bin/include/absl/container/internal/hashtable_debug.h diff --git a/libs/or-tools/include/absl/container/internal/hashtable_debug_hooks.h b/libs/or-tools-bin/include/absl/container/internal/hashtable_debug_hooks.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/hashtable_debug_hooks.h rename to libs/or-tools-bin/include/absl/container/internal/hashtable_debug_hooks.h diff --git a/libs/or-tools/include/absl/container/internal/hashtablez_sampler.h b/libs/or-tools-bin/include/absl/container/internal/hashtablez_sampler.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/hashtablez_sampler.h rename to libs/or-tools-bin/include/absl/container/internal/hashtablez_sampler.h diff --git a/libs/or-tools/include/absl/container/internal/have_sse.h b/libs/or-tools-bin/include/absl/container/internal/have_sse.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/have_sse.h rename to libs/or-tools-bin/include/absl/container/internal/have_sse.h diff --git a/libs/or-tools/include/absl/container/internal/inlined_vector.h b/libs/or-tools-bin/include/absl/container/internal/inlined_vector.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/inlined_vector.h rename to libs/or-tools-bin/include/absl/container/internal/inlined_vector.h diff --git a/libs/or-tools/include/absl/container/internal/layout.h b/libs/or-tools-bin/include/absl/container/internal/layout.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/layout.h rename to libs/or-tools-bin/include/absl/container/internal/layout.h diff --git a/libs/or-tools/include/absl/container/internal/node_hash_policy.h b/libs/or-tools-bin/include/absl/container/internal/node_hash_policy.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/node_hash_policy.h rename to libs/or-tools-bin/include/absl/container/internal/node_hash_policy.h diff --git a/libs/or-tools/include/absl/container/internal/raw_hash_map.h b/libs/or-tools-bin/include/absl/container/internal/raw_hash_map.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/raw_hash_map.h rename to libs/or-tools-bin/include/absl/container/internal/raw_hash_map.h diff --git a/libs/or-tools/include/absl/container/internal/raw_hash_set.h b/libs/or-tools-bin/include/absl/container/internal/raw_hash_set.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/raw_hash_set.h rename to libs/or-tools-bin/include/absl/container/internal/raw_hash_set.h diff --git a/libs/or-tools/include/absl/container/internal/test_instance_tracker.h b/libs/or-tools-bin/include/absl/container/internal/test_instance_tracker.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/test_instance_tracker.h rename to libs/or-tools-bin/include/absl/container/internal/test_instance_tracker.h diff --git a/libs/or-tools/include/absl/container/internal/tracked.h b/libs/or-tools-bin/include/absl/container/internal/tracked.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/tracked.h rename to libs/or-tools-bin/include/absl/container/internal/tracked.h diff --git a/libs/or-tools/include/absl/container/internal/unordered_map_constructor_test.h b/libs/or-tools-bin/include/absl/container/internal/unordered_map_constructor_test.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/unordered_map_constructor_test.h rename to libs/or-tools-bin/include/absl/container/internal/unordered_map_constructor_test.h diff --git a/libs/or-tools/include/absl/container/internal/unordered_map_lookup_test.h b/libs/or-tools-bin/include/absl/container/internal/unordered_map_lookup_test.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/unordered_map_lookup_test.h rename to libs/or-tools-bin/include/absl/container/internal/unordered_map_lookup_test.h diff --git a/libs/or-tools/include/absl/container/internal/unordered_map_members_test.h b/libs/or-tools-bin/include/absl/container/internal/unordered_map_members_test.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/unordered_map_members_test.h rename to libs/or-tools-bin/include/absl/container/internal/unordered_map_members_test.h diff --git a/libs/or-tools/include/absl/container/internal/unordered_map_modifiers_test.h b/libs/or-tools-bin/include/absl/container/internal/unordered_map_modifiers_test.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/unordered_map_modifiers_test.h rename to libs/or-tools-bin/include/absl/container/internal/unordered_map_modifiers_test.h diff --git a/libs/or-tools/include/absl/container/internal/unordered_set_constructor_test.h b/libs/or-tools-bin/include/absl/container/internal/unordered_set_constructor_test.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/unordered_set_constructor_test.h rename to libs/or-tools-bin/include/absl/container/internal/unordered_set_constructor_test.h diff --git a/libs/or-tools/include/absl/container/internal/unordered_set_lookup_test.h b/libs/or-tools-bin/include/absl/container/internal/unordered_set_lookup_test.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/unordered_set_lookup_test.h rename to libs/or-tools-bin/include/absl/container/internal/unordered_set_lookup_test.h diff --git a/libs/or-tools/include/absl/container/internal/unordered_set_members_test.h b/libs/or-tools-bin/include/absl/container/internal/unordered_set_members_test.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/unordered_set_members_test.h rename to libs/or-tools-bin/include/absl/container/internal/unordered_set_members_test.h diff --git a/libs/or-tools/include/absl/container/internal/unordered_set_modifiers_test.h b/libs/or-tools-bin/include/absl/container/internal/unordered_set_modifiers_test.h similarity index 100% rename from libs/or-tools/include/absl/container/internal/unordered_set_modifiers_test.h rename to libs/or-tools-bin/include/absl/container/internal/unordered_set_modifiers_test.h diff --git a/libs/or-tools/include/absl/container/node_hash_map.h b/libs/or-tools-bin/include/absl/container/node_hash_map.h similarity index 100% rename from libs/or-tools/include/absl/container/node_hash_map.h rename to libs/or-tools-bin/include/absl/container/node_hash_map.h diff --git a/libs/or-tools/include/absl/container/node_hash_set.h b/libs/or-tools-bin/include/absl/container/node_hash_set.h similarity index 100% rename from libs/or-tools/include/absl/container/node_hash_set.h rename to libs/or-tools-bin/include/absl/container/node_hash_set.h diff --git a/libs/or-tools/include/absl/debugging/failure_signal_handler.h b/libs/or-tools-bin/include/absl/debugging/failure_signal_handler.h similarity index 100% rename from libs/or-tools/include/absl/debugging/failure_signal_handler.h rename to libs/or-tools-bin/include/absl/debugging/failure_signal_handler.h diff --git a/libs/or-tools/include/absl/debugging/internal/address_is_readable.h b/libs/or-tools-bin/include/absl/debugging/internal/address_is_readable.h similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/address_is_readable.h rename to libs/or-tools-bin/include/absl/debugging/internal/address_is_readable.h diff --git a/libs/or-tools/include/absl/debugging/internal/demangle.h b/libs/or-tools-bin/include/absl/debugging/internal/demangle.h similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/demangle.h rename to libs/or-tools-bin/include/absl/debugging/internal/demangle.h diff --git a/libs/or-tools/include/absl/debugging/internal/elf_mem_image.h b/libs/or-tools-bin/include/absl/debugging/internal/elf_mem_image.h similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/elf_mem_image.h rename to libs/or-tools-bin/include/absl/debugging/internal/elf_mem_image.h diff --git a/libs/or-tools/include/absl/debugging/internal/examine_stack.h b/libs/or-tools-bin/include/absl/debugging/internal/examine_stack.h similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/examine_stack.h rename to libs/or-tools-bin/include/absl/debugging/internal/examine_stack.h diff --git a/libs/or-tools/include/absl/debugging/internal/stack_consumption.h b/libs/or-tools-bin/include/absl/debugging/internal/stack_consumption.h similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/stack_consumption.h rename to libs/or-tools-bin/include/absl/debugging/internal/stack_consumption.h diff --git a/libs/or-tools/include/absl/debugging/internal/stacktrace_aarch64-inl.inc b/libs/or-tools-bin/include/absl/debugging/internal/stacktrace_aarch64-inl.inc similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/stacktrace_aarch64-inl.inc rename to libs/or-tools-bin/include/absl/debugging/internal/stacktrace_aarch64-inl.inc diff --git a/libs/or-tools/include/absl/debugging/internal/stacktrace_arm-inl.inc b/libs/or-tools-bin/include/absl/debugging/internal/stacktrace_arm-inl.inc similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/stacktrace_arm-inl.inc rename to libs/or-tools-bin/include/absl/debugging/internal/stacktrace_arm-inl.inc diff --git a/libs/or-tools/include/absl/debugging/internal/stacktrace_config.h b/libs/or-tools-bin/include/absl/debugging/internal/stacktrace_config.h similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/stacktrace_config.h rename to libs/or-tools-bin/include/absl/debugging/internal/stacktrace_config.h diff --git a/libs/or-tools/include/absl/debugging/internal/stacktrace_generic-inl.inc b/libs/or-tools-bin/include/absl/debugging/internal/stacktrace_generic-inl.inc similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/stacktrace_generic-inl.inc rename to libs/or-tools-bin/include/absl/debugging/internal/stacktrace_generic-inl.inc diff --git a/libs/or-tools/include/absl/debugging/internal/stacktrace_powerpc-inl.inc b/libs/or-tools-bin/include/absl/debugging/internal/stacktrace_powerpc-inl.inc similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/stacktrace_powerpc-inl.inc rename to libs/or-tools-bin/include/absl/debugging/internal/stacktrace_powerpc-inl.inc diff --git a/libs/or-tools/include/absl/debugging/internal/stacktrace_unimplemented-inl.inc b/libs/or-tools-bin/include/absl/debugging/internal/stacktrace_unimplemented-inl.inc similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/stacktrace_unimplemented-inl.inc rename to libs/or-tools-bin/include/absl/debugging/internal/stacktrace_unimplemented-inl.inc diff --git a/libs/or-tools/include/absl/debugging/internal/stacktrace_win32-inl.inc b/libs/or-tools-bin/include/absl/debugging/internal/stacktrace_win32-inl.inc similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/stacktrace_win32-inl.inc rename to libs/or-tools-bin/include/absl/debugging/internal/stacktrace_win32-inl.inc diff --git a/libs/or-tools/include/absl/debugging/internal/stacktrace_x86-inl.inc b/libs/or-tools-bin/include/absl/debugging/internal/stacktrace_x86-inl.inc similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/stacktrace_x86-inl.inc rename to libs/or-tools-bin/include/absl/debugging/internal/stacktrace_x86-inl.inc diff --git a/libs/or-tools/include/absl/debugging/internal/symbolize.h b/libs/or-tools-bin/include/absl/debugging/internal/symbolize.h similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/symbolize.h rename to libs/or-tools-bin/include/absl/debugging/internal/symbolize.h diff --git a/libs/or-tools/include/absl/debugging/internal/vdso_support.h b/libs/or-tools-bin/include/absl/debugging/internal/vdso_support.h similarity index 100% rename from libs/or-tools/include/absl/debugging/internal/vdso_support.h rename to libs/or-tools-bin/include/absl/debugging/internal/vdso_support.h diff --git a/libs/or-tools/include/absl/debugging/leak_check.h b/libs/or-tools-bin/include/absl/debugging/leak_check.h similarity index 100% rename from libs/or-tools/include/absl/debugging/leak_check.h rename to libs/or-tools-bin/include/absl/debugging/leak_check.h diff --git a/libs/or-tools/include/absl/debugging/stacktrace.h b/libs/or-tools-bin/include/absl/debugging/stacktrace.h similarity index 100% rename from libs/or-tools/include/absl/debugging/stacktrace.h rename to libs/or-tools-bin/include/absl/debugging/stacktrace.h diff --git a/libs/or-tools/include/absl/debugging/symbolize.h b/libs/or-tools-bin/include/absl/debugging/symbolize.h similarity index 100% rename from libs/or-tools/include/absl/debugging/symbolize.h rename to libs/or-tools-bin/include/absl/debugging/symbolize.h diff --git a/libs/or-tools/include/absl/debugging/symbolize_elf.inc b/libs/or-tools-bin/include/absl/debugging/symbolize_elf.inc similarity index 100% rename from libs/or-tools/include/absl/debugging/symbolize_elf.inc rename to libs/or-tools-bin/include/absl/debugging/symbolize_elf.inc diff --git a/libs/or-tools/include/absl/debugging/symbolize_unimplemented.inc b/libs/or-tools-bin/include/absl/debugging/symbolize_unimplemented.inc similarity index 100% rename from libs/or-tools/include/absl/debugging/symbolize_unimplemented.inc rename to libs/or-tools-bin/include/absl/debugging/symbolize_unimplemented.inc diff --git a/libs/or-tools/include/absl/debugging/symbolize_win32.inc b/libs/or-tools-bin/include/absl/debugging/symbolize_win32.inc similarity index 100% rename from libs/or-tools/include/absl/debugging/symbolize_win32.inc rename to libs/or-tools-bin/include/absl/debugging/symbolize_win32.inc diff --git a/libs/or-tools/include/absl/flags/config.h b/libs/or-tools-bin/include/absl/flags/config.h similarity index 100% rename from libs/or-tools/include/absl/flags/config.h rename to libs/or-tools-bin/include/absl/flags/config.h diff --git a/libs/or-tools/include/absl/flags/declare.h b/libs/or-tools-bin/include/absl/flags/declare.h similarity index 100% rename from libs/or-tools/include/absl/flags/declare.h rename to libs/or-tools-bin/include/absl/flags/declare.h diff --git a/libs/or-tools/include/absl/flags/flag.h b/libs/or-tools-bin/include/absl/flags/flag.h similarity index 100% rename from libs/or-tools/include/absl/flags/flag.h rename to libs/or-tools-bin/include/absl/flags/flag.h diff --git a/libs/or-tools/include/absl/flags/internal/commandlineflag.h b/libs/or-tools-bin/include/absl/flags/internal/commandlineflag.h similarity index 100% rename from libs/or-tools/include/absl/flags/internal/commandlineflag.h rename to libs/or-tools-bin/include/absl/flags/internal/commandlineflag.h diff --git a/libs/or-tools/include/absl/flags/internal/flag.h b/libs/or-tools-bin/include/absl/flags/internal/flag.h similarity index 100% rename from libs/or-tools/include/absl/flags/internal/flag.h rename to libs/or-tools-bin/include/absl/flags/internal/flag.h diff --git a/libs/or-tools/include/absl/flags/internal/parse.h b/libs/or-tools-bin/include/absl/flags/internal/parse.h similarity index 100% rename from libs/or-tools/include/absl/flags/internal/parse.h rename to libs/or-tools-bin/include/absl/flags/internal/parse.h diff --git a/libs/or-tools/include/absl/flags/internal/path_util.h b/libs/or-tools-bin/include/absl/flags/internal/path_util.h similarity index 100% rename from libs/or-tools/include/absl/flags/internal/path_util.h rename to libs/or-tools-bin/include/absl/flags/internal/path_util.h diff --git a/libs/or-tools/include/absl/flags/internal/program_name.h b/libs/or-tools-bin/include/absl/flags/internal/program_name.h similarity index 100% rename from libs/or-tools/include/absl/flags/internal/program_name.h rename to libs/or-tools-bin/include/absl/flags/internal/program_name.h diff --git a/libs/or-tools/include/absl/flags/internal/registry.h b/libs/or-tools-bin/include/absl/flags/internal/registry.h similarity index 100% rename from libs/or-tools/include/absl/flags/internal/registry.h rename to libs/or-tools-bin/include/absl/flags/internal/registry.h diff --git a/libs/or-tools/include/absl/flags/internal/type_erased.h b/libs/or-tools-bin/include/absl/flags/internal/type_erased.h similarity index 100% rename from libs/or-tools/include/absl/flags/internal/type_erased.h rename to libs/or-tools-bin/include/absl/flags/internal/type_erased.h diff --git a/libs/or-tools/include/absl/flags/internal/usage.h b/libs/or-tools-bin/include/absl/flags/internal/usage.h similarity index 100% rename from libs/or-tools/include/absl/flags/internal/usage.h rename to libs/or-tools-bin/include/absl/flags/internal/usage.h diff --git a/libs/or-tools/include/absl/flags/marshalling.h b/libs/or-tools-bin/include/absl/flags/marshalling.h similarity index 100% rename from libs/or-tools/include/absl/flags/marshalling.h rename to libs/or-tools-bin/include/absl/flags/marshalling.h diff --git a/libs/or-tools/include/absl/flags/parse.h b/libs/or-tools-bin/include/absl/flags/parse.h similarity index 100% rename from libs/or-tools/include/absl/flags/parse.h rename to libs/or-tools-bin/include/absl/flags/parse.h diff --git a/libs/or-tools/include/absl/flags/usage.h b/libs/or-tools-bin/include/absl/flags/usage.h similarity index 100% rename from libs/or-tools/include/absl/flags/usage.h rename to libs/or-tools-bin/include/absl/flags/usage.h diff --git a/libs/or-tools/include/absl/flags/usage_config.h b/libs/or-tools-bin/include/absl/flags/usage_config.h similarity index 100% rename from libs/or-tools/include/absl/flags/usage_config.h rename to libs/or-tools-bin/include/absl/flags/usage_config.h diff --git a/libs/or-tools/include/absl/functional/bind_front.h b/libs/or-tools-bin/include/absl/functional/bind_front.h similarity index 100% rename from libs/or-tools/include/absl/functional/bind_front.h rename to libs/or-tools-bin/include/absl/functional/bind_front.h diff --git a/libs/or-tools/include/absl/functional/function_ref.h b/libs/or-tools-bin/include/absl/functional/function_ref.h similarity index 100% rename from libs/or-tools/include/absl/functional/function_ref.h rename to libs/or-tools-bin/include/absl/functional/function_ref.h diff --git a/libs/or-tools/include/absl/functional/internal/front_binder.h b/libs/or-tools-bin/include/absl/functional/internal/front_binder.h similarity index 100% rename from libs/or-tools/include/absl/functional/internal/front_binder.h rename to libs/or-tools-bin/include/absl/functional/internal/front_binder.h diff --git a/libs/or-tools/include/absl/functional/internal/function_ref.h b/libs/or-tools-bin/include/absl/functional/internal/function_ref.h similarity index 100% rename from libs/or-tools/include/absl/functional/internal/function_ref.h rename to libs/or-tools-bin/include/absl/functional/internal/function_ref.h diff --git a/libs/or-tools/include/absl/hash/hash.h b/libs/or-tools-bin/include/absl/hash/hash.h similarity index 100% rename from libs/or-tools/include/absl/hash/hash.h rename to libs/or-tools-bin/include/absl/hash/hash.h diff --git a/libs/or-tools/include/absl/hash/hash_testing.h b/libs/or-tools-bin/include/absl/hash/hash_testing.h similarity index 100% rename from libs/or-tools/include/absl/hash/hash_testing.h rename to libs/or-tools-bin/include/absl/hash/hash_testing.h diff --git a/libs/or-tools/include/absl/hash/internal/city.h b/libs/or-tools-bin/include/absl/hash/internal/city.h similarity index 100% rename from libs/or-tools/include/absl/hash/internal/city.h rename to libs/or-tools-bin/include/absl/hash/internal/city.h diff --git a/libs/or-tools/include/absl/hash/internal/hash.h b/libs/or-tools-bin/include/absl/hash/internal/hash.h similarity index 100% rename from libs/or-tools/include/absl/hash/internal/hash.h rename to libs/or-tools-bin/include/absl/hash/internal/hash.h diff --git a/libs/or-tools/include/absl/hash/internal/spy_hash_state.h b/libs/or-tools-bin/include/absl/hash/internal/spy_hash_state.h similarity index 100% rename from libs/or-tools/include/absl/hash/internal/spy_hash_state.h rename to libs/or-tools-bin/include/absl/hash/internal/spy_hash_state.h diff --git a/libs/or-tools/include/absl/memory/memory.h b/libs/or-tools-bin/include/absl/memory/memory.h similarity index 100% rename from libs/or-tools/include/absl/memory/memory.h rename to libs/or-tools-bin/include/absl/memory/memory.h diff --git a/libs/or-tools/include/absl/meta/type_traits.h b/libs/or-tools-bin/include/absl/meta/type_traits.h similarity index 100% rename from libs/or-tools/include/absl/meta/type_traits.h rename to libs/or-tools-bin/include/absl/meta/type_traits.h diff --git a/libs/or-tools/include/absl/numeric/int128.h b/libs/or-tools-bin/include/absl/numeric/int128.h similarity index 100% rename from libs/or-tools/include/absl/numeric/int128.h rename to libs/or-tools-bin/include/absl/numeric/int128.h diff --git a/libs/or-tools/include/absl/numeric/int128_have_intrinsic.inc b/libs/or-tools-bin/include/absl/numeric/int128_have_intrinsic.inc similarity index 100% rename from libs/or-tools/include/absl/numeric/int128_have_intrinsic.inc rename to libs/or-tools-bin/include/absl/numeric/int128_have_intrinsic.inc diff --git a/libs/or-tools/include/absl/numeric/int128_no_intrinsic.inc b/libs/or-tools-bin/include/absl/numeric/int128_no_intrinsic.inc similarity index 100% rename from libs/or-tools/include/absl/numeric/int128_no_intrinsic.inc rename to libs/or-tools-bin/include/absl/numeric/int128_no_intrinsic.inc diff --git a/libs/or-tools/include/absl/random/bernoulli_distribution.h b/libs/or-tools-bin/include/absl/random/bernoulli_distribution.h similarity index 100% rename from libs/or-tools/include/absl/random/bernoulli_distribution.h rename to libs/or-tools-bin/include/absl/random/bernoulli_distribution.h diff --git a/libs/or-tools/include/absl/random/beta_distribution.h b/libs/or-tools-bin/include/absl/random/beta_distribution.h similarity index 100% rename from libs/or-tools/include/absl/random/beta_distribution.h rename to libs/or-tools-bin/include/absl/random/beta_distribution.h diff --git a/libs/or-tools/include/absl/random/bit_gen_ref.h b/libs/or-tools-bin/include/absl/random/bit_gen_ref.h similarity index 100% rename from libs/or-tools/include/absl/random/bit_gen_ref.h rename to libs/or-tools-bin/include/absl/random/bit_gen_ref.h diff --git a/libs/or-tools/include/absl/random/discrete_distribution.h b/libs/or-tools-bin/include/absl/random/discrete_distribution.h similarity index 100% rename from libs/or-tools/include/absl/random/discrete_distribution.h rename to libs/or-tools-bin/include/absl/random/discrete_distribution.h diff --git a/libs/or-tools/include/absl/random/distribution_format_traits.h b/libs/or-tools-bin/include/absl/random/distribution_format_traits.h similarity index 100% rename from libs/or-tools/include/absl/random/distribution_format_traits.h rename to libs/or-tools-bin/include/absl/random/distribution_format_traits.h diff --git a/libs/or-tools/include/absl/random/distributions.h b/libs/or-tools-bin/include/absl/random/distributions.h similarity index 100% rename from libs/or-tools/include/absl/random/distributions.h rename to libs/or-tools-bin/include/absl/random/distributions.h diff --git a/libs/or-tools/include/absl/random/exponential_distribution.h b/libs/or-tools-bin/include/absl/random/exponential_distribution.h similarity index 100% rename from libs/or-tools/include/absl/random/exponential_distribution.h rename to libs/or-tools-bin/include/absl/random/exponential_distribution.h diff --git a/libs/or-tools/include/absl/random/gaussian_distribution.h b/libs/or-tools-bin/include/absl/random/gaussian_distribution.h similarity index 100% rename from libs/or-tools/include/absl/random/gaussian_distribution.h rename to libs/or-tools-bin/include/absl/random/gaussian_distribution.h diff --git a/libs/or-tools/include/absl/random/internal/chi_square.h b/libs/or-tools-bin/include/absl/random/internal/chi_square.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/chi_square.h rename to libs/or-tools-bin/include/absl/random/internal/chi_square.h diff --git a/libs/or-tools/include/absl/random/internal/distribution_caller.h b/libs/or-tools-bin/include/absl/random/internal/distribution_caller.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/distribution_caller.h rename to libs/or-tools-bin/include/absl/random/internal/distribution_caller.h diff --git a/libs/or-tools/include/absl/random/internal/distribution_test_util.h b/libs/or-tools-bin/include/absl/random/internal/distribution_test_util.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/distribution_test_util.h rename to libs/or-tools-bin/include/absl/random/internal/distribution_test_util.h diff --git a/libs/or-tools/include/absl/random/internal/distributions.h b/libs/or-tools-bin/include/absl/random/internal/distributions.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/distributions.h rename to libs/or-tools-bin/include/absl/random/internal/distributions.h diff --git a/libs/or-tools/include/absl/random/internal/explicit_seed_seq.h b/libs/or-tools-bin/include/absl/random/internal/explicit_seed_seq.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/explicit_seed_seq.h rename to libs/or-tools-bin/include/absl/random/internal/explicit_seed_seq.h diff --git a/libs/or-tools/include/absl/random/internal/fast_uniform_bits.h b/libs/or-tools-bin/include/absl/random/internal/fast_uniform_bits.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/fast_uniform_bits.h rename to libs/or-tools-bin/include/absl/random/internal/fast_uniform_bits.h diff --git a/libs/or-tools/include/absl/random/internal/fastmath.h b/libs/or-tools-bin/include/absl/random/internal/fastmath.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/fastmath.h rename to libs/or-tools-bin/include/absl/random/internal/fastmath.h diff --git a/libs/or-tools/include/absl/random/internal/generate_real.h b/libs/or-tools-bin/include/absl/random/internal/generate_real.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/generate_real.h rename to libs/or-tools-bin/include/absl/random/internal/generate_real.h diff --git a/libs/or-tools/include/absl/random/internal/iostream_state_saver.h b/libs/or-tools-bin/include/absl/random/internal/iostream_state_saver.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/iostream_state_saver.h rename to libs/or-tools-bin/include/absl/random/internal/iostream_state_saver.h diff --git a/libs/or-tools/include/absl/random/internal/mock_overload_set.h b/libs/or-tools-bin/include/absl/random/internal/mock_overload_set.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/mock_overload_set.h rename to libs/or-tools-bin/include/absl/random/internal/mock_overload_set.h diff --git a/libs/or-tools/include/absl/random/internal/mocking_bit_gen_base.h b/libs/or-tools-bin/include/absl/random/internal/mocking_bit_gen_base.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/mocking_bit_gen_base.h rename to libs/or-tools-bin/include/absl/random/internal/mocking_bit_gen_base.h diff --git a/libs/or-tools/include/absl/random/internal/nanobenchmark.h b/libs/or-tools-bin/include/absl/random/internal/nanobenchmark.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/nanobenchmark.h rename to libs/or-tools-bin/include/absl/random/internal/nanobenchmark.h diff --git a/libs/or-tools/include/absl/random/internal/nonsecure_base.h b/libs/or-tools-bin/include/absl/random/internal/nonsecure_base.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/nonsecure_base.h rename to libs/or-tools-bin/include/absl/random/internal/nonsecure_base.h diff --git a/libs/or-tools/include/absl/random/internal/pcg_engine.h b/libs/or-tools-bin/include/absl/random/internal/pcg_engine.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/pcg_engine.h rename to libs/or-tools-bin/include/absl/random/internal/pcg_engine.h diff --git a/libs/or-tools/include/absl/random/internal/platform.h b/libs/or-tools-bin/include/absl/random/internal/platform.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/platform.h rename to libs/or-tools-bin/include/absl/random/internal/platform.h diff --git a/libs/or-tools/include/absl/random/internal/pool_urbg.h b/libs/or-tools-bin/include/absl/random/internal/pool_urbg.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/pool_urbg.h rename to libs/or-tools-bin/include/absl/random/internal/pool_urbg.h diff --git a/libs/or-tools/include/absl/random/internal/randen-keys.inc b/libs/or-tools-bin/include/absl/random/internal/randen-keys.inc similarity index 100% rename from libs/or-tools/include/absl/random/internal/randen-keys.inc rename to libs/or-tools-bin/include/absl/random/internal/randen-keys.inc diff --git a/libs/or-tools/include/absl/random/internal/randen.h b/libs/or-tools-bin/include/absl/random/internal/randen.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/randen.h rename to libs/or-tools-bin/include/absl/random/internal/randen.h diff --git a/libs/or-tools/include/absl/random/internal/randen_detect.h b/libs/or-tools-bin/include/absl/random/internal/randen_detect.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/randen_detect.h rename to libs/or-tools-bin/include/absl/random/internal/randen_detect.h diff --git a/libs/or-tools/include/absl/random/internal/randen_engine.h b/libs/or-tools-bin/include/absl/random/internal/randen_engine.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/randen_engine.h rename to libs/or-tools-bin/include/absl/random/internal/randen_engine.h diff --git a/libs/or-tools/include/absl/random/internal/randen_hwaes.h b/libs/or-tools-bin/include/absl/random/internal/randen_hwaes.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/randen_hwaes.h rename to libs/or-tools-bin/include/absl/random/internal/randen_hwaes.h diff --git a/libs/or-tools/include/absl/random/internal/randen_slow.h b/libs/or-tools-bin/include/absl/random/internal/randen_slow.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/randen_slow.h rename to libs/or-tools-bin/include/absl/random/internal/randen_slow.h diff --git a/libs/or-tools/include/absl/random/internal/randen_traits.h b/libs/or-tools-bin/include/absl/random/internal/randen_traits.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/randen_traits.h rename to libs/or-tools-bin/include/absl/random/internal/randen_traits.h diff --git a/libs/or-tools/include/absl/random/internal/salted_seed_seq.h b/libs/or-tools-bin/include/absl/random/internal/salted_seed_seq.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/salted_seed_seq.h rename to libs/or-tools-bin/include/absl/random/internal/salted_seed_seq.h diff --git a/libs/or-tools/include/absl/random/internal/seed_material.h b/libs/or-tools-bin/include/absl/random/internal/seed_material.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/seed_material.h rename to libs/or-tools-bin/include/absl/random/internal/seed_material.h diff --git a/libs/or-tools/include/absl/random/internal/sequence_urbg.h b/libs/or-tools-bin/include/absl/random/internal/sequence_urbg.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/sequence_urbg.h rename to libs/or-tools-bin/include/absl/random/internal/sequence_urbg.h diff --git a/libs/or-tools/include/absl/random/internal/traits.h b/libs/or-tools-bin/include/absl/random/internal/traits.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/traits.h rename to libs/or-tools-bin/include/absl/random/internal/traits.h diff --git a/libs/or-tools/include/absl/random/internal/uniform_helper.h b/libs/or-tools-bin/include/absl/random/internal/uniform_helper.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/uniform_helper.h rename to libs/or-tools-bin/include/absl/random/internal/uniform_helper.h diff --git a/libs/or-tools/include/absl/random/internal/wide_multiply.h b/libs/or-tools-bin/include/absl/random/internal/wide_multiply.h similarity index 100% rename from libs/or-tools/include/absl/random/internal/wide_multiply.h rename to libs/or-tools-bin/include/absl/random/internal/wide_multiply.h diff --git a/libs/or-tools/include/absl/random/log_uniform_int_distribution.h b/libs/or-tools-bin/include/absl/random/log_uniform_int_distribution.h similarity index 100% rename from libs/or-tools/include/absl/random/log_uniform_int_distribution.h rename to libs/or-tools-bin/include/absl/random/log_uniform_int_distribution.h diff --git a/libs/or-tools/include/absl/random/mock_distributions.h b/libs/or-tools-bin/include/absl/random/mock_distributions.h similarity index 100% rename from libs/or-tools/include/absl/random/mock_distributions.h rename to libs/or-tools-bin/include/absl/random/mock_distributions.h diff --git a/libs/or-tools/include/absl/random/mocking_bit_gen.h b/libs/or-tools-bin/include/absl/random/mocking_bit_gen.h similarity index 100% rename from libs/or-tools/include/absl/random/mocking_bit_gen.h rename to libs/or-tools-bin/include/absl/random/mocking_bit_gen.h diff --git a/libs/or-tools/include/absl/random/poisson_distribution.h b/libs/or-tools-bin/include/absl/random/poisson_distribution.h similarity index 100% rename from libs/or-tools/include/absl/random/poisson_distribution.h rename to libs/or-tools-bin/include/absl/random/poisson_distribution.h diff --git a/libs/or-tools/include/absl/random/random.h b/libs/or-tools-bin/include/absl/random/random.h similarity index 100% rename from libs/or-tools/include/absl/random/random.h rename to libs/or-tools-bin/include/absl/random/random.h diff --git a/libs/or-tools/include/absl/random/seed_gen_exception.h b/libs/or-tools-bin/include/absl/random/seed_gen_exception.h similarity index 100% rename from libs/or-tools/include/absl/random/seed_gen_exception.h rename to libs/or-tools-bin/include/absl/random/seed_gen_exception.h diff --git a/libs/or-tools/include/absl/random/seed_sequences.h b/libs/or-tools-bin/include/absl/random/seed_sequences.h similarity index 100% rename from libs/or-tools/include/absl/random/seed_sequences.h rename to libs/or-tools-bin/include/absl/random/seed_sequences.h diff --git a/libs/or-tools/include/absl/random/uniform_int_distribution.h b/libs/or-tools-bin/include/absl/random/uniform_int_distribution.h similarity index 100% rename from libs/or-tools/include/absl/random/uniform_int_distribution.h rename to libs/or-tools-bin/include/absl/random/uniform_int_distribution.h diff --git a/libs/or-tools/include/absl/random/uniform_real_distribution.h b/libs/or-tools-bin/include/absl/random/uniform_real_distribution.h similarity index 100% rename from libs/or-tools/include/absl/random/uniform_real_distribution.h rename to libs/or-tools-bin/include/absl/random/uniform_real_distribution.h diff --git a/libs/or-tools/include/absl/random/zipf_distribution.h b/libs/or-tools-bin/include/absl/random/zipf_distribution.h similarity index 100% rename from libs/or-tools/include/absl/random/zipf_distribution.h rename to libs/or-tools-bin/include/absl/random/zipf_distribution.h diff --git a/libs/or-tools/include/absl/status/status.h b/libs/or-tools-bin/include/absl/status/status.h similarity index 100% rename from libs/or-tools/include/absl/status/status.h rename to libs/or-tools-bin/include/absl/status/status.h diff --git a/libs/or-tools/include/absl/status/status_payload_printer.h b/libs/or-tools-bin/include/absl/status/status_payload_printer.h similarity index 100% rename from libs/or-tools/include/absl/status/status_payload_printer.h rename to libs/or-tools-bin/include/absl/status/status_payload_printer.h diff --git a/libs/or-tools/include/absl/strings/ascii.h b/libs/or-tools-bin/include/absl/strings/ascii.h similarity index 100% rename from libs/or-tools/include/absl/strings/ascii.h rename to libs/or-tools-bin/include/absl/strings/ascii.h diff --git a/libs/or-tools/include/absl/strings/charconv.h b/libs/or-tools-bin/include/absl/strings/charconv.h similarity index 100% rename from libs/or-tools/include/absl/strings/charconv.h rename to libs/or-tools-bin/include/absl/strings/charconv.h diff --git a/libs/or-tools/include/absl/strings/cord.h b/libs/or-tools-bin/include/absl/strings/cord.h similarity index 100% rename from libs/or-tools/include/absl/strings/cord.h rename to libs/or-tools-bin/include/absl/strings/cord.h diff --git a/libs/or-tools/include/absl/strings/cord_test_helpers.h b/libs/or-tools-bin/include/absl/strings/cord_test_helpers.h similarity index 100% rename from libs/or-tools/include/absl/strings/cord_test_helpers.h rename to libs/or-tools-bin/include/absl/strings/cord_test_helpers.h diff --git a/libs/or-tools/include/absl/strings/escaping.h b/libs/or-tools-bin/include/absl/strings/escaping.h similarity index 100% rename from libs/or-tools/include/absl/strings/escaping.h rename to libs/or-tools-bin/include/absl/strings/escaping.h diff --git a/libs/or-tools/include/absl/strings/internal/char_map.h b/libs/or-tools-bin/include/absl/strings/internal/char_map.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/char_map.h rename to libs/or-tools-bin/include/absl/strings/internal/char_map.h diff --git a/libs/or-tools/include/absl/strings/internal/charconv_bigint.h b/libs/or-tools-bin/include/absl/strings/internal/charconv_bigint.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/charconv_bigint.h rename to libs/or-tools-bin/include/absl/strings/internal/charconv_bigint.h diff --git a/libs/or-tools/include/absl/strings/internal/charconv_parse.h b/libs/or-tools-bin/include/absl/strings/internal/charconv_parse.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/charconv_parse.h rename to libs/or-tools-bin/include/absl/strings/internal/charconv_parse.h diff --git a/libs/or-tools/include/absl/strings/internal/cord_internal.h b/libs/or-tools-bin/include/absl/strings/internal/cord_internal.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/cord_internal.h rename to libs/or-tools-bin/include/absl/strings/internal/cord_internal.h diff --git a/libs/or-tools/include/absl/strings/internal/escaping.h b/libs/or-tools-bin/include/absl/strings/internal/escaping.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/escaping.h rename to libs/or-tools-bin/include/absl/strings/internal/escaping.h diff --git a/libs/or-tools/include/absl/strings/internal/escaping_test_common.h b/libs/or-tools-bin/include/absl/strings/internal/escaping_test_common.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/escaping_test_common.h rename to libs/or-tools-bin/include/absl/strings/internal/escaping_test_common.h diff --git a/libs/or-tools/include/absl/strings/internal/memutil.h b/libs/or-tools-bin/include/absl/strings/internal/memutil.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/memutil.h rename to libs/or-tools-bin/include/absl/strings/internal/memutil.h diff --git a/libs/or-tools/include/absl/strings/internal/numbers_test_common.h b/libs/or-tools-bin/include/absl/strings/internal/numbers_test_common.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/numbers_test_common.h rename to libs/or-tools-bin/include/absl/strings/internal/numbers_test_common.h diff --git a/libs/or-tools/include/absl/strings/internal/ostringstream.h b/libs/or-tools-bin/include/absl/strings/internal/ostringstream.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/ostringstream.h rename to libs/or-tools-bin/include/absl/strings/internal/ostringstream.h diff --git a/libs/or-tools/include/absl/strings/internal/pow10_helper.h b/libs/or-tools-bin/include/absl/strings/internal/pow10_helper.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/pow10_helper.h rename to libs/or-tools-bin/include/absl/strings/internal/pow10_helper.h diff --git a/libs/or-tools/include/absl/strings/internal/resize_uninitialized.h b/libs/or-tools-bin/include/absl/strings/internal/resize_uninitialized.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/resize_uninitialized.h rename to libs/or-tools-bin/include/absl/strings/internal/resize_uninitialized.h diff --git a/libs/or-tools/include/absl/strings/internal/stl_type_traits.h b/libs/or-tools-bin/include/absl/strings/internal/stl_type_traits.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/stl_type_traits.h rename to libs/or-tools-bin/include/absl/strings/internal/stl_type_traits.h diff --git a/libs/or-tools/include/absl/strings/internal/str_format/arg.h b/libs/or-tools-bin/include/absl/strings/internal/str_format/arg.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/str_format/arg.h rename to libs/or-tools-bin/include/absl/strings/internal/str_format/arg.h diff --git a/libs/or-tools/include/absl/strings/internal/str_format/bind.h b/libs/or-tools-bin/include/absl/strings/internal/str_format/bind.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/str_format/bind.h rename to libs/or-tools-bin/include/absl/strings/internal/str_format/bind.h diff --git a/libs/or-tools/include/absl/strings/internal/str_format/checker.h b/libs/or-tools-bin/include/absl/strings/internal/str_format/checker.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/str_format/checker.h rename to libs/or-tools-bin/include/absl/strings/internal/str_format/checker.h diff --git a/libs/or-tools/include/absl/strings/internal/str_format/extension.h b/libs/or-tools-bin/include/absl/strings/internal/str_format/extension.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/str_format/extension.h rename to libs/or-tools-bin/include/absl/strings/internal/str_format/extension.h diff --git a/libs/or-tools/include/absl/strings/internal/str_format/float_conversion.h b/libs/or-tools-bin/include/absl/strings/internal/str_format/float_conversion.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/str_format/float_conversion.h rename to libs/or-tools-bin/include/absl/strings/internal/str_format/float_conversion.h diff --git a/libs/or-tools/include/absl/strings/internal/str_format/output.h b/libs/or-tools-bin/include/absl/strings/internal/str_format/output.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/str_format/output.h rename to libs/or-tools-bin/include/absl/strings/internal/str_format/output.h diff --git a/libs/or-tools/include/absl/strings/internal/str_format/parser.h b/libs/or-tools-bin/include/absl/strings/internal/str_format/parser.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/str_format/parser.h rename to libs/or-tools-bin/include/absl/strings/internal/str_format/parser.h diff --git a/libs/or-tools/include/absl/strings/internal/str_join_internal.h b/libs/or-tools-bin/include/absl/strings/internal/str_join_internal.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/str_join_internal.h rename to libs/or-tools-bin/include/absl/strings/internal/str_join_internal.h diff --git a/libs/or-tools/include/absl/strings/internal/str_split_internal.h b/libs/or-tools-bin/include/absl/strings/internal/str_split_internal.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/str_split_internal.h rename to libs/or-tools-bin/include/absl/strings/internal/str_split_internal.h diff --git a/libs/or-tools/include/absl/strings/internal/utf8.h b/libs/or-tools-bin/include/absl/strings/internal/utf8.h similarity index 100% rename from libs/or-tools/include/absl/strings/internal/utf8.h rename to libs/or-tools-bin/include/absl/strings/internal/utf8.h diff --git a/libs/or-tools/include/absl/strings/match.h b/libs/or-tools-bin/include/absl/strings/match.h similarity index 100% rename from libs/or-tools/include/absl/strings/match.h rename to libs/or-tools-bin/include/absl/strings/match.h diff --git a/libs/or-tools/include/absl/strings/numbers.h b/libs/or-tools-bin/include/absl/strings/numbers.h similarity index 100% rename from libs/or-tools/include/absl/strings/numbers.h rename to libs/or-tools-bin/include/absl/strings/numbers.h diff --git a/libs/or-tools/include/absl/strings/str_cat.h b/libs/or-tools-bin/include/absl/strings/str_cat.h similarity index 100% rename from libs/or-tools/include/absl/strings/str_cat.h rename to libs/or-tools-bin/include/absl/strings/str_cat.h diff --git a/libs/or-tools/include/absl/strings/str_format.h b/libs/or-tools-bin/include/absl/strings/str_format.h similarity index 100% rename from libs/or-tools/include/absl/strings/str_format.h rename to libs/or-tools-bin/include/absl/strings/str_format.h diff --git a/libs/or-tools/include/absl/strings/str_join.h b/libs/or-tools-bin/include/absl/strings/str_join.h similarity index 100% rename from libs/or-tools/include/absl/strings/str_join.h rename to libs/or-tools-bin/include/absl/strings/str_join.h diff --git a/libs/or-tools/include/absl/strings/str_replace.h b/libs/or-tools-bin/include/absl/strings/str_replace.h similarity index 100% rename from libs/or-tools/include/absl/strings/str_replace.h rename to libs/or-tools-bin/include/absl/strings/str_replace.h diff --git a/libs/or-tools/include/absl/strings/str_split.h b/libs/or-tools-bin/include/absl/strings/str_split.h similarity index 100% rename from libs/or-tools/include/absl/strings/str_split.h rename to libs/or-tools-bin/include/absl/strings/str_split.h diff --git a/libs/or-tools/include/absl/strings/string_view.h b/libs/or-tools-bin/include/absl/strings/string_view.h similarity index 100% rename from libs/or-tools/include/absl/strings/string_view.h rename to libs/or-tools-bin/include/absl/strings/string_view.h diff --git a/libs/or-tools/include/absl/strings/strip.h b/libs/or-tools-bin/include/absl/strings/strip.h similarity index 100% rename from libs/or-tools/include/absl/strings/strip.h rename to libs/or-tools-bin/include/absl/strings/strip.h diff --git a/libs/or-tools/include/absl/strings/substitute.h b/libs/or-tools-bin/include/absl/strings/substitute.h similarity index 100% rename from libs/or-tools/include/absl/strings/substitute.h rename to libs/or-tools-bin/include/absl/strings/substitute.h diff --git a/libs/or-tools/include/absl/synchronization/barrier.h b/libs/or-tools-bin/include/absl/synchronization/barrier.h similarity index 100% rename from libs/or-tools/include/absl/synchronization/barrier.h rename to libs/or-tools-bin/include/absl/synchronization/barrier.h diff --git a/libs/or-tools/include/absl/synchronization/blocking_counter.h b/libs/or-tools-bin/include/absl/synchronization/blocking_counter.h similarity index 100% rename from libs/or-tools/include/absl/synchronization/blocking_counter.h rename to libs/or-tools-bin/include/absl/synchronization/blocking_counter.h diff --git a/libs/or-tools/include/absl/synchronization/internal/create_thread_identity.h b/libs/or-tools-bin/include/absl/synchronization/internal/create_thread_identity.h similarity index 100% rename from libs/or-tools/include/absl/synchronization/internal/create_thread_identity.h rename to libs/or-tools-bin/include/absl/synchronization/internal/create_thread_identity.h diff --git a/libs/or-tools/include/absl/synchronization/internal/graphcycles.h b/libs/or-tools-bin/include/absl/synchronization/internal/graphcycles.h similarity index 100% rename from libs/or-tools/include/absl/synchronization/internal/graphcycles.h rename to libs/or-tools-bin/include/absl/synchronization/internal/graphcycles.h diff --git a/libs/or-tools/include/absl/synchronization/internal/kernel_timeout.h b/libs/or-tools-bin/include/absl/synchronization/internal/kernel_timeout.h similarity index 100% rename from libs/or-tools/include/absl/synchronization/internal/kernel_timeout.h rename to libs/or-tools-bin/include/absl/synchronization/internal/kernel_timeout.h diff --git a/libs/or-tools/include/absl/synchronization/internal/mutex_nonprod.inc b/libs/or-tools-bin/include/absl/synchronization/internal/mutex_nonprod.inc similarity index 100% rename from libs/or-tools/include/absl/synchronization/internal/mutex_nonprod.inc rename to libs/or-tools-bin/include/absl/synchronization/internal/mutex_nonprod.inc diff --git a/libs/or-tools/include/absl/synchronization/internal/per_thread_sem.h b/libs/or-tools-bin/include/absl/synchronization/internal/per_thread_sem.h similarity index 100% rename from libs/or-tools/include/absl/synchronization/internal/per_thread_sem.h rename to libs/or-tools-bin/include/absl/synchronization/internal/per_thread_sem.h diff --git a/libs/or-tools/include/absl/synchronization/internal/thread_pool.h b/libs/or-tools-bin/include/absl/synchronization/internal/thread_pool.h similarity index 100% rename from libs/or-tools/include/absl/synchronization/internal/thread_pool.h rename to libs/or-tools-bin/include/absl/synchronization/internal/thread_pool.h diff --git a/libs/or-tools/include/absl/synchronization/internal/waiter.h b/libs/or-tools-bin/include/absl/synchronization/internal/waiter.h similarity index 100% rename from libs/or-tools/include/absl/synchronization/internal/waiter.h rename to libs/or-tools-bin/include/absl/synchronization/internal/waiter.h diff --git a/libs/or-tools/include/absl/synchronization/mutex.h b/libs/or-tools-bin/include/absl/synchronization/mutex.h similarity index 100% rename from libs/or-tools/include/absl/synchronization/mutex.h rename to libs/or-tools-bin/include/absl/synchronization/mutex.h diff --git a/libs/or-tools/include/absl/synchronization/notification.h b/libs/or-tools-bin/include/absl/synchronization/notification.h similarity index 100% rename from libs/or-tools/include/absl/synchronization/notification.h rename to libs/or-tools-bin/include/absl/synchronization/notification.h diff --git a/libs/or-tools/include/absl/time/civil_time.h b/libs/or-tools-bin/include/absl/time/civil_time.h similarity index 100% rename from libs/or-tools/include/absl/time/civil_time.h rename to libs/or-tools-bin/include/absl/time/civil_time.h diff --git a/libs/or-tools/include/absl/time/clock.h b/libs/or-tools-bin/include/absl/time/clock.h similarity index 100% rename from libs/or-tools/include/absl/time/clock.h rename to libs/or-tools-bin/include/absl/time/clock.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/include/cctz/civil_time.h b/libs/or-tools-bin/include/absl/time/internal/cctz/include/cctz/civil_time.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/include/cctz/civil_time.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/include/cctz/civil_time.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/libs/or-tools-bin/include/absl/time/internal/cctz/include/cctz/civil_time_detail.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/include/cctz/civil_time_detail.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/include/cctz/civil_time_detail.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/include/cctz/time_zone.h b/libs/or-tools-bin/include/absl/time/internal/cctz/include/cctz/time_zone.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/include/cctz/time_zone.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/include/cctz/time_zone.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/include/cctz/zone_info_source.h b/libs/or-tools-bin/include/absl/time/internal/cctz/include/cctz/zone_info_source.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/include/cctz/zone_info_source.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/include/cctz/zone_info_source.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/src/time_zone_fixed.h b/libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_fixed.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/src/time_zone_fixed.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_fixed.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/src/time_zone_if.h b/libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_if.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/src/time_zone_if.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_if.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/src/time_zone_impl.h b/libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_impl.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/src/time_zone_impl.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_impl.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/src/time_zone_info.h b/libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_info.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/src/time_zone_info.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_info.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/src/time_zone_libc.h b/libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_libc.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/src/time_zone_libc.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_libc.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/src/time_zone_posix.h b/libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_posix.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/src/time_zone_posix.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/src/time_zone_posix.h diff --git a/libs/or-tools/include/absl/time/internal/cctz/src/tzfile.h b/libs/or-tools-bin/include/absl/time/internal/cctz/src/tzfile.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/cctz/src/tzfile.h rename to libs/or-tools-bin/include/absl/time/internal/cctz/src/tzfile.h diff --git a/libs/or-tools/include/absl/time/internal/get_current_time_chrono.inc b/libs/or-tools-bin/include/absl/time/internal/get_current_time_chrono.inc similarity index 100% rename from libs/or-tools/include/absl/time/internal/get_current_time_chrono.inc rename to libs/or-tools-bin/include/absl/time/internal/get_current_time_chrono.inc diff --git a/libs/or-tools/include/absl/time/internal/get_current_time_posix.inc b/libs/or-tools-bin/include/absl/time/internal/get_current_time_posix.inc similarity index 100% rename from libs/or-tools/include/absl/time/internal/get_current_time_posix.inc rename to libs/or-tools-bin/include/absl/time/internal/get_current_time_posix.inc diff --git a/libs/or-tools/include/absl/time/internal/test_util.h b/libs/or-tools-bin/include/absl/time/internal/test_util.h similarity index 100% rename from libs/or-tools/include/absl/time/internal/test_util.h rename to libs/or-tools-bin/include/absl/time/internal/test_util.h diff --git a/libs/or-tools/include/absl/time/internal/zoneinfo.inc b/libs/or-tools-bin/include/absl/time/internal/zoneinfo.inc similarity index 100% rename from libs/or-tools/include/absl/time/internal/zoneinfo.inc rename to libs/or-tools-bin/include/absl/time/internal/zoneinfo.inc diff --git a/libs/or-tools/include/absl/time/time.h b/libs/or-tools-bin/include/absl/time/time.h similarity index 100% rename from libs/or-tools/include/absl/time/time.h rename to libs/or-tools-bin/include/absl/time/time.h diff --git a/libs/or-tools/include/absl/types/any.h b/libs/or-tools-bin/include/absl/types/any.h similarity index 100% rename from libs/or-tools/include/absl/types/any.h rename to libs/or-tools-bin/include/absl/types/any.h diff --git a/libs/or-tools/include/absl/types/bad_any_cast.h b/libs/or-tools-bin/include/absl/types/bad_any_cast.h similarity index 100% rename from libs/or-tools/include/absl/types/bad_any_cast.h rename to libs/or-tools-bin/include/absl/types/bad_any_cast.h diff --git a/libs/or-tools/include/absl/types/bad_optional_access.h b/libs/or-tools-bin/include/absl/types/bad_optional_access.h similarity index 100% rename from libs/or-tools/include/absl/types/bad_optional_access.h rename to libs/or-tools-bin/include/absl/types/bad_optional_access.h diff --git a/libs/or-tools/include/absl/types/bad_variant_access.h b/libs/or-tools-bin/include/absl/types/bad_variant_access.h similarity index 100% rename from libs/or-tools/include/absl/types/bad_variant_access.h rename to libs/or-tools-bin/include/absl/types/bad_variant_access.h diff --git a/libs/or-tools/include/absl/types/compare.h b/libs/or-tools-bin/include/absl/types/compare.h similarity index 100% rename from libs/or-tools/include/absl/types/compare.h rename to libs/or-tools-bin/include/absl/types/compare.h diff --git a/libs/or-tools/include/absl/types/internal/conformance_aliases.h b/libs/or-tools-bin/include/absl/types/internal/conformance_aliases.h similarity index 100% rename from libs/or-tools/include/absl/types/internal/conformance_aliases.h rename to libs/or-tools-bin/include/absl/types/internal/conformance_aliases.h diff --git a/libs/or-tools/include/absl/types/internal/conformance_archetype.h b/libs/or-tools-bin/include/absl/types/internal/conformance_archetype.h similarity index 100% rename from libs/or-tools/include/absl/types/internal/conformance_archetype.h rename to libs/or-tools-bin/include/absl/types/internal/conformance_archetype.h diff --git a/libs/or-tools/include/absl/types/internal/conformance_profile.h b/libs/or-tools-bin/include/absl/types/internal/conformance_profile.h similarity index 100% rename from libs/or-tools/include/absl/types/internal/conformance_profile.h rename to libs/or-tools-bin/include/absl/types/internal/conformance_profile.h diff --git a/libs/or-tools/include/absl/types/internal/optional.h b/libs/or-tools-bin/include/absl/types/internal/optional.h similarity index 100% rename from libs/or-tools/include/absl/types/internal/optional.h rename to libs/or-tools-bin/include/absl/types/internal/optional.h diff --git a/libs/or-tools/include/absl/types/internal/span.h b/libs/or-tools-bin/include/absl/types/internal/span.h similarity index 100% rename from libs/or-tools/include/absl/types/internal/span.h rename to libs/or-tools-bin/include/absl/types/internal/span.h diff --git a/libs/or-tools/include/absl/types/internal/variant.h b/libs/or-tools-bin/include/absl/types/internal/variant.h similarity index 100% rename from libs/or-tools/include/absl/types/internal/variant.h rename to libs/or-tools-bin/include/absl/types/internal/variant.h diff --git a/libs/or-tools/include/absl/types/optional.h b/libs/or-tools-bin/include/absl/types/optional.h similarity index 100% rename from libs/or-tools/include/absl/types/optional.h rename to libs/or-tools-bin/include/absl/types/optional.h diff --git a/libs/or-tools/include/absl/types/span.h b/libs/or-tools-bin/include/absl/types/span.h similarity index 100% rename from libs/or-tools/include/absl/types/span.h rename to libs/or-tools-bin/include/absl/types/span.h diff --git a/libs/or-tools/include/absl/types/variant.h b/libs/or-tools-bin/include/absl/types/variant.h similarity index 100% rename from libs/or-tools/include/absl/types/variant.h rename to libs/or-tools-bin/include/absl/types/variant.h diff --git a/libs/or-tools/include/absl/utility/utility.h b/libs/or-tools-bin/include/absl/utility/utility.h similarity index 100% rename from libs/or-tools/include/absl/utility/utility.h rename to libs/or-tools-bin/include/absl/utility/utility.h diff --git a/libs/or-tools/include/coin/CbcBranchActual.hpp b/libs/or-tools-bin/include/coin/CbcBranchActual.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcBranchActual.hpp rename to libs/or-tools-bin/include/coin/CbcBranchActual.hpp diff --git a/libs/or-tools/include/coin/CbcBranchAllDifferent.hpp b/libs/or-tools-bin/include/coin/CbcBranchAllDifferent.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcBranchAllDifferent.hpp rename to libs/or-tools-bin/include/coin/CbcBranchAllDifferent.hpp diff --git a/libs/or-tools/include/coin/CbcBranchBase.hpp b/libs/or-tools-bin/include/coin/CbcBranchBase.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcBranchBase.hpp rename to libs/or-tools-bin/include/coin/CbcBranchBase.hpp diff --git a/libs/or-tools/include/coin/CbcBranchCut.hpp b/libs/or-tools-bin/include/coin/CbcBranchCut.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcBranchCut.hpp rename to libs/or-tools-bin/include/coin/CbcBranchCut.hpp diff --git a/libs/or-tools/include/coin/CbcBranchDecision.hpp b/libs/or-tools-bin/include/coin/CbcBranchDecision.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcBranchDecision.hpp rename to libs/or-tools-bin/include/coin/CbcBranchDecision.hpp diff --git a/libs/or-tools/include/coin/CbcBranchDefaultDecision.hpp b/libs/or-tools-bin/include/coin/CbcBranchDefaultDecision.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcBranchDefaultDecision.hpp rename to libs/or-tools-bin/include/coin/CbcBranchDefaultDecision.hpp diff --git a/libs/or-tools/include/coin/CbcBranchDynamic.hpp b/libs/or-tools-bin/include/coin/CbcBranchDynamic.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcBranchDynamic.hpp rename to libs/or-tools-bin/include/coin/CbcBranchDynamic.hpp diff --git a/libs/or-tools/include/coin/CbcBranchLotsize.hpp b/libs/or-tools-bin/include/coin/CbcBranchLotsize.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcBranchLotsize.hpp rename to libs/or-tools-bin/include/coin/CbcBranchLotsize.hpp diff --git a/libs/or-tools/include/coin/CbcBranchToFixLots.hpp b/libs/or-tools-bin/include/coin/CbcBranchToFixLots.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcBranchToFixLots.hpp rename to libs/or-tools-bin/include/coin/CbcBranchToFixLots.hpp diff --git a/libs/or-tools/include/coin/CbcBranchingObject.hpp b/libs/or-tools-bin/include/coin/CbcBranchingObject.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcBranchingObject.hpp rename to libs/or-tools-bin/include/coin/CbcBranchingObject.hpp diff --git a/libs/or-tools/include/coin/CbcClique.hpp b/libs/or-tools-bin/include/coin/CbcClique.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcClique.hpp rename to libs/or-tools-bin/include/coin/CbcClique.hpp diff --git a/libs/or-tools/include/coin/CbcCompare.hpp b/libs/or-tools-bin/include/coin/CbcCompare.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCompare.hpp rename to libs/or-tools-bin/include/coin/CbcCompare.hpp diff --git a/libs/or-tools/include/coin/CbcCompareActual.hpp b/libs/or-tools-bin/include/coin/CbcCompareActual.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCompareActual.hpp rename to libs/or-tools-bin/include/coin/CbcCompareActual.hpp diff --git a/libs/or-tools/include/coin/CbcCompareBase.hpp b/libs/or-tools-bin/include/coin/CbcCompareBase.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCompareBase.hpp rename to libs/or-tools-bin/include/coin/CbcCompareBase.hpp diff --git a/libs/or-tools/include/coin/CbcCompareDefault.hpp b/libs/or-tools-bin/include/coin/CbcCompareDefault.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCompareDefault.hpp rename to libs/or-tools-bin/include/coin/CbcCompareDefault.hpp diff --git a/libs/or-tools/include/coin/CbcCompareDepth.hpp b/libs/or-tools-bin/include/coin/CbcCompareDepth.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCompareDepth.hpp rename to libs/or-tools-bin/include/coin/CbcCompareDepth.hpp diff --git a/libs/or-tools/include/coin/CbcCompareEstimate.hpp b/libs/or-tools-bin/include/coin/CbcCompareEstimate.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCompareEstimate.hpp rename to libs/or-tools-bin/include/coin/CbcCompareEstimate.hpp diff --git a/libs/or-tools/include/coin/CbcCompareObjective.hpp b/libs/or-tools-bin/include/coin/CbcCompareObjective.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCompareObjective.hpp rename to libs/or-tools-bin/include/coin/CbcCompareObjective.hpp diff --git a/libs/or-tools/include/coin/CbcConfig.h b/libs/or-tools-bin/include/coin/CbcConfig.h similarity index 100% rename from libs/or-tools/include/coin/CbcConfig.h rename to libs/or-tools-bin/include/coin/CbcConfig.h diff --git a/libs/or-tools/include/coin/CbcConsequence.hpp b/libs/or-tools-bin/include/coin/CbcConsequence.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcConsequence.hpp rename to libs/or-tools-bin/include/coin/CbcConsequence.hpp diff --git a/libs/or-tools/include/coin/CbcCountRowCut.hpp b/libs/or-tools-bin/include/coin/CbcCountRowCut.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCountRowCut.hpp rename to libs/or-tools-bin/include/coin/CbcCountRowCut.hpp diff --git a/libs/or-tools/include/coin/CbcCutGenerator.hpp b/libs/or-tools-bin/include/coin/CbcCutGenerator.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCutGenerator.hpp rename to libs/or-tools-bin/include/coin/CbcCutGenerator.hpp diff --git a/libs/or-tools/include/coin/CbcCutModifier.hpp b/libs/or-tools-bin/include/coin/CbcCutModifier.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCutModifier.hpp rename to libs/or-tools-bin/include/coin/CbcCutModifier.hpp diff --git a/libs/or-tools/include/coin/CbcCutSubsetModifier.hpp b/libs/or-tools-bin/include/coin/CbcCutSubsetModifier.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcCutSubsetModifier.hpp rename to libs/or-tools-bin/include/coin/CbcCutSubsetModifier.hpp diff --git a/libs/or-tools/include/coin/CbcDummyBranchingObject.hpp b/libs/or-tools-bin/include/coin/CbcDummyBranchingObject.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcDummyBranchingObject.hpp rename to libs/or-tools-bin/include/coin/CbcDummyBranchingObject.hpp diff --git a/libs/or-tools/include/coin/CbcEventHandler.hpp b/libs/or-tools-bin/include/coin/CbcEventHandler.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcEventHandler.hpp rename to libs/or-tools-bin/include/coin/CbcEventHandler.hpp diff --git a/libs/or-tools/include/coin/CbcFathom.hpp b/libs/or-tools-bin/include/coin/CbcFathom.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcFathom.hpp rename to libs/or-tools-bin/include/coin/CbcFathom.hpp diff --git a/libs/or-tools/include/coin/CbcFathomDynamicProgramming.hpp b/libs/or-tools-bin/include/coin/CbcFathomDynamicProgramming.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcFathomDynamicProgramming.hpp rename to libs/or-tools-bin/include/coin/CbcFathomDynamicProgramming.hpp diff --git a/libs/or-tools/include/coin/CbcFeasibilityBase.hpp b/libs/or-tools-bin/include/coin/CbcFeasibilityBase.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcFeasibilityBase.hpp rename to libs/or-tools-bin/include/coin/CbcFeasibilityBase.hpp diff --git a/libs/or-tools/include/coin/CbcFixVariable.hpp b/libs/or-tools-bin/include/coin/CbcFixVariable.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcFixVariable.hpp rename to libs/or-tools-bin/include/coin/CbcFixVariable.hpp diff --git a/libs/or-tools/include/coin/CbcFollowOn.hpp b/libs/or-tools-bin/include/coin/CbcFollowOn.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcFollowOn.hpp rename to libs/or-tools-bin/include/coin/CbcFollowOn.hpp diff --git a/libs/or-tools/include/coin/CbcFullNodeInfo.hpp b/libs/or-tools-bin/include/coin/CbcFullNodeInfo.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcFullNodeInfo.hpp rename to libs/or-tools-bin/include/coin/CbcFullNodeInfo.hpp diff --git a/libs/or-tools/include/coin/CbcGeneral.hpp b/libs/or-tools-bin/include/coin/CbcGeneral.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcGeneral.hpp rename to libs/or-tools-bin/include/coin/CbcGeneral.hpp diff --git a/libs/or-tools/include/coin/CbcGeneralDepth.hpp b/libs/or-tools-bin/include/coin/CbcGeneralDepth.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcGeneralDepth.hpp rename to libs/or-tools-bin/include/coin/CbcGeneralDepth.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristic.hpp b/libs/or-tools-bin/include/coin/CbcHeuristic.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristic.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristic.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicDINS.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicDINS.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicDINS.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicDINS.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicDW.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicDW.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicDW.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicDW.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicDive.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicDive.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicDive.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicDive.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicDiveCoefficient.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicDiveCoefficient.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicDiveCoefficient.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicDiveCoefficient.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicDiveFractional.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicDiveFractional.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicDiveFractional.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicDiveFractional.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicDiveGuided.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicDiveGuided.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicDiveGuided.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicDiveGuided.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicDiveLineSearch.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicDiveLineSearch.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicDiveLineSearch.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicDiveLineSearch.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicDivePseudoCost.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicDivePseudoCost.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicDivePseudoCost.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicDivePseudoCost.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicDiveVectorLength.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicDiveVectorLength.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicDiveVectorLength.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicDiveVectorLength.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicFPump.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicFPump.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicFPump.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicFPump.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicGreedy.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicGreedy.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicGreedy.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicGreedy.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicLocal.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicLocal.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicLocal.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicLocal.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicPivotAndFix.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicPivotAndFix.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicPivotAndFix.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicPivotAndFix.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicRENS.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicRENS.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicRENS.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicRENS.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicRINS.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicRINS.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicRINS.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicRINS.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicRandRound.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicRandRound.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicRandRound.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicRandRound.hpp diff --git a/libs/or-tools/include/coin/CbcHeuristicVND.hpp b/libs/or-tools-bin/include/coin/CbcHeuristicVND.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcHeuristicVND.hpp rename to libs/or-tools-bin/include/coin/CbcHeuristicVND.hpp diff --git a/libs/or-tools/include/coin/CbcLinked.hpp b/libs/or-tools-bin/include/coin/CbcLinked.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcLinked.hpp rename to libs/or-tools-bin/include/coin/CbcLinked.hpp diff --git a/libs/or-tools/include/coin/CbcMessage.hpp b/libs/or-tools-bin/include/coin/CbcMessage.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcMessage.hpp rename to libs/or-tools-bin/include/coin/CbcMessage.hpp diff --git a/libs/or-tools/include/coin/CbcMipStartIO.hpp b/libs/or-tools-bin/include/coin/CbcMipStartIO.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcMipStartIO.hpp rename to libs/or-tools-bin/include/coin/CbcMipStartIO.hpp diff --git a/libs/or-tools/include/coin/CbcModel.hpp b/libs/or-tools-bin/include/coin/CbcModel.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcModel.hpp rename to libs/or-tools-bin/include/coin/CbcModel.hpp diff --git a/libs/or-tools/include/coin/CbcNWay.hpp b/libs/or-tools-bin/include/coin/CbcNWay.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcNWay.hpp rename to libs/or-tools-bin/include/coin/CbcNWay.hpp diff --git a/libs/or-tools/include/coin/CbcNode.hpp b/libs/or-tools-bin/include/coin/CbcNode.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcNode.hpp rename to libs/or-tools-bin/include/coin/CbcNode.hpp diff --git a/libs/or-tools/include/coin/CbcNodeInfo.hpp b/libs/or-tools-bin/include/coin/CbcNodeInfo.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcNodeInfo.hpp rename to libs/or-tools-bin/include/coin/CbcNodeInfo.hpp diff --git a/libs/or-tools/include/coin/CbcObject.hpp b/libs/or-tools-bin/include/coin/CbcObject.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcObject.hpp rename to libs/or-tools-bin/include/coin/CbcObject.hpp diff --git a/libs/or-tools/include/coin/CbcObjectUpdateData.hpp b/libs/or-tools-bin/include/coin/CbcObjectUpdateData.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcObjectUpdateData.hpp rename to libs/or-tools-bin/include/coin/CbcObjectUpdateData.hpp diff --git a/libs/or-tools/include/coin/CbcOrClpParam.cpp b/libs/or-tools-bin/include/coin/CbcOrClpParam.cpp similarity index 100% rename from libs/or-tools/include/coin/CbcOrClpParam.cpp rename to libs/or-tools-bin/include/coin/CbcOrClpParam.cpp diff --git a/libs/or-tools/include/coin/CbcOrClpParam.hpp b/libs/or-tools-bin/include/coin/CbcOrClpParam.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcOrClpParam.hpp rename to libs/or-tools-bin/include/coin/CbcOrClpParam.hpp diff --git a/libs/or-tools/include/coin/CbcParam.hpp b/libs/or-tools-bin/include/coin/CbcParam.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcParam.hpp rename to libs/or-tools-bin/include/coin/CbcParam.hpp diff --git a/libs/or-tools/include/coin/CbcPartialNodeInfo.hpp b/libs/or-tools-bin/include/coin/CbcPartialNodeInfo.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcPartialNodeInfo.hpp rename to libs/or-tools-bin/include/coin/CbcPartialNodeInfo.hpp diff --git a/libs/or-tools/include/coin/CbcSOS.hpp b/libs/or-tools-bin/include/coin/CbcSOS.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcSOS.hpp rename to libs/or-tools-bin/include/coin/CbcSOS.hpp diff --git a/libs/or-tools/include/coin/CbcSimpleInteger.hpp b/libs/or-tools-bin/include/coin/CbcSimpleInteger.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcSimpleInteger.hpp rename to libs/or-tools-bin/include/coin/CbcSimpleInteger.hpp diff --git a/libs/or-tools/include/coin/CbcSimpleIntegerDynamicPseudoCost.hpp b/libs/or-tools-bin/include/coin/CbcSimpleIntegerDynamicPseudoCost.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcSimpleIntegerDynamicPseudoCost.hpp rename to libs/or-tools-bin/include/coin/CbcSimpleIntegerDynamicPseudoCost.hpp diff --git a/libs/or-tools/include/coin/CbcSimpleIntegerPseudoCost.hpp b/libs/or-tools-bin/include/coin/CbcSimpleIntegerPseudoCost.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcSimpleIntegerPseudoCost.hpp rename to libs/or-tools-bin/include/coin/CbcSimpleIntegerPseudoCost.hpp diff --git a/libs/or-tools/include/coin/CbcSolver.hpp b/libs/or-tools-bin/include/coin/CbcSolver.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcSolver.hpp rename to libs/or-tools-bin/include/coin/CbcSolver.hpp diff --git a/libs/or-tools/include/coin/CbcStrategy.hpp b/libs/or-tools-bin/include/coin/CbcStrategy.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcStrategy.hpp rename to libs/or-tools-bin/include/coin/CbcStrategy.hpp diff --git a/libs/or-tools/include/coin/CbcSubProblem.hpp b/libs/or-tools-bin/include/coin/CbcSubProblem.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcSubProblem.hpp rename to libs/or-tools-bin/include/coin/CbcSubProblem.hpp diff --git a/libs/or-tools/include/coin/CbcTree.hpp b/libs/or-tools-bin/include/coin/CbcTree.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcTree.hpp rename to libs/or-tools-bin/include/coin/CbcTree.hpp diff --git a/libs/or-tools/include/coin/CbcTreeLocal.hpp b/libs/or-tools-bin/include/coin/CbcTreeLocal.hpp similarity index 100% rename from libs/or-tools/include/coin/CbcTreeLocal.hpp rename to libs/or-tools-bin/include/coin/CbcTreeLocal.hpp diff --git a/libs/or-tools/include/coin/Cbc_C_Interface.h b/libs/or-tools-bin/include/coin/Cbc_C_Interface.h similarity index 100% rename from libs/or-tools/include/coin/Cbc_C_Interface.h rename to libs/or-tools-bin/include/coin/Cbc_C_Interface.h diff --git a/libs/or-tools/include/coin/Cgl012cut.hpp b/libs/or-tools-bin/include/coin/Cgl012cut.hpp similarity index 100% rename from libs/or-tools/include/coin/Cgl012cut.hpp rename to libs/or-tools-bin/include/coin/Cgl012cut.hpp diff --git a/libs/or-tools/include/coin/CglAllDifferent.hpp b/libs/or-tools-bin/include/coin/CglAllDifferent.hpp similarity index 100% rename from libs/or-tools/include/coin/CglAllDifferent.hpp rename to libs/or-tools-bin/include/coin/CglAllDifferent.hpp diff --git a/libs/or-tools/include/coin/CglClique.hpp b/libs/or-tools-bin/include/coin/CglClique.hpp similarity index 100% rename from libs/or-tools/include/coin/CglClique.hpp rename to libs/or-tools-bin/include/coin/CglClique.hpp diff --git a/libs/or-tools/include/coin/CglConfig.h b/libs/or-tools-bin/include/coin/CglConfig.h similarity index 100% rename from libs/or-tools/include/coin/CglConfig.h rename to libs/or-tools-bin/include/coin/CglConfig.h diff --git a/libs/or-tools/include/coin/CglCutGenerator.hpp b/libs/or-tools-bin/include/coin/CglCutGenerator.hpp similarity index 100% rename from libs/or-tools/include/coin/CglCutGenerator.hpp rename to libs/or-tools-bin/include/coin/CglCutGenerator.hpp diff --git a/libs/or-tools/include/coin/CglDuplicateRow.hpp b/libs/or-tools-bin/include/coin/CglDuplicateRow.hpp similarity index 100% rename from libs/or-tools/include/coin/CglDuplicateRow.hpp rename to libs/or-tools-bin/include/coin/CglDuplicateRow.hpp diff --git a/libs/or-tools/include/coin/CglFlowCover.hpp b/libs/or-tools-bin/include/coin/CglFlowCover.hpp similarity index 100% rename from libs/or-tools/include/coin/CglFlowCover.hpp rename to libs/or-tools-bin/include/coin/CglFlowCover.hpp diff --git a/libs/or-tools/include/coin/CglGMI.hpp b/libs/or-tools-bin/include/coin/CglGMI.hpp similarity index 100% rename from libs/or-tools/include/coin/CglGMI.hpp rename to libs/or-tools-bin/include/coin/CglGMI.hpp diff --git a/libs/or-tools/include/coin/CglGMIParam.hpp b/libs/or-tools-bin/include/coin/CglGMIParam.hpp similarity index 100% rename from libs/or-tools/include/coin/CglGMIParam.hpp rename to libs/or-tools-bin/include/coin/CglGMIParam.hpp diff --git a/libs/or-tools/include/coin/CglGomory.hpp b/libs/or-tools-bin/include/coin/CglGomory.hpp similarity index 100% rename from libs/or-tools/include/coin/CglGomory.hpp rename to libs/or-tools-bin/include/coin/CglGomory.hpp diff --git a/libs/or-tools/include/coin/CglKnapsackCover.hpp b/libs/or-tools-bin/include/coin/CglKnapsackCover.hpp similarity index 100% rename from libs/or-tools/include/coin/CglKnapsackCover.hpp rename to libs/or-tools-bin/include/coin/CglKnapsackCover.hpp diff --git a/libs/or-tools/include/coin/CglLandP.hpp b/libs/or-tools-bin/include/coin/CglLandP.hpp similarity index 100% rename from libs/or-tools/include/coin/CglLandP.hpp rename to libs/or-tools-bin/include/coin/CglLandP.hpp diff --git a/libs/or-tools/include/coin/CglLandPValidator.hpp b/libs/or-tools-bin/include/coin/CglLandPValidator.hpp similarity index 100% rename from libs/or-tools/include/coin/CglLandPValidator.hpp rename to libs/or-tools-bin/include/coin/CglLandPValidator.hpp diff --git a/libs/or-tools/include/coin/CglLiftAndProject.hpp b/libs/or-tools-bin/include/coin/CglLiftAndProject.hpp similarity index 100% rename from libs/or-tools/include/coin/CglLiftAndProject.hpp rename to libs/or-tools-bin/include/coin/CglLiftAndProject.hpp diff --git a/libs/or-tools/include/coin/CglMessage.hpp b/libs/or-tools-bin/include/coin/CglMessage.hpp similarity index 100% rename from libs/or-tools/include/coin/CglMessage.hpp rename to libs/or-tools-bin/include/coin/CglMessage.hpp diff --git a/libs/or-tools/include/coin/CglMixedIntegerRounding.hpp b/libs/or-tools-bin/include/coin/CglMixedIntegerRounding.hpp similarity index 100% rename from libs/or-tools/include/coin/CglMixedIntegerRounding.hpp rename to libs/or-tools-bin/include/coin/CglMixedIntegerRounding.hpp diff --git a/libs/or-tools/include/coin/CglMixedIntegerRounding2.hpp b/libs/or-tools-bin/include/coin/CglMixedIntegerRounding2.hpp similarity index 100% rename from libs/or-tools/include/coin/CglMixedIntegerRounding2.hpp rename to libs/or-tools-bin/include/coin/CglMixedIntegerRounding2.hpp diff --git a/libs/or-tools/include/coin/CglOddHole.hpp b/libs/or-tools-bin/include/coin/CglOddHole.hpp similarity index 100% rename from libs/or-tools/include/coin/CglOddHole.hpp rename to libs/or-tools-bin/include/coin/CglOddHole.hpp diff --git a/libs/or-tools/include/coin/CglParam.hpp b/libs/or-tools-bin/include/coin/CglParam.hpp similarity index 100% rename from libs/or-tools/include/coin/CglParam.hpp rename to libs/or-tools-bin/include/coin/CglParam.hpp diff --git a/libs/or-tools/include/coin/CglPreProcess.hpp b/libs/or-tools-bin/include/coin/CglPreProcess.hpp similarity index 100% rename from libs/or-tools/include/coin/CglPreProcess.hpp rename to libs/or-tools-bin/include/coin/CglPreProcess.hpp diff --git a/libs/or-tools/include/coin/CglProbing.hpp b/libs/or-tools-bin/include/coin/CglProbing.hpp similarity index 100% rename from libs/or-tools/include/coin/CglProbing.hpp rename to libs/or-tools-bin/include/coin/CglProbing.hpp diff --git a/libs/or-tools/include/coin/CglRedSplit.hpp b/libs/or-tools-bin/include/coin/CglRedSplit.hpp similarity index 100% rename from libs/or-tools/include/coin/CglRedSplit.hpp rename to libs/or-tools-bin/include/coin/CglRedSplit.hpp diff --git a/libs/or-tools/include/coin/CglRedSplit2.hpp b/libs/or-tools-bin/include/coin/CglRedSplit2.hpp similarity index 100% rename from libs/or-tools/include/coin/CglRedSplit2.hpp rename to libs/or-tools-bin/include/coin/CglRedSplit2.hpp diff --git a/libs/or-tools/include/coin/CglRedSplit2Param.hpp b/libs/or-tools-bin/include/coin/CglRedSplit2Param.hpp similarity index 100% rename from libs/or-tools/include/coin/CglRedSplit2Param.hpp rename to libs/or-tools-bin/include/coin/CglRedSplit2Param.hpp diff --git a/libs/or-tools/include/coin/CglRedSplitParam.hpp b/libs/or-tools-bin/include/coin/CglRedSplitParam.hpp similarity index 100% rename from libs/or-tools/include/coin/CglRedSplitParam.hpp rename to libs/or-tools-bin/include/coin/CglRedSplitParam.hpp diff --git a/libs/or-tools/include/coin/CglResidualCapacity.hpp b/libs/or-tools-bin/include/coin/CglResidualCapacity.hpp similarity index 100% rename from libs/or-tools/include/coin/CglResidualCapacity.hpp rename to libs/or-tools-bin/include/coin/CglResidualCapacity.hpp diff --git a/libs/or-tools/include/coin/CglSimpleRounding.hpp b/libs/or-tools-bin/include/coin/CglSimpleRounding.hpp similarity index 100% rename from libs/or-tools/include/coin/CglSimpleRounding.hpp rename to libs/or-tools-bin/include/coin/CglSimpleRounding.hpp diff --git a/libs/or-tools/include/coin/CglStored.hpp b/libs/or-tools-bin/include/coin/CglStored.hpp similarity index 100% rename from libs/or-tools/include/coin/CglStored.hpp rename to libs/or-tools-bin/include/coin/CglStored.hpp diff --git a/libs/or-tools/include/coin/CglTreeInfo.hpp b/libs/or-tools-bin/include/coin/CglTreeInfo.hpp similarity index 100% rename from libs/or-tools/include/coin/CglTreeInfo.hpp rename to libs/or-tools-bin/include/coin/CglTreeInfo.hpp diff --git a/libs/or-tools/include/coin/CglTwomir.hpp b/libs/or-tools-bin/include/coin/CglTwomir.hpp similarity index 100% rename from libs/or-tools/include/coin/CglTwomir.hpp rename to libs/or-tools-bin/include/coin/CglTwomir.hpp diff --git a/libs/or-tools/include/coin/CglZeroHalf.hpp b/libs/or-tools-bin/include/coin/CglZeroHalf.hpp similarity index 100% rename from libs/or-tools/include/coin/CglZeroHalf.hpp rename to libs/or-tools-bin/include/coin/CglZeroHalf.hpp diff --git a/libs/or-tools/include/coin/ClpAmplObjective.hpp b/libs/or-tools-bin/include/coin/ClpAmplObjective.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpAmplObjective.hpp rename to libs/or-tools-bin/include/coin/ClpAmplObjective.hpp diff --git a/libs/or-tools/include/coin/ClpCholeskyBase.hpp b/libs/or-tools-bin/include/coin/ClpCholeskyBase.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpCholeskyBase.hpp rename to libs/or-tools-bin/include/coin/ClpCholeskyBase.hpp diff --git a/libs/or-tools/include/coin/ClpCholeskyDense.hpp b/libs/or-tools-bin/include/coin/ClpCholeskyDense.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpCholeskyDense.hpp rename to libs/or-tools-bin/include/coin/ClpCholeskyDense.hpp diff --git a/libs/or-tools/include/coin/ClpCholeskyPardiso.hpp b/libs/or-tools-bin/include/coin/ClpCholeskyPardiso.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpCholeskyPardiso.hpp rename to libs/or-tools-bin/include/coin/ClpCholeskyPardiso.hpp diff --git a/libs/or-tools/include/coin/ClpConfig.h b/libs/or-tools-bin/include/coin/ClpConfig.h similarity index 100% rename from libs/or-tools/include/coin/ClpConfig.h rename to libs/or-tools-bin/include/coin/ClpConfig.h diff --git a/libs/or-tools/include/coin/ClpConstraint.hpp b/libs/or-tools-bin/include/coin/ClpConstraint.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpConstraint.hpp rename to libs/or-tools-bin/include/coin/ClpConstraint.hpp diff --git a/libs/or-tools/include/coin/ClpConstraintAmpl.hpp b/libs/or-tools-bin/include/coin/ClpConstraintAmpl.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpConstraintAmpl.hpp rename to libs/or-tools-bin/include/coin/ClpConstraintAmpl.hpp diff --git a/libs/or-tools/include/coin/ClpConstraintLinear.hpp b/libs/or-tools-bin/include/coin/ClpConstraintLinear.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpConstraintLinear.hpp rename to libs/or-tools-bin/include/coin/ClpConstraintLinear.hpp diff --git a/libs/or-tools/include/coin/ClpConstraintQuadratic.hpp b/libs/or-tools-bin/include/coin/ClpConstraintQuadratic.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpConstraintQuadratic.hpp rename to libs/or-tools-bin/include/coin/ClpConstraintQuadratic.hpp diff --git a/libs/or-tools/include/coin/ClpDualRowDantzig.hpp b/libs/or-tools-bin/include/coin/ClpDualRowDantzig.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpDualRowDantzig.hpp rename to libs/or-tools-bin/include/coin/ClpDualRowDantzig.hpp diff --git a/libs/or-tools/include/coin/ClpDualRowPivot.hpp b/libs/or-tools-bin/include/coin/ClpDualRowPivot.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpDualRowPivot.hpp rename to libs/or-tools-bin/include/coin/ClpDualRowPivot.hpp diff --git a/libs/or-tools/include/coin/ClpDualRowSteepest.hpp b/libs/or-tools-bin/include/coin/ClpDualRowSteepest.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpDualRowSteepest.hpp rename to libs/or-tools-bin/include/coin/ClpDualRowSteepest.hpp diff --git a/libs/or-tools/include/coin/ClpDummyMatrix.hpp b/libs/or-tools-bin/include/coin/ClpDummyMatrix.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpDummyMatrix.hpp rename to libs/or-tools-bin/include/coin/ClpDummyMatrix.hpp diff --git a/libs/or-tools/include/coin/ClpDynamicExampleMatrix.hpp b/libs/or-tools-bin/include/coin/ClpDynamicExampleMatrix.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpDynamicExampleMatrix.hpp rename to libs/or-tools-bin/include/coin/ClpDynamicExampleMatrix.hpp diff --git a/libs/or-tools/include/coin/ClpDynamicMatrix.hpp b/libs/or-tools-bin/include/coin/ClpDynamicMatrix.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpDynamicMatrix.hpp rename to libs/or-tools-bin/include/coin/ClpDynamicMatrix.hpp diff --git a/libs/or-tools/include/coin/ClpEventHandler.hpp b/libs/or-tools-bin/include/coin/ClpEventHandler.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpEventHandler.hpp rename to libs/or-tools-bin/include/coin/ClpEventHandler.hpp diff --git a/libs/or-tools/include/coin/ClpFactorization.hpp b/libs/or-tools-bin/include/coin/ClpFactorization.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpFactorization.hpp rename to libs/or-tools-bin/include/coin/ClpFactorization.hpp diff --git a/libs/or-tools/include/coin/ClpGubDynamicMatrix.hpp b/libs/or-tools-bin/include/coin/ClpGubDynamicMatrix.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpGubDynamicMatrix.hpp rename to libs/or-tools-bin/include/coin/ClpGubDynamicMatrix.hpp diff --git a/libs/or-tools/include/coin/ClpGubMatrix.hpp b/libs/or-tools-bin/include/coin/ClpGubMatrix.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpGubMatrix.hpp rename to libs/or-tools-bin/include/coin/ClpGubMatrix.hpp diff --git a/libs/or-tools/include/coin/ClpInterior.hpp b/libs/or-tools-bin/include/coin/ClpInterior.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpInterior.hpp rename to libs/or-tools-bin/include/coin/ClpInterior.hpp diff --git a/libs/or-tools/include/coin/ClpLinearObjective.hpp b/libs/or-tools-bin/include/coin/ClpLinearObjective.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpLinearObjective.hpp rename to libs/or-tools-bin/include/coin/ClpLinearObjective.hpp diff --git a/libs/or-tools/include/coin/ClpMatrixBase.hpp b/libs/or-tools-bin/include/coin/ClpMatrixBase.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpMatrixBase.hpp rename to libs/or-tools-bin/include/coin/ClpMatrixBase.hpp diff --git a/libs/or-tools/include/coin/ClpMessage.hpp b/libs/or-tools-bin/include/coin/ClpMessage.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpMessage.hpp rename to libs/or-tools-bin/include/coin/ClpMessage.hpp diff --git a/libs/or-tools/include/coin/ClpModel.hpp b/libs/or-tools-bin/include/coin/ClpModel.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpModel.hpp rename to libs/or-tools-bin/include/coin/ClpModel.hpp diff --git a/libs/or-tools/include/coin/ClpNetworkMatrix.hpp b/libs/or-tools-bin/include/coin/ClpNetworkMatrix.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpNetworkMatrix.hpp rename to libs/or-tools-bin/include/coin/ClpNetworkMatrix.hpp diff --git a/libs/or-tools/include/coin/ClpNode.hpp b/libs/or-tools-bin/include/coin/ClpNode.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpNode.hpp rename to libs/or-tools-bin/include/coin/ClpNode.hpp diff --git a/libs/or-tools/include/coin/ClpNonLinearCost.hpp b/libs/or-tools-bin/include/coin/ClpNonLinearCost.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpNonLinearCost.hpp rename to libs/or-tools-bin/include/coin/ClpNonLinearCost.hpp diff --git a/libs/or-tools/include/coin/ClpObjective.hpp b/libs/or-tools-bin/include/coin/ClpObjective.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpObjective.hpp rename to libs/or-tools-bin/include/coin/ClpObjective.hpp diff --git a/libs/or-tools/include/coin/ClpPEDualRowDantzig.hpp b/libs/or-tools-bin/include/coin/ClpPEDualRowDantzig.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPEDualRowDantzig.hpp rename to libs/or-tools-bin/include/coin/ClpPEDualRowDantzig.hpp diff --git a/libs/or-tools/include/coin/ClpPEDualRowSteepest.hpp b/libs/or-tools-bin/include/coin/ClpPEDualRowSteepest.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPEDualRowSteepest.hpp rename to libs/or-tools-bin/include/coin/ClpPEDualRowSteepest.hpp diff --git a/libs/or-tools/include/coin/ClpPEPrimalColumnDantzig.hpp b/libs/or-tools-bin/include/coin/ClpPEPrimalColumnDantzig.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPEPrimalColumnDantzig.hpp rename to libs/or-tools-bin/include/coin/ClpPEPrimalColumnDantzig.hpp diff --git a/libs/or-tools/include/coin/ClpPEPrimalColumnSteepest.hpp b/libs/or-tools-bin/include/coin/ClpPEPrimalColumnSteepest.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPEPrimalColumnSteepest.hpp rename to libs/or-tools-bin/include/coin/ClpPEPrimalColumnSteepest.hpp diff --git a/libs/or-tools/include/coin/ClpPESimplex.hpp b/libs/or-tools-bin/include/coin/ClpPESimplex.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPESimplex.hpp rename to libs/or-tools-bin/include/coin/ClpPESimplex.hpp diff --git a/libs/or-tools/include/coin/ClpPackedMatrix.hpp b/libs/or-tools-bin/include/coin/ClpPackedMatrix.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPackedMatrix.hpp rename to libs/or-tools-bin/include/coin/ClpPackedMatrix.hpp diff --git a/libs/or-tools/include/coin/ClpParameters.hpp b/libs/or-tools-bin/include/coin/ClpParameters.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpParameters.hpp rename to libs/or-tools-bin/include/coin/ClpParameters.hpp diff --git a/libs/or-tools/include/coin/ClpPdcoBase.hpp b/libs/or-tools-bin/include/coin/ClpPdcoBase.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPdcoBase.hpp rename to libs/or-tools-bin/include/coin/ClpPdcoBase.hpp diff --git a/libs/or-tools/include/coin/ClpPlusMinusOneMatrix.hpp b/libs/or-tools-bin/include/coin/ClpPlusMinusOneMatrix.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPlusMinusOneMatrix.hpp rename to libs/or-tools-bin/include/coin/ClpPlusMinusOneMatrix.hpp diff --git a/libs/or-tools/include/coin/ClpPresolve.hpp b/libs/or-tools-bin/include/coin/ClpPresolve.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPresolve.hpp rename to libs/or-tools-bin/include/coin/ClpPresolve.hpp diff --git a/libs/or-tools/include/coin/ClpPrimalColumnDantzig.hpp b/libs/or-tools-bin/include/coin/ClpPrimalColumnDantzig.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPrimalColumnDantzig.hpp rename to libs/or-tools-bin/include/coin/ClpPrimalColumnDantzig.hpp diff --git a/libs/or-tools/include/coin/ClpPrimalColumnPivot.hpp b/libs/or-tools-bin/include/coin/ClpPrimalColumnPivot.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPrimalColumnPivot.hpp rename to libs/or-tools-bin/include/coin/ClpPrimalColumnPivot.hpp diff --git a/libs/or-tools/include/coin/ClpPrimalColumnSteepest.hpp b/libs/or-tools-bin/include/coin/ClpPrimalColumnSteepest.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpPrimalColumnSteepest.hpp rename to libs/or-tools-bin/include/coin/ClpPrimalColumnSteepest.hpp diff --git a/libs/or-tools/include/coin/ClpQuadraticObjective.hpp b/libs/or-tools-bin/include/coin/ClpQuadraticObjective.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpQuadraticObjective.hpp rename to libs/or-tools-bin/include/coin/ClpQuadraticObjective.hpp diff --git a/libs/or-tools/include/coin/ClpSimplex.hpp b/libs/or-tools-bin/include/coin/ClpSimplex.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpSimplex.hpp rename to libs/or-tools-bin/include/coin/ClpSimplex.hpp diff --git a/libs/or-tools/include/coin/ClpSimplexDual.hpp b/libs/or-tools-bin/include/coin/ClpSimplexDual.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpSimplexDual.hpp rename to libs/or-tools-bin/include/coin/ClpSimplexDual.hpp diff --git a/libs/or-tools/include/coin/ClpSimplexNonlinear.hpp b/libs/or-tools-bin/include/coin/ClpSimplexNonlinear.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpSimplexNonlinear.hpp rename to libs/or-tools-bin/include/coin/ClpSimplexNonlinear.hpp diff --git a/libs/or-tools/include/coin/ClpSimplexOther.hpp b/libs/or-tools-bin/include/coin/ClpSimplexOther.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpSimplexOther.hpp rename to libs/or-tools-bin/include/coin/ClpSimplexOther.hpp diff --git a/libs/or-tools/include/coin/ClpSimplexPrimal.hpp b/libs/or-tools-bin/include/coin/ClpSimplexPrimal.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpSimplexPrimal.hpp rename to libs/or-tools-bin/include/coin/ClpSimplexPrimal.hpp diff --git a/libs/or-tools/include/coin/ClpSolve.hpp b/libs/or-tools-bin/include/coin/ClpSolve.hpp similarity index 100% rename from libs/or-tools/include/coin/ClpSolve.hpp rename to libs/or-tools-bin/include/coin/ClpSolve.hpp diff --git a/libs/or-tools/include/coin/Clp_C_Interface.h b/libs/or-tools-bin/include/coin/Clp_C_Interface.h similarity index 100% rename from libs/or-tools/include/coin/Clp_C_Interface.h rename to libs/or-tools-bin/include/coin/Clp_C_Interface.h diff --git a/libs/or-tools/include/coin/CoinAlloc.hpp b/libs/or-tools-bin/include/coin/CoinAlloc.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinAlloc.hpp rename to libs/or-tools-bin/include/coin/CoinAlloc.hpp diff --git a/libs/or-tools/include/coin/CoinBuild.hpp b/libs/or-tools-bin/include/coin/CoinBuild.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinBuild.hpp rename to libs/or-tools-bin/include/coin/CoinBuild.hpp diff --git a/libs/or-tools/include/coin/CoinDenseFactorization.hpp b/libs/or-tools-bin/include/coin/CoinDenseFactorization.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinDenseFactorization.hpp rename to libs/or-tools-bin/include/coin/CoinDenseFactorization.hpp diff --git a/libs/or-tools/include/coin/CoinDenseVector.hpp b/libs/or-tools-bin/include/coin/CoinDenseVector.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinDenseVector.hpp rename to libs/or-tools-bin/include/coin/CoinDenseVector.hpp diff --git a/libs/or-tools/include/coin/CoinDistance.hpp b/libs/or-tools-bin/include/coin/CoinDistance.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinDistance.hpp rename to libs/or-tools-bin/include/coin/CoinDistance.hpp diff --git a/libs/or-tools/include/coin/CoinError.hpp b/libs/or-tools-bin/include/coin/CoinError.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinError.hpp rename to libs/or-tools-bin/include/coin/CoinError.hpp diff --git a/libs/or-tools/include/coin/CoinFactorization.hpp b/libs/or-tools-bin/include/coin/CoinFactorization.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinFactorization.hpp rename to libs/or-tools-bin/include/coin/CoinFactorization.hpp diff --git a/libs/or-tools/include/coin/CoinFileIO.hpp b/libs/or-tools-bin/include/coin/CoinFileIO.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinFileIO.hpp rename to libs/or-tools-bin/include/coin/CoinFileIO.hpp diff --git a/libs/or-tools/include/coin/CoinFinite.hpp b/libs/or-tools-bin/include/coin/CoinFinite.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinFinite.hpp rename to libs/or-tools-bin/include/coin/CoinFinite.hpp diff --git a/libs/or-tools/include/coin/CoinFloatEqual.hpp b/libs/or-tools-bin/include/coin/CoinFloatEqual.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinFloatEqual.hpp rename to libs/or-tools-bin/include/coin/CoinFloatEqual.hpp diff --git a/libs/or-tools/include/coin/CoinHelperFunctions.hpp b/libs/or-tools-bin/include/coin/CoinHelperFunctions.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinHelperFunctions.hpp rename to libs/or-tools-bin/include/coin/CoinHelperFunctions.hpp diff --git a/libs/or-tools/include/coin/CoinIndexedVector.hpp b/libs/or-tools-bin/include/coin/CoinIndexedVector.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinIndexedVector.hpp rename to libs/or-tools-bin/include/coin/CoinIndexedVector.hpp diff --git a/libs/or-tools/include/coin/CoinLpIO.hpp b/libs/or-tools-bin/include/coin/CoinLpIO.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinLpIO.hpp rename to libs/or-tools-bin/include/coin/CoinLpIO.hpp diff --git a/libs/or-tools/include/coin/CoinMessage.hpp b/libs/or-tools-bin/include/coin/CoinMessage.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinMessage.hpp rename to libs/or-tools-bin/include/coin/CoinMessage.hpp diff --git a/libs/or-tools/include/coin/CoinMessageHandler.hpp b/libs/or-tools-bin/include/coin/CoinMessageHandler.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinMessageHandler.hpp rename to libs/or-tools-bin/include/coin/CoinMessageHandler.hpp diff --git a/libs/or-tools/include/coin/CoinModel.hpp b/libs/or-tools-bin/include/coin/CoinModel.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinModel.hpp rename to libs/or-tools-bin/include/coin/CoinModel.hpp diff --git a/libs/or-tools/include/coin/CoinModelUseful.hpp b/libs/or-tools-bin/include/coin/CoinModelUseful.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinModelUseful.hpp rename to libs/or-tools-bin/include/coin/CoinModelUseful.hpp diff --git a/libs/or-tools/include/coin/CoinMpsIO.hpp b/libs/or-tools-bin/include/coin/CoinMpsIO.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinMpsIO.hpp rename to libs/or-tools-bin/include/coin/CoinMpsIO.hpp diff --git a/libs/or-tools/include/coin/CoinOslFactorization.hpp b/libs/or-tools-bin/include/coin/CoinOslFactorization.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinOslFactorization.hpp rename to libs/or-tools-bin/include/coin/CoinOslFactorization.hpp diff --git a/libs/or-tools/include/coin/CoinPackedMatrix.hpp b/libs/or-tools-bin/include/coin/CoinPackedMatrix.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPackedMatrix.hpp rename to libs/or-tools-bin/include/coin/CoinPackedMatrix.hpp diff --git a/libs/or-tools/include/coin/CoinPackedVector.hpp b/libs/or-tools-bin/include/coin/CoinPackedVector.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPackedVector.hpp rename to libs/or-tools-bin/include/coin/CoinPackedVector.hpp diff --git a/libs/or-tools/include/coin/CoinPackedVectorBase.hpp b/libs/or-tools-bin/include/coin/CoinPackedVectorBase.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPackedVectorBase.hpp rename to libs/or-tools-bin/include/coin/CoinPackedVectorBase.hpp diff --git a/libs/or-tools/include/coin/CoinParam.hpp b/libs/or-tools-bin/include/coin/CoinParam.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinParam.hpp rename to libs/or-tools-bin/include/coin/CoinParam.hpp diff --git a/libs/or-tools/include/coin/CoinPragma.hpp b/libs/or-tools-bin/include/coin/CoinPragma.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPragma.hpp rename to libs/or-tools-bin/include/coin/CoinPragma.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveDoubleton.hpp b/libs/or-tools-bin/include/coin/CoinPresolveDoubleton.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveDoubleton.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveDoubleton.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveDual.hpp b/libs/or-tools-bin/include/coin/CoinPresolveDual.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveDual.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveDual.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveDupcol.hpp b/libs/or-tools-bin/include/coin/CoinPresolveDupcol.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveDupcol.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveDupcol.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveEmpty.hpp b/libs/or-tools-bin/include/coin/CoinPresolveEmpty.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveEmpty.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveEmpty.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveFixed.hpp b/libs/or-tools-bin/include/coin/CoinPresolveFixed.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveFixed.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveFixed.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveForcing.hpp b/libs/or-tools-bin/include/coin/CoinPresolveForcing.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveForcing.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveForcing.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveImpliedFree.hpp b/libs/or-tools-bin/include/coin/CoinPresolveImpliedFree.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveImpliedFree.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveImpliedFree.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveIsolated.hpp b/libs/or-tools-bin/include/coin/CoinPresolveIsolated.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveIsolated.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveIsolated.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveMatrix.hpp b/libs/or-tools-bin/include/coin/CoinPresolveMatrix.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveMatrix.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveMatrix.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveMonitor.hpp b/libs/or-tools-bin/include/coin/CoinPresolveMonitor.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveMonitor.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveMonitor.hpp diff --git a/libs/or-tools/include/coin/CoinPresolvePsdebug.hpp b/libs/or-tools-bin/include/coin/CoinPresolvePsdebug.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolvePsdebug.hpp rename to libs/or-tools-bin/include/coin/CoinPresolvePsdebug.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveSingleton.hpp b/libs/or-tools-bin/include/coin/CoinPresolveSingleton.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveSingleton.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveSingleton.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveSubst.hpp b/libs/or-tools-bin/include/coin/CoinPresolveSubst.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveSubst.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveSubst.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveTighten.hpp b/libs/or-tools-bin/include/coin/CoinPresolveTighten.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveTighten.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveTighten.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveTripleton.hpp b/libs/or-tools-bin/include/coin/CoinPresolveTripleton.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveTripleton.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveTripleton.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveUseless.hpp b/libs/or-tools-bin/include/coin/CoinPresolveUseless.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveUseless.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveUseless.hpp diff --git a/libs/or-tools/include/coin/CoinPresolveZeros.hpp b/libs/or-tools-bin/include/coin/CoinPresolveZeros.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinPresolveZeros.hpp rename to libs/or-tools-bin/include/coin/CoinPresolveZeros.hpp diff --git a/libs/or-tools/include/coin/CoinRational.hpp b/libs/or-tools-bin/include/coin/CoinRational.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinRational.hpp rename to libs/or-tools-bin/include/coin/CoinRational.hpp diff --git a/libs/or-tools/include/coin/CoinSearchTree.hpp b/libs/or-tools-bin/include/coin/CoinSearchTree.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinSearchTree.hpp rename to libs/or-tools-bin/include/coin/CoinSearchTree.hpp diff --git a/libs/or-tools/include/coin/CoinShallowPackedVector.hpp b/libs/or-tools-bin/include/coin/CoinShallowPackedVector.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinShallowPackedVector.hpp rename to libs/or-tools-bin/include/coin/CoinShallowPackedVector.hpp diff --git a/libs/or-tools/include/coin/CoinSignal.hpp b/libs/or-tools-bin/include/coin/CoinSignal.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinSignal.hpp rename to libs/or-tools-bin/include/coin/CoinSignal.hpp diff --git a/libs/or-tools/include/coin/CoinSimpFactorization.hpp b/libs/or-tools-bin/include/coin/CoinSimpFactorization.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinSimpFactorization.hpp rename to libs/or-tools-bin/include/coin/CoinSimpFactorization.hpp diff --git a/libs/or-tools/include/coin/CoinSmartPtr.hpp b/libs/or-tools-bin/include/coin/CoinSmartPtr.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinSmartPtr.hpp rename to libs/or-tools-bin/include/coin/CoinSmartPtr.hpp diff --git a/libs/or-tools/include/coin/CoinSnapshot.hpp b/libs/or-tools-bin/include/coin/CoinSnapshot.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinSnapshot.hpp rename to libs/or-tools-bin/include/coin/CoinSnapshot.hpp diff --git a/libs/or-tools/include/coin/CoinSort.hpp b/libs/or-tools-bin/include/coin/CoinSort.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinSort.hpp rename to libs/or-tools-bin/include/coin/CoinSort.hpp diff --git a/libs/or-tools/include/coin/CoinStructuredModel.hpp b/libs/or-tools-bin/include/coin/CoinStructuredModel.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinStructuredModel.hpp rename to libs/or-tools-bin/include/coin/CoinStructuredModel.hpp diff --git a/libs/or-tools/include/coin/CoinTime.hpp b/libs/or-tools-bin/include/coin/CoinTime.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinTime.hpp rename to libs/or-tools-bin/include/coin/CoinTime.hpp diff --git a/libs/or-tools/include/coin/CoinTypes.hpp b/libs/or-tools-bin/include/coin/CoinTypes.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinTypes.hpp rename to libs/or-tools-bin/include/coin/CoinTypes.hpp diff --git a/libs/or-tools/include/coin/CoinUtility.hpp b/libs/or-tools-bin/include/coin/CoinUtility.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinUtility.hpp rename to libs/or-tools-bin/include/coin/CoinUtility.hpp diff --git a/libs/or-tools/include/coin/CoinUtilsConfig.h b/libs/or-tools-bin/include/coin/CoinUtilsConfig.h similarity index 100% rename from libs/or-tools/include/coin/CoinUtilsConfig.h rename to libs/or-tools-bin/include/coin/CoinUtilsConfig.h diff --git a/libs/or-tools/include/coin/CoinWarmStart.hpp b/libs/or-tools-bin/include/coin/CoinWarmStart.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinWarmStart.hpp rename to libs/or-tools-bin/include/coin/CoinWarmStart.hpp diff --git a/libs/or-tools/include/coin/CoinWarmStartBasis.hpp b/libs/or-tools-bin/include/coin/CoinWarmStartBasis.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinWarmStartBasis.hpp rename to libs/or-tools-bin/include/coin/CoinWarmStartBasis.hpp diff --git a/libs/or-tools/include/coin/CoinWarmStartDual.hpp b/libs/or-tools-bin/include/coin/CoinWarmStartDual.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinWarmStartDual.hpp rename to libs/or-tools-bin/include/coin/CoinWarmStartDual.hpp diff --git a/libs/or-tools/include/coin/CoinWarmStartPrimalDual.hpp b/libs/or-tools-bin/include/coin/CoinWarmStartPrimalDual.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinWarmStartPrimalDual.hpp rename to libs/or-tools-bin/include/coin/CoinWarmStartPrimalDual.hpp diff --git a/libs/or-tools/include/coin/CoinWarmStartVector.hpp b/libs/or-tools-bin/include/coin/CoinWarmStartVector.hpp similarity index 100% rename from libs/or-tools/include/coin/CoinWarmStartVector.hpp rename to libs/or-tools-bin/include/coin/CoinWarmStartVector.hpp diff --git a/libs/or-tools/include/coin/Coin_C_defines.h b/libs/or-tools-bin/include/coin/Coin_C_defines.h similarity index 100% rename from libs/or-tools/include/coin/Coin_C_defines.h rename to libs/or-tools-bin/include/coin/Coin_C_defines.h diff --git a/libs/or-tools/include/coin/Idiot.hpp b/libs/or-tools-bin/include/coin/Idiot.hpp similarity index 100% rename from libs/or-tools/include/coin/Idiot.hpp rename to libs/or-tools-bin/include/coin/Idiot.hpp diff --git a/libs/or-tools/include/coin/OsiAuxInfo.hpp b/libs/or-tools-bin/include/coin/OsiAuxInfo.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiAuxInfo.hpp rename to libs/or-tools-bin/include/coin/OsiAuxInfo.hpp diff --git a/libs/or-tools/include/coin/OsiBranchingObject.hpp b/libs/or-tools-bin/include/coin/OsiBranchingObject.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiBranchingObject.hpp rename to libs/or-tools-bin/include/coin/OsiBranchingObject.hpp diff --git a/libs/or-tools/include/coin/OsiCbcSolverInterface.hpp b/libs/or-tools-bin/include/coin/OsiCbcSolverInterface.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiCbcSolverInterface.hpp rename to libs/or-tools-bin/include/coin/OsiCbcSolverInterface.hpp diff --git a/libs/or-tools/include/coin/OsiChooseVariable.hpp b/libs/or-tools-bin/include/coin/OsiChooseVariable.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiChooseVariable.hpp rename to libs/or-tools-bin/include/coin/OsiChooseVariable.hpp diff --git a/libs/or-tools/include/coin/OsiClpSolverInterface.hpp b/libs/or-tools-bin/include/coin/OsiClpSolverInterface.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiClpSolverInterface.hpp rename to libs/or-tools-bin/include/coin/OsiClpSolverInterface.hpp diff --git a/libs/or-tools/include/coin/OsiColCut.hpp b/libs/or-tools-bin/include/coin/OsiColCut.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiColCut.hpp rename to libs/or-tools-bin/include/coin/OsiColCut.hpp diff --git a/libs/or-tools/include/coin/OsiCollections.hpp b/libs/or-tools-bin/include/coin/OsiCollections.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiCollections.hpp rename to libs/or-tools-bin/include/coin/OsiCollections.hpp diff --git a/libs/or-tools/include/coin/OsiConfig.h b/libs/or-tools-bin/include/coin/OsiConfig.h similarity index 100% rename from libs/or-tools/include/coin/OsiConfig.h rename to libs/or-tools-bin/include/coin/OsiConfig.h diff --git a/libs/or-tools/include/coin/OsiCut.hpp b/libs/or-tools-bin/include/coin/OsiCut.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiCut.hpp rename to libs/or-tools-bin/include/coin/OsiCut.hpp diff --git a/libs/or-tools/include/coin/OsiCuts.hpp b/libs/or-tools-bin/include/coin/OsiCuts.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiCuts.hpp rename to libs/or-tools-bin/include/coin/OsiCuts.hpp diff --git a/libs/or-tools/include/coin/OsiPresolve.hpp b/libs/or-tools-bin/include/coin/OsiPresolve.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiPresolve.hpp rename to libs/or-tools-bin/include/coin/OsiPresolve.hpp diff --git a/libs/or-tools/include/coin/OsiRowCut.hpp b/libs/or-tools-bin/include/coin/OsiRowCut.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiRowCut.hpp rename to libs/or-tools-bin/include/coin/OsiRowCut.hpp diff --git a/libs/or-tools/include/coin/OsiRowCutDebugger.hpp b/libs/or-tools-bin/include/coin/OsiRowCutDebugger.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiRowCutDebugger.hpp rename to libs/or-tools-bin/include/coin/OsiRowCutDebugger.hpp diff --git a/libs/or-tools/include/coin/OsiSolverBranch.hpp b/libs/or-tools-bin/include/coin/OsiSolverBranch.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiSolverBranch.hpp rename to libs/or-tools-bin/include/coin/OsiSolverBranch.hpp diff --git a/libs/or-tools/include/coin/OsiSolverInterface.hpp b/libs/or-tools-bin/include/coin/OsiSolverInterface.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiSolverInterface.hpp rename to libs/or-tools-bin/include/coin/OsiSolverInterface.hpp diff --git a/libs/or-tools/include/coin/OsiSolverParameters.hpp b/libs/or-tools-bin/include/coin/OsiSolverParameters.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiSolverParameters.hpp rename to libs/or-tools-bin/include/coin/OsiSolverParameters.hpp diff --git a/libs/or-tools/include/coin/OsiUnitTests.hpp b/libs/or-tools-bin/include/coin/OsiUnitTests.hpp similarity index 100% rename from libs/or-tools/include/coin/OsiUnitTests.hpp rename to libs/or-tools-bin/include/coin/OsiUnitTests.hpp diff --git a/libs/or-tools/include/gflags/gflags.h b/libs/or-tools-bin/include/gflags/gflags.h similarity index 100% rename from libs/or-tools/include/gflags/gflags.h rename to libs/or-tools-bin/include/gflags/gflags.h diff --git a/libs/or-tools/include/gflags/gflags_completions.h b/libs/or-tools-bin/include/gflags/gflags_completions.h similarity index 100% rename from libs/or-tools/include/gflags/gflags_completions.h rename to libs/or-tools-bin/include/gflags/gflags_completions.h diff --git a/libs/or-tools/include/gflags/gflags_declare.h b/libs/or-tools-bin/include/gflags/gflags_declare.h similarity index 100% rename from libs/or-tools/include/gflags/gflags_declare.h rename to libs/or-tools-bin/include/gflags/gflags_declare.h diff --git a/libs/or-tools/include/glog/log_severity.h b/libs/or-tools-bin/include/glog/log_severity.h similarity index 100% rename from libs/or-tools/include/glog/log_severity.h rename to libs/or-tools-bin/include/glog/log_severity.h diff --git a/libs/or-tools/include/glog/logging.h b/libs/or-tools-bin/include/glog/logging.h similarity index 100% rename from libs/or-tools/include/glog/logging.h rename to libs/or-tools-bin/include/glog/logging.h diff --git a/libs/or-tools/include/glog/raw_logging.h b/libs/or-tools-bin/include/glog/raw_logging.h similarity index 100% rename from libs/or-tools/include/glog/raw_logging.h rename to libs/or-tools-bin/include/glog/raw_logging.h diff --git a/libs/or-tools/include/glog/stl_logging.h b/libs/or-tools-bin/include/glog/stl_logging.h similarity index 100% rename from libs/or-tools/include/glog/stl_logging.h rename to libs/or-tools-bin/include/glog/stl_logging.h diff --git a/libs/or-tools/include/glog/vlog_is_on.h b/libs/or-tools-bin/include/glog/vlog_is_on.h similarity index 100% rename from libs/or-tools/include/glog/vlog_is_on.h rename to libs/or-tools-bin/include/glog/vlog_is_on.h diff --git a/libs/or-tools/include/google/protobuf/any.h b/libs/or-tools-bin/include/google/protobuf/any.h similarity index 100% rename from libs/or-tools/include/google/protobuf/any.h rename to libs/or-tools-bin/include/google/protobuf/any.h diff --git a/libs/or-tools/include/google/protobuf/any.pb.h b/libs/or-tools-bin/include/google/protobuf/any.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/any.pb.h rename to libs/or-tools-bin/include/google/protobuf/any.pb.h diff --git a/libs/or-tools/include/google/protobuf/any.proto b/libs/or-tools-bin/include/google/protobuf/any.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/any.proto rename to libs/or-tools-bin/include/google/protobuf/any.proto diff --git a/libs/or-tools/include/google/protobuf/api.pb.h b/libs/or-tools-bin/include/google/protobuf/api.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/api.pb.h rename to libs/or-tools-bin/include/google/protobuf/api.pb.h diff --git a/libs/or-tools/include/google/protobuf/api.proto b/libs/or-tools-bin/include/google/protobuf/api.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/api.proto rename to libs/or-tools-bin/include/google/protobuf/api.proto diff --git a/libs/or-tools/include/google/protobuf/arena.h b/libs/or-tools-bin/include/google/protobuf/arena.h similarity index 100% rename from libs/or-tools/include/google/protobuf/arena.h rename to libs/or-tools-bin/include/google/protobuf/arena.h diff --git a/libs/or-tools/include/google/protobuf/arena_impl.h b/libs/or-tools-bin/include/google/protobuf/arena_impl.h similarity index 100% rename from libs/or-tools/include/google/protobuf/arena_impl.h rename to libs/or-tools-bin/include/google/protobuf/arena_impl.h diff --git a/libs/or-tools/include/google/protobuf/arenastring.h b/libs/or-tools-bin/include/google/protobuf/arenastring.h similarity index 100% rename from libs/or-tools/include/google/protobuf/arenastring.h rename to libs/or-tools-bin/include/google/protobuf/arenastring.h diff --git a/libs/or-tools/include/google/protobuf/compiler/code_generator.h b/libs/or-tools-bin/include/google/protobuf/compiler/code_generator.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/code_generator.h rename to libs/or-tools-bin/include/google/protobuf/compiler/code_generator.h diff --git a/libs/or-tools/include/google/protobuf/compiler/command_line_interface.h b/libs/or-tools-bin/include/google/protobuf/compiler/command_line_interface.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/command_line_interface.h rename to libs/or-tools-bin/include/google/protobuf/compiler/command_line_interface.h diff --git a/libs/or-tools/include/google/protobuf/compiler/cpp/cpp_generator.h b/libs/or-tools-bin/include/google/protobuf/compiler/cpp/cpp_generator.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/cpp/cpp_generator.h rename to libs/or-tools-bin/include/google/protobuf/compiler/cpp/cpp_generator.h diff --git a/libs/or-tools/include/google/protobuf/compiler/csharp/csharp_generator.h b/libs/or-tools-bin/include/google/protobuf/compiler/csharp/csharp_generator.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/csharp/csharp_generator.h rename to libs/or-tools-bin/include/google/protobuf/compiler/csharp/csharp_generator.h diff --git a/libs/or-tools/include/google/protobuf/compiler/csharp/csharp_names.h b/libs/or-tools-bin/include/google/protobuf/compiler/csharp/csharp_names.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/csharp/csharp_names.h rename to libs/or-tools-bin/include/google/protobuf/compiler/csharp/csharp_names.h diff --git a/libs/or-tools/include/google/protobuf/compiler/importer.h b/libs/or-tools-bin/include/google/protobuf/compiler/importer.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/importer.h rename to libs/or-tools-bin/include/google/protobuf/compiler/importer.h diff --git a/libs/or-tools/include/google/protobuf/compiler/java/java_generator.h b/libs/or-tools-bin/include/google/protobuf/compiler/java/java_generator.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/java/java_generator.h rename to libs/or-tools-bin/include/google/protobuf/compiler/java/java_generator.h diff --git a/libs/or-tools/include/google/protobuf/compiler/java/java_names.h b/libs/or-tools-bin/include/google/protobuf/compiler/java/java_names.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/java/java_names.h rename to libs/or-tools-bin/include/google/protobuf/compiler/java/java_names.h diff --git a/libs/or-tools/include/google/protobuf/compiler/js/js_generator.h b/libs/or-tools-bin/include/google/protobuf/compiler/js/js_generator.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/js/js_generator.h rename to libs/or-tools-bin/include/google/protobuf/compiler/js/js_generator.h diff --git a/libs/or-tools/include/google/protobuf/compiler/js/well_known_types_embed.h b/libs/or-tools-bin/include/google/protobuf/compiler/js/well_known_types_embed.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/js/well_known_types_embed.h rename to libs/or-tools-bin/include/google/protobuf/compiler/js/well_known_types_embed.h diff --git a/libs/or-tools/include/google/protobuf/compiler/objectivec/objectivec_generator.h b/libs/or-tools-bin/include/google/protobuf/compiler/objectivec/objectivec_generator.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/objectivec/objectivec_generator.h rename to libs/or-tools-bin/include/google/protobuf/compiler/objectivec/objectivec_generator.h diff --git a/libs/or-tools/include/google/protobuf/compiler/objectivec/objectivec_helpers.h b/libs/or-tools-bin/include/google/protobuf/compiler/objectivec/objectivec_helpers.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/objectivec/objectivec_helpers.h rename to libs/or-tools-bin/include/google/protobuf/compiler/objectivec/objectivec_helpers.h diff --git a/libs/or-tools/include/google/protobuf/compiler/parser.h b/libs/or-tools-bin/include/google/protobuf/compiler/parser.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/parser.h rename to libs/or-tools-bin/include/google/protobuf/compiler/parser.h diff --git a/libs/or-tools/include/google/protobuf/compiler/php/php_generator.h b/libs/or-tools-bin/include/google/protobuf/compiler/php/php_generator.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/php/php_generator.h rename to libs/or-tools-bin/include/google/protobuf/compiler/php/php_generator.h diff --git a/libs/or-tools/include/google/protobuf/compiler/plugin.h b/libs/or-tools-bin/include/google/protobuf/compiler/plugin.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/plugin.h rename to libs/or-tools-bin/include/google/protobuf/compiler/plugin.h diff --git a/libs/or-tools/include/google/protobuf/compiler/plugin.pb.h b/libs/or-tools-bin/include/google/protobuf/compiler/plugin.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/plugin.pb.h rename to libs/or-tools-bin/include/google/protobuf/compiler/plugin.pb.h diff --git a/libs/or-tools/include/google/protobuf/compiler/plugin.proto b/libs/or-tools-bin/include/google/protobuf/compiler/plugin.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/plugin.proto rename to libs/or-tools-bin/include/google/protobuf/compiler/plugin.proto diff --git a/libs/or-tools/include/google/protobuf/compiler/python/python_generator.h b/libs/or-tools-bin/include/google/protobuf/compiler/python/python_generator.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/python/python_generator.h rename to libs/or-tools-bin/include/google/protobuf/compiler/python/python_generator.h diff --git a/libs/or-tools/include/google/protobuf/compiler/ruby/ruby_generator.h b/libs/or-tools-bin/include/google/protobuf/compiler/ruby/ruby_generator.h similarity index 100% rename from libs/or-tools/include/google/protobuf/compiler/ruby/ruby_generator.h rename to libs/or-tools-bin/include/google/protobuf/compiler/ruby/ruby_generator.h diff --git a/libs/or-tools/include/google/protobuf/descriptor.h b/libs/or-tools-bin/include/google/protobuf/descriptor.h similarity index 100% rename from libs/or-tools/include/google/protobuf/descriptor.h rename to libs/or-tools-bin/include/google/protobuf/descriptor.h diff --git a/libs/or-tools/include/google/protobuf/descriptor.pb.h b/libs/or-tools-bin/include/google/protobuf/descriptor.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/descriptor.pb.h rename to libs/or-tools-bin/include/google/protobuf/descriptor.pb.h diff --git a/libs/or-tools/include/google/protobuf/descriptor.proto b/libs/or-tools-bin/include/google/protobuf/descriptor.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/descriptor.proto rename to libs/or-tools-bin/include/google/protobuf/descriptor.proto diff --git a/libs/or-tools/include/google/protobuf/descriptor_database.h b/libs/or-tools-bin/include/google/protobuf/descriptor_database.h similarity index 100% rename from libs/or-tools/include/google/protobuf/descriptor_database.h rename to libs/or-tools-bin/include/google/protobuf/descriptor_database.h diff --git a/libs/or-tools/include/google/protobuf/duration.pb.h b/libs/or-tools-bin/include/google/protobuf/duration.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/duration.pb.h rename to libs/or-tools-bin/include/google/protobuf/duration.pb.h diff --git a/libs/or-tools/include/google/protobuf/duration.proto b/libs/or-tools-bin/include/google/protobuf/duration.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/duration.proto rename to libs/or-tools-bin/include/google/protobuf/duration.proto diff --git a/libs/or-tools/include/google/protobuf/dynamic_message.h b/libs/or-tools-bin/include/google/protobuf/dynamic_message.h similarity index 100% rename from libs/or-tools/include/google/protobuf/dynamic_message.h rename to libs/or-tools-bin/include/google/protobuf/dynamic_message.h diff --git a/libs/or-tools/include/google/protobuf/empty.pb.h b/libs/or-tools-bin/include/google/protobuf/empty.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/empty.pb.h rename to libs/or-tools-bin/include/google/protobuf/empty.pb.h diff --git a/libs/or-tools/include/google/protobuf/empty.proto b/libs/or-tools-bin/include/google/protobuf/empty.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/empty.proto rename to libs/or-tools-bin/include/google/protobuf/empty.proto diff --git a/libs/or-tools/include/google/protobuf/extension_set.h b/libs/or-tools-bin/include/google/protobuf/extension_set.h similarity index 100% rename from libs/or-tools/include/google/protobuf/extension_set.h rename to libs/or-tools-bin/include/google/protobuf/extension_set.h diff --git a/libs/or-tools/include/google/protobuf/extension_set_inl.h b/libs/or-tools-bin/include/google/protobuf/extension_set_inl.h similarity index 100% rename from libs/or-tools/include/google/protobuf/extension_set_inl.h rename to libs/or-tools-bin/include/google/protobuf/extension_set_inl.h diff --git a/libs/or-tools/include/google/protobuf/field_mask.pb.h b/libs/or-tools-bin/include/google/protobuf/field_mask.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/field_mask.pb.h rename to libs/or-tools-bin/include/google/protobuf/field_mask.pb.h diff --git a/libs/or-tools/include/google/protobuf/field_mask.proto b/libs/or-tools-bin/include/google/protobuf/field_mask.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/field_mask.proto rename to libs/or-tools-bin/include/google/protobuf/field_mask.proto diff --git a/libs/or-tools/include/google/protobuf/generated_enum_reflection.h b/libs/or-tools-bin/include/google/protobuf/generated_enum_reflection.h similarity index 100% rename from libs/or-tools/include/google/protobuf/generated_enum_reflection.h rename to libs/or-tools-bin/include/google/protobuf/generated_enum_reflection.h diff --git a/libs/or-tools/include/google/protobuf/generated_enum_util.h b/libs/or-tools-bin/include/google/protobuf/generated_enum_util.h similarity index 100% rename from libs/or-tools/include/google/protobuf/generated_enum_util.h rename to libs/or-tools-bin/include/google/protobuf/generated_enum_util.h diff --git a/libs/or-tools/include/google/protobuf/generated_message_reflection.h b/libs/or-tools-bin/include/google/protobuf/generated_message_reflection.h similarity index 100% rename from libs/or-tools/include/google/protobuf/generated_message_reflection.h rename to libs/or-tools-bin/include/google/protobuf/generated_message_reflection.h diff --git a/libs/or-tools/include/google/protobuf/generated_message_table_driven.h b/libs/or-tools-bin/include/google/protobuf/generated_message_table_driven.h similarity index 100% rename from libs/or-tools/include/google/protobuf/generated_message_table_driven.h rename to libs/or-tools-bin/include/google/protobuf/generated_message_table_driven.h diff --git a/libs/or-tools/include/google/protobuf/generated_message_util.h b/libs/or-tools-bin/include/google/protobuf/generated_message_util.h similarity index 100% rename from libs/or-tools/include/google/protobuf/generated_message_util.h rename to libs/or-tools-bin/include/google/protobuf/generated_message_util.h diff --git a/libs/or-tools/include/google/protobuf/has_bits.h b/libs/or-tools-bin/include/google/protobuf/has_bits.h similarity index 100% rename from libs/or-tools/include/google/protobuf/has_bits.h rename to libs/or-tools-bin/include/google/protobuf/has_bits.h diff --git a/libs/or-tools/include/google/protobuf/implicit_weak_message.h b/libs/or-tools-bin/include/google/protobuf/implicit_weak_message.h similarity index 100% rename from libs/or-tools/include/google/protobuf/implicit_weak_message.h rename to libs/or-tools-bin/include/google/protobuf/implicit_weak_message.h diff --git a/libs/or-tools/include/google/protobuf/inlined_string_field.h b/libs/or-tools-bin/include/google/protobuf/inlined_string_field.h similarity index 100% rename from libs/or-tools/include/google/protobuf/inlined_string_field.h rename to libs/or-tools-bin/include/google/protobuf/inlined_string_field.h diff --git a/libs/or-tools/include/google/protobuf/io/coded_stream.h b/libs/or-tools-bin/include/google/protobuf/io/coded_stream.h similarity index 100% rename from libs/or-tools/include/google/protobuf/io/coded_stream.h rename to libs/or-tools-bin/include/google/protobuf/io/coded_stream.h diff --git a/libs/or-tools/include/google/protobuf/io/gzip_stream.h b/libs/or-tools-bin/include/google/protobuf/io/gzip_stream.h similarity index 100% rename from libs/or-tools/include/google/protobuf/io/gzip_stream.h rename to libs/or-tools-bin/include/google/protobuf/io/gzip_stream.h diff --git a/libs/or-tools/include/google/protobuf/io/io_win32.h b/libs/or-tools-bin/include/google/protobuf/io/io_win32.h similarity index 100% rename from libs/or-tools/include/google/protobuf/io/io_win32.h rename to libs/or-tools-bin/include/google/protobuf/io/io_win32.h diff --git a/libs/or-tools/include/google/protobuf/io/printer.h b/libs/or-tools-bin/include/google/protobuf/io/printer.h similarity index 100% rename from libs/or-tools/include/google/protobuf/io/printer.h rename to libs/or-tools-bin/include/google/protobuf/io/printer.h diff --git a/libs/or-tools/include/google/protobuf/io/strtod.h b/libs/or-tools-bin/include/google/protobuf/io/strtod.h similarity index 100% rename from libs/or-tools/include/google/protobuf/io/strtod.h rename to libs/or-tools-bin/include/google/protobuf/io/strtod.h diff --git a/libs/or-tools/include/google/protobuf/io/tokenizer.h b/libs/or-tools-bin/include/google/protobuf/io/tokenizer.h similarity index 100% rename from libs/or-tools/include/google/protobuf/io/tokenizer.h rename to libs/or-tools-bin/include/google/protobuf/io/tokenizer.h diff --git a/libs/or-tools/include/google/protobuf/io/zero_copy_stream.h b/libs/or-tools-bin/include/google/protobuf/io/zero_copy_stream.h similarity index 100% rename from libs/or-tools/include/google/protobuf/io/zero_copy_stream.h rename to libs/or-tools-bin/include/google/protobuf/io/zero_copy_stream.h diff --git a/libs/or-tools/include/google/protobuf/io/zero_copy_stream_impl.h b/libs/or-tools-bin/include/google/protobuf/io/zero_copy_stream_impl.h similarity index 100% rename from libs/or-tools/include/google/protobuf/io/zero_copy_stream_impl.h rename to libs/or-tools-bin/include/google/protobuf/io/zero_copy_stream_impl.h diff --git a/libs/or-tools/include/google/protobuf/io/zero_copy_stream_impl_lite.h b/libs/or-tools-bin/include/google/protobuf/io/zero_copy_stream_impl_lite.h similarity index 100% rename from libs/or-tools/include/google/protobuf/io/zero_copy_stream_impl_lite.h rename to libs/or-tools-bin/include/google/protobuf/io/zero_copy_stream_impl_lite.h diff --git a/libs/or-tools/include/google/protobuf/map.h b/libs/or-tools-bin/include/google/protobuf/map.h similarity index 100% rename from libs/or-tools/include/google/protobuf/map.h rename to libs/or-tools-bin/include/google/protobuf/map.h diff --git a/libs/or-tools/include/google/protobuf/map_entry.h b/libs/or-tools-bin/include/google/protobuf/map_entry.h similarity index 100% rename from libs/or-tools/include/google/protobuf/map_entry.h rename to libs/or-tools-bin/include/google/protobuf/map_entry.h diff --git a/libs/or-tools/include/google/protobuf/map_entry_lite.h b/libs/or-tools-bin/include/google/protobuf/map_entry_lite.h similarity index 100% rename from libs/or-tools/include/google/protobuf/map_entry_lite.h rename to libs/or-tools-bin/include/google/protobuf/map_entry_lite.h diff --git a/libs/or-tools/include/google/protobuf/map_field.h b/libs/or-tools-bin/include/google/protobuf/map_field.h similarity index 100% rename from libs/or-tools/include/google/protobuf/map_field.h rename to libs/or-tools-bin/include/google/protobuf/map_field.h diff --git a/libs/or-tools/include/google/protobuf/map_field_inl.h b/libs/or-tools-bin/include/google/protobuf/map_field_inl.h similarity index 100% rename from libs/or-tools/include/google/protobuf/map_field_inl.h rename to libs/or-tools-bin/include/google/protobuf/map_field_inl.h diff --git a/libs/or-tools/include/google/protobuf/map_field_lite.h b/libs/or-tools-bin/include/google/protobuf/map_field_lite.h similarity index 100% rename from libs/or-tools/include/google/protobuf/map_field_lite.h rename to libs/or-tools-bin/include/google/protobuf/map_field_lite.h diff --git a/libs/or-tools/include/google/protobuf/map_type_handler.h b/libs/or-tools-bin/include/google/protobuf/map_type_handler.h similarity index 100% rename from libs/or-tools/include/google/protobuf/map_type_handler.h rename to libs/or-tools-bin/include/google/protobuf/map_type_handler.h diff --git a/libs/or-tools/include/google/protobuf/message.h b/libs/or-tools-bin/include/google/protobuf/message.h similarity index 100% rename from libs/or-tools/include/google/protobuf/message.h rename to libs/or-tools-bin/include/google/protobuf/message.h diff --git a/libs/or-tools/include/google/protobuf/message_lite.h b/libs/or-tools-bin/include/google/protobuf/message_lite.h similarity index 100% rename from libs/or-tools/include/google/protobuf/message_lite.h rename to libs/or-tools-bin/include/google/protobuf/message_lite.h diff --git a/libs/or-tools/include/google/protobuf/metadata.h b/libs/or-tools-bin/include/google/protobuf/metadata.h similarity index 100% rename from libs/or-tools/include/google/protobuf/metadata.h rename to libs/or-tools-bin/include/google/protobuf/metadata.h diff --git a/libs/or-tools/include/google/protobuf/metadata_lite.h b/libs/or-tools-bin/include/google/protobuf/metadata_lite.h similarity index 100% rename from libs/or-tools/include/google/protobuf/metadata_lite.h rename to libs/or-tools-bin/include/google/protobuf/metadata_lite.h diff --git a/libs/or-tools/include/google/protobuf/parse_context.h b/libs/or-tools-bin/include/google/protobuf/parse_context.h similarity index 100% rename from libs/or-tools/include/google/protobuf/parse_context.h rename to libs/or-tools-bin/include/google/protobuf/parse_context.h diff --git a/libs/or-tools/include/google/protobuf/port.h b/libs/or-tools-bin/include/google/protobuf/port.h similarity index 100% rename from libs/or-tools/include/google/protobuf/port.h rename to libs/or-tools-bin/include/google/protobuf/port.h diff --git a/libs/or-tools/include/google/protobuf/port_def.inc b/libs/or-tools-bin/include/google/protobuf/port_def.inc similarity index 100% rename from libs/or-tools/include/google/protobuf/port_def.inc rename to libs/or-tools-bin/include/google/protobuf/port_def.inc diff --git a/libs/or-tools/include/google/protobuf/port_undef.inc b/libs/or-tools-bin/include/google/protobuf/port_undef.inc similarity index 100% rename from libs/or-tools/include/google/protobuf/port_undef.inc rename to libs/or-tools-bin/include/google/protobuf/port_undef.inc diff --git a/libs/or-tools/include/google/protobuf/reflection.h b/libs/or-tools-bin/include/google/protobuf/reflection.h similarity index 100% rename from libs/or-tools/include/google/protobuf/reflection.h rename to libs/or-tools-bin/include/google/protobuf/reflection.h diff --git a/libs/or-tools/include/google/protobuf/reflection_ops.h b/libs/or-tools-bin/include/google/protobuf/reflection_ops.h similarity index 100% rename from libs/or-tools/include/google/protobuf/reflection_ops.h rename to libs/or-tools-bin/include/google/protobuf/reflection_ops.h diff --git a/libs/or-tools/include/google/protobuf/repeated_field.h b/libs/or-tools-bin/include/google/protobuf/repeated_field.h similarity index 100% rename from libs/or-tools/include/google/protobuf/repeated_field.h rename to libs/or-tools-bin/include/google/protobuf/repeated_field.h diff --git a/libs/or-tools/include/google/protobuf/service.h b/libs/or-tools-bin/include/google/protobuf/service.h similarity index 100% rename from libs/or-tools/include/google/protobuf/service.h rename to libs/or-tools-bin/include/google/protobuf/service.h diff --git a/libs/or-tools/include/google/protobuf/source_context.pb.h b/libs/or-tools-bin/include/google/protobuf/source_context.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/source_context.pb.h rename to libs/or-tools-bin/include/google/protobuf/source_context.pb.h diff --git a/libs/or-tools/include/google/protobuf/source_context.proto b/libs/or-tools-bin/include/google/protobuf/source_context.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/source_context.proto rename to libs/or-tools-bin/include/google/protobuf/source_context.proto diff --git a/libs/or-tools/include/google/protobuf/struct.pb.h b/libs/or-tools-bin/include/google/protobuf/struct.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/struct.pb.h rename to libs/or-tools-bin/include/google/protobuf/struct.pb.h diff --git a/libs/or-tools/include/google/protobuf/struct.proto b/libs/or-tools-bin/include/google/protobuf/struct.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/struct.proto rename to libs/or-tools-bin/include/google/protobuf/struct.proto diff --git a/libs/or-tools/include/google/protobuf/stubs/bytestream.h b/libs/or-tools-bin/include/google/protobuf/stubs/bytestream.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/bytestream.h rename to libs/or-tools-bin/include/google/protobuf/stubs/bytestream.h diff --git a/libs/or-tools/include/google/protobuf/stubs/callback.h b/libs/or-tools-bin/include/google/protobuf/stubs/callback.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/callback.h rename to libs/or-tools-bin/include/google/protobuf/stubs/callback.h diff --git a/libs/or-tools/include/google/protobuf/stubs/casts.h b/libs/or-tools-bin/include/google/protobuf/stubs/casts.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/casts.h rename to libs/or-tools-bin/include/google/protobuf/stubs/casts.h diff --git a/libs/or-tools/include/google/protobuf/stubs/common.h b/libs/or-tools-bin/include/google/protobuf/stubs/common.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/common.h rename to libs/or-tools-bin/include/google/protobuf/stubs/common.h diff --git a/libs/or-tools/include/google/protobuf/stubs/fastmem.h b/libs/or-tools-bin/include/google/protobuf/stubs/fastmem.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/fastmem.h rename to libs/or-tools-bin/include/google/protobuf/stubs/fastmem.h diff --git a/libs/or-tools/include/google/protobuf/stubs/hash.h b/libs/or-tools-bin/include/google/protobuf/stubs/hash.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/hash.h rename to libs/or-tools-bin/include/google/protobuf/stubs/hash.h diff --git a/libs/or-tools/include/google/protobuf/stubs/logging.h b/libs/or-tools-bin/include/google/protobuf/stubs/logging.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/logging.h rename to libs/or-tools-bin/include/google/protobuf/stubs/logging.h diff --git a/libs/or-tools/include/google/protobuf/stubs/macros.h b/libs/or-tools-bin/include/google/protobuf/stubs/macros.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/macros.h rename to libs/or-tools-bin/include/google/protobuf/stubs/macros.h diff --git a/libs/or-tools/include/google/protobuf/stubs/map_util.h b/libs/or-tools-bin/include/google/protobuf/stubs/map_util.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/map_util.h rename to libs/or-tools-bin/include/google/protobuf/stubs/map_util.h diff --git a/libs/or-tools/include/google/protobuf/stubs/mutex.h b/libs/or-tools-bin/include/google/protobuf/stubs/mutex.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/mutex.h rename to libs/or-tools-bin/include/google/protobuf/stubs/mutex.h diff --git a/libs/or-tools/include/google/protobuf/stubs/once.h b/libs/or-tools-bin/include/google/protobuf/stubs/once.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/once.h rename to libs/or-tools-bin/include/google/protobuf/stubs/once.h diff --git a/libs/or-tools/include/google/protobuf/stubs/platform_macros.h b/libs/or-tools-bin/include/google/protobuf/stubs/platform_macros.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/platform_macros.h rename to libs/or-tools-bin/include/google/protobuf/stubs/platform_macros.h diff --git a/libs/or-tools/include/google/protobuf/stubs/port.h b/libs/or-tools-bin/include/google/protobuf/stubs/port.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/port.h rename to libs/or-tools-bin/include/google/protobuf/stubs/port.h diff --git a/libs/or-tools/include/google/protobuf/stubs/status.h b/libs/or-tools-bin/include/google/protobuf/stubs/status.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/status.h rename to libs/or-tools-bin/include/google/protobuf/stubs/status.h diff --git a/libs/or-tools/include/google/protobuf/stubs/stl_util.h b/libs/or-tools-bin/include/google/protobuf/stubs/stl_util.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/stl_util.h rename to libs/or-tools-bin/include/google/protobuf/stubs/stl_util.h diff --git a/libs/or-tools/include/google/protobuf/stubs/stringpiece.h b/libs/or-tools-bin/include/google/protobuf/stubs/stringpiece.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/stringpiece.h rename to libs/or-tools-bin/include/google/protobuf/stubs/stringpiece.h diff --git a/libs/or-tools/include/google/protobuf/stubs/strutil.h b/libs/or-tools-bin/include/google/protobuf/stubs/strutil.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/strutil.h rename to libs/or-tools-bin/include/google/protobuf/stubs/strutil.h diff --git a/libs/or-tools/include/google/protobuf/stubs/template_util.h b/libs/or-tools-bin/include/google/protobuf/stubs/template_util.h similarity index 100% rename from libs/or-tools/include/google/protobuf/stubs/template_util.h rename to libs/or-tools-bin/include/google/protobuf/stubs/template_util.h diff --git a/libs/or-tools/include/google/protobuf/text_format.h b/libs/or-tools-bin/include/google/protobuf/text_format.h similarity index 100% rename from libs/or-tools/include/google/protobuf/text_format.h rename to libs/or-tools-bin/include/google/protobuf/text_format.h diff --git a/libs/or-tools/include/google/protobuf/timestamp.pb.h b/libs/or-tools-bin/include/google/protobuf/timestamp.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/timestamp.pb.h rename to libs/or-tools-bin/include/google/protobuf/timestamp.pb.h diff --git a/libs/or-tools/include/google/protobuf/timestamp.proto b/libs/or-tools-bin/include/google/protobuf/timestamp.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/timestamp.proto rename to libs/or-tools-bin/include/google/protobuf/timestamp.proto diff --git a/libs/or-tools/include/google/protobuf/type.pb.h b/libs/or-tools-bin/include/google/protobuf/type.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/type.pb.h rename to libs/or-tools-bin/include/google/protobuf/type.pb.h diff --git a/libs/or-tools/include/google/protobuf/type.proto b/libs/or-tools-bin/include/google/protobuf/type.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/type.proto rename to libs/or-tools-bin/include/google/protobuf/type.proto diff --git a/libs/or-tools/include/google/protobuf/unknown_field_set.h b/libs/or-tools-bin/include/google/protobuf/unknown_field_set.h similarity index 100% rename from libs/or-tools/include/google/protobuf/unknown_field_set.h rename to libs/or-tools-bin/include/google/protobuf/unknown_field_set.h diff --git a/libs/or-tools/include/google/protobuf/util/delimited_message_util.h b/libs/or-tools-bin/include/google/protobuf/util/delimited_message_util.h similarity index 100% rename from libs/or-tools/include/google/protobuf/util/delimited_message_util.h rename to libs/or-tools-bin/include/google/protobuf/util/delimited_message_util.h diff --git a/libs/or-tools/include/google/protobuf/util/field_comparator.h b/libs/or-tools-bin/include/google/protobuf/util/field_comparator.h similarity index 100% rename from libs/or-tools/include/google/protobuf/util/field_comparator.h rename to libs/or-tools-bin/include/google/protobuf/util/field_comparator.h diff --git a/libs/or-tools/include/google/protobuf/util/field_mask_util.h b/libs/or-tools-bin/include/google/protobuf/util/field_mask_util.h similarity index 100% rename from libs/or-tools/include/google/protobuf/util/field_mask_util.h rename to libs/or-tools-bin/include/google/protobuf/util/field_mask_util.h diff --git a/libs/or-tools/include/google/protobuf/util/json_util.h b/libs/or-tools-bin/include/google/protobuf/util/json_util.h similarity index 100% rename from libs/or-tools/include/google/protobuf/util/json_util.h rename to libs/or-tools-bin/include/google/protobuf/util/json_util.h diff --git a/libs/or-tools/include/google/protobuf/util/message_differencer.h b/libs/or-tools-bin/include/google/protobuf/util/message_differencer.h similarity index 100% rename from libs/or-tools/include/google/protobuf/util/message_differencer.h rename to libs/or-tools-bin/include/google/protobuf/util/message_differencer.h diff --git a/libs/or-tools/include/google/protobuf/util/time_util.h b/libs/or-tools-bin/include/google/protobuf/util/time_util.h similarity index 100% rename from libs/or-tools/include/google/protobuf/util/time_util.h rename to libs/or-tools-bin/include/google/protobuf/util/time_util.h diff --git a/libs/or-tools/include/google/protobuf/util/type_resolver.h b/libs/or-tools-bin/include/google/protobuf/util/type_resolver.h similarity index 100% rename from libs/or-tools/include/google/protobuf/util/type_resolver.h rename to libs/or-tools-bin/include/google/protobuf/util/type_resolver.h diff --git a/libs/or-tools/include/google/protobuf/util/type_resolver_util.h b/libs/or-tools-bin/include/google/protobuf/util/type_resolver_util.h similarity index 100% rename from libs/or-tools/include/google/protobuf/util/type_resolver_util.h rename to libs/or-tools-bin/include/google/protobuf/util/type_resolver_util.h diff --git a/libs/or-tools/include/google/protobuf/wire_format.h b/libs/or-tools-bin/include/google/protobuf/wire_format.h similarity index 100% rename from libs/or-tools/include/google/protobuf/wire_format.h rename to libs/or-tools-bin/include/google/protobuf/wire_format.h diff --git a/libs/or-tools/include/google/protobuf/wire_format_lite.h b/libs/or-tools-bin/include/google/protobuf/wire_format_lite.h similarity index 100% rename from libs/or-tools/include/google/protobuf/wire_format_lite.h rename to libs/or-tools-bin/include/google/protobuf/wire_format_lite.h diff --git a/libs/or-tools/include/google/protobuf/wrappers.pb.h b/libs/or-tools-bin/include/google/protobuf/wrappers.pb.h similarity index 100% rename from libs/or-tools/include/google/protobuf/wrappers.pb.h rename to libs/or-tools-bin/include/google/protobuf/wrappers.pb.h diff --git a/libs/or-tools/include/google/protobuf/wrappers.proto b/libs/or-tools-bin/include/google/protobuf/wrappers.proto similarity index 100% rename from libs/or-tools/include/google/protobuf/wrappers.proto rename to libs/or-tools-bin/include/google/protobuf/wrappers.proto diff --git a/libs/or-tools/include/ortools/algorithms/dense_doubly_linked_list.h b/libs/or-tools-bin/include/ortools/algorithms/dense_doubly_linked_list.h similarity index 100% rename from libs/or-tools/include/ortools/algorithms/dense_doubly_linked_list.h rename to libs/or-tools-bin/include/ortools/algorithms/dense_doubly_linked_list.h diff --git a/libs/or-tools/include/ortools/algorithms/dynamic_partition.h b/libs/or-tools-bin/include/ortools/algorithms/dynamic_partition.h similarity index 100% rename from libs/or-tools/include/ortools/algorithms/dynamic_partition.h rename to libs/or-tools-bin/include/ortools/algorithms/dynamic_partition.h diff --git a/libs/or-tools/include/ortools/algorithms/dynamic_permutation.h b/libs/or-tools-bin/include/ortools/algorithms/dynamic_permutation.h similarity index 100% rename from libs/or-tools/include/ortools/algorithms/dynamic_permutation.h rename to libs/or-tools-bin/include/ortools/algorithms/dynamic_permutation.h diff --git a/libs/or-tools/include/ortools/algorithms/find_graph_symmetries.h b/libs/or-tools-bin/include/ortools/algorithms/find_graph_symmetries.h similarity index 100% rename from libs/or-tools/include/ortools/algorithms/find_graph_symmetries.h rename to libs/or-tools-bin/include/ortools/algorithms/find_graph_symmetries.h diff --git a/libs/or-tools/include/ortools/algorithms/hungarian.h b/libs/or-tools-bin/include/ortools/algorithms/hungarian.h similarity index 100% rename from libs/or-tools/include/ortools/algorithms/hungarian.h rename to libs/or-tools-bin/include/ortools/algorithms/hungarian.h diff --git a/libs/or-tools/include/ortools/algorithms/knapsack_solver.h b/libs/or-tools-bin/include/ortools/algorithms/knapsack_solver.h similarity index 100% rename from libs/or-tools/include/ortools/algorithms/knapsack_solver.h rename to libs/or-tools-bin/include/ortools/algorithms/knapsack_solver.h diff --git a/libs/or-tools/include/ortools/algorithms/knapsack_solver_for_cuts.h b/libs/or-tools-bin/include/ortools/algorithms/knapsack_solver_for_cuts.h similarity index 100% rename from libs/or-tools/include/ortools/algorithms/knapsack_solver_for_cuts.h rename to libs/or-tools-bin/include/ortools/algorithms/knapsack_solver_for_cuts.h diff --git a/libs/or-tools/include/ortools/algorithms/sparse_permutation.h b/libs/or-tools-bin/include/ortools/algorithms/sparse_permutation.h similarity index 100% rename from libs/or-tools/include/ortools/algorithms/sparse_permutation.h rename to libs/or-tools-bin/include/ortools/algorithms/sparse_permutation.h diff --git a/libs/or-tools/include/ortools/base/accurate_sum.h b/libs/or-tools-bin/include/ortools/base/accurate_sum.h similarity index 100% rename from libs/or-tools/include/ortools/base/accurate_sum.h rename to libs/or-tools-bin/include/ortools/base/accurate_sum.h diff --git a/libs/or-tools/include/ortools/base/adjustable_priority_queue-inl.h b/libs/or-tools-bin/include/ortools/base/adjustable_priority_queue-inl.h similarity index 100% rename from libs/or-tools/include/ortools/base/adjustable_priority_queue-inl.h rename to libs/or-tools-bin/include/ortools/base/adjustable_priority_queue-inl.h diff --git a/libs/or-tools/include/ortools/base/adjustable_priority_queue.h b/libs/or-tools-bin/include/ortools/base/adjustable_priority_queue.h similarity index 100% rename from libs/or-tools/include/ortools/base/adjustable_priority_queue.h rename to libs/or-tools-bin/include/ortools/base/adjustable_priority_queue.h diff --git a/libs/or-tools/include/ortools/base/base_export.h b/libs/or-tools-bin/include/ortools/base/base_export.h similarity index 100% rename from libs/or-tools/include/ortools/base/base_export.h rename to libs/or-tools-bin/include/ortools/base/base_export.h diff --git a/libs/or-tools/include/ortools/base/basictypes.h b/libs/or-tools-bin/include/ortools/base/basictypes.h similarity index 100% rename from libs/or-tools/include/ortools/base/basictypes.h rename to libs/or-tools-bin/include/ortools/base/basictypes.h diff --git a/libs/or-tools/include/ortools/base/bitmap.h b/libs/or-tools-bin/include/ortools/base/bitmap.h similarity index 100% rename from libs/or-tools/include/ortools/base/bitmap.h rename to libs/or-tools-bin/include/ortools/base/bitmap.h diff --git a/libs/or-tools/include/ortools/base/canonical_errors.h b/libs/or-tools-bin/include/ortools/base/canonical_errors.h similarity index 100% rename from libs/or-tools/include/ortools/base/canonical_errors.h rename to libs/or-tools-bin/include/ortools/base/canonical_errors.h diff --git a/libs/or-tools/include/ortools/base/cleanup.h b/libs/or-tools-bin/include/ortools/base/cleanup.h similarity index 100% rename from libs/or-tools/include/ortools/base/cleanup.h rename to libs/or-tools-bin/include/ortools/base/cleanup.h diff --git a/libs/or-tools/include/ortools/base/commandlineflags.h b/libs/or-tools-bin/include/ortools/base/commandlineflags.h similarity index 100% rename from libs/or-tools/include/ortools/base/commandlineflags.h rename to libs/or-tools-bin/include/ortools/base/commandlineflags.h diff --git a/libs/or-tools/include/ortools/base/encodingutils.h b/libs/or-tools-bin/include/ortools/base/encodingutils.h similarity index 100% rename from libs/or-tools/include/ortools/base/encodingutils.h rename to libs/or-tools-bin/include/ortools/base/encodingutils.h diff --git a/libs/or-tools/include/ortools/base/file.h b/libs/or-tools-bin/include/ortools/base/file.h similarity index 100% rename from libs/or-tools/include/ortools/base/file.h rename to libs/or-tools-bin/include/ortools/base/file.h diff --git a/libs/or-tools/include/ortools/base/filelineiter.h b/libs/or-tools-bin/include/ortools/base/filelineiter.h similarity index 100% rename from libs/or-tools/include/ortools/base/filelineiter.h rename to libs/or-tools-bin/include/ortools/base/filelineiter.h diff --git a/libs/or-tools/include/ortools/base/hash.h b/libs/or-tools-bin/include/ortools/base/hash.h similarity index 100% rename from libs/or-tools/include/ortools/base/hash.h rename to libs/or-tools-bin/include/ortools/base/hash.h diff --git a/libs/or-tools/include/ortools/base/int_type.h b/libs/or-tools-bin/include/ortools/base/int_type.h similarity index 100% rename from libs/or-tools/include/ortools/base/int_type.h rename to libs/or-tools-bin/include/ortools/base/int_type.h diff --git a/libs/or-tools/include/ortools/base/int_type_indexed_vector.h b/libs/or-tools-bin/include/ortools/base/int_type_indexed_vector.h similarity index 100% rename from libs/or-tools/include/ortools/base/int_type_indexed_vector.h rename to libs/or-tools-bin/include/ortools/base/int_type_indexed_vector.h diff --git a/libs/or-tools/include/ortools/base/integral_types.h b/libs/or-tools-bin/include/ortools/base/integral_types.h similarity index 100% rename from libs/or-tools/include/ortools/base/integral_types.h rename to libs/or-tools-bin/include/ortools/base/integral_types.h diff --git a/libs/or-tools/include/ortools/base/iterator_adaptors.h b/libs/or-tools-bin/include/ortools/base/iterator_adaptors.h similarity index 100% rename from libs/or-tools/include/ortools/base/iterator_adaptors.h rename to libs/or-tools-bin/include/ortools/base/iterator_adaptors.h diff --git a/libs/or-tools/include/ortools/base/jniutil.h b/libs/or-tools-bin/include/ortools/base/jniutil.h similarity index 100% rename from libs/or-tools/include/ortools/base/jniutil.h rename to libs/or-tools-bin/include/ortools/base/jniutil.h diff --git a/libs/or-tools/include/ortools/base/logging.h b/libs/or-tools-bin/include/ortools/base/logging.h similarity index 100% rename from libs/or-tools/include/ortools/base/logging.h rename to li