diff --git a/libs/or-tools-src-ubuntu/LICENSE-2.0.txt b/libs/or-tools-src-ubuntu/LICENSE-2.0.txt new file mode 100644 index 0000000000000000000000000000000000000000..0f3822be4adba1970288328d9a838ad4e87935bf --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/README.md b/libs/or-tools-src-ubuntu/README.md new file mode 100644 index 0000000000000000000000000000000000000000..50b2c5c43dd40f724b1723a728567c58d8ac2726 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/bin/cbc b/libs/or-tools-src-ubuntu/bin/cbc new file mode 100755 index 0000000000000000000000000000000000000000..dc34739945a2cdccf96ca56fd57ec5fa1fa4859e Binary files /dev/null and b/libs/or-tools-src-ubuntu/bin/cbc differ diff --git a/libs/or-tools-src-ubuntu/bin/gflags_completions.sh b/libs/or-tools-src-ubuntu/bin/gflags_completions.sh new file mode 100755 index 0000000000000000000000000000000000000000..c5fb7e6bc57bf71ff148721ff180807849ac3b55 --- /dev/null +++ b/libs/or-tools-src-ubuntu/bin/gflags_completions.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# Copyright (c) 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# --- +# Author: Dave Nicponski +# +# This script is invoked by bash in response to a matching compspec. When +# this happens, bash calls this script using the command shown in the -C +# block of the complete entry, but also appends 3 arguments. They are: +# - The command being used for completion +# - The word being completed +# - The word preceding the completion word. +# +# Here's an example of how you might use this script: +# $ complete -o bashdefault -o default -o nospace -C \ +# '/usr/local/bin/gflags_completions.sh --tab_completion_columns $COLUMNS' \ +# time env binary_name another_binary [...] + +# completion_word_index gets the index of the (N-1)th argument for +# this command line. completion_word gets the actual argument from +# this command line at the (N-1)th position +completion_word_index="$(($# - 1))" +completion_word="${!completion_word_index}" + +# TODO(user): Replace this once gflags_completions.cc has +# a bool parameter indicating unambiguously to hijack the process for +# completion purposes. +if [ -z "$completion_word" ]; then + # Until an empty value for the completion word stops being misunderstood + # by binaries, don't actually execute the binary or the process + # won't be hijacked! + exit 0 +fi + +# binary_index gets the index of the command being completed (which bash +# places in the (N-2)nd position. binary gets the actual command from +# this command line at that (N-2)nd position +binary_index="$(($# - 2))" +binary="${!binary_index}" + +# For completions to be universal, we may have setup the compspec to +# trigger on 'harmless pass-through' commands, like 'time' or 'env'. +# If the command being completed is one of those two, we'll need to +# identify the actual command being executed. To do this, we need +# the actual command line that the was pressed on. Bash helpfully +# places this in the $COMP_LINE variable. +if [ "$binary" == "time" ] || [ "$binary" == "env" ]; then + # we'll assume that the first 'argument' is actually the + # binary + + + # TODO(user): This is not perfect - the 'env' command, for instance, + # is allowed to have options between the 'env' and 'the command to + # be executed'. For example, consider: + # $ env FOO="bar" bin/do_something --help + # In this case, we'll mistake the FOO="bar" portion as the binary. + # Perhaps we should continuing consuming leading words until we + # either run out of words, or find a word that is a valid file + # marked as executable. I can't think of any reason this wouldn't + # work. + + # Break up the 'original command line' (not this script's command line, + # rather the one the was pressed on) and find the second word. + parts=( ${COMP_LINE} ) + binary=${parts[1]} +fi + +# Build the command line to use for completion. Basically it involves +# passing through all the arguments given to this script (except the 3 +# that bash added), and appending a '--tab_completion_word "WORD"' to +# the arguments. +params="" +for ((i=1; i<=$(($# - 3)); ++i)); do + params="$params \"${!i}\""; +done +params="$params --tab_completion_word \"$completion_word\"" + +# TODO(user): Perhaps stash the output in a temporary file somewhere +# in /tmp, and only cat it to stdout if the command returned a success +# code, to prevent false positives + +# If we think we have a reasonable command to execute, then execute it +# and hope for the best. +candidate=$(type -p "$binary") +if [ ! -z "$candidate" ]; then + eval "$candidate 2>/dev/null $params" +elif [ -f "$binary" ] && [ -x "$binary" ]; then + eval "$binary 2>/dev/null $params" +fi diff --git a/libs/or-tools-src-ubuntu/bin/protoc b/libs/or-tools-src-ubuntu/bin/protoc new file mode 100755 index 0000000000000000000000000000000000000000..11f693cf6afa8d70f2c175f4cb1e8682cb7a6f62 Binary files /dev/null and b/libs/or-tools-src-ubuntu/bin/protoc differ diff --git a/libs/or-tools-src-ubuntu/bin/protoc-3.12.2.0 b/libs/or-tools-src-ubuntu/bin/protoc-3.12.2.0 new file mode 100755 index 0000000000000000000000000000000000000000..11f693cf6afa8d70f2c175f4cb1e8682cb7a6f62 Binary files /dev/null and b/libs/or-tools-src-ubuntu/bin/protoc-3.12.2.0 differ diff --git a/libs/or-tools-src-ubuntu/examples/cpp/README.md b/libs/or-tools-src-ubuntu/examples/cpp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f4aa1d0b888d194878bedb2a15790211214021ff --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/assignment_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/assignment_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..e5c640b8ec1bfbf4455af339244eb49ff085e6df --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/assignment_sat.cc @@ -0,0 +1,110 @@ +// 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 "ortools/sat/cp_model.h" +// [END import] +namespace operations_research { +namespace sat { + +void IntegerProgrammingExample() { + // Data + // [START data_model] + const std::vector> costs{ + {90, 80, 75, 70}, {35, 85, 55, 65}, {125, 95, 90, 95}, + {45, 110, 95, 115}, {50, 100, 90, 100}, + }; + const int num_workers = costs.size(); + const int num_tasks = costs[0].size(); + // [END data_model] + + // Model + // [START model] + CpModelBuilder cp_model; + // [END model] + + // Variables + // [START variables] + // x[i][j] is an array of Boolean variables. x[i][j] is true + // if worker i is assigned to task j. + std::vector> x(num_workers, + std::vector(num_tasks)); + for (int i = 0; i < num_workers; ++i) { + for (int j = 0; j < num_tasks; ++j) { + x[i][j] = cp_model.NewBoolVar(); + } + } + // [END variables] + + // Constraints + // [START constraints] + // Each worker is assigned to at most one task. + for (int i = 0; i < num_workers; ++i) { + LinearExpr worker_sum; + for (int j = 0; j < num_tasks; ++j) { + worker_sum.AddTerm(x[i][j], 1); + } + cp_model.AddLessOrEqual(worker_sum, 1); + } + // Each task is assigned to exactly one worker. + for (int j = 0; j < num_tasks; ++j) { + LinearExpr task_sum; + for (int i = 0; i < num_workers; ++i) { + task_sum.AddTerm(x[i][j], 1); + } + cp_model.AddEquality(task_sum, 1); + } + // [END constraints] + + // Objective + // [START objective] + LinearExpr total_cost; + for (int i = 0; i < num_workers; ++i) { + for (int j = 0; j < num_tasks; ++j) { + total_cost.AddTerm(x[i][j], costs[i][j]); + } + } + cp_model.Minimize(total_cost); + // [END objective] + + // Solve + // [START solve] + const CpSolverResponse response = Solve(cp_model.Build()); + // [END solve] + + // Print solution. + // [START print_solution] + if (response.status() == CpSolverStatus::INFEASIBLE) { + LOG(FATAL) << "No solution found."; + } + + LOG(INFO) << "Total cost: " << response.objective_value(); + LOG(INFO); + for (int i = 0; i < num_workers; ++i) { + for (int j = 0; j < num_tasks; ++j) { + if (SolutionBooleanValue(response, x[i][j])) { + LOG(INFO) << "Task " << i << " assigned to worker " << j + << ". Cost: " << costs[i][j]; + } + } + } + // [END print_solution] +} +} // namespace sat +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::sat::IntegerProgrammingExample(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/bin_packing_mip.cc b/libs/or-tools-src-ubuntu/examples/cpp/bin_packing_mip.cc new file mode 100644 index 0000000000000000000000000000000000000000..241015a7ee90eeb54f857df1f53f8929d9377fc3 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/bin_packing_mip.cc @@ -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. + +// [START program] +// [START import] +#include +#include +#include + +#include "ortools/linear_solver/linear_expr.h" +#include "ortools/linear_solver/linear_solver.h" +// [END import] + +// [START program_part1] +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector weights = {48, 30, 19, 36, 36, 27, + 42, 42, 36, 24, 30}; + const int num_items = weights.size(); + const int num_bins = weights.size(); + const int bin_capacity = 100; +}; +// [END data_model] + +void BinPackingMip() { + // [START data] + DataModel data; + // [END data] + // [END program_part1] + + // [START solver] + // Create the mip solver with the CBC backend. + MPSolver solver("bin_packing_mip", + MPSolver::CBC_MIXED_INTEGER_PROGRAMMING); + // [END solver] + + // [START program_part2] + // [START variables] + std::vector> x( + data.num_items, std::vector(data.num_bins)); + for (int i = 0; i < data.num_items; ++i) { + for (int j = 0; j < data.num_bins; ++j) { + x[i][j] = solver.MakeIntVar(0.0, 1.0, ""); + } + } + // y[j] = 1 if bin j is used. + std::vector y(data.num_bins); + for (int j = 0; j < data.num_bins; ++j) { + y[j] = solver.MakeIntVar(0.0, 1.0, ""); + } + // [END variables] + + // [START constraints] + // Create the constraints. + // Each item is in exactly one bin. + for (int i = 0; i < data.num_items; ++i) { + LinearExpr sum; + for (int j = 0; j < data.num_bins; ++j) { + sum += x[i][j]; + } + solver.MakeRowConstraint(sum == 1.0); + } + // For each bin that is used, the total packed weight can be at most + // the bin capacity. + for (int j = 0; j < data.num_bins; ++j) { + LinearExpr weight; + for (int i = 0; i < data.num_items; ++i) { + weight += data.weights[i] * LinearExpr(x[i][j]); + } + solver.MakeRowConstraint(weight <= LinearExpr(y[j]) * data.bin_capacity); + } + // [END constraints] + + // [START objective] + // Create the objective function. + MPObjective* const objective = solver.MutableObjective(); + LinearExpr num_bins_used; + for (int j = 0; j < data.num_bins; ++j) { + num_bins_used += y[j]; + } + objective->MinimizeLinearExpr(num_bins_used); + // [END objective] + + // [START solve] + const MPSolver::ResultStatus result_status = solver.Solve(); + // [END solve] + + // [START print_solution] + // Check that the problem has an optimal solution. + if (result_status != MPSolver::OPTIMAL) { + std::cerr << "The problem does not have an optimal solution!"; + return; + } + std::cout << "Number of bins used: " << objective->Value() << std::endl + << std::endl; + double total_weight = 0; + for (int j = 0; j < data.num_bins; ++j) { + if (y[j]->solution_value() == 1) { + std::cout << "Bin " << j << std::endl << std::endl; + double bin_weight = 0; + for (int i = 0; i < data.num_items; ++i) { + if (x[i][j]->solution_value() == 1) { + std::cout << "Item " << i << " - Weight: " << data.weights[i] + << std::endl; + bin_weight += data.weights[i]; + } + } + std::cout << "Packed bin weight: " << bin_weight << std::endl + << std::endl; + total_weight += bin_weight; + } + } + std::cout << "Total packed weight: " << total_weight << std::endl; + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::BinPackingMip(); + return EXIT_SUCCESS; +} +// [END program_part2] +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/binpacking_problem_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/binpacking_problem_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..7f9441e3c1ef26a026123f245c66c52476a6122d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/binpacking_problem_sat.cc @@ -0,0 +1,92 @@ +// 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/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void BinpackingProblemSat() { + // Data. + const int kBinCapacity = 100; + const int kSlackCapacity = 20; + const int kNumBins = 5; + + const std::vector> items = { + {20, 6}, {15, 6}, {30, 4}, {45, 3}}; + const int num_items = items.size(); + + // Model. + CpModelBuilder cp_model; + + // Main variables. + std::vector> x(num_items); + for (int i = 0; i < num_items; ++i) { + const int num_copies = items[i][1]; + for (int b = 0; b < kNumBins; ++b) { + x[i].push_back(cp_model.NewIntVar({0, num_copies})); + } + } + + // Load variables. + std::vector load(kNumBins); + for (int b = 0; b < kNumBins; ++b) { + load[b] = cp_model.NewIntVar({0, kBinCapacity}); + } + + // Slack variables. + std::vector slacks(kNumBins); + for (int b = 0; b < kNumBins; ++b) { + slacks[b] = cp_model.NewBoolVar(); + } + + // Links load and x. + for (int b = 0; b < kNumBins; ++b) { + LinearExpr expr; + for (int i = 0; i < num_items; ++i) { + expr.AddTerm(x[i][b], items[i][0]); + } + cp_model.AddEquality(expr, load[b]); + } + + // Place all items. + for (int i = 0; i < num_items; ++i) { + cp_model.AddEquality(LinearExpr::Sum(x[i]), items[i][1]); + } + + // Links load and slack through an equivalence relation. + const int safe_capacity = kBinCapacity - kSlackCapacity; + for (int b = 0; b < kNumBins; ++b) { + // slack[b] => load[b] <= safe_capacity. + cp_model.AddLessOrEqual(load[b], safe_capacity).OnlyEnforceIf(slacks[b]); + // not(slack[b]) => load[b] > safe_capacity. + cp_model.AddGreaterThan(load[b], safe_capacity) + .OnlyEnforceIf(Not(slacks[b])); + } + + // Maximize sum of slacks. + cp_model.Maximize(LinearExpr::BooleanSum(slacks)); + + // Solving part. + const CpSolverResponse response = Solve(cp_model.Build()); + LOG(INFO) << CpSolverResponseStats(response); +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::BinpackingProblemSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/bool_or_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/bool_or_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..aa490d79ff43a8d94b036c0fb6d58467fab3e48a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/bool_or_sample_sat.cc @@ -0,0 +1,34 @@ +// 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/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void BoolOrSampleSat() { + CpModelBuilder cp_model; + + const BoolVar x = cp_model.NewBoolVar(); + const BoolVar y = cp_model.NewBoolVar(); + cp_model.AddBoolOr({x, Not(y)}); +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::BoolOrSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/channeling_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/channeling_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..a6f4d04dd6f0ad2982045995df49f3f90b3d39dc --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/channeling_sample_sat.cc @@ -0,0 +1,67 @@ +// 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/sat/cp_model.h" +#include "ortools/sat/model.h" +#include "ortools/sat/sat_parameters.pb.h" + +namespace operations_research { +namespace sat { + +void ChannelingSampleSat() { + // Create the CP-SAT model. + CpModelBuilder cp_model; + + // Declare our two primary variables. + const IntVar x = cp_model.NewIntVar({0, 10}); + const IntVar y = cp_model.NewIntVar({0, 10}); + + // Declare our intermediate boolean variable. + const BoolVar b = cp_model.NewBoolVar(); + + // Implement b == (x >= 5). + cp_model.AddGreaterOrEqual(x, 5).OnlyEnforceIf(b); + cp_model.AddLessThan(x, 5).OnlyEnforceIf(Not(b)); + + // Create our two half-reified constraints. + // First, b implies (y == 10 - x). + cp_model.AddEquality(LinearExpr::Sum({x, y}), 10).OnlyEnforceIf(b); + // Second, not(b) implies y == 0. + cp_model.AddEquality(y, 0).OnlyEnforceIf(Not(b)); + + // Search for x values in increasing order. + cp_model.AddDecisionStrategy({x}, DecisionStrategyProto::CHOOSE_FIRST, + DecisionStrategyProto::SELECT_MIN_VALUE); + + // Create a solver and solve with a fixed search. + Model model; + SatParameters parameters; + parameters.set_search_branching(SatParameters::FIXED_SEARCH); + parameters.set_enumerate_all_solutions(true); + model.Add(NewSatParameters(parameters)); + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + LOG(INFO) << "x=" << SolutionIntegerValue(r, x) + << " y=" << SolutionIntegerValue(r, y) + << " b=" << SolutionBooleanValue(r, b); + })); + SolveCpModel(cp_model.Build(), &model); +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::ChannelingSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/costas_array_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/costas_array_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..8df6eaa0e2878692ea537b2c13e7285a4594fd8b --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/cp_is_fun_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/cp_is_fun_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..b7d4b3a676190c7859e3dfe73d098c1d5768e996 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/cp_is_fun_sat.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. + +// [START program] +// Cryptarithmetic puzzle +// +// First attempt to solve equation CP + IS + FUN = TRUE +// where each letter represents a unique digit. +// +// This problem has 72 different solutions in base 10. + +#include + +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" +#include "ortools/sat/sat_parameters.pb.h" + +namespace operations_research { +namespace sat { + +void CPIsFunSat() { + // Instantiate the solver. + CpModelBuilder cp_model; + + // [START variables] + const int64 kBase = 10; + + // Define decision variables. + Domain digit(0, kBase - 1); + Domain non_zero_digit(1, kBase - 1); + + IntVar c = cp_model.NewIntVar(non_zero_digit).WithName("C"); + IntVar p = cp_model.NewIntVar(digit).WithName("P"); + IntVar i = cp_model.NewIntVar(non_zero_digit).WithName("I"); + IntVar s = cp_model.NewIntVar(digit).WithName("S"); + IntVar f = cp_model.NewIntVar(non_zero_digit).WithName("F"); + IntVar u = cp_model.NewIntVar(digit).WithName("U"); + IntVar n = cp_model.NewIntVar(digit).WithName("N"); + IntVar t = cp_model.NewIntVar(non_zero_digit).WithName("T"); + IntVar r = cp_model.NewIntVar(digit).WithName("R"); + IntVar e = cp_model.NewIntVar(digit).WithName("E"); + // [END variables] + + // [START constraints] + // Define constraints. + cp_model.AddAllDifferent({c, p, i, s, f, u, n, t, r, e}); + + // CP + IS + FUN = TRUE + cp_model.AddEquality( + LinearExpr::ScalProd({c, p, i, s, f, u, n}, + {kBase, 1, kBase, 1, kBase * kBase, kBase, 1}), + LinearExpr::ScalProd({t, r, u, e}, + {kBase * kBase * kBase, kBase * kBase, kBase, 1})); + // [END constraints] + + // [START solution_printing] + Model model; + int num_solutions = 0; + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& response) { + LOG(INFO) << "Solution " << num_solutions; + LOG(INFO) << "C=" << SolutionIntegerValue(response, c) << " " + << "P=" << SolutionIntegerValue(response, p) << " " + << "I=" << SolutionIntegerValue(response, i) << " " + << "S=" << SolutionIntegerValue(response, s) << " " + << "F=" << SolutionIntegerValue(response, f) << " " + << "U=" << SolutionIntegerValue(response, u) << " " + << "N=" << SolutionIntegerValue(response, n) << " " + << "T=" << SolutionIntegerValue(response, t) << " " + << "R=" << SolutionIntegerValue(response, r) << " " + << "E=" << SolutionIntegerValue(response, e); + num_solutions++; + })); + // [END solution_printing] + + // [START solve] + // Tell the solver to enumerate all solutions. + SatParameters parameters; + parameters.set_enumerate_all_solutions(true); + model.Add(NewSatParameters(parameters)); + + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + LOG(INFO) << "Number of solutions found: " << num_solutions; + // [END solve] +} + +} // namespace sat +} // namespace operations_research + +// ----- MAIN ----- +int main(int argc, char** argv) { + operations_research::sat::CPIsFunSat(); + + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/cvrp_disjoint_tw.cc b/libs/or-tools-src-ubuntu/examples/cpp/cvrp_disjoint_tw.cc new file mode 100644 index 0000000000000000000000000000000000000000..aef1eb2f3ff69c1893e61b140c95446f99e7df69 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/cvrptw.cc b/libs/or-tools-src-ubuntu/examples/cpp/cvrptw.cc new file mode 100644 index 0000000000000000000000000000000000000000..9696115f7e7058fbbe2fb6c7f054419940feaeba --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/cvrptw_lib.h b/libs/or-tools-src-ubuntu/examples/cpp/cvrptw_lib.h new file mode 100644 index 0000000000000000000000000000000000000000..887821e6b51c51148cb24c4811d1387eb556421b --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/cvrptw_with_breaks.cc b/libs/or-tools-src-ubuntu/examples/cpp/cvrptw_with_breaks.cc new file mode 100644 index 0000000000000000000000000000000000000000..0c5ac3af2147ad29fb089d9253169cb38a0d9a44 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/cvrptw_with_refueling.cc b/libs/or-tools-src-ubuntu/examples/cpp/cvrptw_with_refueling.cc new file mode 100644 index 0000000000000000000000000000000000000000..9eb02136d02a4a6bfef59c6d949e6d54aee47db6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/cvrptw_with_resources.cc b/libs/or-tools-src-ubuntu/examples/cpp/cvrptw_with_resources.cc new file mode 100644 index 0000000000000000000000000000000000000000..93bc87c898babc0b38f661899bfc1d23bc3d3fee --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/cvrptw_with_stop_times_and_resources.cc b/libs/or-tools-src-ubuntu/examples/cpp/cvrptw_with_stop_times_and_resources.cc new file mode 100644 index 0000000000000000000000000000000000000000..1020afc99214ae77a689ac93f884332fc2d2d262 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/dimacs_assignment.cc b/libs/or-tools-src-ubuntu/examples/cpp/dimacs_assignment.cc new file mode 100644 index 0000000000000000000000000000000000000000..cc0570d9566aef51a5860c28e14ad93fd4c37a5f --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/dobble_ls.cc b/libs/or-tools-src-ubuntu/examples/cpp/dobble_ls.cc new file mode 100644 index 0000000000000000000000000000000000000000..e0e8eeabc3feebfb6a2f6e2790e3032289878190 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/earliness_tardiness_cost_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/earliness_tardiness_cost_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..5845b1d64f0a71f1a1e127d61d55d2382d4b7b13 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/earliness_tardiness_cost_sample_sat.cc @@ -0,0 +1,82 @@ +// 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/sat/cp_model.h" +#include "ortools/sat/model.h" +#include "ortools/sat/sat_parameters.pb.h" + +namespace operations_research { +namespace sat { + +void EarlinessTardinessCostSampleSat() { + const int64 kEarlinessDate = 5; + const int64 kEarlinessCost = 8; + const int64 kLatenessDate = 15; + const int64 kLatenessCost = 12; + + // Create the CP-SAT model. + CpModelBuilder cp_model; + + // Declare our primary variable. + const IntVar x = cp_model.NewIntVar({0, 20}); + + // Create the expression variable and implement the piecewise linear function. + // + // \ / + // \______/ + // ed ld + // + const int64 kLargeConstant = 1000; + const IntVar expr = cp_model.NewIntVar({0, kLargeConstant}); + + // First segment. + const IntVar s1 = cp_model.NewIntVar({-kLargeConstant, kLargeConstant}); + cp_model.AddEquality(s1, LinearExpr::ScalProd({x}, {-kEarlinessCost}) + .AddConstant(kEarlinessCost * kEarlinessDate)); + + // Second segment. + const IntVar s2 = cp_model.NewConstant(0); + + // Third segment. + const IntVar s3 = cp_model.NewIntVar({-kLargeConstant, kLargeConstant}); + cp_model.AddEquality(s3, LinearExpr::ScalProd({x}, {kLatenessCost}) + .AddConstant(-kLatenessCost * kLatenessDate)); + + // Link together expr and x through s1, s2, and s3. + cp_model.AddMaxEquality(expr, {s1, s2, s3}); + + // Search for x values in increasing order. + cp_model.AddDecisionStrategy({x}, DecisionStrategyProto::CHOOSE_FIRST, + DecisionStrategyProto::SELECT_MIN_VALUE); + + // Create a solver and solve with a fixed search. + Model model; + SatParameters parameters; + parameters.set_search_branching(SatParameters::FIXED_SEARCH); + parameters.set_enumerate_all_solutions(true); + model.Add(NewSatParameters(parameters)); + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + LOG(INFO) << "x=" << SolutionIntegerValue(r, x) << " expr" + << SolutionIntegerValue(r, expr); + })); + SolveCpModel(cp_model.Build(), &model); +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::EarlinessTardinessCostSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/fap_model_printer.h b/libs/or-tools-src-ubuntu/examples/cpp/fap_model_printer.h new file mode 100644 index 0000000000000000000000000000000000000000..7528da83cd50fa762fd9ac6c4b76402f5d13710e --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/fap_parser.h b/libs/or-tools-src-ubuntu/examples/cpp/fap_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..74027507c7b357fcfecc48bdae21eb6b5d312dc7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/fap_utilities.h b/libs/or-tools-src-ubuntu/examples/cpp/fap_utilities.h new file mode 100644 index 0000000000000000000000000000000000000000..a8195340ab4ed6530bc95bac2dc4fd14e96b69b4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/flow_api.cc b/libs/or-tools-src-ubuntu/examples/cpp/flow_api.cc new file mode 100644 index 0000000000000000000000000000000000000000..8dcaaa0a3dc94b92cf3fa1c839f2eeb5c1706004 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/frequency_assignment_problem.cc b/libs/or-tools-src-ubuntu/examples/cpp/frequency_assignment_problem.cc new file mode 100644 index 0000000000000000000000000000000000000000..016802ae7360ff0ac9fb4cbf5733a9200b483bfd --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/golomb_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/golomb_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..d8b2a2bd42867c7ff2a47255d4f6e2a51e532d6c --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/interval_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/interval_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..7b957bdb26c655475b3acc87fc73f1b997a8b4bf --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/interval_sample_sat.cc @@ -0,0 +1,43 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ortools/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void IntervalSampleSat() { + CpModelBuilder cp_model; + const int kHorizon = 100; + + const Domain horizon(0, kHorizon); + const IntVar start_var = cp_model.NewIntVar(horizon).WithName("start"); + const IntVar duration_var = cp_model.NewConstant(10); + const IntVar end_var = cp_model.NewIntVar(horizon).WithName("end"); + + const IntervalVar interval_var = + cp_model.NewIntervalVar(start_var, duration_var, end_var) + .WithName("interval"); + LOG(INFO) << "start_var = " << start_var + << ", duration_var = " << duration_var << ", end_var = " << end_var + << ", interval_var = " << interval_var; +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::IntervalSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/jobshop_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/jobshop_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..9401587222475e1ccdcfb79540708ee088e41589 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/knapsack.cc b/libs/or-tools-src-ubuntu/examples/cpp/knapsack.cc new file mode 100644 index 0000000000000000000000000000000000000000..d3306a9be65a629ab1a65e4d260c5f8b8bdaa645 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/linear_assignment_api.cc b/libs/or-tools-src-ubuntu/examples/cpp/linear_assignment_api.cc new file mode 100644 index 0000000000000000000000000000000000000000..4a7664e4673d0311f5988c0ecc42c91b9b7d9d25 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/linear_solver_protocol_buffers.cc b/libs/or-tools-src-ubuntu/examples/cpp/linear_solver_protocol_buffers.cc new file mode 100644 index 0000000000000000000000000000000000000000..c9757bb6973a82c916e5b79e1f897906ad67e49c --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/literal_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/literal_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..0110b1abc64e1f7ec786efe04ba8248d5dbea2dd --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/literal_sample_sat.cc @@ -0,0 +1,34 @@ +// 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/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void LiteralSampleSat() { + CpModelBuilder cp_model; + + const BoolVar x = cp_model.NewBoolVar().WithName("x"); + const BoolVar not_x = Not(x); + LOG(INFO) << "x = " << x << ", not(x) = " << not_x; +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::LiteralSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/magic_square_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/magic_square_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..a411385958162521500df846e4a3e8133781e145 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/max_flow.cc b/libs/or-tools-src-ubuntu/examples/cpp/max_flow.cc new file mode 100644 index 0000000000000000000000000000000000000000..4f212c5d5e29e3c73cb05e4e1ebe3bf96b1ef542 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/min_cost_flow.cc b/libs/or-tools-src-ubuntu/examples/cpp/min_cost_flow.cc new file mode 100644 index 0000000000000000000000000000000000000000..3ef937a81239f450112e5f0b5a54ff6255bd3b2b --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/minimal_jobshop_cp.cc b/libs/or-tools-src-ubuntu/examples/cpp/minimal_jobshop_cp.cc new file mode 100644 index 0000000000000000000000000000000000000000..3e6a7cea7c9f2835a2a7e9351515eb09f2f7aad0 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/minimal_jobshop_cp.cc @@ -0,0 +1,189 @@ +// 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 +#include // std::iota + +#include "ortools/base/logging.h" +#include "ortools/constraint_solver/constraint_solver.h" + +// Solve a job shop problem: + +namespace operations_research { +void SolveJobShopExample() { + // Instantiate the solver. + Solver solver("JobShopExample"); + std::array machines; + std::iota(std::begin(machines), std::end(machines), 0); + { + std::ostringstream oss; + for (auto i : machines) oss << ' ' << i; + LOG(INFO) << "Machines: " << oss.str(); + } + + // Jobs definition + using MachineIndex = int; + using ProcessingTime = int; + using Task = std::pair; + using Job = std::vector; + std::vector jobs = { + {{0, 3}, {1, 2}, {2, 2}}, {{0, 2}, {2, 1}, {1, 4}}, {{1, 4}, {2, 3}}}; + LOG(INFO) << "Jobs:"; + for (int i = 0; i < jobs.size(); ++i) { + std::ostringstream problem; + problem << "Job " << i << ": ["; + for (const Task& task : jobs[i]) { + problem << "(" << task.first << ", " << task.second << ")"; + } + problem << "]" << std::endl; + LOG(INFO) << problem.str(); + } + + // Computes horizon. + ProcessingTime horizon = 0; + for (const Job& job : jobs) { + for (const Task& task : job) { + horizon += task.second; + } + } + LOG(INFO) << "Horizon: " << horizon; + + // Creates tasks. + std::vector> tasks_matrix(jobs.size()); + for (int i = 0; i < jobs.size(); ++i) { + for (int j = 0; j < jobs[i].size(); ++j) { + std::ostringstream oss; + oss << "Job_" << i << "_" << j; + tasks_matrix[i].push_back(solver.MakeFixedDurationIntervalVar( + 0, horizon, jobs[i][j].second, false, oss.str())); + } + } + + // Add conjunctive contraints. + for (int i = 0; i < jobs.size(); ++i) { + for (int j = 0; j < jobs[i].size() - 1; ++j) { + solver.AddConstraint(solver.MakeIntervalVarRelation( + tasks_matrix[i][j + 1], Solver::STARTS_AFTER_END, + tasks_matrix[i][j])); + } + } + + // Creates sequence variables and add disjunctive constraints. + std::vector all_sequences; + std::vector all_machines_jobs; + for (const auto machine : machines) { + std::vector machines_jobs; + for (int i = 0; i < jobs.size(); ++i) { + for (int j = 0; j < jobs[i].size(); ++j) { + if (jobs[i][j].first == machine) + machines_jobs.push_back(tasks_matrix[i][j]); + } + } + DisjunctiveConstraint* const disj = solver.MakeDisjunctiveConstraint( + machines_jobs, "Machine_" + std::to_string(machine)); + solver.AddConstraint(disj); + all_sequences.push_back(disj->MakeSequenceVar()); + } + + // Set the objective. + std::vector all_ends; + for (const auto& job : tasks_matrix) { + IntervalVar* const task = job.back(); + all_ends.push_back(task->EndExpr()->Var()); + } + IntVar* const obj_var = solver.MakeMax(all_ends)->Var(); + OptimizeVar* const objective_monitor = solver.MakeMinimize(obj_var, 1); + + // ----- Search monitors and decision builder ----- + + // This decision builder will rank all tasks on all machines. + DecisionBuilder* const sequence_phase = + solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT); + + // After the ranking of tasks, the schedule is still loose and any + // task can be postponed at will. But, because the problem is now a PERT + // (http://en.wikipedia.org/wiki/Program_Evaluation_and_Review_Technique), + // we can schedule each task at its earliest start time. This is + // conveniently done by fixing the objective variable to its + // minimum value. + DecisionBuilder* const obj_phase = solver.MakePhase( + obj_var, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE); + + // The main decision builder (ranks all tasks, then fixes the + // objective_variable). + DecisionBuilder* const main_phase = solver.Compose(sequence_phase, obj_phase); + + // Search log. + const int kLogFrequency = 1000000; + SearchMonitor* const search_log = + solver.MakeSearchLog(kLogFrequency, objective_monitor); + + SearchLimit* limit = nullptr; + + // Create the solution collector. + SolutionCollector* const collector = solver.MakeLastSolutionCollector(); + collector->Add(all_sequences); + collector->AddObjective(obj_var); + for (const auto machine : machines) { + SequenceVar* const sequence = all_sequences[machine]; + for (int i = 0; i < sequence->size(); ++i) { + IntervalVar* const t = sequence->Interval(i); + collector->Add(t->StartExpr()->Var()); + collector->Add(t->EndExpr()->Var()); + } + } + + // Solve the problem. + if (solver.Solve(main_phase, search_log, objective_monitor, limit, + collector)) { + LOG(INFO) << "Optimal Schedule Length: " << collector->objective_value(0); + LOG(INFO) << ""; + + LOG(INFO) << "Optimal Schedule:"; + std::vector machine_intervals_list; + for (const auto machine : machines) { + std::ostringstream machine_tasks; + SequenceVar* seq = all_sequences[machine]; + machine_tasks << "Machine " << machine << ": "; + for (const auto s : collector->ForwardSequence(0, seq)) { + machine_tasks << seq->Interval(s)->name() << " "; + } + LOG(INFO) << machine_tasks.str(); + + std::ostringstream machine_intervals; + machine_intervals << "Machine " << machine << ": "; + for (const auto s : collector->ForwardSequence(0, seq)) { + IntervalVar* t = seq->Interval(s); + machine_intervals << "[" << std::setw(2) + << collector->Value(0, t->StartExpr()->Var()) << ", " + << std::setw(2) + << collector->Value(0, t->EndExpr()->Var()) << "] "; + } + machine_intervals_list.push_back(machine_intervals.str()); + } + LOG(INFO) << "Time Intervals for Tasks: "; + for (const auto& intervals : machine_intervals_list) { + LOG(INFO) << intervals; + } + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Time: " << solver.wall_time() << "ms"; + } +} +} // namespace operations_research + +int main(int argc, char** argv) { + google::InitGoogleLogging(argv[0]); + FLAGS_logtostderr = 1; + operations_research::SolveJobShopExample(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/mps_driver.cc b/libs/or-tools-src-ubuntu/examples/cpp/mps_driver.cc new file mode 100644 index 0000000000000000000000000000000000000000..ae605163d39add3296bde49883e8d043c3d9c579 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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/status/status.h" +#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/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 absl::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-src-ubuntu/examples/cpp/multi_knapsack_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/multi_knapsack_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..9b4661f5a27559b84bcf2ef72aec80f33f91ca51 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/multiple_knapsack_mip.cc b/libs/or-tools-src-ubuntu/examples/cpp/multiple_knapsack_mip.cc new file mode 100644 index 0000000000000000000000000000000000000000..70e288ee74cb8b4c57633f104234603346512bc6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/multiple_knapsack_mip.cc @@ -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. + +// [START program] +// [START import] +#include +#include +#include + +#include "ortools/linear_solver/linear_expr.h" +#include "ortools/linear_solver/linear_solver.h" +// [END import] + +// [START program_part1] +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector weights = {48, 30, 42, 36, 36, 48, 42, 42, + 36, 24, 30, 30, 42, 36, 36}; + const std::vector values = {10, 30, 25, 50, 35, 30, 15, 40, + 30, 35, 45, 10, 20, 30, 25}; + const int num_items = weights.size(); + const int total_value = std::accumulate(values.begin(), values.end(), 0); + const std::vector bin_capacities = {{100, 100, 100, 100, 100}}; + const int num_bins = 5; +}; +// [END data_model] + +void MultipleKnapsackMip() { + // [START data] + DataModel data; + // [END data] + // [END program_part1] + + // [START solver] + // Create the mip solver with the CBC backend. + MPSolver solver("multiple_knapsack_mip", + MPSolver::CBC_MIXED_INTEGER_PROGRAMMING); + // [END solver] + + // [START program_part2] + // [START variables] + std::vector> x( + data.num_items, std::vector(data.num_bins)); + for (int i = 0; i < data.num_items; ++i) { + for (int j = 0; j < data.num_bins; ++j) { + x[i][j] = solver.MakeIntVar(0.0, 1.0, ""); + } + } + // [END variables] + + // [START constraints] + // Create the constraints. + // Each item is in at most one bin. + for (int i = 0; i < data.num_items; ++i) { + LinearExpr sum; + for (int j = 0; j < data.num_bins; ++j) { + sum += x[i][j]; + } + solver.MakeRowConstraint(sum <= 1.0); + } + // For each bin that is used, the total packed weight can be at most + // the bin capacity. + for (int j = 0; j < data.num_bins; ++j) { + LinearExpr weight; + for (int i = 0; i < data.num_items; ++i) { + weight += data.weights[i] * LinearExpr(x[i][j]); + } + solver.MakeRowConstraint(weight <= data.bin_capacities[j]); + } + // [END constraints] + + // [START objective] + // Create the objective function. + MPObjective* const objective = solver.MutableObjective(); + LinearExpr value; + for (int i = 0; i < data.num_items; ++i) { + for (int j = 0; j < data.num_bins; ++j) { + value += data.values[i] * LinearExpr(x[i][j]); + } + } + objective->MaximizeLinearExpr(value); + // [END objective] + + // [START solve] + const MPSolver::ResultStatus result_status = solver.Solve(); + // [END solve] + + // [START print_solution] + // Check that the problem has an optimal solution. + if (result_status != MPSolver::OPTIMAL) { + std::cerr << "The problem does not have an optimal solution!"; + } + std::cout << "Total packed value: " << objective->Value() << "\n\n"; + double total_weight = 0; + for (int j = 0; j < data.num_bins; ++j) { + double bin_weight = 0; + double bin_value = 0; + std::cout << "Bin " << j << std::endl << std::endl; + for (int i = 0; i < data.num_items; ++i) { + if (x[i][j]->solution_value() == 1) { + std::cout << "Item " << i << " - weight: " << data.weights[i] + << " value: " << data.values[i] << std::endl; + bin_weight += data.weights[i]; + bin_value += data.values[i]; + } + } + std::cout << "Packed bin weight: " << bin_weight << std::endl; + std::cout << "Packed bin value: " << bin_value << std::endl << std::endl; + total_weight += bin_weight; + } + std::cout << "Total packed weight: " << total_weight << std::endl; + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::MultipleKnapsackMip(); + return EXIT_SUCCESS; +} +// [END program_part2] +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/multiple_knapsack_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/multiple_knapsack_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..5c7aa732dec7e7937df76c1f3812a2cce32f49af --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/multiple_knapsack_sat.cc @@ -0,0 +1,148 @@ +// 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 "ortools/sat/cp_model.h" +// [END import] +namespace operations_research { +namespace sat { + +// [START data_model] +struct DataModel { + const std::vector weights = {{ + 48, + 30, + 42, + 36, + 36, + 48, + 42, + 42, + 36, + 24, + 30, + 30, + 42, + 36, + 36, + }}; + const std::vector values = { + {10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25}}; + const int num_items = weights.size(); + const int total_value = accumulate(values.begin(), values.end(), 0); + const std::vector kBinCapacities = {{100, 100, 100, 100, 100}}; + const int kNumBins = 5; +}; +// [END data_model] + +// [START solution_printer] +void PrintSolution(const DataModel data, + const std::vector> x, + const std::vector load, + const std::vector value, + const CpSolverResponse response) { + for (int b = 0; b < data.kNumBins; ++b) { + LOG(INFO) << "Bin " << b; + LOG(INFO); + for (int i = 0; i < data.num_items; ++i) { + if (SolutionIntegerValue(response, x[i][b]) > 0) { + LOG(INFO) << "Item " << i << " - Weight: " << data.weights[i] + << " Value: " << data.values[i]; + } + } + LOG(INFO) << "Packed bin weight: " + << SolutionIntegerValue(response, load[b]); + LOG(INFO) << "Packed bin value: " + << SolutionIntegerValue(response, value[b]); + LOG(INFO); + } + LOG(INFO) << "Total packed weight: " + << SolutionIntegerValue(response, LinearExpr::Sum(load)); + LOG(INFO) << "Total packed value: " + << SolutionIntegerValue(response, LinearExpr::Sum(value)); +} +// [END solution_printer] + +void MultipleKnapsackSat() { + // [START data] + DataModel data; + // [END data] + + // [START model] + CpModelBuilder cp_model; + // [END model] + + // [START variables] + // Main variables. + std::vector> x(data.num_items); + for (int i = 0; i < data.num_items; ++i) { + for (int b = 0; b < data.kNumBins; ++b) { + x[i].push_back(cp_model.NewIntVar({0, 1})); + } + } + + // Load variables. + std::vector load(data.kNumBins); + std::vector value(data.kNumBins); + for (int b = 0; b < data.kNumBins; ++b) { + load[b] = cp_model.NewIntVar({0, data.kBinCapacities[b]}); + value[b] = cp_model.NewIntVar({0, data.total_value}); + } + + // Links load and x. + for (int b = 0; b < data.kNumBins; ++b) { + LinearExpr weightsExpr; + LinearExpr valuesExpr; + for (int i = 0; i < data.num_items; ++i) { + weightsExpr.AddTerm(x[i][b], data.weights[i]); + valuesExpr.AddTerm(x[i][b], data.values[i]); + } + cp_model.AddEquality(weightsExpr, load[b]); + cp_model.AddEquality(valuesExpr, value[b]); + } + // [END variables] + + // [START constraints] + // Each item can be in at most one bin. + for (int i = 0; i < data.num_items; ++i) { + LinearExpr expr; + for (int b = 0; b < data.kNumBins; ++b) { + expr.AddTerm(x[i][b], 1); + } + cp_model.AddLessOrEqual(expr, 1); + } + // [END constraints] + // Maximize sum of load. + // [START objective] + cp_model.Maximize(LinearExpr::Sum(value)); + // [END objective] + + // [START solve] + const CpSolverResponse response = Solve(cp_model.Build()); + // [END solve] + + // [START print_solution] + PrintSolution(data, x, load, value, response); + // [END print_solution] +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::MultipleKnapsackSat(); + + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/network_routing_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/network_routing_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..6d339dc6f58fb6e0457f7c4f80f7056a89f8e66d --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/no_overlap_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/no_overlap_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..9b7dc78754e6343864423a9216c7719862fc0625 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/no_overlap_sample_sat.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/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void NoOverlapSampleSat() { + CpModelBuilder cp_model; + const int64 kHorizon = 21; // 3 weeks. + + const Domain horizon(0, kHorizon); + // Task 0, duration 2. + const IntVar start_0 = cp_model.NewIntVar(horizon); + const IntVar duration_0 = cp_model.NewConstant(2); + const IntVar end_0 = cp_model.NewIntVar(horizon); + const IntervalVar task_0 = + cp_model.NewIntervalVar(start_0, duration_0, end_0); + + // Task 1, duration 4. + const IntVar start_1 = cp_model.NewIntVar(horizon); + const IntVar duration_1 = cp_model.NewConstant(4); + const IntVar end_1 = cp_model.NewIntVar(horizon); + const IntervalVar task_1 = + cp_model.NewIntervalVar(start_1, duration_1, end_1); + + // Task 2, duration 3. + const IntVar start_2 = cp_model.NewIntVar(horizon); + const IntVar duration_2 = cp_model.NewConstant(3); + const IntVar end_2 = cp_model.NewIntVar(horizon); + const IntervalVar task_2 = + cp_model.NewIntervalVar(start_2, duration_2, end_2); + + // Week ends. + const IntervalVar weekend_0 = + cp_model.NewIntervalVar(cp_model.NewConstant(5), cp_model.NewConstant(2), + cp_model.NewConstant(7)); + const IntervalVar weekend_1 = + cp_model.NewIntervalVar(cp_model.NewConstant(12), cp_model.NewConstant(2), + cp_model.NewConstant(14)); + const IntervalVar weekend_2 = + cp_model.NewIntervalVar(cp_model.NewConstant(19), cp_model.NewConstant(2), + cp_model.NewConstant(21)); + + // No Overlap constraint. + cp_model.AddNoOverlap( + {task_0, task_1, task_2, weekend_0, weekend_1, weekend_2}); + + // Makespan. + IntVar makespan = cp_model.NewIntVar(horizon); + cp_model.AddLessOrEqual(end_0, makespan); + cp_model.AddLessOrEqual(end_1, makespan); + cp_model.AddLessOrEqual(end_2, makespan); + + cp_model.Minimize(makespan); + + // Solving part. + Model model; + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + LOG(INFO) << CpSolverResponseStats(response); + + if (response.status() == CpSolverStatus::OPTIMAL) { + LOG(INFO) << "Optimal Schedule Length: " << response.objective_value(); + LOG(INFO) << "Task 0 starts at " << SolutionIntegerValue(response, start_0); + LOG(INFO) << "Task 1 starts at " << SolutionIntegerValue(response, start_1); + LOG(INFO) << "Task 2 starts at " << SolutionIntegerValue(response, start_2); + } +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::NoOverlapSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/nqueens.cc b/libs/or-tools-src-ubuntu/examples/cpp/nqueens.cc new file mode 100644 index 0000000000000000000000000000000000000000..d99a6eff17eee0a6e4fd7088d91f0f8fc18365b6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/nurses_cp.cc b/libs/or-tools-src-ubuntu/examples/cpp/nurses_cp.cc new file mode 100644 index 0000000000000000000000000000000000000000..bfbaa32228172c4d98f25c311c85489f7bc2302f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/nurses_cp.cc @@ -0,0 +1,204 @@ +// 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 // std::iota + +#include "ortools/base/logging.h" +#include "ortools/constraint_solver/constraint_solver.h" + +namespace operations_research { +void SolveNursesExample() { + // Instantiate the solver. + Solver solver("NursesExample"); + std::array nurses; + std::iota(std::begin(nurses), std::end(nurses), 0); + { + std::ostringstream oss; + for (auto i : nurses) oss << ' ' << i; + LOG(INFO) << "Nurses:" << oss.str(); + } + + // Nurse assigned to shift 0 means not working that day. + std::array shifts; + std::iota(std::begin(shifts), std::end(shifts), 0); + { + std::ostringstream oss; + for (auto i : shifts) oss << ' ' << i; + LOG(INFO) << "Shifts:" << oss.str(); + } + + std::array days; + std::iota(std::begin(days), std::end(days), 0); + { + std::ostringstream oss; + for (auto i : days) oss << ' ' << i; + LOG(INFO) << "Days:" << oss.str(); + } + + // Create shift variables. + std::vector> shifts_matrix(nurses.size()); + std::vector shifts_flat; + for (const auto nurse : nurses) { + for (const auto day : days) { + std::ostringstream oss; + oss << "shifts(nurse: " << nurse << ", day: " << day << ")"; + IntVar *var = solver.MakeIntVar(shifts.front(), shifts.back(), oss.str()); + shifts_matrix[nurse].push_back(var); + shifts_flat.push_back(var); + } + } + + // Create nurse variables. + std::vector> nurses_matrix(shifts.size()); + for (const auto shift : shifts) { + for (const auto day : days) { + std::ostringstream oss; + oss << "nurses(shift: " << shift << ", day: " << day << ")"; + IntVar *var = solver.MakeIntVar(nurses.front(), nurses.back(), oss.str()); + nurses_matrix[shift].push_back(var); + } + } + + // Set relationships between shifts and nurses. + for (const auto day : days) { + std::vector nurses_for_day(shifts.size()); + for (const auto shift : shifts) { + nurses_for_day[shift] = nurses_matrix[shift][day]; + } + for (const auto nurse : nurses) { + IntVar *s = shifts_matrix[nurse][day]; + solver.AddConstraint( + solver.MakeEquality(solver.MakeElement(nurses_for_day, s), nurse)); + } + } + + // Make assignments different on each day i.e. + for (const auto day : days) { + // no shift can have two nurses + std::vector shifts_for_day(nurses.size()); + for (const auto nurse : nurses) { + shifts_for_day[nurse] = shifts_matrix[nurse][day]; + } + solver.AddConstraint(solver.MakeAllDifferent(shifts_for_day)); + + // no nurses can have more than one shifts a day + std::vector nurses_for_day(shifts.size()); + for (const auto shift : shifts) { + nurses_for_day[shift] = nurses_matrix[shift][day]; + } + solver.AddConstraint(solver.MakeAllDifferent(nurses_for_day)); + } + + // Each nurse works 5 or 6 days in a week. + for (const auto nurse : nurses) { + std::vector nurse_is_working; + for (const auto day : days) { + nurse_is_working.push_back( + solver.MakeIsGreaterOrEqualCstVar(shifts_matrix[nurse][day], 1)); + } + solver.AddConstraint(solver.MakeSumGreaterOrEqual(nurse_is_working, 5)); + solver.AddConstraint(solver.MakeSumLessOrEqual(nurse_is_working, 6)); + } + + // Create works_shift variables. + // works_shift_matrix[n][s] is True if + // nurse n works shift s at least once during the week. + std::vector> works_shift_matrix(nurses.size()); + for (const auto nurse : nurses) { + for (const auto shift : shifts) { + std::ostringstream oss; + oss << "work_shift(nurse: " << nurse << ", shift: " << shift << ")"; + works_shift_matrix[nurse].push_back(solver.MakeBoolVar(oss.str())); + } + } + + for (const auto nurse : nurses) { + for (const auto shift : shifts) { + std::vector shift_s_for_nurse; + for (const auto day : days) { + shift_s_for_nurse.push_back( + solver.MakeIsEqualCstVar(shifts_matrix[nurse][day], shift)); + } + solver.AddConstraint( + solver.MakeEquality(works_shift_matrix[nurse][shift], + solver.MakeMax(shift_s_for_nurse)->Var())); + } + } + + // For each shift(other than 0), at most 2 nurses are assigned to that shift + // during the week. + for (std::size_t shift = 1; shift < shifts.size(); ++shift) { + std::vector nurses_for_shift; + for (const auto nurse : nurses) { + nurses_for_shift.push_back(works_shift_matrix[nurse][shift]); + } + solver.AddConstraint(solver.MakeSumLessOrEqual(nurses_for_shift, 2)); + } + + // If a nurse works shifts 2 or 3 on, + // he must also work that shift the previous day or the following day. + for (const auto shift : {2, 3}) { + for (const auto day : days) { + IntVar *v0 = solver.MakeIsEqualVar(nurses_matrix[shift][day], + nurses_matrix[shift][(day + 1) % 7]); + IntVar *v1 = solver.MakeIsEqualVar(nurses_matrix[shift][(day + 1) % 7], + nurses_matrix[shift][(day + 2) % 7]); + solver.AddConstraint(solver.MakeEquality(solver.MakeMax(v0, v1), 1)); + } + } + + // ----- Search monitors and decision builder ----- + + // Create the decision builder. + DecisionBuilder *const main_phase = solver.MakePhase( + shifts_flat, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE); + + // Search log. + SearchMonitor *const search_log = nullptr; + + SearchLimit *limit = nullptr; + + // Create the solution collector. + SolutionCollector *const collector = solver.MakeAllSolutionCollector(); + collector->Add(shifts_flat); + + // Solve + solver.Solve(main_phase, search_log, nullptr, limit, collector); + LOG(INFO) << "Number of solutions: " << collector->solution_count(); + LOG(INFO) << ""; + + // Display a few solutions picked at random. + std::array a_few_solutions = {859, 2034, 5091, 7003}; + for (const auto solution : a_few_solutions) { + LOG(INFO) << "Solution " << solution << ":"; + for (const auto day : days) { + LOG(INFO) << "Day " << day << ":"; + for (const auto nurse : nurses) { + LOG(INFO) << "Nurse " << nurse << " assigned to " + << "Task " + << collector->Value(solution, + shifts_flat[nurse * days.size() + day]); + } + } + } + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Time: " << solver.wall_time() << "ms"; +} +} // namespace operations_research + +int main(int argc, char **argv) { + google::InitGoogleLogging(argv[0]); + FLAGS_logtostderr = 1; + operations_research::SolveNursesExample(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/opb_reader.h b/libs/or-tools-src-ubuntu/examples/cpp/opb_reader.h new file mode 100644 index 0000000000000000000000000000000000000000..515a32af466e5859754f0bc33a3192280bacb6d7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/optional_interval_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/optional_interval_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..497280c7b2aacd79363b8b03dc21e7a0e67718f6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/optional_interval_sample_sat.cc @@ -0,0 +1,48 @@ +// 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/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void OptionalIntervalSampleSat() { + CpModelBuilder cp_model; + const int kHorizon = 100; + + const Domain horizon(0, kHorizon); + const IntVar start_var = cp_model.NewIntVar(horizon).WithName("start"); + const IntVar duration_var = cp_model.NewConstant(10); + const IntVar end_var = cp_model.NewIntVar(horizon).WithName("end"); + const BoolVar presence_var = cp_model.NewBoolVar().WithName("presence"); + + const IntervalVar interval_var = + cp_model + .NewOptionalIntervalVar(start_var, duration_var, end_var, + presence_var) + .WithName("interval"); + + LOG(INFO) << "start_var = " << start_var + << ", duration_var = " << duration_var << ", end_var = " << end_var + << ", presence_var = " << presence_var + << ", interval_var = " << interval_var; +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::OptionalIntervalSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/parse_dimacs_assignment.h b/libs/or-tools-src-ubuntu/examples/cpp/parse_dimacs_assignment.h new file mode 100644 index 0000000000000000000000000000000000000000..06b97d74b20dbbb8ae06163176a6b88e34720bd5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/pdptw.cc b/libs/or-tools-src-ubuntu/examples/cpp/pdptw.cc new file mode 100644 index 0000000000000000000000000000000000000000..096a9522aacc6e12bbb1c55a401d53b5106ade5f --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/print_dimacs_assignment.h b/libs/or-tools-src-ubuntu/examples/cpp/print_dimacs_assignment.h new file mode 100644 index 0000000000000000000000000000000000000000..43d4fb452f1c053c1495908f6ed5e58cab6d449b --- /dev/null +++ b/libs/or-tools-src-ubuntu/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/status/status.h" +#include "absl/strings/str_format.h" +#include "ortools/base/file.h" +#include "ortools/base/logging.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-src-ubuntu/examples/cpp/rabbits_and_pheasants_cp.cc b/libs/or-tools-src-ubuntu/examples/cpp/rabbits_and_pheasants_cp.cc new file mode 100644 index 0000000000000000000000000000000000000000..460cf97ca96c3457014008fa8c6bdcdfb38609b5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/rabbits_and_pheasants_cp.cc @@ -0,0 +1,67 @@ +// 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. + +// Knowing that we see 20 heads and 56 legs, +// how many pheasants and rabbits are we looking at ? + +#include "ortools/base/logging.h" +#include "ortools/constraint_solver/constraint_solver.h" + +namespace operations_research { +void RunConstraintProgrammingExample() { + // Instantiate the solver. + Solver solver("RabbitsPheasantsExample"); + + // Define decision variables. + IntVar* const rabbits = solver.MakeIntVar(0, 20, "rabbits"); + IntVar* const pheasants = solver.MakeIntVar(0, 20, "pheasants"); + + // Define constraints. + IntExpr* const heads = solver.MakeSum(rabbits, pheasants); + Constraint* const c0 = solver.MakeEquality(heads, 20); + solver.AddConstraint(c0); + + IntExpr* const legs = solver.MakeSum(solver.MakeProd(rabbits, 4), + solver.MakeProd(pheasants, 2)); + Constraint* const c1 = solver.MakeEquality(legs, 56); + solver.AddConstraint(c1); + + DecisionBuilder* const db = + solver.MakePhase(rabbits, pheasants, Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MIN_VALUE); + + bool has_result = solver.Solve(db); + // Check that the problem has a solution. + if (has_result != true) { + LOG(FATAL) << "The problem does not have a solution!"; + } + int count = 0; + while (solver.NextSolution()) { + count++; + LOG(INFO) << "Solution " << count << ":"; + LOG(INFO) << "rabbits = " << rabbits->Value(); + LOG(INFO) << "pheasants = " << rabbits->Value(); + } + LOG(INFO) << "Number of solutions: " << count; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds"; +} +} // 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-src-ubuntu/examples/cpp/rabbits_and_pheasants_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/rabbits_and_pheasants_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..0857a013de9c314a953bfa0493e5391dd9e0ad25 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/rabbits_and_pheasants_sat.cc @@ -0,0 +1,46 @@ +// 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/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void RabbitsAndPheasantsSat() { + CpModelBuilder cp_model; + + const Domain all_animals(0, 20); + const IntVar rabbits = cp_model.NewIntVar(all_animals).WithName("rabbits"); + const IntVar pheasants = + cp_model.NewIntVar(all_animals).WithName("pheasants"); + + cp_model.AddEquality(LinearExpr::Sum({rabbits, pheasants}), 20); + cp_model.AddEquality(LinearExpr::ScalProd({rabbits, pheasants}, {4, 2}), 56); + + const CpSolverResponse response = Solve(cp_model.Build()); + + if (response.status() == CpSolverStatus::FEASIBLE) { + // Get the value of x in the solution. + LOG(INFO) << SolutionIntegerValue(response, rabbits) << " rabbits, and " + << SolutionIntegerValue(response, pheasants) << " pheasants"; + } +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::RabbitsAndPheasantsSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/random_tsp.cc b/libs/or-tools-src-ubuntu/examples/cpp/random_tsp.cc new file mode 100644 index 0000000000000000000000000000000000000000..426c93dac4ff7987e719c8af163ee059a9baab9c --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/ranking_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/ranking_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..249883c4407706e9e6e828bc8c34739576a5c985 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/ranking_sample_sat.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. + +#include "ortools/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void RankingSampleSat() { + CpModelBuilder cp_model; + const int kHorizon = 100; + const int kNumTasks = 4; + + auto add_task_ranking = [&cp_model](const std::vector& starts, + const std::vector& presences, + const std::vector& ranks) { + const int num_tasks = starts.size(); + + // Creates precedence variables between pairs of intervals. + std::vector> precedences(num_tasks); + for (int i = 0; i < num_tasks; ++i) { + precedences[i].resize(num_tasks); + for (int j = 0; j < num_tasks; ++j) { + if (i == j) { + precedences[i][i] = presences[i]; + } else { + BoolVar prec = cp_model.NewBoolVar(); + precedences[i][j] = prec; + cp_model.AddLessOrEqual(starts[i], starts[j]).OnlyEnforceIf(prec); + } + } + } + + // Treats optional intervals. + for (int i = 0; i < num_tasks - 1; ++i) { + for (int j = i + 1; j < num_tasks; ++j) { + // Makes sure that if i is not performed, all precedences are + // false. + cp_model.AddImplication(Not(presences[i]), Not(precedences[i][j])); + cp_model.AddImplication(Not(presences[i]), Not(precedences[j][i])); + // Makes sure that if j is not performed, all precedences are + // false. + cp_model.AddImplication(Not(presences[j]), Not(precedences[i][j])); + cp_model.AddImplication(Not(presences[i]), Not(precedences[j][i])); + // The following bool_or will enforce that for any two intervals: + // i precedes j or j precedes i or at least one interval is not + // performed. + cp_model.AddBoolOr({precedences[i][j], precedences[j][i], + Not(presences[i]), Not(presences[j])}); + // Redundant constraint: it propagates early that at most one + // precedence is true. + cp_model.AddImplication(precedences[i][j], Not(precedences[j][i])); + cp_model.AddImplication(precedences[j][i], Not(precedences[i][j])); + } + } + // Links precedences and ranks. + for (int i = 0; i < num_tasks; ++i) { + LinearExpr sum_of_predecessors(-1); + for (int j = 0; j < num_tasks; ++j) { + sum_of_predecessors.AddVar(precedences[j][i]); + } + cp_model.AddEquality(ranks[i], sum_of_predecessors); + } + }; + + std::vector starts; + std::vector ends; + std::vector intervals; + std::vector presences; + std::vector ranks; + + const Domain horizon(0, kHorizon); + const Domain possible_ranks(-1, kNumTasks - 1); + + for (int t = 0; t < kNumTasks; ++t) { + const IntVar start = cp_model.NewIntVar(horizon); + const IntVar duration = cp_model.NewConstant(t + 1); + const IntVar end = cp_model.NewIntVar(horizon); + const BoolVar presence = + t < kNumTasks / 2 ? cp_model.TrueVar() : cp_model.NewBoolVar(); + const IntervalVar interval = + cp_model.NewOptionalIntervalVar(start, duration, end, presence); + const IntVar rank = cp_model.NewIntVar(possible_ranks); + + starts.push_back(start); + ends.push_back(end); + intervals.push_back(interval); + presences.push_back(presence); + ranks.push_back(rank); + } + + // Adds NoOverlap constraint. + cp_model.AddNoOverlap(intervals); + + // Ranks tasks. + add_task_ranking(starts, presences, ranks); + + // Adds a constraint on ranks. + cp_model.AddLessThan(ranks[0], ranks[1]); + + // Creates makespan variables. + const IntVar makespan = cp_model.NewIntVar(horizon); + for (int t = 0; t < kNumTasks; ++t) { + cp_model.AddLessOrEqual(ends[t], makespan).OnlyEnforceIf(presences[t]); + } + + // Create objective: minimize 2 * makespan - 7 * sum of presences. + // That is you gain 7 by interval performed, but you pay 2 by day of delays. + LinearExpr objective; + objective.AddTerm(makespan, 2); + for (int t = 0; t < kNumTasks; ++t) { + objective.AddTerm(presences[t], -7); + } + cp_model.Minimize(objective); + + // Solving part. + const CpSolverResponse response = Solve(cp_model.Build()); + LOG(INFO) << CpSolverResponseStats(response); + + if (response.status() == CpSolverStatus::OPTIMAL) { + LOG(INFO) << "Optimal cost: " << response.objective_value(); + LOG(INFO) << "Makespan: " << SolutionIntegerValue(response, makespan); + for (int t = 0; t < kNumTasks; ++t) { + if (SolutionBooleanValue(response, presences[t])) { + LOG(INFO) << "task " << t << " starts at " + << SolutionIntegerValue(response, starts[t]) << " with rank " + << SolutionIntegerValue(response, ranks[t]); + } else { + LOG(INFO) << "task " << t << " is not performed and ranked at " + << SolutionIntegerValue(response, ranks[t]); + } + } + } +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::RankingSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/reified_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/reified_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..a5d102c0fd9271b7d4e969aee5dcf58490490de5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/reified_sample_sat.cc @@ -0,0 +1,45 @@ +// 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/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void ReifiedSampleSat() { + CpModelBuilder cp_model; + + const BoolVar x = cp_model.NewBoolVar(); + const BoolVar y = cp_model.NewBoolVar(); + const BoolVar b = cp_model.NewBoolVar(); + + // First version using a half-reified bool and. + cp_model.AddBoolAnd({x, Not(y)}).OnlyEnforceIf(b); + + // Second version using implications. + cp_model.AddImplication(b, x); + cp_model.AddImplication(b, Not(y)); + + // Third version using bool or. + cp_model.AddBoolOr({Not(b), x}); + cp_model.AddBoolOr({Not(b), Not(y)}); +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::ReifiedSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/sat_cnf_reader.h b/libs/or-tools-src-ubuntu/examples/cpp/sat_cnf_reader.h new file mode 100644 index 0000000000000000000000000000000000000000..78460acddc0fad3c7fe71bcd3ac2d1e1ae6a283f --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/sat_runner.cc b/libs/or-tools-src-ubuntu/examples/cpp/sat_runner.cc new file mode 100644 index 0000000000000000000000000000000000000000..7ccb04c51c9304ee7ee09791f6c4d6df68dbdd10 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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/status/status.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/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 absl::Status status = ValidateBooleanProblem(problem); + if (!status.ok()) { + LOG(ERROR) << "Invalid Boolean problem: " << status.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-src-ubuntu/examples/cpp/search_for_all_solutions_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/search_for_all_solutions_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..4819bcb8bd4e2461694e2692c44641f056a28e94 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/search_for_all_solutions_sample_sat.cc @@ -0,0 +1,70 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" +#include "ortools/sat/sat_parameters.pb.h" + +namespace operations_research { +namespace sat { + +void SearchAllSolutionsSampleSat() { + // [START model] + CpModelBuilder cp_model; + // [END model] + + // [START variables] + const Domain domain(0, 2); + const IntVar x = cp_model.NewIntVar(domain).WithName("x"); + const IntVar y = cp_model.NewIntVar(domain).WithName("y"); + const IntVar z = cp_model.NewIntVar(domain).WithName("z"); + // [END variables] + + // [START constraints] + cp_model.AddNotEqual(x, y); + // [END constraints] + + // [START print_solution] + Model model; + + int num_solutions = 0; + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + LOG(INFO) << "Solution " << num_solutions; + LOG(INFO) << " x = " << SolutionIntegerValue(r, x); + LOG(INFO) << " y = " << SolutionIntegerValue(r, y); + LOG(INFO) << " z = " << SolutionIntegerValue(r, z); + num_solutions++; + })); + // [END print_solution] + + // Tell the solver to enumerate all solutions. + // [START solve] + SatParameters parameters; + parameters.set_enumerate_all_solutions(true); + model.Add(NewSatParameters(parameters)); + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + // [END solve] + + LOG(INFO) << "Number of solutions found: " << num_solutions; +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::SearchAllSolutionsSampleSat(); + + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/shift_minimization_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/shift_minimization_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..9cb1f7e437be565534d2117bc389e83ebbbbeb11 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/simple_cp_program.cc b/libs/or-tools-src-ubuntu/examples/cpp/simple_cp_program.cc new file mode 100644 index 0000000000000000000000000000000000000000..371be0a6c0039ef9ad0d4be831b392836842c235 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/simple_cp_program.cc @@ -0,0 +1,77 @@ +// 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 "ortools/constraint_solver/constraint_solver.h" +// [END import] + +namespace operations_research { + +void SimpleCpProgram() { + // Instantiate the solver. + // [START solver] + Solver solver("CpSimple"); + // [END solver] + + // Create the variables. + // [START variables] + const int64 num_vals = 3; + IntVar* const x = solver.MakeIntVar(0, num_vals - 1, "x"); + IntVar* const y = solver.MakeIntVar(0, num_vals - 1, "y"); + IntVar* const z = solver.MakeIntVar(0, num_vals - 1, "z"); + // [END variables] + + // Constraint 0: x != y.. + // [START constraints] + solver.AddConstraint(solver.MakeAllDifferent({x, y})); + LOG(INFO) << "Number of constraints: " + << std::to_string(solver.constraints()); + // [END constraints] + + // Solve the problem. + // [START solve] + DecisionBuilder* const db = solver.MakePhase( + {x, y, z}, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE); + // [END solve] + + // Print solution on console. + // [START print_solution] + int count = 0; + solver.NewSearch(db); + while (solver.NextSolution()) { + ++count; + LOG(INFO) << "Solution " << count << ":" << std::endl + << " x=" << x->Value() << " y=" << y->Value() + << " z=" << z->Value(); + } + solver.EndSearch(); + LOG(INFO) << "Number of solutions found: " << solver.solutions(); + // [END print_solution] + + // [START advanced] + LOG(INFO) << "Advanced usage:" << std::endl + << "Problem solved in " << std::to_string(solver.wall_time()) + << "ms" << std::endl + << "Memory usage: " << std::to_string(Solver::MemoryUsage()) + << "bytes"; + // [END advanced] +} + +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::SimpleCpProgram(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/simple_knapsack_program.cc b/libs/or-tools-src-ubuntu/examples/cpp/simple_knapsack_program.cc new file mode 100644 index 0000000000000000000000000000000000000000..8b91f15fe08593d5ec1d77d799dceaba52c17083 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/simple_knapsack_program.cc @@ -0,0 +1,81 @@ +// 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 { +namespace algorithms { + +void SimpleKnapsackProgram() { + // [START solver] + KnapsackSolver solver(KnapsackSolver::KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER, + "SimpleKnapsackExample"); + // [END solver] + + // [START data] + std::vector> weights = {{565, 406, 194, 130, 435, 367, 230, + 315, 393, 125, 670, 892, 600, 293, + 712, 147, 421, 255}}; + std::vector capacities = {850}; + std::vector values = weights[0]; + // [END data] + + // [START solve] + solver.Init(values, weights, capacities); + int64 computed_value = solver.Solve(); + // [END solve] + + // [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 algorithms +} // namespace operations_research + +int main() { + operations_research::algorithms::SimpleKnapsackProgram(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/simple_ls_program.cc b/libs/or-tools-src-ubuntu/examples/cpp/simple_ls_program.cc new file mode 100644 index 0000000000000000000000000000000000000000..a954f353e3252fe318cd17428c8ef1a8c4ffe913 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/simple_ls_program.cc @@ -0,0 +1,206 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file illustrate the API for Large Neighborhood Search and +// Local Search. It solves the same trivial problem with a Large +// Neighborhood Search approach, a Local Search approach, and a Local +// Search with Filter approach. + +#include "ortools/base/commandlineflags.h" +#include "ortools/base/hash.h" +#include "ortools/base/map_util.h" +#include "ortools/base/random.h" +#include "ortools/base/stl_util.h" +#include "ortools/constraint_solver/constraint_solver.h" +#include "ortools/constraint_solver/constraint_solveri.h" + +namespace operations_research { +class OneVarLns : public BaseLns { + public: + explicit OneVarLns(const std::vector& vars) + : BaseLns(vars), index_(0) {} + + ~OneVarLns() override {} + + void InitFragments() override { index_ = 0; } + + bool NextFragment() override { + const int size = Size(); + if (index_ < size) { + AppendToFragment(index_); + ++index_; + return true; + } else { + return false; + } + } + + private: + int index_; +}; + +class MoveOneVar : public IntVarLocalSearchOperator { + public: + explicit MoveOneVar(const std::vector& variables) + : IntVarLocalSearchOperator(variables), + variable_index_(0), + move_up_(false) {} + + ~MoveOneVar() override {} + + protected: + // Make a neighbor assigning one variable to its target value. + bool MakeOneNeighbor() override { + const int64 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; + } + + private: + void OnStart() override { + CHECK_GE(variable_index_, 0); + CHECK_LT(variable_index_, Size()); + } + + // Index of the next variable to try to restore + int64 variable_index_; + // Direction of the modification. + bool move_up_; +}; + +class SumFilter : public IntVarLocalSearchFilter { + public: + explicit SumFilter(const std::vector& vars) + : IntVarLocalSearchFilter(vars), sum_(0) {} + + ~SumFilter() override {} + + void OnSynchronize(const Assignment* delta) override { + sum_ = 0; + for (int index = 0; index < Size(); ++index) { + sum_ += Value(index); + } + } + + 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 filter 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" + << " SumFilter::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; + } + } + int64 new_sum = sum_; + VLOG(1) << "No LNS, size = " << solution_delta_size; + for (int index = 0; index < solution_delta_size; ++index) { + int64 touched_var = -1; + FindIndex(solution_delta.Element(index).Var(), &touched_var); + const int64 old_value = Value(touched_var); + const int64 new_value = solution_delta.Element(index).Value(); + new_sum += new_value - old_value; + } + VLOG(1) << "new sum = " << new_sum << ", old sum = " << sum_; + return new_sum < sum_; + } + + private: + int64 sum_; +}; + +enum SolveType { LNS, LS, LS_WITH_FILTER }; + +void SolveProblem(SolveType solve_type) { + Solver s("Sample"); + std::vector vars; + s.MakeIntVarArray(4, 0, 4, &vars); + IntVar* const sum_var = s.MakeSum(vars)->Var(); + OptimizeVar* const obj = s.MakeMinimize(sum_var, 1); + DecisionBuilder* const db = + s.MakePhase(vars, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MAX_VALUE); + DecisionBuilder* ls = nullptr; + switch (solve_type) { + case LNS: { + LOG(INFO) << "Large Neighborhood Search"; + OneVarLns* const one_var_lns = s.RevAlloc(new OneVarLns(vars)); + LocalSearchPhaseParameters* const ls_params = + s.MakeLocalSearchPhaseParameters(sum_var, one_var_lns, db); + ls = s.MakeLocalSearchPhase(vars, db, ls_params); + break; + } + case LS: { + LOG(INFO) << "Local Search"; + MoveOneVar* const one_var_ls = s.RevAlloc(new MoveOneVar(vars)); + LocalSearchPhaseParameters* const ls_params = + s.MakeLocalSearchPhaseParameters(sum_var, one_var_ls, db); + ls = s.MakeLocalSearchPhase(vars, db, ls_params); + break; + } + case LS_WITH_FILTER: { + LOG(INFO) << "Local Search with Filter"; + MoveOneVar* const one_var_ls = s.RevAlloc(new MoveOneVar(vars)); + std::vector filters; + filters.push_back(s.RevAlloc(new SumFilter(vars))); + + LocalSearchPhaseParameters* const ls_params = + s.MakeLocalSearchPhaseParameters(sum_var, one_var_ls, db, + nullptr, filters); + ls = s.MakeLocalSearchPhase(vars, db, ls_params); + break; + } + } + SolutionCollector* const collector = s.MakeLastSolutionCollector(); + collector->Add(vars); + collector->AddObjective(sum_var); + SearchMonitor* const log = s.MakeSearchLog(1000, obj); + s.Solve(ls, collector, obj, log); + LOG(INFO) << "Objective value = " << collector->objective_value(0); +} +} // namespace operations_research + +int main(int argc, char** argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + operations_research::SolveProblem(operations_research::LNS); + operations_research::SolveProblem(operations_research::LS); + operations_research::SolveProblem(operations_research::LS_WITH_FILTER); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/simple_max_flow_program.cc b/libs/or-tools-src-ubuntu/examples/cpp/simple_max_flow_program.cc new file mode 100644 index 0000000000000000000000000000000000000000..11df9cbf4354519e2b75aea835aeb86675b81899 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/simple_max_flow_program.cc @@ -0,0 +1,70 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +// From Taha 'Introduction to Operations Research', example 6.4-2.""" +// [START import] +#include "ortools/graph/max_flow.h" +// [END import] + +namespace operations_research { + +// MaxFlow simple interface example. +void SimpleMaxFlowProgram() { + // [START data] + // Define three parallel arrays: start_nodes, end_nodes, and the capacities + // between each pair. For instance, the arc from node 0 to node 1 has a + // capacity of 20. + std::vector start_nodes = {0, 0, 0, 1, 1, 2, 2, 3, 3}; + std::vector end_nodes = {1, 2, 3, 2, 4, 3, 4, 2, 4}; + std::vector capacities = {20, 30, 10, 40, 30, 10, 20, 5, 20}; + // [END data] + + // [START constraints] + // Instantiate a SimpleMaxFlow solver. + SimpleMaxFlow max_flow; + + // Add each arc. + for (int i = 0; i < start_nodes.size(); ++i) { + max_flow.AddArcWithCapacity(start_nodes[i], end_nodes[i], capacities[i]); + } + // [END constraints] + + // [START solve] + // Find the maximum flow between node 0 and node 4. + int solve_status = max_flow.Solve(0, 4); + // [END solve] + + // [START print_solution] + if (solve_status == MaxFlow::OPTIMAL) { + LOG(INFO) << "Max flow: " << max_flow.OptimalFlow(); + LOG(INFO) << ""; + LOG(INFO) << " Arc Flow / Capacity"; + for (std::size_t i = 0; i < max_flow.NumArcs(); ++i) { + LOG(INFO) << max_flow.Tail(i) << " -> " << max_flow.Head(i) << " " + << max_flow.Flow(i) << " / " << max_flow.Capacity(i); + } + } else { + LOG(INFO) << "Solving the max flow problem failed. Solver status: " + << solve_status; + } + // [END print_solution] +} + +} // namespace operations_research + +int main() { + operations_research::SimpleMaxFlowProgram(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/simple_min_cost_flow_program.cc b/libs/or-tools-src-ubuntu/examples/cpp/simple_min_cost_flow_program.cc new file mode 100644 index 0000000000000000000000000000000000000000..62995c783f942bf8b0b4a6f0b25518d4cf84db19 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/simple_min_cost_flow_program.cc @@ -0,0 +1,82 @@ +// 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] +// From Bradley, H., and M., 'Applied Mathematical Programming', figure 8.1. +// [START import] +#include "ortools/graph/min_cost_flow.h" +// [END import] + +namespace operations_research { + +void SimpleMinCostFlowProgram() { + // [START data] + // Define four parallel arrays: sources, destinations, capacities, + // and unit costs between each pair. For instance, the arc from node 0 + // to node 1 has a capacity of 15. + std::vector start_nodes = {0, 0, 1, 1, 1, 2, 2, 3, 4}; + std::vector end_nodes = {1, 2, 2, 3, 4, 3, 4, 4, 2}; + std::vector capacities = {15, 8, 20, 4, 10, 15, 4, 20, 5}; + std::vector unit_costs = {4, 4, 2, 2, 6, 1, 3, 2, 3}; + + // Define an array of supplies at each node. + std::vector supplies = {20, 0, 0, -5, -15}; + // [END data] + + // [START constraints] + // Instantiate a SimpleMinCostFlow solver. + SimpleMinCostFlow min_cost_flow; + + // Add each arc. + for (int i = 0; i < start_nodes.size(); ++i) { + int arc = min_cost_flow.AddArcWithCapacityAndUnitCost( + start_nodes[i], end_nodes[i], capacities[i], unit_costs[i]); + if (arc != i) LOG(FATAL) << "Internal error"; + } + + // Add node supplies. + for (int i = 0; i < supplies.size(); ++i) { + min_cost_flow.SetNodeSupply(i, supplies[i]); + } + // [END constraints] + + // [START solve] + // Find the min cost flow. + int solve_status = min_cost_flow.Solve(); + // [END solve] + + // [START print_solution] + if (solve_status == MinCostFlow::OPTIMAL) { + LOG(INFO) << "Minimum cost flow: " << min_cost_flow.OptimalCost(); + LOG(INFO) << ""; + LOG(INFO) << " Arc Flow / Capacity Cost"; + for (std::size_t i = 0; i < min_cost_flow.NumArcs(); ++i) { + int64 cost = min_cost_flow.Flow(i) * min_cost_flow.UnitCost(i); + LOG(INFO) << min_cost_flow.Tail(i) << " -> " << min_cost_flow.Head(i) + << " " << min_cost_flow.Flow(i) << " / " + << min_cost_flow.Capacity(i) << " " << cost; + } + } else { + LOG(INFO) << "Solving the min cost flow problem failed. Solver status: " + << solve_status; + } + // [END print_solution] +} + +} // namespace operations_research + +int main() { + operations_research::SimpleMinCostFlowProgram(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/simple_routing_program.cc b/libs/or-tools-src-ubuntu/examples/cpp/simple_routing_program.cc new file mode 100644 index 0000000000000000000000000000000000000000..8b292e875db2f6d51ee155869c09d15eb6d1fafd --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/simple_routing_program.cc @@ -0,0 +1,94 @@ +// 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 "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" +// [END import] + +namespace operations_research { + +void SimpleRoutingProgram() { + // Instantiate the data problem. + // [START data] + int num_location = 5; + int num_vehicles = 1; + RoutingIndexManager::NodeIndex depot{0}; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(num_location, num_vehicles, depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Define cost of each arc. + // [START arc_cost] + int distance_call_index = routing.RegisterTransitCallback( + [&manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to user NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return std::abs(to_node - from_node); + }); + routing.SetArcCostEvaluatorOfAllVehicles(distance_call_index); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + LOG(INFO) << "Objective: " << solution->ObjectiveValue(); + // Inspect solution. + int64 index = routing.Start(0); + LOG(INFO) << "Route for Vehicle 0:"; + int64 route_distance{0}; + std::ostringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution->Value(routing.NextVar(index)); + route_distance += + routing.GetArcCostForVehicle(previous_index, index, int64{0}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << route_distance << "m"; + // [END print_solution] +} + +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::SimpleRoutingProgram(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/simple_sat_program.cc b/libs/or-tools-src-ubuntu/examples/cpp/simple_sat_program.cc new file mode 100644 index 0000000000000000000000000000000000000000..171b080121d0421db70eae8024ebfc681556ae7a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/simple_sat_program.cc @@ -0,0 +1,58 @@ +// 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] +#include "ortools/sat/cp_model.h" + +namespace operations_research { +namespace sat { + +void SimpleSatProgram() { + // [START model] + CpModelBuilder cp_model; + // [END model] + + // [START variables] + const Domain domain(0, 2); + const IntVar x = cp_model.NewIntVar(domain).WithName("x"); + const IntVar y = cp_model.NewIntVar(domain).WithName("y"); + const IntVar z = cp_model.NewIntVar(domain).WithName("z"); + // [END variables] + + // [START constraints] + cp_model.AddNotEqual(x, y); + // [END constraints] + + // Solving part. + // [START solve] + const CpSolverResponse response = Solve(cp_model.Build()); + LOG(INFO) << CpSolverResponseStats(response); + // [END solve] + + if (response.status() == CpSolverStatus::FEASIBLE) { + // Get the value of x in the solution. + LOG(INFO) << "x = " << SolutionIntegerValue(response, x); + LOG(INFO) << "y = " << SolutionIntegerValue(response, y); + LOG(INFO) << "z = " << SolutionIntegerValue(response, z); + } +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::SimpleSatProgram(); + + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/slitherlink_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/slitherlink_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..f6895a261a4e934d150f5f72d86ffe5b94bd99da --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/solution_hinting_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/solution_hinting_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..3dd93f6eb13cad04529d5c3c61c93e2cdb3332f9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/solution_hinting_sample_sat.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. + +// [START program] +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" + +namespace operations_research { +namespace sat { + +void SolutionHintingSampleSat() { + // [START model] + CpModelBuilder cp_model; + // [END model] + + // [START variables] + const Domain domain(0, 2); + const IntVar x = cp_model.NewIntVar(domain).WithName("x"); + const IntVar y = cp_model.NewIntVar(domain).WithName("y"); + const IntVar z = cp_model.NewIntVar(domain).WithName("z"); + // [END variables] + + // [START constraints] + cp_model.AddNotEqual(x, y); + // [END constraints] + + // [START objective] + cp_model.Maximize(LinearExpr::ScalProd({x, y, z}, {1, 2, 3})); + // [END objective] + + // Solution hinting: x <- 1, y <- 2 + cp_model.AddHint(x, 1); + cp_model.AddHint(y, 2); + + // [START print_solution] + Model model; + + int num_solutions = 0; + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + LOG(INFO) << "Solution " << num_solutions; + LOG(INFO) << " x = " << SolutionIntegerValue(r, x); + LOG(INFO) << " y = " << SolutionIntegerValue(r, y); + LOG(INFO) << " z = " << SolutionIntegerValue(r, z); + num_solutions++; + })); + // [END print_solution] + + // Solving part. + // [START solve] + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + LOG(INFO) << CpSolverResponseStats(response); + // [END solve] +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::SolutionHintingSampleSat(); + + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/solve.cc b/libs/or-tools-src-ubuntu/examples/cpp/solve.cc new file mode 100644 index 0000000000000000000000000000000000000000..41017c938bbf7985a8c3226384f96170cbd47cdd --- /dev/null +++ b/libs/or-tools-src-ubuntu/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 absl::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-src-ubuntu/examples/cpp/solve_and_print_intermediate_solutions_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/solve_and_print_intermediate_solutions_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..0319ec37c201986b8518329d0837144e46c4c9ae --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/solve_and_print_intermediate_solutions_sample_sat.cc @@ -0,0 +1,69 @@ +// 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] +#include "ortools/sat/cp_model.h" +#include "ortools/sat/model.h" + +namespace operations_research { +namespace sat { + +void SolveAndPrintIntermediateSolutionsSampleSat() { + // [START model] + CpModelBuilder cp_model; + // [END model] + + // [START variables] + const Domain domain(0, 2); + const IntVar x = cp_model.NewIntVar(domain).WithName("x"); + const IntVar y = cp_model.NewIntVar(domain).WithName("y"); + const IntVar z = cp_model.NewIntVar(domain).WithName("z"); + // [END variables] + + // [START constraints] + cp_model.AddNotEqual(x, y); + // [END constraints] + + // [START objective] + cp_model.Maximize(LinearExpr::ScalProd({x, y, z}, {1, 2, 3})); + // [END objective] + + // [START print_solution] + Model model; + int num_solutions = 0; + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + LOG(INFO) << "Solution " << num_solutions; + LOG(INFO) << " objective value = " << r.objective_value(); + LOG(INFO) << " x = " << SolutionIntegerValue(r, x); + LOG(INFO) << " y = " << SolutionIntegerValue(r, y); + LOG(INFO) << " z = " << SolutionIntegerValue(r, z); + num_solutions++; + })); + // [END print_solution] + + // [START solve] + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + // [END solve] + + LOG(INFO) << "Number of solutions found: " << num_solutions; +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::SolveAndPrintIntermediateSolutionsSampleSat(); + + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/solve_with_time_limit_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/solve_with_time_limit_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..ebd553f4be6c57fa916fd58e55c88a5e77313420 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/solve_with_time_limit_sample_sat.cc @@ -0,0 +1,57 @@ +// 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/sat/cp_model.h" +#include "ortools/sat/model.h" +#include "ortools/sat/sat_parameters.pb.h" + +namespace operations_research { +namespace sat { + +void SolveWithTimeLimitSampleSat() { + CpModelBuilder cp_model; + + const Domain domain(0, 2); + const IntVar x = cp_model.NewIntVar(domain).WithName("x"); + const IntVar y = cp_model.NewIntVar(domain).WithName("y"); + const IntVar z = cp_model.NewIntVar(domain).WithName("z"); + + cp_model.AddNotEqual(x, y); + + // Solving part. + Model model; + + // Sets a time limit of 10 seconds. + SatParameters parameters; + parameters.set_max_time_in_seconds(10.0); + model.Add(NewSatParameters(parameters)); + + // Solve. + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + LOG(INFO) << CpSolverResponseStats(response); + + if (response.status() == CpSolverStatus::FEASIBLE) { + LOG(INFO) << " x = " << SolutionIntegerValue(response, x); + LOG(INFO) << " y = " << SolutionIntegerValue(response, y); + LOG(INFO) << " z = " << SolutionIntegerValue(response, z); + } +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::SolveWithTimeLimitSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/sports_scheduling_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/sports_scheduling_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..eb2523b92f9a3e3950d9e9874457a86aa6b9136c --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/step_function_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/step_function_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..3c0c11c5fdd9c5256a89c1850bbb80c1dd2782f5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/step_function_sample_sat.cc @@ -0,0 +1,84 @@ +// 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/sat/cp_model.h" +#include "ortools/sat/model.h" +#include "ortools/sat/sat_parameters.pb.h" + +namespace operations_research { +namespace sat { + +void StepFunctionSampleSat() { + // Create the CP-SAT model. + CpModelBuilder cp_model; + + // Declare our primary variable. + const IntVar x = cp_model.NewIntVar({0, 20}); + + // Create the expression variable and implement the step function + // Note it is not defined for var == 2. + // + // - 3 + // -- -- --------- 2 + // 1 + // -- --- 0 + // 0 ================ 20 + // + IntVar expr = cp_model.NewIntVar({0, 3}); + + // expr == 0 on [5, 6] U [8, 10] + BoolVar b0 = cp_model.NewBoolVar(); + cp_model.AddLinearConstraint(x, Domain::FromValues({5, 6, 8, 9, 10})) + .OnlyEnforceIf(b0); + cp_model.AddEquality(expr, 0).OnlyEnforceIf(b0); + + // expr == 2 on [0, 1] U [3, 4] U [11, 20] + BoolVar b2 = cp_model.NewBoolVar(); + cp_model + .AddLinearConstraint(x, Domain::FromIntervals({{0, 1}, {3, 4}, {11, 20}})) + .OnlyEnforceIf(b2); + cp_model.AddEquality(expr, 2).OnlyEnforceIf(b2); + + // expr == 3 when x = 7 + BoolVar b3 = cp_model.NewBoolVar(); + cp_model.AddEquality(x, 7).OnlyEnforceIf(b3); + cp_model.AddEquality(expr, 3).OnlyEnforceIf(b3); + + // At least one bi is true. (we could use a sum == 1). + cp_model.AddBoolOr({b0, b2, b3}); + + // Search for x values in increasing order. + cp_model.AddDecisionStrategy({x}, DecisionStrategyProto::CHOOSE_FIRST, + DecisionStrategyProto::SELECT_MIN_VALUE); + + // Create a solver and solve with a fixed search. + Model model; + SatParameters parameters; + parameters.set_search_branching(SatParameters::FIXED_SEARCH); + parameters.set_enumerate_all_solutions(true); + model.Add(NewSatParameters(parameters)); + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + LOG(INFO) << "x=" << SolutionIntegerValue(r, x) << " expr" + << SolutionIntegerValue(r, expr); + })); + SolveCpModel(cp_model.Build(), &model); +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::StepFunctionSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/stigler_diet.cc b/libs/or-tools-src-ubuntu/examples/cpp/stigler_diet.cc new file mode 100644 index 0000000000000000000000000000000000000000..728189251bf087e4ccc2f3ec319c7c3279a8c110 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/stop_after_n_solutions_sample_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/stop_after_n_solutions_sample_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..49d2875dc3071a503ac0549d8f10d5d5dd83e1f3 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/stop_after_n_solutions_sample_sat.cc @@ -0,0 +1,69 @@ +// 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/sat/cp_model.h" +#include "ortools/sat/model.h" +#include "ortools/sat/sat_parameters.pb.h" +#include "ortools/util/time_limit.h" + +namespace operations_research { +namespace sat { + +void StopAfterNSolutionsSampleSat() { + CpModelBuilder cp_model; + + const Domain domain(0, 2); + const IntVar x = cp_model.NewIntVar(domain).WithName("x"); + const IntVar y = cp_model.NewIntVar(domain).WithName("y"); + const IntVar z = cp_model.NewIntVar(domain).WithName("z"); + + 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 = 5; + int num_solutions = 0; + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + LOG(INFO) << "Solution " << num_solutions; + LOG(INFO) << " x = " << SolutionIntegerValue(r, x); + LOG(INFO) << " y = " << SolutionIntegerValue(r, y); + LOG(INFO) << " z = " << SolutionIntegerValue(r, z); + num_solutions++; + if (num_solutions >= kSolutionLimit) { + stopped = true; + LOG(INFO) << "Stop search after " << kSolutionLimit << " solutions."; + } + })); + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + LOG(INFO) << "Number of solutions found: " << num_solutions; + CHECK_EQ(num_solutions, kSolutionLimit); +} + +} // namespace sat +} // namespace operations_research + +int main() { + operations_research::sat::StopAfterNSolutionsSampleSat(); + + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/strawberry_fields_with_column_generation.cc b/libs/or-tools-src-ubuntu/examples/cpp/strawberry_fields_with_column_generation.cc new file mode 100644 index 0000000000000000000000000000000000000000..6b70415c2018700821dcab0770a9ec68aad83b4e --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/tsp.cc b/libs/or-tools-src-ubuntu/examples/cpp/tsp.cc new file mode 100644 index 0000000000000000000000000000000000000000..16a2226386d889cd59e52399bf1e4f27939cdbd6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/tsp.cc @@ -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. + +// [START program] +// [START import] +#include +#include + +#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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> locations{ + {4, 4}, {2, 0}, {8, 0}, {0, 1}, {1, 1}, {5, 2}, {7, 2}, {3, 3}, {6, 3}, + {5, 5}, {8, 5}, {1, 6}, {2, 6}, {3, 7}, {6, 7}, {0, 8}, {7, 8}, + }; + const int num_vehicles = 1; + const RoutingIndexManager::NodeIndex depot{0}; + DataModel() { + // Convert locations in meters using a city block dimension of 114m x 80m. + for (auto& it : const_cast>&>(locations)) { + it[0] *= 114; + it[1] *= 80; + } + } +}; +// [END data_model] + +// [START manhattan_distance_matrix] +/*! @brief Generate Manhattan distance matrix. + * @details It uses the data.locations to computes the Manhattan distance + * between the two positions of two different indices.*/ +std::vector> GenerateManhattanDistanceMatrix( + const std::vector>& locations) { + std::vector> distances = std::vector>( + locations.size(), std::vector(locations.size(), int64{0})); + for (int fromNode = 0; fromNode < locations.size(); fromNode++) { + for (int toNode = 0; toNode < locations.size(); toNode++) { + if (fromNode != toNode) + distances[fromNode][toNode] = + int64{std::abs(locations[toNode][0] - locations[fromNode][0]) + + std::abs(locations[toNode][1] - locations[fromNode][1])}; + } + } + return distances; +} +// [END manhattan_distance_matrix] + +// [START solution_printer] +//! @brief Print the solution +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + LOG(INFO) << "Objective: " << solution.ObjectiveValue(); + // Inspect solution. + int64 index = routing.Start(0); + LOG(INFO) << "Route for Vehicle 0:"; + int64 distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + distance += routing.GetArcCostForVehicle(previous_index, index, int64{0}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void Tsp() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.locations.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const auto distance_matrix = GenerateManhattanDistanceMatrix(data.locations); + const int transit_callback_index = routing.RegisterTransitCallback( + [&distance_matrix, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return distance_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(manager, routing, *solution); + // [END print_solution] +} + +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::Tsp(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/tsp_circuit_board.cc b/libs/or-tools-src-ubuntu/examples/cpp/tsp_circuit_board.cc new file mode 100644 index 0000000000000000000000000000000000000000..4946c8510aff15245111be6efcc13a50513990ff --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/tsp_circuit_board.cc @@ -0,0 +1,183 @@ +// 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 "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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> locations{ + {288, 149}, {288, 129}, {270, 133}, {256, 141}, {256, 157}, {246, 157}, + {236, 169}, {228, 169}, {228, 161}, {220, 169}, {212, 169}, {204, 169}, + {196, 169}, {188, 169}, {196, 161}, {188, 145}, {172, 145}, {164, 145}, + {156, 145}, {148, 145}, {140, 145}, {148, 169}, {164, 169}, {172, 169}, + {156, 169}, {140, 169}, {132, 169}, {124, 169}, {116, 161}, {104, 153}, + {104, 161}, {104, 169}, {90, 165}, {80, 157}, {64, 157}, {64, 165}, + {56, 169}, {56, 161}, {56, 153}, {56, 145}, {56, 137}, {56, 129}, + {56, 121}, {40, 121}, {40, 129}, {40, 137}, {40, 145}, {40, 153}, + {40, 161}, {40, 169}, {32, 169}, {32, 161}, {32, 153}, {32, 145}, + {32, 137}, {32, 129}, {32, 121}, {32, 113}, {40, 113}, {56, 113}, + {56, 105}, {48, 99}, {40, 99}, {32, 97}, {32, 89}, {24, 89}, + {16, 97}, {16, 109}, {8, 109}, {8, 97}, {8, 89}, {8, 81}, + {8, 73}, {8, 65}, {8, 57}, {16, 57}, {8, 49}, {8, 41}, + {24, 45}, {32, 41}, {32, 49}, {32, 57}, {32, 65}, {32, 73}, + {32, 81}, {40, 83}, {40, 73}, {40, 63}, {40, 51}, {44, 43}, + {44, 35}, {44, 27}, {32, 25}, {24, 25}, {16, 25}, {16, 17}, + {24, 17}, {32, 17}, {44, 11}, {56, 9}, {56, 17}, {56, 25}, + {56, 33}, {56, 41}, {64, 41}, {72, 41}, {72, 49}, {56, 49}, + {48, 51}, {56, 57}, {56, 65}, {48, 63}, {48, 73}, {56, 73}, + {56, 81}, {48, 83}, {56, 89}, {56, 97}, {104, 97}, {104, 105}, + {104, 113}, {104, 121}, {104, 129}, {104, 137}, {104, 145}, {116, 145}, + {124, 145}, {132, 145}, {132, 137}, {140, 137}, {148, 137}, {156, 137}, + {164, 137}, {172, 125}, {172, 117}, {172, 109}, {172, 101}, {172, 93}, + {172, 85}, {180, 85}, {180, 77}, {180, 69}, {180, 61}, {180, 53}, + {172, 53}, {172, 61}, {172, 69}, {172, 77}, {164, 81}, {148, 85}, + {124, 85}, {124, 93}, {124, 109}, {124, 125}, {124, 117}, {124, 101}, + {104, 89}, {104, 81}, {104, 73}, {104, 65}, {104, 49}, {104, 41}, + {104, 33}, {104, 25}, {104, 17}, {92, 9}, {80, 9}, {72, 9}, + {64, 21}, {72, 25}, {80, 25}, {80, 25}, {80, 41}, {88, 49}, + {104, 57}, {124, 69}, {124, 77}, {132, 81}, {140, 65}, {132, 61}, + {124, 61}, {124, 53}, {124, 45}, {124, 37}, {124, 29}, {132, 21}, + {124, 21}, {120, 9}, {128, 9}, {136, 9}, {148, 9}, {162, 9}, + {156, 25}, {172, 21}, {180, 21}, {180, 29}, {172, 29}, {172, 37}, + {172, 45}, {180, 45}, {180, 37}, {188, 41}, {196, 49}, {204, 57}, + {212, 65}, {220, 73}, {228, 69}, {228, 77}, {236, 77}, {236, 69}, + {236, 61}, {228, 61}, {228, 53}, {236, 53}, {236, 45}, {228, 45}, + {228, 37}, {236, 37}, {236, 29}, {228, 29}, {228, 21}, {236, 21}, + {252, 21}, {260, 29}, {260, 37}, {260, 45}, {260, 53}, {260, 61}, + {260, 69}, {260, 77}, {276, 77}, {276, 69}, {276, 61}, {276, 53}, + {284, 53}, {284, 61}, {284, 69}, {284, 77}, {284, 85}, {284, 93}, + {284, 101}, {288, 109}, {280, 109}, {276, 101}, {276, 93}, {276, 85}, + {268, 97}, {260, 109}, {252, 101}, {260, 93}, {260, 85}, {236, 85}, + {228, 85}, {228, 93}, {236, 93}, {236, 101}, {228, 101}, {228, 109}, + {228, 117}, {228, 125}, {220, 125}, {212, 117}, {204, 109}, {196, 101}, + {188, 93}, {180, 93}, {180, 101}, {180, 109}, {180, 117}, {180, 125}, + {196, 145}, {204, 145}, {212, 145}, {220, 145}, {228, 145}, {236, 145}, + {246, 141}, {252, 125}, {260, 129}, {280, 133}, + }; + const int num_vehicles = 1; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START distance_matrix] +// @brief Generate distance matrix. +std::vector> ComputeEuclideanDistanceMatrix( + const std::vector>& locations) { + std::vector> distances = std::vector>( + locations.size(), std::vector(locations.size(), int64{0})); + for (int fromNode = 0; fromNode < locations.size(); fromNode++) { + for (int toNode = 0; toNode < locations.size(); toNode++) { + if (fromNode != toNode) + distances[fromNode][toNode] = static_cast( + std::hypot((locations[toNode][0] - locations[fromNode][0]), + (locations[toNode][1] - locations[fromNode][1]))); + } + } + return distances; +} +// [END distance_matrix] + +// [START solution_printer] +//! @brief Print the solution +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + LOG(INFO) << "Objective: " << solution.ObjectiveValue(); + // Inspect solution. + int64 index = routing.Start(0); + LOG(INFO) << "Route:"; + int64 distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + distance += routing.GetArcCostForVehicle(previous_index, index, int64{0}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Route distance: " << distance << "miles"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void Tsp() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.locations.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // [START transit_callback] + const auto distance_matrix = ComputeEuclideanDistanceMatrix(data.locations); + const int transit_callback_index = routing.RegisterTransitCallback( + [&distance_matrix, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return distance_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::Tsp(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/tsp_cities.cc b/libs/or-tools-src-ubuntu/examples/cpp/tsp_cities.cc new file mode 100644 index 0000000000000000000000000000000000000000..7a218c68209176727fe81b7530d9783d06728793 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/tsp_cities.cc @@ -0,0 +1,131 @@ +// 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 "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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 2451, 713, 1018, 1631, 1374, 2408, 213, 2571, 875, 1420, 2145, 1972}, + {2451, 0, 1745, 1524, 831, 1240, 959, 2596, 403, 1589, 1374, 357, 579}, + {713, 1745, 0, 355, 920, 803, 1737, 851, 1858, 262, 940, 1453, 1260}, + {1018, 1524, 355, 0, 700, 862, 1395, 1123, 1584, 466, 1056, 1280, 987}, + {1631, 831, 920, 700, 0, 663, 1021, 1769, 949, 796, 879, 586, 371}, + {1374, 1240, 803, 862, 663, 0, 1681, 1551, 1765, 547, 225, 887, 999}, + {2408, 959, 1737, 1395, 1021, 1681, 0, 2493, 678, 1724, 1891, 1114, 701}, + {213, 2596, 851, 1123, 1769, 1551, 2493, 0, 2699, 1038, 1605, 2300, 2099}, + {2571, 403, 1858, 1584, 949, 1765, 678, 2699, 0, 1744, 1645, 653, 600}, + {875, 1589, 262, 466, 796, 547, 1724, 1038, 1744, 0, 679, 1272, 1162}, + {1420, 1374, 940, 1056, 879, 225, 1891, 1605, 1645, 679, 0, 1017, 1200}, + {2145, 357, 1453, 1280, 586, 887, 1114, 2300, 653, 1272, 1017, 0, 504}, + {1972, 579, 1260, 987, 371, 999, 701, 2099, 600, 1162, 1200, 504, 0}, + }; + const int num_vehicles = 1; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + // Inspect solution. + LOG(INFO) << "Objective: " << solution.ObjectiveValue() << " miles"; + int64 index = routing.Start(0); + LOG(INFO) << "Route:"; + int64 distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + distance += routing.GetArcCostForVehicle(previous_index, index, int64{0}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Route distance: " << distance << "miles"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void Tsp() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(manager, routing, *solution); + // [END print_solution] +} + +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::Tsp(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/tsp_distance_matrix.cc b/libs/or-tools-src-ubuntu/examples/cpp/tsp_distance_matrix.cc new file mode 100644 index 0000000000000000000000000000000000000000..a7d5f49e65d7c97dc8cc08866de167d1f7fe301f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/tsp_distance_matrix.cc @@ -0,0 +1,152 @@ +// 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 "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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, + 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, + 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, + 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, + 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, + 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, + 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, + 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, + 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, + 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, + 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, + 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, + 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, + 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, + 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, + 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, + 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, + 194, 798, 0}, + }; + const int num_vehicles = 1; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + LOG(INFO) << "Objective: " << solution.ObjectiveValue(); + // Inspect solution. + int64 index = routing.Start(0); + LOG(INFO) << "Route for Vehicle 0:"; + int64 distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + distance += routing.GetArcCostForVehicle(previous_index, index, int64{0}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void Tsp() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(manager, routing, *solution); + // [END print_solution] +} + +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::Tsp(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/variable_intervals_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/variable_intervals_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..1f4de6465178a480762a6b17d99a49c7e6d039d7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/cpp/vrp.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp.cc new file mode 100644 index 0000000000000000000000000000000000000000..9dbbe54bc6e1f24629ed52e56d60972bf2d53fee --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp.cc @@ -0,0 +1,157 @@ +// 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 "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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, + 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, + 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, + 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, + 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, + 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, + 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, + 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, + 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, + 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, + 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, + 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, + 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, + 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, + 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, + 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, + 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, + 194, 798, 0}, + }; + const int num_vehicles = 4; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + LOG(INFO) << "Objective: " << solution.ObjectiveValue(); + int64 total_distance{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for Vehicle " << vehicle_id << ":"; + int64 distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + distance += routing.GetArcCostForVehicle(previous_index, index, + int64{vehicle_id}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << distance << "m"; + total_distance += distance; + } + LOG(INFO) << "Total distance of all routes: " << total_distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void Vrp() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::Vrp(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_capacity.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_capacity.cc new file mode 100644 index 0000000000000000000000000000000000000000..44d2e727d83cdd75622195b2b79aaf04ae16a39e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_capacity.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. + +// [START program] +// [START import] +#include + +#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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, + 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, + 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, + 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, + 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, + 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, + 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, + 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, + 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, + 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, + 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, + 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, + 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, + 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, + 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, + 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, + 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, + 194, 798, 0}, + }; + // [START demands_capacities] + const std::vector demands{ + 0, 1, 1, 2, 4, 2, 4, 8, 8, 1, 2, 1, 2, 4, 4, 8, 8, + }; + const std::vector vehicle_capacities{15, 15, 15, 15}; + // [END demands_capacities] + const int num_vehicles = 4; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + int64 total_distance{0}; + int64 total_load{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for Vehicle " << vehicle_id << ":"; + int64 route_distance{0}; + int64 route_load{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + int64 node_index = manager.IndexToNode(index).value(); + route_load += data.demands[node_index]; + route << node_index << " Load(" << route_load << ") -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + route_distance += routing.GetArcCostForVehicle(previous_index, index, + int64{vehicle_id}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << route_distance << "m"; + LOG(INFO) << "Load of the route: " << route_load; + total_distance += route_distance; + total_load += route_load; + } + LOG(INFO) << "Total distance of all routes: " << total_distance << "m"; + LOG(INFO) << "Total load of all routes: " << total_load; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpCapacity() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + int from_node = manager.IndexToNode(from_index).value(); + int to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Capacity constraint. + // [START capacity_constraint] + const int demand_callback_index = routing.RegisterUnaryTransitCallback( + [&data, &manager](int64 from_index) -> int64 { + // Convert from routing variable Index to demand NodeIndex. + int from_node = manager.IndexToNode(from_index).value(); + return data.demands[from_node]; + }); + routing.AddDimensionWithVehicleCapacity( + demand_callback_index, // transit callback index + int64{0}, // null capacity slack + data.vehicle_capacities, // vehicle maximum capacities + true, // start cumul to zero + "Capacity"); + // [END capacity_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpCapacity(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_drop_nodes.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_drop_nodes.cc new file mode 100644 index 0000000000000000000000000000000000000000..3e0810e6e876e94352a8320f0b239c5b07999438 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_drop_nodes.cc @@ -0,0 +1,201 @@ +// 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 "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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, + 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, + 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, + 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, + 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, + 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, + 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, + 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, + 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, + 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, + 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, + 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, + 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, + 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, + 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, + 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, + 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, + 194, 798, 0}, + }; + // [START demands_capacities] + const std::vector demands{ + 0, 1, 1, 3, 6, 3, 6, 8, 8, 1, 2, 1, 2, 6, 6, 8, 8, + }; + const std::vector vehicle_capacities{15, 15, 15, 15}; + // [END demands_capacities] + const int num_vehicles = 4; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + // Display dropped nodes. + std::ostringstream dropped_nodes; + for (int64 node = 0; node < routing.Size(); ++node) { + if (routing.IsStart(node) || routing.IsEnd(node)) continue; + if (solution.Value(routing.NextVar(node)) == node) { + dropped_nodes << " " << manager.IndexToNode(node).value(); + } + } + LOG(INFO) << "Dropped nodes:" << dropped_nodes.str(); + // Display routes + int64 total_distance{0}; + int64 total_load{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for Vehicle " << vehicle_id << ":"; + int64 route_distance{0}; + int64 route_load{0}; + std::ostringstream route; + while (routing.IsEnd(index) == false) { + int64 node_index = manager.IndexToNode(index).value(); + route_load += data.demands[node_index]; + route << node_index << " Load(" << route_load << ") -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + route_distance += routing.GetArcCostForVehicle(previous_index, index, + int64{vehicle_id}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << route_distance << "m"; + LOG(INFO) << "Load of the route: " << route_load; + total_distance += route_distance; + total_load += route_load; + } + LOG(INFO) << "Total distance of all routes: " << total_distance << "m"; + LOG(INFO) << "Total load of all routes: " << total_load; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpDropNodes() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Capacity constraint. + // [START capacity_constraint] + const int demand_callback_index = routing.RegisterUnaryTransitCallback( + [&data, &manager](int64 from_index) -> int64 { + // Convert from routing variable Index to demand NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + return data.demands[from_node]; + }); + routing.AddDimensionWithVehicleCapacity( + demand_callback_index, // transit callback index + int64{0}, // null capacity slack + data.vehicle_capacities, // vehicle maximum capacities + true, // start cumul to zero + "Capacity"); + // Allow to drop nodes. + int64 penalty{1000}; + for (int i = 1; i < data.distance_matrix.size(); ++i) { + routing.AddDisjunction( + {manager.NodeToIndex(RoutingIndexManager::NodeIndex(i))}, penalty); + } + // [END capacity_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpDropNodes(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_global_span.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_global_span.cc new file mode 100644 index 0000000000000000000000000000000000000000..d8ba8e49d3ef30dc1a56be6d8298ff28cd6a1f68 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_global_span.cc @@ -0,0 +1,163 @@ +// 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 "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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, + 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, + 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, + 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, + 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, + 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, + 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, + 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, + 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, + 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, + 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, + 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, + 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, + 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, + 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, + 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, + 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, + 194, 798, 0}, + }; + const int num_vehicles = 4; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +// [START solution_printer] +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + int64 max_route_distance{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for Vehicle " << vehicle_id << ":"; + int64 route_distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + route_distance += routing.GetArcCostForVehicle(previous_index, index, + int64{vehicle_id}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << route_distance << "m"; + max_route_distance = std::max(route_distance, max_route_distance); + } + LOG(INFO) << "Maximum of the route distances: " << max_route_distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpGlobalSpan() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transit_callback_index, 0, 3000, + true, // start cumul to zero + "Distance"); + routing.GetMutableDimension("Distance")->SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpGlobalSpan(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_initial_routes.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_initial_routes.cc new file mode 100644 index 0000000000000000000000000000000000000000..702cc5ed71d8f6d4a72ea02b48537a2106d66274 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_initial_routes.cc @@ -0,0 +1,178 @@ +// 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 "ortools/constraint_solver/routing.h" +#include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_parameters.h" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, + 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, + 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, + 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, + 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, + 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, + 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, + 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, + 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, + 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, + 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, + 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, + 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, + 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, + 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, + 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, + 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, + 194, 798, 0}, + }; + // [START initial_routes] + const std::vector> initial_routes{ + {8, 16, 14, 13, 12, 11}, + {3, 4, 9, 10}, + {15, 1}, + {7, 5, 2, 6}, + }; + // [END initial_routes] + const int num_vehicles = 4; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +// [START solution_printer] +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + int64 max_route_distance{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for Vehicle " << vehicle_id << ":"; + int64 route_distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + route_distance += routing.GetArcCostForVehicle(previous_index, index, + int64{vehicle_id}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << route_distance << "m"; + max_route_distance = std::max(route_distance, max_route_distance); + } + LOG(INFO) << "Maximum of the route distances: " << max_route_distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpInitialRoutes() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transit_callback_index, 0, 3000, + true, // start cumul to zero + "Distance"); + routing.GetMutableDimension("Distance")->SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Get initial solution from routes. + // [START print_initial_solution] + const Assignment* initial_solution = + routing.ReadAssignmentFromRoutes(data.initial_routes, true); + // Print initial solution on console. + LOG(INFO) << "Initial solution: "; + PrintSolution(data, manager, routing, *initial_solution); + // [END print_initial_solution] + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + // [END parameters] + + // Solve from initial solution. + // [START solve] + const Assignment* solution = routing.SolveFromAssignmentWithParameters( + initial_solution, searchParameters); + // [START solve] + + // Print solution on console. + // [START print_solution] + LOG(INFO) << ""; + LOG(INFO) << "Solution from search: "; + PrintSolution(data, manager, routing, *solution); + // [START print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpInitialRoutes(); + return EXIT_SUCCESS; +} diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_pickup_delivery.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_pickup_delivery.cc new file mode 100644 index 0000000000000000000000000000000000000000..9da4d9f9c02caefc4dc042fab16663917d4b8d0c --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_pickup_delivery.cc @@ -0,0 +1,200 @@ +// 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 "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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, + 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, + 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, + 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, + 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, + 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, + 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, + 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, + 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, + 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, + 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, + 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, + 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, + 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, + 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, + 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, + 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, + 194, 798, 0}, + }; + // [START pickups_deliveries] + const std::vector> + pickups_deliveries{ + {RoutingIndexManager::NodeIndex{1}, + RoutingIndexManager::NodeIndex{6}}, + {RoutingIndexManager::NodeIndex{2}, + RoutingIndexManager::NodeIndex{10}}, + {RoutingIndexManager::NodeIndex{4}, + RoutingIndexManager::NodeIndex{3}}, + {RoutingIndexManager::NodeIndex{5}, + RoutingIndexManager::NodeIndex{9}}, + {RoutingIndexManager::NodeIndex{7}, + RoutingIndexManager::NodeIndex{8}}, + {RoutingIndexManager::NodeIndex{15}, + RoutingIndexManager::NodeIndex{11}}, + {RoutingIndexManager::NodeIndex{13}, + RoutingIndexManager::NodeIndex{12}}, + {RoutingIndexManager::NodeIndex{16}, + RoutingIndexManager::NodeIndex{14}}, + }; + // [END pickups_deliveries] + const int num_vehicles = 4; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + int64 total_distance{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for Vehicle " << vehicle_id << ":"; + int64 route_distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + route_distance += routing.GetArcCostForVehicle(previous_index, index, + int64{vehicle_id}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << route_distance << "m"; + total_distance += route_distance; + } + LOG(INFO) << "Total distance of all routes: " << total_distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpGlobalSpan() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Define cost of each arc. + // [START arc_cost] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transit_callback_index, // transit callback + 0, // no slack + 3000, // vehicle maximum travel distance + true, // start cumul to zero + "Distance"); + RoutingDimension* distance_dimension = + routing.GetMutableDimension("Distance"); + distance_dimension->SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Define Transportation Requests. + // [START pickup_delivery_constraint] + Solver* const solver = routing.solver(); + for (const auto& request : data.pickups_deliveries) { + int64 pickup_index = manager.NodeToIndex(request[0]); + int64 delivery_index = manager.NodeToIndex(request[1]); + routing.AddPickupAndDelivery(pickup_index, delivery_index); + solver->AddConstraint(solver->MakeEquality( + routing.VehicleVar(pickup_index), routing.VehicleVar(delivery_index))); + solver->AddConstraint( + solver->MakeLessOrEqual(distance_dimension->CumulVar(pickup_index), + distance_dimension->CumulVar(delivery_index))); + } + // [END pickup_delivery_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpGlobalSpan(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_pickup_delivery_fifo.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_pickup_delivery_fifo.cc new file mode 100644 index 0000000000000000000000000000000000000000..3df700d131f927883d73fcb468ae4801bc137b77 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_pickup_delivery_fifo.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. + +// [START program] +// [START import] +#include + +#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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, + 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, + 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, + 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, + 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, + 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, + 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, + 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, + 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, + 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, + 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, + 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, + 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, + 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, + 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, + 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, + 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, + 194, 798, 0}, + }; + // [START pickups_deliveries] + const std::vector> + pickups_deliveries{ + {RoutingIndexManager::NodeIndex{1}, + RoutingIndexManager::NodeIndex{6}}, + {RoutingIndexManager::NodeIndex{2}, + RoutingIndexManager::NodeIndex{10}}, + {RoutingIndexManager::NodeIndex{4}, + RoutingIndexManager::NodeIndex{3}}, + {RoutingIndexManager::NodeIndex{5}, + RoutingIndexManager::NodeIndex{9}}, + {RoutingIndexManager::NodeIndex{7}, + RoutingIndexManager::NodeIndex{8}}, + {RoutingIndexManager::NodeIndex{15}, + RoutingIndexManager::NodeIndex{11}}, + {RoutingIndexManager::NodeIndex{13}, + RoutingIndexManager::NodeIndex{12}}, + {RoutingIndexManager::NodeIndex{16}, + RoutingIndexManager::NodeIndex{14}}, + }; + // [END pickups_deliveries] + const int num_vehicles = 4; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + int64 total_distance{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for Vehicle " << vehicle_id << ":"; + int64 route_distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + route_distance += routing.GetArcCostForVehicle(previous_index, index, + int64{vehicle_id}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << route_distance << "m"; + total_distance += route_distance; + } + LOG(INFO) << "Total distance of all routes: " << total_distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpGlobalSpan() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Define cost of each arc. + // [START arc_cost] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transit_callback_index, // transit callback + 0, // no slack + 3000, // vehicle maximum travel distance + true, // start cumul to zero + "Distance"); + RoutingDimension* distance_dimension = + routing.GetMutableDimension("Distance"); + distance_dimension->SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Define Transportation Requests. + // [START pickup_delivery_constraint] + Solver* const solver = routing.solver(); + for (const auto& request : data.pickups_deliveries) { + int64 pickup_index = manager.NodeToIndex(request[0]); + int64 delivery_index = manager.NodeToIndex(request[1]); + routing.AddPickupAndDelivery(pickup_index, delivery_index); + solver->AddConstraint(solver->MakeEquality( + routing.VehicleVar(pickup_index), routing.VehicleVar(delivery_index))); + solver->AddConstraint( + solver->MakeLessOrEqual(distance_dimension->CumulVar(pickup_index), + distance_dimension->CumulVar(delivery_index))); + } + routing.SetPickupAndDeliveryPolicyOfAllVehicles( + RoutingModel::PICKUP_AND_DELIVERY_FIFO); + // [END pickup_delivery_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpGlobalSpan(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_pickup_delivery_lifo.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_pickup_delivery_lifo.cc new file mode 100644 index 0000000000000000000000000000000000000000..85d98e28cf8880d50cef5a0d286f56a953425017 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_pickup_delivery_lifo.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. + +// [START program] +// [START import] +#include + +#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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, + 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, + 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, + 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, + 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, + 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, + 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, + 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, + 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, + 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, + 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, + 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, + 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, + 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, + 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, + 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, + 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, + 194, 798, 0}, + }; + // [START pickups_deliveries] + const std::vector> + pickups_deliveries{ + {RoutingIndexManager::NodeIndex{1}, + RoutingIndexManager::NodeIndex{6}}, + {RoutingIndexManager::NodeIndex{2}, + RoutingIndexManager::NodeIndex{10}}, + {RoutingIndexManager::NodeIndex{4}, + RoutingIndexManager::NodeIndex{3}}, + {RoutingIndexManager::NodeIndex{5}, + RoutingIndexManager::NodeIndex{9}}, + {RoutingIndexManager::NodeIndex{7}, + RoutingIndexManager::NodeIndex{8}}, + {RoutingIndexManager::NodeIndex{15}, + RoutingIndexManager::NodeIndex{11}}, + {RoutingIndexManager::NodeIndex{13}, + RoutingIndexManager::NodeIndex{12}}, + {RoutingIndexManager::NodeIndex{16}, + RoutingIndexManager::NodeIndex{14}}, + }; + // [END pickups_deliveries] + const int num_vehicles = 4; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + int64 total_distance{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for Vehicle " << vehicle_id << ":"; + int64 route_distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + route_distance += routing.GetArcCostForVehicle(previous_index, index, + int64{vehicle_id}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << route_distance << "m"; + total_distance += route_distance; + } + LOG(INFO) << "Total distance of all routes: " << total_distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpGlobalSpan() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Define cost of each arc. + // [START arc_cost] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transit_callback_index, // transit callback + 0, // no slack + 3000, // vehicle maximum travel distance + true, // start cumul to zero + "Distance"); + RoutingDimension* distance_dimension = + routing.GetMutableDimension("Distance"); + distance_dimension->SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Define Transportation Requests. + // [START pickup_delivery_constraint] + Solver* const solver = routing.solver(); + for (const auto& request : data.pickups_deliveries) { + int64 pickup_index = manager.NodeToIndex(request[0]); + int64 delivery_index = manager.NodeToIndex(request[1]); + routing.AddPickupAndDelivery(pickup_index, delivery_index); + solver->AddConstraint(solver->MakeEquality( + routing.VehicleVar(pickup_index), routing.VehicleVar(delivery_index))); + solver->AddConstraint( + solver->MakeLessOrEqual(distance_dimension->CumulVar(pickup_index), + distance_dimension->CumulVar(delivery_index))); + } + routing.SetPickupAndDeliveryPolicyOfAllVehicles( + RoutingModel::PICKUP_AND_DELIVERY_LIFO); + // [END pickup_delivery_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpGlobalSpan(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_resources.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_resources.cc new file mode 100644 index 0000000000000000000000000000000000000000..85c38bc60d466a86c651b72fbe6d15831143087e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_resources.cc @@ -0,0 +1,222 @@ +// 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 "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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> time_matrix{ + {0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7}, + {6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14}, + {9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9}, + {8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16}, + {7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14}, + {3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8}, + {6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5}, + {2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10}, + {3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6}, + {2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5}, + {6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4}, + {6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10}, + {4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8}, + {4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6}, + {5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2}, + {9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9}, + {7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0}, + }; + const std::vector> time_windows{ + {0, 5}, // depot + {7, 12}, // 1 + {10, 15}, // 2 + {5, 14}, // 3 + {5, 13}, // 4 + {0, 5}, // 5 + {5, 10}, // 6 + {0, 10}, // 7 + {5, 10}, // 8 + {0, 5}, // 9 + {10, 16}, // 10 + {10, 15}, // 11 + {0, 5}, // 12 + {5, 10}, // 13 + {7, 12}, // 14 + {10, 15}, // 15 + {5, 15}, // 16 + }; + const int num_vehicles = 4; + // [START resources_data] + const int vehicle_load_time = 5; + const int vehicle_unload_time = 5; + const int depot_capacity = 2; + // [END resources_data] + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + const RoutingDimension& time_dimension = routing.GetDimensionOrDie("Time"); + int64 total_time{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for vehicle " << vehicle_id << ":"; + std::ostringstream route; + while (routing.IsEnd(index) == false) { + auto time_var = time_dimension.CumulVar(index); + route << manager.IndexToNode(index).value() << " Time(" + << solution.Min(time_var) << ", " << solution.Max(time_var) + << ") -> "; + index = solution.Value(routing.NextVar(index)); + } + auto time_var = time_dimension.CumulVar(index); + LOG(INFO) << route.str() << manager.IndexToNode(index).value() << " Time(" + << solution.Min(time_var) << ", " << solution.Max(time_var) + << ")"; + LOG(INFO) << "Time of the route: " << solution.Min(time_var) << "min"; + total_time += solution.Min(time_var); + } + LOG(INFO) << "Total time of all routes: " << total_time << "min"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpTimeWindows() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.time_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to time matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.time_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Time constraint. + // [START time_constraint] + std::string time{"Time"}; + routing.AddDimension(transit_callback_index, // transit callback index + int64{30}, // allow waiting time + int64{30}, // maximum time per vehicle + false, // Don't force start cumul to zero + time); + const RoutingDimension& time_dimension = routing.GetDimensionOrDie(time); + // Add time window constraints for each location except depot. + for (int i = 1; i < data.time_windows.size(); ++i) { + int64 index = manager.NodeToIndex(RoutingIndexManager::NodeIndex(i)); + time_dimension.CumulVar(index)->SetRange(data.time_windows[i].first, + data.time_windows[i].second); + } + // Add time window constraints for each vehicle start node. + for (int i = 0; i < data.num_vehicles; ++i) { + int64 index = routing.Start(i); + time_dimension.CumulVar(index)->SetRange(data.time_windows[0].first, + data.time_windows[0].second); + } + // [END time_constraint] + + // Add resource constraints at the depot. + // [START depot_load_time] + Solver* solver = routing.solver(); + std::vector intervals; + for (int i = 0; i < data.num_vehicles; ++i) { + // Add load duration at start of routes + intervals.push_back(solver->MakeFixedDurationIntervalVar( + time_dimension.CumulVar(routing.Start(i)), data.vehicle_load_time, + "depot_interval")); + // Add unload duration at end of routes. + intervals.push_back(solver->MakeFixedDurationIntervalVar( + time_dimension.CumulVar(routing.End(i)), data.vehicle_unload_time, + "depot_interval")); + } + // [END depot_load_time] + + // [START depot_capacity] + std::vector depot_usage(intervals.size(), 1); + solver->AddConstraint(solver->MakeCumulative(intervals, depot_usage, + data.depot_capacity, "depot")); + // [END depot_capacity] + + // Instantiate route start and end times to produce feasible times. + // [START depot_start_end_times] + for (int i = 0; i < data.num_vehicles; ++i) { + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.Start(i))); + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.End(i))); + } + // [END depot_start_end_times] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpTimeWindows(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_starts_ends.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_starts_ends.cc new file mode 100644 index 0000000000000000000000000000000000000000..fa7782e1efa48dfe86804ad0e616fd4c3c15e754 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_starts_ends.cc @@ -0,0 +1,175 @@ +// 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 "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" +// [END import] + +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> distance_matrix{ + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, + 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, + 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, + 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, + 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, + 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, + 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, + 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, + 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, + 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, + 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, + 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, + 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, + 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, + 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, + 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, + 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, + 194, 798, 0}, + }; + const int num_vehicles = 4; + // [START starts_ends] + const std::vector starts{ + RoutingIndexManager::NodeIndex{1}, + RoutingIndexManager::NodeIndex{2}, + RoutingIndexManager::NodeIndex{15}, + RoutingIndexManager::NodeIndex{16}, + }; + const std::vector ends{ + RoutingIndexManager::NodeIndex{0}, + RoutingIndexManager::NodeIndex{0}, + RoutingIndexManager::NodeIndex{0}, + RoutingIndexManager::NodeIndex{0}, + }; + // [END starts_ends] +}; +// [END data_model] + +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +// [START solution_printer] +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + int64 max_route_distance{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for Vehicle " << vehicle_id << ":"; + int64 route_distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + route_distance += routing.GetArcCostForVehicle(previous_index, index, + int64{vehicle_id}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << route_distance << "m"; + max_route_distance = std::max(route_distance, max_route_distance); + } + LOG(INFO) << "Maximum of the route distances: " << max_route_distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpStartsEnds() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles, + data.starts, data.ends); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.distance_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transit_callback_index, 0, 2000, + /*fix_start_cumul_to_zero=*/true, "Distance"); + routing.GetMutableDimension("Distance")->SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpStartsEnds(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_time_windows.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_time_windows.cc new file mode 100644 index 0000000000000000000000000000000000000000..40b3f1e0887b453cf480602aee4633a36f3d6193 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_time_windows.cc @@ -0,0 +1,197 @@ +// 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 "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" +// [END import] + +// [START program_part1] +namespace operations_research { +// [START data_model] +struct DataModel { + const std::vector> time_matrix{ + {0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7}, + {6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14}, + {9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9}, + {8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16}, + {7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14}, + {3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8}, + {6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5}, + {2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10}, + {3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6}, + {2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5}, + {6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4}, + {6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10}, + {4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8}, + {4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6}, + {5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2}, + {9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9}, + {7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0}, + }; + const std::vector> time_windows{ + {0, 5}, // depot + {7, 12}, // 1 + {10, 15}, // 2 + {16, 18}, // 3 + {10, 13}, // 4 + {0, 5}, // 5 + {5, 10}, // 6 + {0, 4}, // 7 + {5, 10}, // 8 + {0, 3}, // 9 + {10, 16}, // 10 + {10, 15}, // 11 + {0, 5}, // 12 + {5, 10}, // 13 + {7, 8}, // 14 + {10, 15}, // 15 + {11, 15}, // 16 + }; + const int num_vehicles = 4; + const RoutingIndexManager::NodeIndex depot{0}; +}; +// [END data_model] + +// [START solution_printer] +//! @brief Print the solution. +//! @param[in] data Data of the problem. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +void PrintSolution(const DataModel& data, const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + const RoutingDimension& time_dimension = routing.GetDimensionOrDie("Time"); + int64 total_time{0}; + for (int vehicle_id = 0; vehicle_id < data.num_vehicles; ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for vehicle " << vehicle_id << ":"; + std::ostringstream route; + while (routing.IsEnd(index) == false) { + auto time_var = time_dimension.CumulVar(index); + route << manager.IndexToNode(index).value() << " Time(" + << solution.Min(time_var) << ", " << solution.Max(time_var) + << ") -> "; + index = solution.Value(routing.NextVar(index)); + } + auto time_var = time_dimension.CumulVar(index); + LOG(INFO) << route.str() << manager.IndexToNode(index).value() << " Time(" + << solution.Min(time_var) << ", " << solution.Max(time_var) + << ")"; + LOG(INFO) << "Time of the route: " << solution.Min(time_var) << "min"; + total_time += solution.Min(time_var); + } + LOG(INFO) << "Total time of all routes: " << total_time << "min"; + LOG(INFO) << ""; + LOG(INFO) << "Advanced usage:"; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpTimeWindows() { + // Instantiate the data problem. + // [START data] + DataModel data; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager(data.time_matrix.size(), data.num_vehicles, + data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&data, &manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to time matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return data.time_matrix[from_node][to_node]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Time constraint. + // [START time_constraint] + std::string time{"Time"}; + routing.AddDimension(transit_callback_index, // transit callback index + int64{30}, // allow waiting time + int64{30}, // maximum time per vehicle + false, // Don't force start cumul to zero + time); + const RoutingDimension& time_dimension = routing.GetDimensionOrDie(time); + // Add time window constraints for each location except depot. + for (int i = 1; i < data.time_windows.size(); ++i) { + int64 index = manager.NodeToIndex(RoutingIndexManager::NodeIndex(i)); + time_dimension.CumulVar(index)->SetRange(data.time_windows[i].first, + data.time_windows[i].second); + } + // Add time window constraints for each vehicle start node. + for (int i = 0; i < data.num_vehicles; ++i) { + int64 index = routing.Start(i); + time_dimension.CumulVar(index)->SetRange(data.time_windows[0].first, + data.time_windows[0].second); + } + // [END time_constraint] + + // Instantiate route start and end times to produce feasible times. + // [START depot_start_end_times] + for (int i = 0; i < data.num_vehicles; ++i) { + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.Start(i))); + routing.AddVariableMinimizedByFinalizer( + time_dimension.CumulVar(routing.End(i))); + } + // [END depot_start_end_times] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters(); + searchParameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpTimeWindows(); + return EXIT_SUCCESS; +} +// [END program_part1] +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/vrp_with_time_limit.cc b/libs/or-tools-src-ubuntu/examples/cpp/vrp_with_time_limit.cc new file mode 100644 index 0000000000000000000000000000000000000000..9c62e1d3ee6ba5199704dcd2f151b7b0b8c2453e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/cpp/vrp_with_time_limit.cc @@ -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. + +// [START program] +// [START import] +#include +#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" +// [END import] + +namespace operations_research { +//! @brief Print the solution. +//! @param[in] manager Index manager used. +//! @param[in] routing Routing solver used. +//! @param[in] solution Solution found by the solver. +// [START solution_printer] +void PrintSolution(const RoutingIndexManager& manager, + const RoutingModel& routing, const Assignment& solution) { + int64 max_route_distance{0}; + for (int vehicle_id = 0; vehicle_id < manager.num_vehicles(); ++vehicle_id) { + int64 index = routing.Start(vehicle_id); + LOG(INFO) << "Route for Vehicle " << vehicle_id << ":"; + int64 route_distance{0}; + std::stringstream route; + while (routing.IsEnd(index) == false) { + route << manager.IndexToNode(index).value() << " -> "; + int64 previous_index = index; + index = solution.Value(routing.NextVar(index)); + route_distance += const_cast(routing).GetArcCostForVehicle( + previous_index, index, int64{vehicle_id}); + } + LOG(INFO) << route.str() << manager.IndexToNode(index).value(); + LOG(INFO) << "Distance of the route: " << route_distance << "m"; + max_route_distance = std::max(route_distance, max_route_distance); + } + LOG(INFO) << "Maximum of the route distances: " << max_route_distance << "m"; + LOG(INFO) << ""; + LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms"; +} +// [END solution_printer] + +void VrpGlobalSpan() { + // Instantiate the data problem. + // [START data] + const int num_locations = 20; + const int num_vehicles = 5; + const RoutingIndexManager::NodeIndex depot{0}; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager( + num_locations, + num_vehicles, + depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + const int transit_callback_index = routing.RegisterTransitCallback( + [&manager](int64 from_index, int64 to_index) -> int64 { + // Convert from routing variable Index to distance matrix NodeIndex. + auto from_node = manager.IndexToNode(from_index).value(); + auto to_node = manager.IndexToNode(to_index).value(); + return 1; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension( + transit_callback_index, + /*slack=*/0, + /*horizon=*/3000, + /*start_cumul_to_zero=*/true, + "Distance"); + const RoutingDimension& distance_dimension = + routing.GetDimensionOrDie("Distance"); + const_cast(distance_dimension) + .SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters search_parameters = DefaultRoutingSearchParameters(); + search_parameters.set_first_solution_strategy( + FirstSolutionStrategy::PATH_CHEAPEST_ARC); + search_parameters.set_local_search_metaheuristic( + LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH); + search_parameters.set_log_search(true); + search_parameters.mutable_time_limit()->set_seconds(10); + // [END parameters] + + // Solve the problem. + // [START solve] + const Assignment* solution = routing.SolveWithParameters(search_parameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(manager, routing, *solution); + // [END print_solution] +} +} // namespace operations_research + +int main(int argc, char** argv) { + operations_research::VrpGlobalSpan(); + return EXIT_SUCCESS; +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/cpp/weighted_tardiness_sat.cc b/libs/or-tools-src-ubuntu/examples/cpp/weighted_tardiness_sat.cc new file mode 100644 index 0000000000000000000000000000000000000000..88e45996a59975b8052971048900ce8d67f1f480 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/3_jugs_regular.cs b/libs/or-tools-src-ubuntu/examples/dotnet/3_jugs_regular.cs new file mode 100644 index 0000000000000000000000000000000000000000..e9febc5f344781632fb766bed5db8a238ebf2e24 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/3_jugs_regular.cs @@ -0,0 +1,293 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class ThreeJugsRegular +{ + + + /* + * Global constraint regular + * + * This is a translation of MiniZinc's regular constraint (defined in + * lib/zinc/globals.mzn), via the Comet code refered above. + * All comments are from the MiniZinc code. + * """ + * The sequence of values in array 'x' (which must all be in the range 1..S) + * is accepted by the DFA of 'Q' states with input 1..S and transition + * function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' + * (which must be in 1..Q) and accepting states 'F' (which all must be in + * 1..Q). We reserve state 0 to be an always failing state. + * """ + * + * x : IntVar array + * Q : number of states + * S : input_max + * d : transition matrix + * q0: initial state + * F : accepting states + * + */ + static void MyRegular(Solver solver, + IntVar[] x, + int Q, + int S, + int[,] d, + int q0, + int[] F) { + + + + Debug.Assert(Q > 0, "regular: 'Q' must be greater than zero"); + Debug.Assert(S > 0, "regular: 'S' must be greater than zero"); + + // d2 is the same as d, except we add one extra transition for + // each possible input; each extra transition is from state zero + // to state zero. This allows us to continue even if we hit a + // non-accepted input. + int[][] d2 = new int[Q+1][]; + for(int i = 0; i <= Q; i++) { + int[] row = new int[S]; + for(int j = 0; j < S; j++) { + if (i == 0) { + row[j] = 0; + } else { + row[j] = d[i-1,j]; + } + } + d2[i] = row; + } + + int[] d2_flatten = (from i in Enumerable.Range(0, Q+1) + from j in Enumerable.Range(0, S) + select d2[i][j]).ToArray(); + + // If x has index set m..n, then a[m-1] holds the initial state + // (q0), and a[i+1] holds the state we're in after processing + // x[i]. If a[n] is in F, then we succeed (ie. accept the + // string). + int m = 0; + int n = x.Length; + + IntVar[] a = solver.MakeIntVarArray(n+1-m, 0,Q+1, "a"); + // Check that the final state is in F + solver.Add(a[a.Length-1].Member(F)); + // First state is q0 + solver.Add(a[m] == q0); + + for(int i = 0; i < n; i++) { + solver.Add(x[i] >= 1); + solver.Add(x[i] <= S); + // Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == d2_flatten.Element(((a[i]*S)+(x[i]-1)))); + + } + + } + + + + + /** + * + * 3 jugs problem using regular constraint in Google CP Solver. + * + * A.k.a. water jugs problem. + * + * Problem from Taha 'Introduction to Operations Research', + * page 245f . + * + * For more info about the problem, see: + * http://mathworld.wolfram.com/ThreeJugProblem.html + * + * This model use a regular constraint for handling the + * transitions between the states. Instead of minimizing + * the cost in a cost matrix (as shortest path problem), + * we here call the model with increasing length of the + * sequence array (x). + * + * + * Also see http://www.hakank.org/or-tools/3_jugs_regular.py + * + */ + private static bool Solve(int n) + { + Solver solver = new Solver("ThreeJugProblem"); + + // + // Data + // + + // the DFA (for regular) + int n_states = 14; + int input_max = 15; + int initial_state = 1; // state 0 is for the failing state + int[] accepting_states = {15}; + + // + // Manually crafted DFA + // (from the adjacency matrix used in the other models) + // + /* + int[,] transition_fn = { + // 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + {0, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0}, // 1 + {0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 2 + {0, 0, 0, 4, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0}, // 3 + {0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 4 + {0, 0, 0, 0, 0, 6, 0, 0, 9, 0, 0, 0, 0, 0, 0}, // 5 + {0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0}, // 6 + {0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0}, // 7 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15}, // 8 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0}, // 9 + {0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0}, // 10 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0}, // 11 + {0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0}, // 12 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0}, // 13 + {0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15}, // 14 + // 15 + }; + */ + + // + // However, the DFA is easy to create from adjacency lists. + // + int[][] states = { + new int[] {2,9}, // state 1 + new int[] {3}, // state 2 + new int[] {4, 9}, // state 3 + new int[] {5}, // state 4 + new int[] {6,9}, // state 5 + new int[] {7}, // state 6 + new int[] {8,9}, // state 7 + new int[] {15}, // state 8 + new int[] {10}, // state 9 + new int[] {11}, // state 10 + new int[] {12}, // state 11 + new int[] {13}, // state 12 + new int[] {14}, // state 13 + new int[] {15} // state 14 + }; + + int[,] transition_fn = new int[n_states,input_max]; + for(int i = 0; i < n_states; i++) { + for(int j = 1; j <= input_max; j++) { + bool in_states = false; + for(int s = 0; s < states[i].Length; s++) { + if (j == states[i][s]) { + in_states = true; + break; + } + } + if (in_states) { + transition_fn[i,j-1] = j; + } else { + transition_fn[i,j-1] = 0; + } + } + } + + // + // The name of the nodes, for printing + // the solution. + // + string[] nodes = { + "8,0,0", // 1 start + "5,0,3", // 2 + "5,3,0", // 3 + "2,3,3", // 4 + "2,5,1", // 5 + "7,0,1", // 6 + "7,1,0", // 7 + "4,1,3", // 8 + "3,5,0", // 9 + "3,2,3", // 10 + "6,2,0", // 11 + "6,0,2", // 12 + "1,5,2", // 13 + "1,4,3", // 14 + "4,4,0" // 15 goal + }; + + // + // Decision variables + // + + // Note: We use 1..2 (instead of 0..1) and subtract 1 in the solution + IntVar[] x = solver.MakeIntVarArray(n, 1, input_max, "x"); + + + // + // Constraints + // + MyRegular(solver, x, n_states, input_max, transition_fn, + initial_state, accepting_states); + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + bool found = false; + while (solver.NextSolution()) { + Console.WriteLine("\nFound a solution of length {0}", n+1); + int[] x_val = new int[n]; + x_val[0] = 1; + Console.WriteLine("{0} -> {1}", nodes[0], nodes[x_val[0]]); + for(int i = 1; i < n; i++) { + // Note: here we subtract 1 to get 0..1 + int val = (int)x[i].Value()-1; + x_val[i] = val; + Console.WriteLine("{0} -> {1}", nodes[x_val[i-1]], nodes[x_val[i]]); + } + Console.WriteLine(); + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + found = true; + + } + + solver.EndSearch(); + + return found; + + } + + public static void Main(String[] args) + { + + for(int n = 1; n < 15; n++) { + bool found = Solve(n); + if (found) { + break; + } + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/AssignmentSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/AssignmentSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..a102f788d0344d452474311299b3af43cb029088 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/AssignmentSat.cs @@ -0,0 +1,123 @@ +// 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] +using System; +using Google.OrTools.Sat; +// [END import] + +public class AssignmentSat +{ + static void Main() + { + // Data. + // [START data_model] + int[,] costs = { + {90, 80, 75, 70}, + {35, 85, 55, 65}, + {125, 95, 90, 95}, + {45, 110, 95, 115}, + {50, 100, 90, 100}, + }; + int numWorkers = costs.GetLength(0); + int numTasks = costs.GetLength(1); + // [END data_model] + + // Model. + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Variables. + // [START variables] + IntVar[,] x = new IntVar[numWorkers, numTasks]; + // Variables in a 1-dim array. + IntVar[] xFlat = new IntVar[numWorkers * numTasks]; + int[] costsFlat = new int[numWorkers * numTasks]; + for (int i = 0; i < numWorkers; ++i) + { + for (int j = 0; j < numTasks; ++j) + { + x[i, j] = model.NewIntVar(0, 1, $"worker_{i}_task_{j}"); + int k = i * numTasks + j; + xFlat[k] = x[i, j]; + costsFlat[k] = costs[i, j]; + } + } + // [END variables] + + // Constraints + // [START constraints] + // Each worker is assigned to at most one task. + for (int i = 0; i < numWorkers; ++i) + { + IntVar[] vars = new IntVar[numTasks]; + for (int j = 0; j < numTasks; ++j) + { + vars[j] = x[i, j]; + } + model.Add(LinearExpr.Sum(vars) <= 1); + } + + // Each task is assigned to exactly one worker. + for (int j = 0; j < numTasks; ++j) + { + IntVar[] vars = new IntVar[numWorkers]; + for (int i = 0; i < numWorkers; ++i) + { + vars[i] = x[i, j]; + } + model.Add(LinearExpr.Sum(vars) == 1); + } + // [END constraints] + + // Objective + // [START objective] + model.Minimize(LinearExpr.ScalProd(xFlat, costsFlat)); + // [END objective] + + // Solve + // [START solve] + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + Console.WriteLine($"Solve status: {status}"); + // [END solve] + + // Print solution. + // [START print_solution] + // Check that the problem has a feasible solution. + if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) { + Console.WriteLine($"Total cost: {solver.ObjectiveValue}\n"); + for (int i = 0; i < numWorkers; ++i) + { + for (int j = 0; j < numTasks; ++j) + { + if (solver.Value(x[i, j]) > 0.5) + { + Console.WriteLine($"Worker {i} assigned to task {j}. Cost: {costs[i, j]}"); + } + } + } + } else { + Console.WriteLine("No solution found."); + } + // [END print_solution] + + Console.WriteLine("Statistics"); + Console.WriteLine($" - conflicts : {solver.NumConflicts()}"); + Console.WriteLine($" - branches : {solver.NumBranches()}"); + Console.WriteLine($" - wall time : {solver.WallTime()}s"); + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/BalanceGroupSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/BalanceGroupSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..54adcc908e246678a6dc58466a6e5018b9a68b91 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/BinPackingProblemSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/BinPackingProblemSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..95d01a41d9e76a5b67daf264ea663219ae1d7a2e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/BinPackingProblemSat.cs @@ -0,0 +1,123 @@ +// 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.Sat; + +public class BinPackingProblemSat +{ + static void Main() + { + // Data. + int bin_capacity = 100; + int slack_capacity = 20; + int num_bins = 5; + + int[,] items = new int[,] { { 20, 6 }, { 15, 6 }, { 30, 4 }, { 45, 3 } }; + int num_items = items.GetLength(0); + + // Model. + CpModel model = new CpModel(); + + // Main variables. + IntVar[,] x = new IntVar[num_items, num_bins]; + for (int i = 0; i < num_items; ++i) + { + int num_copies = items[i, 1]; + for (int b = 0; b < num_bins; ++b) + { + x[i, b] = model.NewIntVar(0, num_copies, String.Format("x_{0}_{1}", i, b)); + } + } + + // Load variables. + IntVar[] load = new IntVar[num_bins]; + for (int b = 0; b < num_bins; ++b) + { + load[b] = model.NewIntVar(0, bin_capacity, String.Format("load_{0}", b)); + } + + // Slack variables. + IntVar[] slacks = new IntVar[num_bins]; + for (int b = 0; b < num_bins; ++b) + { + slacks[b] = model.NewBoolVar(String.Format("slack_{0}", b)); + } + + // Links load and x. + int[] sizes = new int[num_items]; + for (int i = 0; i < num_items; ++i) + { + sizes[i] = items[i, 0]; + } + for (int b = 0; b < num_bins; ++b) + { + IntVar[] tmp = new IntVar[num_items]; + for (int i = 0; i < num_items; ++i) + { + tmp[i] = x[i, b]; + } + model.Add(load[b] == LinearExpr.ScalProd(tmp, sizes)); + } + + // Place all items. + for (int i = 0; i < num_items; ++i) + { + IntVar[] tmp = new IntVar[num_bins]; + for (int b = 0; b < num_bins; ++b) + { + tmp[b] = x[i, b]; + } + model.Add(LinearExpr.Sum(tmp) == items[i, 1]); + } + + // Links load and slack. + int safe_capacity = bin_capacity - slack_capacity; + for (int b = 0; b < num_bins; ++b) + { + // slack[b] => load[b] <= safe_capacity. + model.Add(load[b] <= safe_capacity).OnlyEnforceIf(slacks[b]); + // not(slack[b]) => load[b] > safe_capacity. + model.Add(load[b] > safe_capacity).OnlyEnforceIf(slacks[b].Not()); + } + + // Maximize sum of slacks. + model.Maximize(LinearExpr.Sum(slacks)); + + // Solves and prints out the solution. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + Console.WriteLine(String.Format("Solve status: {0}", status)); + if (status == CpSolverStatus.Optimal) { + Console.WriteLine(String.Format("Optimal objective value: {0}", + solver.ObjectiveValue)); + for (int b = 0; b < num_bins; ++b) + { + Console.WriteLine(String.Format("load_{0} = {1}", + b, solver.Value(load[b]))); + for (int i = 0; i < num_items; ++i) + { + Console.WriteLine(string.Format(" item_{0}_{1} = {2}", + i, b, solver.Value(x[i, b]))); + } + } + } + Console.WriteLine("Statistics"); + Console.WriteLine(String.Format(" - conflicts : {0}", + solver.NumConflicts())); + Console.WriteLine(String.Format(" - branches : {0}", + solver.NumBranches())); + Console.WriteLine(String.Format(" - wall time : {0} s", + solver.WallTime())); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/BoolOrSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/BoolOrSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..27a2db0feb5e063ae638c90d8416b67bbc8f8e74 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/BoolOrSampleSat.cs @@ -0,0 +1,28 @@ +// 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.Sat; + +public class BoolOrSampleSat +{ + static void Main() + { + CpModel model = new CpModel(); + + IntVar x = model.NewBoolVar("x"); + IntVar y = model.NewBoolVar("y"); + + model.AddBoolOr(new ILiteral[] { x, y.Not() }); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/ChannelingSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/ChannelingSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..288741fa2048579ec929586aea5549902c821403 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/ChannelingSampleSat.cs @@ -0,0 +1,79 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using Google.OrTools.Sat; +using Google.OrTools.Util; + +public class VarArraySolutionPrinter : CpSolverSolutionCallback +{ + public VarArraySolutionPrinter(IntVar[] variables) + { + variables_ = variables; + } + + public override void OnSolutionCallback() + { + { + foreach (IntVar v in variables_) + { + Console.Write(String.Format("{0}={1} ", v.ShortString(), Value(v))); + } + Console.WriteLine(); + } + } + + private IntVar[] variables_; +} + +public class ChannelingSampleSat +{ + static void Main() + { + // Create the CP-SAT model. + CpModel model = new CpModel(); + + // Declare our two primary variables. + IntVar x = model.NewIntVar(0, 10, "x"); + IntVar y = model.NewIntVar(0, 10, "y"); + + // Declare our intermediate boolean variable. + IntVar b = model.NewBoolVar("b"); + + // Implement b == (x >= 5). + model.Add(x >= 5).OnlyEnforceIf(b); + model.Add(x < 5).OnlyEnforceIf(b.Not()); + + // Create our two half-reified constraints. + // First, b implies (y == 10 - x). + model.Add(y == 10 - x).OnlyEnforceIf(b); + // Second, not(b) implies y == 0. + model.Add(y == 0).OnlyEnforceIf(b.Not()); + + // Search for x values in increasing order. + model.AddDecisionStrategy( + new IntVar[] {x}, + DecisionStrategyProto.Types.VariableSelectionStrategy.ChooseFirst, + DecisionStrategyProto.Types.DomainReductionStrategy.SelectMinValue); + + // Create the solver. + CpSolver solver = new CpSolver(); + + // Force solver to follow the decision strategy exactly. + solver.StringParameters = "search_branching:FIXED_SEARCH"; + + VarArraySolutionPrinter cb = + new VarArraySolutionPrinter(new IntVar[] {x, y, b}); + solver.SearchAllSolutions(model, cb); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/CpIsFunSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/CpIsFunSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..e905c1e4787897fb1acfb5581aa6bc4fe98320b8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/CpIsFunSat.cs @@ -0,0 +1,99 @@ +// 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] +using System; +using Google.OrTools.Sat; + +// [START solution_printing] +public class VarArraySolutionPrinter : CpSolverSolutionCallback +{ + public VarArraySolutionPrinter(IntVar[] variables) + { + variables_ = variables; + } + + public override void OnSolutionCallback() + { + { + foreach (IntVar v in variables_) + { + Console.Write( + String.Format(" {0}={1}", v.ShortString(), Value(v))); + } + Console.WriteLine(); + solution_count_++; + } + } + + public int SolutionCount() + { + return solution_count_; + } + + private int solution_count_; + private IntVar[] variables_; +} +// [END solution_printing] + +public class CpIsFunSat +{ + // Solve the CP+IS+FUN==TRUE cryptarithm. + static void Main() + { + // Constraint programming engine + CpModel model = new CpModel(); + + // [START variables] + int kBase = 10; + + IntVar c = model.NewIntVar(1, kBase - 1, "C"); + IntVar p = model.NewIntVar(0, kBase - 1, "P"); + IntVar i = model.NewIntVar(1, kBase - 1, "I"); + IntVar s = model.NewIntVar(0, kBase - 1, "S"); + IntVar f = model.NewIntVar(1, kBase - 1, "F"); + IntVar u = model.NewIntVar(0, kBase - 1, "U"); + IntVar n = model.NewIntVar(0, kBase - 1, "N"); + IntVar t = model.NewIntVar(1, kBase - 1, "T"); + IntVar r = model.NewIntVar(0, kBase - 1, "R"); + IntVar e = model.NewIntVar(0, kBase - 1, "E"); + + // We need to group variables in a list to use the constraint AllDifferent. + IntVar[] letters = new IntVar[] {c, p, i, s, f, u, n, t, r, e}; + // [END variables] + + // [START constraints] + // Define constraints. + model.AddAllDifferent(letters); + + // CP + IS + FUN = TRUE + model.Add(c * kBase + p + i * kBase + s + f * kBase * kBase + u * kBase + n == + t * kBase * kBase * kBase + r * kBase * kBase + u * kBase + e); + // [END constraints] + + // [START solve] + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + VarArraySolutionPrinter cb = new VarArraySolutionPrinter(letters); + solver.SearchAllSolutions(model, cb); + // [END solve] + + Console.WriteLine("Statistics"); + Console.WriteLine($" - conflicts : {solver.NumConflicts()}"); + Console.WriteLine($" - branches : {solver.NumBranches()}"); + Console.WriteLine($" - wall time : {solver.WallTime()} s"); + Console.WriteLine($" - number of solutions found: {cb.SolutionCount()}"); + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/EarlinessTardinessCostSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/EarlinessTardinessCostSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..4aee3a649268dbf66e28088cfc1e84ec5a49dfa6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/EarlinessTardinessCostSampleSat.cs @@ -0,0 +1,94 @@ +// 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.Sat; +using Google.OrTools.Util; + +public class VarArraySolutionPrinter : CpSolverSolutionCallback +{ + public VarArraySolutionPrinter(IntVar[] variables) + { + variables_ = variables; + } + + public override void OnSolutionCallback() + { + { + foreach (IntVar v in variables_) + { + Console.Write(String.Format("{0}={1} ", v.ShortString(), Value(v))); + } + Console.WriteLine(); + } + } + + private IntVar[] variables_; +} + +public class EarlinessTardinessCostSampleSat +{ + static void Main() + { + long earliness_date = 5; + long earliness_cost = 8; + long lateness_date = 15; + long lateness_cost = 12; + + // Create the CP-SAT model. + CpModel model = new CpModel(); + + // Declare our primary variable. + IntVar x = model.NewIntVar(0, 20, "x"); + + // Create the expression variable and implement the piecewise linear + // function. + // + // \ / + // \______/ + // ed ld + // + long large_constant = 1000; + IntVar expr = model.NewIntVar(0, large_constant, "expr"); + + // First segment. + IntVar s1 = model.NewIntVar(-large_constant, large_constant, "s1"); + model.Add(s1 == earliness_cost * (earliness_date - x)); + + // Second segment. + IntVar s2 = model.NewConstant(0); + + // Third segment. + IntVar s3 = model.NewIntVar(-large_constant, large_constant, "s3"); + model.Add(s3 == lateness_cost * (x - lateness_date)); + + // Link together expr and x through s1, s2, and s3. + model.AddMaxEquality(expr, new IntVar[] {s1, s2, s3}); + + // Search for x values in increasing order. + model.AddDecisionStrategy( + new IntVar[] {x}, + DecisionStrategyProto.Types.VariableSelectionStrategy.ChooseFirst, + DecisionStrategyProto.Types.DomainReductionStrategy.SelectMinValue); + + // Create the solver. + CpSolver solver = new CpSolver(); + + // Force solver to follow the decision strategy exactly. + solver.StringParameters = "search_branching:FIXED_SEARCH"; + + VarArraySolutionPrinter cb = + new VarArraySolutionPrinter(new IntVar[] {x, expr}); + solver.SearchAllSolutions(model, cb); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/GateSchedulingSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/GateSchedulingSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..3f7d90190e86aa39d8e8a9e49f83345fdf07e057 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/IntervalSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/IntervalSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..a61533e1ca00a5f97440c6f546b19d3c754d4fab --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/IntervalSampleSat.cs @@ -0,0 +1,30 @@ +// 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.Sat; + +public class IntervalSampleSat +{ + static void Main() + { + CpModel model = new CpModel(); + int horizon = 100; + IntVar start_var = model.NewIntVar(0, horizon, "start"); + // C# code supports IntVar or integer constants in intervals. + int duration = 10; + IntVar end_var = model.NewIntVar(0, horizon, "end"); + IntervalVar interval = + model.NewIntervalVar(start_var, duration, end_var, "interval"); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/JobshopFt06Sat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/JobshopFt06Sat.cs new file mode 100644 index 0000000000000000000000000000000000000000..6acfc7acc205d600f186514c6656aa9082498301 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/JobshopSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/JobshopSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..395096446c77e7da087deeacbd5fdecf02d4f4dd --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/Knapsack.cs b/libs/or-tools-src-ubuntu/examples/dotnet/Knapsack.cs new file mode 100644 index 0000000000000000000000000000000000000000..9422119efe83a11af16512cc937eb6382aaa9085 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/Knapsack.cs @@ -0,0 +1,58 @@ +// 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] +using System; +using Google.OrTools.Algorithms; +// [END import] + +public class Knapsack +{ + static void Main() + { + // [START solver] + KnapsackSolver solver = new KnapsackSolver( + KnapsackSolver.SolverType.KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, + "KnapsackExample"); + // [END solver] + + // [START data] + long[] 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 }; + + 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 }; + // [END data] + + // [START solve] + solver.Init(values, weights, capacities); + long computedValue = solver.Solve(); + // [END solve] + + // [START print_solution] + Console.WriteLine("Optimal Value = " + computedValue); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/LiteralSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/LiteralSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..d6828989b2b8e838b08f1d00960eaad7bf453020 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/LiteralSampleSat.cs @@ -0,0 +1,25 @@ +// 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.Sat; + +public class LiteralSampleSat +{ + static void Main() + { + CpModel model = new CpModel(); + IntVar x = model.NewBoolVar("x"); + ILiteral not_x = x.Not(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/NetworkRoutingSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/NetworkRoutingSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..48a4a1a64b1ee97eb9436d7f293e2211f9584887 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/NoOverlapSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/NoOverlapSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..abaecc3f50d5226b847f4d662d4833fa8b9eae72 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/NoOverlapSampleSat.cs @@ -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. + +using System; +using Google.OrTools.Sat; + +public class NoOverlapSampleSat +{ + static void Main() + { + CpModel model = new CpModel(); + // Three weeks. + int horizon = 21; + + // Task 0, duration 2. + IntVar start_0 = model.NewIntVar(0, horizon, "start_0"); + int duration_0 = 2; + IntVar end_0 = model.NewIntVar(0, horizon, "end_0"); + IntervalVar task_0 = + model.NewIntervalVar(start_0, duration_0, end_0, "task_0"); + + // Task 1, duration 4. + IntVar start_1 = model.NewIntVar(0, horizon, "start_1"); + int duration_1 = 4; + IntVar end_1 = model.NewIntVar(0, horizon, "end_1"); + IntervalVar task_1 = + model.NewIntervalVar(start_1, duration_1, end_1, "task_1"); + + // Task 2, duration 3. + IntVar start_2 = model.NewIntVar(0, horizon, "start_2"); + int duration_2 = 3; + IntVar end_2 = model.NewIntVar(0, horizon, "end_2"); + IntervalVar task_2 = + model.NewIntervalVar(start_2, duration_2, end_2, "task_2"); + + // Weekends. + IntervalVar weekend_0 = model.NewIntervalVar(5, 2, 7, "weekend_0"); + IntervalVar weekend_1 = model.NewIntervalVar(12, 2, 14, "weekend_1"); + IntervalVar weekend_2 = model.NewIntervalVar(19, 2, 21, "weekend_2"); + + // No Overlap constraint. + model.AddNoOverlap(new IntervalVar[] {task_0, task_1, task_2, weekend_0, + weekend_1, weekend_2}); + + // Makespan objective. + IntVar obj = model.NewIntVar(0, horizon, "makespan"); + model.AddMaxEquality(obj, new IntVar[] {end_0, end_1, end_2}); + model.Minimize(obj); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Optimal) + { + Console.WriteLine("Optimal Schedule Length: " + solver.ObjectiveValue); + Console.WriteLine("Task 0 starts at " + solver.Value(start_0)); + Console.WriteLine("Task 1 starts at " + solver.Value(start_1)); + Console.WriteLine("Task 2 starts at " + solver.Value(start_2)); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/NursesSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/NursesSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..727ba7eeb71b9c6786b63c06b2be046a5bdeba6a --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/OptionalIntervalSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/OptionalIntervalSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..af32991bdf04d8ee72abf8471f7572098d130b52 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/OptionalIntervalSampleSat.cs @@ -0,0 +1,31 @@ +// 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.Sat; + +public class OptionalIntervalSampleSat +{ + static void Main() + { + CpModel model = new CpModel(); + int horizon = 100; + IntVar start_var = model.NewIntVar(0, horizon, "start"); + // C# code supports IntVar or integer constants in intervals. + int duration = 10; + IntVar end_var = model.NewIntVar(0, horizon, "end"); + IntVar presence_var = model.NewBoolVar("presence"); + IntervalVar interval = model.NewOptionalIntervalVar( + start_var, duration, end_var, presence_var, "interval"); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/README.md b/libs/or-tools-src-ubuntu/examples/dotnet/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2e341c0caf79bdf5c622e38ecbaab53274b749b7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/RabbitsAndPheasantsSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/RabbitsAndPheasantsSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..481a1b615ffb4ad43b1f6be90ad0da862b7910a7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/RabbitsAndPheasantsSat.cs @@ -0,0 +1,41 @@ +// 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.Sat; + +public class RabbitsAndPheasantsSat +{ + static void Main() + { + // Creates the model. + CpModel model = new CpModel(); + // Creates the variables. + IntVar r = model.NewIntVar(0, 100, "r"); + IntVar p = model.NewIntVar(0, 100, "p"); + // 20 heads. + model.Add(r + p == 20); + // 56 legs. + model.Add(4 * r + 2 * p == 56); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Feasible) + { + Console.WriteLine(solver.Value(r) + " rabbits, and " + + solver.Value(p) + " pheasants"); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/RankingSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/RankingSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..246af0ce910efe4c5b4907da87c3461aed22817e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/RankingSampleSat.cs @@ -0,0 +1,158 @@ +// 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.Sat; + +public class RankingSampleSat +{ + static void RankTasks(CpModel model, + IntVar[] starts, + ILiteral[] presences, + IntVar[] ranks) { + int num_tasks = starts.Length; + + // Creates precedence variables between pairs of intervals. + ILiteral[,] precedences = new ILiteral[num_tasks, num_tasks]; + for (int i = 0; i < num_tasks; ++i) { + for (int j = 0; j < num_tasks; ++j) { + if (i == j) { + precedences[i, i] = presences[i]; + } else { + IntVar prec = model.NewBoolVar(String.Format("{0} before {1}", i, j)); + precedences[i, j] = prec; + model.Add(starts[i] < starts[j]).OnlyEnforceIf(prec); + } + } + } + + // Treats optional intervals. + for (int i = 0; i < num_tasks - 1; ++i) { + for (int j = i + 1; j < num_tasks; ++j) { + List tmp_array = new List(); + tmp_array.Add(precedences[i, j]); + tmp_array.Add(precedences[j, i]); + tmp_array.Add(presences[i].Not()); + // Makes sure that if i is not performed, all precedences are false. + model.AddImplication(presences[i].Not(), precedences[i, j].Not()); + model.AddImplication(presences[i].Not(), precedences[j, i].Not()); + tmp_array.Add(presences[j].Not()); + // Makes sure that if j is not performed, all precedences are false. + model.AddImplication(presences[j].Not(), precedences[i, j].Not()); + model.AddImplication(presences[j].Not(), precedences[j, i].Not()); + // The following bool_or will enforce that for any two intervals: + // i precedes j or j precedes i or at least one interval is not + // performed. + model.AddBoolOr(tmp_array); + // Redundant constraint: it propagates early that at most one precedence + // is true. + model.AddImplication(precedences[i, j], precedences[j, i].Not()); + model.AddImplication(precedences[j, i], precedences[i, j].Not()); + } + } + + // Links precedences and ranks. + for (int i = 0; i < num_tasks; ++i) { + IntVar[] tmp_array = new IntVar[num_tasks]; + for (int j = 0; j < num_tasks; ++j) { + tmp_array[j] = (IntVar)precedences[j, i]; + } + model.Add(ranks[i] == LinearExpr.Sum(tmp_array) - 1); + } + } + + static void Main() + { + CpModel model = new CpModel(); + // Three weeks. + int horizon = 100; + int num_tasks = 4; + + IntVar[] starts = new IntVar[num_tasks]; + IntVar[] ends = new IntVar[num_tasks]; + IntervalVar[] intervals = new IntervalVar[num_tasks]; + ILiteral[] presences = new ILiteral[num_tasks]; + IntVar[] ranks = new IntVar[num_tasks]; + + IntVar true_var = model.NewConstant(1); + + // Creates intervals, half of them are optional. + for (int t = 0; t < num_tasks; ++t) { + starts[t] = model.NewIntVar(0, horizon, String.Format("start_{0}", t)); + int duration = t + 1; + ends[t] = model.NewIntVar(0, horizon, String.Format("end_{0}", t)); + if (t < num_tasks / 2) { + intervals[t] = model.NewIntervalVar(starts[t], duration, ends[t], + String.Format("interval_{0}", t)); + presences[t] = true_var; + } else { + presences[t] = model.NewBoolVar(String.Format("presence_{0}", t)); + intervals[t] = model.NewOptionalIntervalVar( + starts[t], duration, ends[t], presences[t], + String.Format("o_interval_{0}", t)); + } + + // Ranks = -1 if and only if the tasks is not performed. + ranks[t] = + model.NewIntVar(-1, num_tasks - 1, String.Format("rank_{0}", t)); + } + + // Adds NoOverlap constraint. + model.AddNoOverlap(intervals); + + // Adds ranking constraint. + RankTasks(model, starts, presences, ranks); + + // Adds a constraint on ranks. + model.Add(ranks[0] < ranks[1]); + + // Creates makespan variable. + IntVar makespan = model.NewIntVar(0, horizon, "makespan"); + for (int t = 0; t < num_tasks; ++t) { + model.Add(ends[t] <= makespan).OnlyEnforceIf(presences[t]); + } + // Minimizes makespan - fixed gain per tasks performed. + // As the fixed cost is less that the duration of the last interval, + // the solver will not perform the last interval. + IntVar[] presences_as_int_vars = new IntVar[num_tasks]; + for (int t = 0; t < num_tasks; ++t) { + presences_as_int_vars[t] = (IntVar)presences[t]; + } + model.Minimize(2 * makespan - 7 * LinearExpr.Sum(presences_as_int_vars)); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Optimal) { + Console.WriteLine(String.Format("Optimal cost: {0}", + solver.ObjectiveValue)); + Console.WriteLine(String.Format("Makespan: {0}", solver.Value(makespan))); + for (int t = 0; t < num_tasks; ++t) { + if (solver.BooleanValue(presences[t])) { + Console.WriteLine(String.Format( + "Task {0} starts at {1} with rank {2}", + t, solver.Value(starts[t]), solver.Value(ranks[t]))); + } else { + Console.WriteLine(String.Format( + "Task {0} in not performed and ranked at {1}", t, + solver.Value(ranks[t]))); + } + } + } else { + Console.WriteLine( + String.Format("Solver exited with nonoptimal status: {0}", status)); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/ReifiedSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/ReifiedSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..1307557e75bdc1f2f92000f3229b213b4fa569c8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/ReifiedSampleSat.cs @@ -0,0 +1,38 @@ +// 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.Sat; + +public class ReifiedSampleSat +{ + static void Main() + { + CpModel model = new CpModel(); + + IntVar x = model.NewBoolVar("x"); + IntVar y = model.NewBoolVar("y"); + IntVar b = model.NewBoolVar("b"); + + // First version using a half-reified bool and. + model.AddBoolAnd(new ILiteral[] {x, y.Not()}).OnlyEnforceIf(b); + + // Second version using implications. + model.AddImplication(b, x); + model.AddImplication(b, y.Not()); + + // Third version using bool or. + model.AddBoolOr(new ILiteral[] {b.Not(), x}); + model.AddBoolOr(new ILiteral[] {b.Not(), y.Not()}); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/SearchForAllSolutionsSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/SearchForAllSolutionsSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..3d0b5b6275246c7b815742252db17979f8fb121e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/SearchForAllSolutionsSampleSat.cs @@ -0,0 +1,85 @@ +// 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] +using System; +using Google.OrTools.Sat; + +// [START print_solution] +public class VarArraySolutionPrinter : CpSolverSolutionCallback +{ + public VarArraySolutionPrinter(IntVar[] variables) + { + variables_ = variables; + } + + public override void OnSolutionCallback() + { + { + Console.WriteLine(String.Format("Solution #{0}: time = {1:F2} s", + solution_count_, WallTime())); + foreach (IntVar v in variables_) + { + Console.WriteLine( + String.Format(" {0} = {1}", v.ShortString(), Value(v))); + } + solution_count_++; + } + } + + public int SolutionCount() + { + return solution_count_; + } + + private int solution_count_; + private IntVar[] variables_; +} +// [END print_solution] + +public class SearchForAllSolutionsSampleSat +{ + static void Main() + { + // Creates the model. + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Creates the variables. + // [START variables] + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + // [END variables] + + // Adds a different constraint. + // [START constraints] + model.Add(x != y); + // [END constraints] + + // Creates a solver and solves the model. + // [START solve] + CpSolver solver = new CpSolver(); + VarArraySolutionPrinter cb = + new VarArraySolutionPrinter(new IntVar[] { x, y, z }); + solver.SearchAllSolutions(model, cb); + // [END solve] + + Console.WriteLine(String.Format("Number of solutions found: {0}", + cb.SolutionCount())); + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/ShiftSchedulingSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/ShiftSchedulingSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..3f11d18bd6ce5f0d98f61091865deb268f2603ac --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/SimpleCpProgram.cs b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleCpProgram.cs new file mode 100644 index 0000000000000000000000000000000000000000..f5a832fca37aba90b16a4365a4f785c6602b0e46 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleCpProgram.cs @@ -0,0 +1,71 @@ +// 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] +using System; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// This is a simple CP program. +/// +public class SimpleCpProgram { + public static void Main(String[] args) { + // Instantiate the solver. + // [START solver] + Solver solver = new Solver("CpSimple"); + // [END solver] + + // Create the variables. + // [START variables] + const long numVals = 3; + IntVar x = solver.MakeIntVar(0, numVals - 1, "x"); + IntVar y = solver.MakeIntVar(0, numVals - 1, "y"); + IntVar z = solver.MakeIntVar(0, numVals - 1, "z"); + // [END variables] + + // Constraint 0: x != y.. + // [START constraints] + solver.Add(solver.MakeAllDifferent(new IntVar[]{x, y})); + Console.WriteLine($"Number of constraints: {solver.Constraints()}"); + // [END constraints] + + // Solve the problem. + // [START solve] + DecisionBuilder db = solver.MakePhase( + new IntVar[]{x, y, z}, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + // [END solve] + + // Print solution on console. + // [START print_solution] + int count = 0; + solver.NewSearch(db); + while (solver.NextSolution()) { + ++count; + Console.WriteLine($"Solution: {count}\n x={x.Value()} y={y.Value()} z={z.Value()}"); + } + solver.EndSearch(); + Console.WriteLine($"Number of solutions found: {solver.Solutions()}"); + // [END print_solution] + + // [START advanced] + Console.WriteLine("Advanced usage:"); + Console.WriteLine($"Problem solved in {solver.WallTime()}ms"); + Console.WriteLine($"Memory usage: {Solver.MemoryUsage()}bytes"); + // [END advanced] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/SimpleMaxFlowProgram.cs b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleMaxFlowProgram.cs new file mode 100644 index 0000000000000000000000000000000000000000..ed1360fc30ea61c66bf6fadcb394199670e4ec63 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleMaxFlowProgram.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. + +// [START program] +using System; +using Google.OrTools.Graph; + +public class SimpleMaxFlowProgram +{ + static void Main() + { + // [START data] + // Define three parallel arrays: start_nodes, end_nodes, and the capacities + // between each pair. For instance, the arc from node 0 to node 1 has a + // capacity of 20. + // From Taha's 'Introduction to Operations Research', + // example 6.4-2. + int[] startNodes = {0, 0, 0, 1, 1, 2, 2, 3, 3}; + int[] endNodes = {1, 2, 3, 2, 4, 3, 4, 2, 4}; + int[] capacities = {20, 30, 10, 40, 30, 10, 20, 5, 20}; + // [END data] + + // [START constraints] + // Instantiate a SimpleMaxFlow solver. + MaxFlow maxFlow = new MaxFlow(); + + // Add each arc. + for (int i = 0; i < startNodes.Length; ++i) + { + int arc = maxFlow.AddArcWithCapacity(startNodes[i], endNodes[i], + capacities[i]); + if (arc != i) throw new Exception("Internal error"); + } + // [END constraints] + + // [START solve] + // Find the maximum flow between node 0 and node 4. + MaxFlow.Status solveStatus = maxFlow.Solve(0, 4); + // [END solve] + + // [START print_solution] + if (solveStatus == MaxFlow.Status.OPTIMAL) + { + Console.WriteLine("Max. flow: " + maxFlow.OptimalFlow()); + Console.WriteLine(""); + Console.WriteLine(" Arc Flow / Capacity"); + for (int i = 0; i < maxFlow.NumArcs(); ++i) + { + Console.WriteLine(maxFlow.Tail(i) + " -> " + + maxFlow.Head(i) + " " + + string.Format("{0,3}", maxFlow.Flow(i)) + " / " + + string.Format("{0,3}", maxFlow.Capacity(i))); + } + } + else + { + Console.WriteLine("Solving the max flow problem failed. Solver status: " + + solveStatus); + } + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/SimpleMinCostFlowProgram.cs b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleMinCostFlowProgram.cs new file mode 100644 index 0000000000000000000000000000000000000000..254e3a7fa26486c78f2004bdf49ea7d879417dfd --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleMinCostFlowProgram.cs @@ -0,0 +1,87 @@ +// 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. + +// """From Bradley, Hax, and Magnanti, 'Applied Mathematical Programming', figure 8.1.""" +// [START program] +using System; +using Google.OrTools.Graph; + +public class SimpleMinCostFlowProgram +{ + static void Main() + { + // [START data] + // Define four parallel arrays: sources, destinations, capacities, and unit costs + // between each pair. For instance, the arc from node 0 to node 1 has a + // capacity of 15. + // Problem taken From Taha's 'Introduction to Operations Research', + // example 6.4-2. + int[] startNodes = {0, 0, 1, 1, 1, 2, 2, 3, 4}; + int[] endNodes = {1, 2, 2, 3, 4, 3, 4, 4, 2}; + int[] capacities = {15, 8, 20, 4, 10, 15, 4, 20, 5}; + int[] unitCosts = {4, 4, 2, 2, 6, 1, 3, 2, 3}; + + // Define an array of supplies at each node. + int[] supplies = {20, 0, 0, -5, -15}; + // [END data] + + // [START constraints] + // Instantiate a SimpleMinCostFlow solver. + MinCostFlow minCostFlow = new MinCostFlow(); + + // Add each arc. + for (int i = 0; i < startNodes.Length; ++i) + { + int arc = minCostFlow.AddArcWithCapacityAndUnitCost(startNodes[i], endNodes[i], + capacities[i], unitCosts[i]); + if (arc != i) throw new Exception("Internal error"); + } + + // Add node supplies. + for (int i = 0; i < supplies.Length; ++i) + { + minCostFlow.SetNodeSupply(i, supplies[i]); + } + + // [END constraints] + + // [START solve] + // Find the min cost flow. + MinCostFlow.Status solveStatus = minCostFlow.Solve(); + // [END solve] + + // [START print_solution] + if (solveStatus == MinCostFlow.Status.OPTIMAL) + { + Console.WriteLine("Minimum cost: " + minCostFlow.OptimalCost()); + Console.WriteLine(""); + Console.WriteLine(" Edge Flow / Capacity Cost"); + for (int i = 0; i < minCostFlow.NumArcs(); ++i) + { + long cost = minCostFlow.Flow(i) * minCostFlow.UnitCost(i); + Console.WriteLine(minCostFlow.Tail(i) + " -> " + + minCostFlow.Head(i) + " " + + string.Format("{0,3}", minCostFlow.Flow(i)) + " / " + + string.Format ("{0,3}", minCostFlow.Capacity(i)) + " " + + string.Format ("{0,3}", cost)); + } + } + else + { + Console.WriteLine("Solving the min cost flow problem failed. Solver status: " + + solveStatus); + } + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/SimpleProgramFSharp.fs b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleProgramFSharp.fs new file mode 100644 index 0000000000000000000000000000000000000000..1c706e825c5a84a20afa138e7794dde295162c7c --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleProgramFSharp.fs @@ -0,0 +1,40 @@ +// 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. + +// [START program] +open System +open Google.OrTools.FSharp +open Google.OrTools.LinearSolver + +let SimpleProgramFSharp = + let svr = Solver.CreateSolver("SimpleProgramFSharp", LinearProgramming.GLOP.ToString()) + + // Create the variable x and y. + let x = svr.MakeNumVar(0.0, 1.0, "x") + let y = svr.MakeNumVar(0.0, 2.0, "y") + // Create the objective function, x + y. + let objective = svr.Objective() + objective.SetCoefficient(x, 1.0) + objective.SetCoefficient(y, 1.0) + objective.SetMaximization() + // Call the solver and display the results. + svr.Solve() |> ignore + printfn "Solution:" + printfn "x = %f" (x.SolutionValue()) + printfn "y = %f" (y.SolutionValue()) + +[] +let main = + SimpleProgramFSharp + exit 0 +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/SimpleRoutingProgram.cs b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleRoutingProgram.cs new file mode 100644 index 0000000000000000000000000000000000000000..e4d2a6320d5c9d28b3f69627c328c2ca58db33d9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleRoutingProgram.cs @@ -0,0 +1,92 @@ +// 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] +using System; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// This is a sample using the routing library .Net wrapper. +/// +public class SimpleRoutingProgram { + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + const int numLocation = 5; + const int numVehicles = 1; + const int depot = 0; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + numLocation, + numVehicles, + depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return Math.Abs(toNode - fromNode); + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); + // Inspect solution. + long index = routing.Start(0); + Console.WriteLine("Route for Vehicle 0:"); + long route_distance = 0; + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + long previousIndex = index; + index = solution.Value(routing.NextVar(index)); + route_distance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode(index)); + Console.WriteLine("Distance of the route: {0}m", route_distance); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/SimpleSatProgram.cs b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleSatProgram.cs new file mode 100644 index 0000000000000000000000000000000000000000..d1c36d06a676020d62b4a88f28723e7c205bff65 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/SimpleSatProgram.cs @@ -0,0 +1,55 @@ +// 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] +using System; +using Google.OrTools.Sat; + +public class SimpleSatProgram +{ + static void Main() + { + // Creates the model. + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Creates the variables. + // [START variables] + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + // [END variables] + + // Creates the constraints. + // [START constraints] + model.Add(x != y); + // [END constraints] + + // Creates a solver and solves the model. + // [START solve] + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.Solve(model); + // [END solve] + + if (status == CpSolverStatus.Feasible) + { + Console.WriteLine("x = " + solver.Value(x)); + Console.WriteLine("y = " + solver.Value(y)); + Console.WriteLine("z = " + solver.Value(z)); + } + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/SolutionHintingSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/SolutionHintingSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..78d29c560448a8a5a3a98eacff211787a7f7bd51 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/SolutionHintingSampleSat.cs @@ -0,0 +1,91 @@ +// 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] +using System; +using Google.OrTools.Sat; + +// [START print_solution] +public class VarArraySolutionPrinter : CpSolverSolutionCallback +{ + public VarArraySolutionPrinter(IntVar[] variables) + { + variables_ = variables; + } + + public override void OnSolutionCallback() + { + { + Console.WriteLine(String.Format("Solution #{0}: time = {1:F2} s", + solution_count_, WallTime())); + foreach (IntVar v in variables_) + { + Console.WriteLine( + String.Format(" {0} = {1}", v.ShortString(), Value(v))); + } + solution_count_++; + } + } + + public int SolutionCount() + { + return solution_count_; + } + + private int solution_count_; + private IntVar[] variables_; +} +// [END print_solution] + +public class SolutionHintingSampleSat +{ + static void Main() + { + // Creates the model. + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Creates the variables. + // [START variables] + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + // [END variables] + + // Creates the constraints. + // [START constraints] + model.Add(x != y); + // [END constraints] + + // Solution hinting: x <- 1, y <- 2 + model.AddHint(x, 1); + model.AddHint(y, 2); + + // [START objective] + model.Maximize(LinearExpr.ScalProd(new IntVar[] {x, y, z}, new int[] {1, 2, 3})); + // [END objective] + + // Creates a solver and solves the model. + // [START solve] + CpSolver solver = new CpSolver(); + VarArraySolutionPrinter cb = + new VarArraySolutionPrinter(new IntVar[] { x, y, z }); + CpSolverStatus status = solver.SolveWithSolutionCallback(model, cb); + // [END solve] + + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..d9eb1c8d96723ff6b8131ed8f46cf4f753c4886e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/SolveAndPrintIntermediateSolutionsSampleSat.cs @@ -0,0 +1,90 @@ +// 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] +using System; +using Google.OrTools.Sat; + +// [START print_solution] +public class VarArraySolutionPrinterWithObjective : CpSolverSolutionCallback +{ + public VarArraySolutionPrinterWithObjective(IntVar[] variables) + { + variables_ = variables; + } + + public override void OnSolutionCallback() + { + Console.WriteLine(String.Format("Solution #{0}: time = {1:F2} s", + solution_count_, WallTime())); + Console.WriteLine( + String.Format(" objective value = {0}", ObjectiveValue())); + foreach (IntVar v in variables_) + { + Console.WriteLine( + String.Format(" {0} = {1}", v.ShortString(), Value(v))); + } + solution_count_++; + } + + public int SolutionCount() + { + return solution_count_; + } + + private int solution_count_; + private IntVar[] variables_; +} +// [END print_solution] + +public class SolveAndPrintIntermediateSolutionsSampleSat +{ + static void Main() + { + // Creates the model. + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Creates the variables. + // [START variables] + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + // [END variables] + + // Adds a different constraint. + // [START constraints] + model.Add(x != y); + // [END constraints] + + // Maximizes a linear combination of variables. + // [START objective] + model.Maximize(x + 2 * y + 3 * z); + // [END objective] + + // Creates a solver and solves the model. + // [START solve] + CpSolver solver = new CpSolver(); + VarArraySolutionPrinterWithObjective cb = + new VarArraySolutionPrinterWithObjective(new IntVar[] { x, y, z }); + solver.SolveWithSolutionCallback(model, cb); + // [END solve] + + Console.WriteLine(String.Format("Number of solutions found: {0}", + cb.SolutionCount())); + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/SolveWithTimeLimitSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/SolveWithTimeLimitSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..d0f4a5e92143cf8ae533834a922f5920316a4922 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/SolveWithTimeLimitSampleSat.cs @@ -0,0 +1,47 @@ +// 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.Sat; + +public class SolveWithTimeLimitSampleSat +{ + static void Main() + { + // Creates the model. + CpModel model = new CpModel(); + // Creates the variables. + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + // Adds a different constraint. + model.Add(x != y); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + + // Adds a time limit. Parameters are stored as strings in the solver. + solver.StringParameters = "max_time_in_seconds:10.0"; + + CpSolverStatus status = solver.Solve(model); + + if (status == CpSolverStatus.Feasible) + { + Console.WriteLine("x = " + solver.Value(x)); + Console.WriteLine("y = " + solver.Value(y)); + Console.WriteLine("z = " + solver.Value(z)); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/SpeakerSchedulingSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/SpeakerSchedulingSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..8359ad0cc958cd27718cb1a01efab18e2e303ba4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/StepFunctionSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/StepFunctionSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..5879a9965c11c8f4d7e8cc58871efacd20c19549 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/StepFunctionSampleSat.cs @@ -0,0 +1,101 @@ +// 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.Sat; +using Google.OrTools.Util; + +public class VarArraySolutionPrinter : CpSolverSolutionCallback +{ + public VarArraySolutionPrinter(IntVar[] variables) + { + variables_ = variables; + } + + public override void OnSolutionCallback() + { + { + foreach (IntVar v in variables_) + { + Console.Write(String.Format("{0}={1} ", v.ShortString(), Value(v))); + } + Console.WriteLine(); + } + } + + private IntVar[] variables_; +} + +public class StepFunctionSampleSat +{ + static void Main() + { + // Create the CP-SAT model. + CpModel model = new CpModel(); + + // Declare our primary variable. + IntVar x = model.NewIntVar(0, 20, "x"); + + // Create the expression variable and implement the step function + // Note it is not defined for var == 2. + // + // - 3 + // -- -- --------- 2 + // 1 + // -- --- 0 + // 0 ================ 20 + // + IntVar expr = model.NewIntVar(0, 3, "expr"); + + // expr == 0 on [5, 6] U [8, 10] + ILiteral b0 = model.NewBoolVar("b0"); + model.AddLinearExpressionInDomain( + x, + Domain.FromValues(new long[] { 5, 6, 8, 9, 10 })).OnlyEnforceIf(b0); + model.Add(expr == 0).OnlyEnforceIf(b0); + + // expr == 2 on [0, 1] U [3, 4] U [11, 20] + ILiteral b2 = model.NewBoolVar("b2"); + model.AddLinearExpressionInDomain( + x, + Domain.FromIntervals( + new long[][] {new long[] {0, 1}, + new long[] {3, 4}, + new long[] {11, 20}})).OnlyEnforceIf(b2); + model.Add(expr == 2).OnlyEnforceIf(b2); + + // expr == 3 when x == 7 + ILiteral b3 = model.NewBoolVar("b3"); + model.Add(x == 7).OnlyEnforceIf(b3); + model.Add(expr == 3).OnlyEnforceIf(b3); + + // At least one bi is true. (we could use a sum == 1). + model.AddBoolOr(new ILiteral[] { b0, b2, b3 }); + + // Search for x values in increasing order. + model.AddDecisionStrategy( + new IntVar[] { x }, + DecisionStrategyProto.Types.VariableSelectionStrategy.ChooseFirst, + DecisionStrategyProto.Types.DomainReductionStrategy.SelectMinValue); + + // Create the solver. + CpSolver solver = new CpSolver(); + + // Force solver to follow the decision strategy exactly. + solver.StringParameters = "search_branching:FIXED_SEARCH"; + + VarArraySolutionPrinter cb = + new VarArraySolutionPrinter(new IntVar[] { x, expr }); + solver.SearchAllSolutions(model, cb); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/StopAfterNSolutionsSampleSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/StopAfterNSolutionsSampleSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..5ba1d8b58efe20dd7fcf29807fa4b88fc98736ea --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/StopAfterNSolutionsSampleSat.cs @@ -0,0 +1,77 @@ +// 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.Sat; + +public class VarArraySolutionPrinterWithLimit : CpSolverSolutionCallback +{ + public VarArraySolutionPrinterWithLimit(IntVar[] variables, + int solution_limit) + { + variables_ = variables; + solution_limit_ = solution_limit; + } + + public override void OnSolutionCallback() + { + Console.WriteLine(String.Format("Solution #{0}: time = {1:F2} s", + solution_count_, WallTime())); + foreach (IntVar v in variables_) + { + Console.WriteLine( + String.Format(" {0} = {1}", v.ShortString(), Value(v))); + } + solution_count_++; + if (solution_count_ >= solution_limit_) + { + Console.WriteLine( + String.Format("Stopping search after {0} solutions", + solution_limit_)); + StopSearch(); + } + } + + public int SolutionCount() + { + return solution_count_; + } + + private int solution_count_; + private IntVar[] variables_; + private int solution_limit_; +} + +public class StopAfterNSolutionsSampleSat +{ + static void Main() + { + // Creates the model. + CpModel model = new CpModel(); + // Creates the variables. + int num_vals = 3; + + IntVar x = model.NewIntVar(0, num_vals - 1, "x"); + IntVar y = model.NewIntVar(0, num_vals - 1, "y"); + IntVar z = model.NewIntVar(0, num_vals - 1, "z"); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + VarArraySolutionPrinterWithLimit cb = + new VarArraySolutionPrinterWithLimit(new IntVar[] { x, y, z }, 5); + solver.SearchAllSolutions(model, cb); + Console.WriteLine(String.Format("Number of solutions found: {0}", + cb.SolutionCount())); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/TaskSchedulingSat.cs b/libs/or-tools-src-ubuntu/examples/dotnet/TaskSchedulingSat.cs new file mode 100644 index 0000000000000000000000000000000000000000..72a29f02ad2e3c89885989c557b90cb397d7be19 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/Tsp.cs b/libs/or-tools-src-ubuntu/examples/dotnet/Tsp.cs new file mode 100644 index 0000000000000000000000000000000000000000..7e0f805decaa5b438fe60df006388927c130910f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/Tsp.cs @@ -0,0 +1,166 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal TSP. +/// A description of the problem can be found here: +/// http://en.wikipedia.org/wiki/Travelling_salesman_problem. +/// +public class Tsp { + // [START data_model] + class DataModel { + // Constructor: + public DataModel() { + // Convert locations in meters using a city block dimension of 114m x 80m. + for (int i=0; i < Locations.GetLength(0); i++) { + Locations[i, 0] *= 114; + Locations[i, 1] *= 80; + } + } + public int[,] Locations = { + {4, 4}, + {2, 0}, {8, 0}, + {0, 1}, {1, 1}, + {5, 2}, {7, 2}, + {3, 3}, {6, 3}, + {5, 5}, {8, 5}, + {1, 6}, {2, 6}, + {3, 7}, {6, 7}, + {0, 8}, {7, 8} + }; + public int VehicleNumber = 1; + public int Depot = 0; + }; + // [END data_model] + + // [START manhattan_distance] + /// + /// 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 ManhattanDistance { + public ManhattanDistance( + in DataModel data, + in RoutingIndexManager manager) { + // precompute distance between location to have distance callback in O(1) + int locationNumber = data.Locations.GetLength(0); + distancesMatrix_ = new long[locationNumber, locationNumber]; + indexManager_ = manager; + for (int fromNode = 0; fromNode < locationNumber; fromNode++) { + for (int toNode = 0; toNode < locationNumber; toNode++) { + if (fromNode == toNode) + distancesMatrix_[fromNode, toNode] = 0; + else + distancesMatrix_[fromNode, toNode] = + Math.Abs(data.Locations[toNode, 0] - data.Locations[fromNode, 0]) + + Math.Abs(data.Locations[toNode, 1] - data.Locations[fromNode, 1]); + } + } + } + + /// + /// Returns the manhattan distance between the two nodes + /// + public long Call(long fromIndex, long toIndex) { + // Convert from routing variable Index to distance matrix NodeIndex. + int fromNode = indexManager_.IndexToNode(fromIndex); + int toNode = indexManager_.IndexToNode(toIndex); + return distancesMatrix_[fromNode, toNode]; + } + private long[,] distancesMatrix_; + private RoutingIndexManager indexManager_; + }; + // [END manhattan_distance] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); + // Inspect solution. + Console.WriteLine("Route for Vehicle 0:"); + long routeDistance = 0; + var index = routing.Start(0); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.Locations.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + var distanceCallback = new ManhattanDistance(data, manager); + int transitCallbackIndex = routing.RegisterTransitCallback(distanceCallback.Call); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/TspCircuitBoard.cs b/libs/or-tools-src-ubuntu/examples/dotnet/TspCircuitBoard.cs new file mode 100644 index 0000000000000000000000000000000000000000..cea3c63366f837b710139cb3bff7936c4850c090 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/TspCircuitBoard.cs @@ -0,0 +1,184 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal TSP. +/// A description of the problem can be found here: +/// http://en.wikipedia.org/wiki/Travelling_salesman_problem. +/// +public class TspCircuitBoard { + // [START data_model] + class DataModel { + public int[,] Locations = { + {288, 149}, {288, 129}, {270, 133}, {256, 141}, {256, 157}, {246, 157}, + {236, 169}, {228, 169}, {228, 161}, {220, 169}, {212, 169}, {204, 169}, + {196, 169}, {188, 169}, {196, 161}, {188, 145}, {172, 145}, {164, 145}, + {156, 145}, {148, 145}, {140, 145}, {148, 169}, {164, 169}, {172, 169}, + {156, 169}, {140, 169}, {132, 169}, {124, 169}, {116, 161}, {104, 153}, + {104, 161}, {104, 169}, {90, 165}, {80, 157}, {64, 157}, {64, 165}, + {56, 169}, {56, 161}, {56, 153}, {56, 145}, {56, 137}, {56, 129}, + {56, 121}, {40, 121}, {40, 129}, {40, 137}, {40, 145}, {40, 153}, + {40, 161}, {40, 169}, {32, 169}, {32, 161}, {32, 153}, {32, 145}, + {32, 137}, {32, 129}, {32, 121}, {32, 113}, {40, 113}, {56, 113}, + {56, 105}, {48, 99}, {40, 99}, {32, 97}, {32, 89}, {24, 89}, {16, 97}, + {16, 109}, {8, 109}, {8, 97}, {8, 89}, {8, 81}, {8, 73}, {8, 65}, + {8, 57}, {16, 57}, {8, 49}, {8, 41}, {24, 45}, {32, 41}, {32, 49}, + {32, 57}, {32, 65}, {32, 73}, {32, 81}, {40, 83}, {40, 73}, {40, 63}, + {40, 51}, {44, 43}, {44, 35}, {44, 27}, {32, 25}, {24, 25}, {16, 25}, + {16, 17}, {24, 17}, {32, 17}, {44, 11}, {56, 9}, {56, 17}, {56, 25}, + {56, 33}, {56, 41}, {64, 41}, {72, 41}, {72, 49}, {56, 49}, {48, 51}, + {56, 57}, {56, 65}, {48, 63}, {48, 73}, {56, 73}, {56, 81}, {48, 83}, + {56, 89}, {56, 97}, {104, 97}, {104, 105}, {104, 113}, {104, 121}, + {104, 129}, {104, 137}, {104, 145}, {116, 145}, {124, 145}, {132, 145}, + {132, 137}, {140, 137}, {148, 137}, {156, 137}, {164, 137}, {172, 125}, + {172, 117}, {172, 109}, {172, 101}, {172, 93}, {172, 85}, {180, 85}, + {180, 77}, {180, 69}, {180, 61}, {180, 53}, {172, 53}, {172, 61}, + {172, 69}, {172, 77}, {164, 81}, {148, 85}, {124, 85}, {124, 93}, + {124, 109}, {124, 125}, {124, 117}, {124, 101}, {104, 89}, {104, 81}, + {104, 73}, {104, 65}, {104, 49}, {104, 41}, {104, 33}, {104, 25}, + {104, 17}, {92, 9}, {80, 9}, {72, 9}, {64, 21}, {72, 25}, {80, 25}, + {80, 25}, {80, 41}, {88, 49}, {104, 57}, {124, 69}, {124, 77}, {132, 81}, + {140, 65}, {132, 61}, {124, 61}, {124, 53}, {124, 45}, {124, 37}, + {124, 29}, {132, 21}, {124, 21}, {120, 9}, {128, 9}, {136, 9}, {148, 9}, + {162, 9}, {156, 25}, {172, 21}, {180, 21}, {180, 29}, {172, 29}, + {172, 37}, {172, 45}, {180, 45}, {180, 37}, {188, 41}, {196, 49}, + {204, 57}, {212, 65}, {220, 73}, {228, 69}, {228, 77}, {236, 77}, + {236, 69}, {236, 61}, {228, 61}, {228, 53}, {236, 53}, {236, 45}, + {228, 45}, {228, 37}, {236, 37}, {236, 29}, {228, 29}, {228, 21}, + {236, 21}, {252, 21}, {260, 29}, {260, 37}, {260, 45}, {260, 53}, + {260, 61}, {260, 69}, {260, 77}, {276, 77}, {276, 69}, {276, 61}, + {276, 53}, {284, 53}, {284, 61}, {284, 69}, {284, 77}, {284, 85}, + {284, 93}, {284, 101}, {288, 109}, {280, 109}, {276, 101}, {276, 93}, + {276, 85}, {268, 97}, {260, 109}, {252, 101}, {260, 93}, {260, 85}, + {236, 85}, {228, 85}, {228, 93}, {236, 93}, {236, 101}, {228, 101}, + {228, 109}, {228, 117}, {228, 125}, {220, 125}, {212, 117}, {204, 109}, + {196, 101}, {188, 93}, {180, 93}, {180, 101}, {180, 109}, {180, 117}, + {180, 125}, {196, 145}, {204, 145}, {212, 145}, {220, 145}, {228, 145}, + {236, 145}, {246, 141}, {252, 125}, {260, 129}, {280, 133}, + }; + public int VehicleNumber = 1; + public int Depot = 0; + }; + // [END data_model] + + // [START euclidean_distance] + /// + /// Euclidean distance implemented as a callback. It uses an array of + /// positions and computes the Euclidean distance between the two + /// positions of two different indices. + /// + static long[,] ComputeEuclideanDistanceMatrix(in int[,] locations) { + // Calculate the distance matrix using Euclidean distance. + int locationNumber = locations.GetLength(0); + long[,] distanceMatrix = new long[locationNumber, locationNumber]; + for (int fromNode = 0; fromNode < locationNumber; fromNode++) { + for (int toNode = 0; toNode < locationNumber; toNode++) { + if (fromNode == toNode) + distanceMatrix[fromNode, toNode] = 0; + else + distanceMatrix[fromNode, toNode] = (long) + Math.Sqrt( + Math.Pow(locations[toNode, 0] - locations[fromNode, 0], 2) + + Math.Pow(locations[toNode, 1] - locations[fromNode, 1], 2)); + } + } + return distanceMatrix; + } + // [END euclidean_distance] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); + // Inspect solution. + Console.WriteLine("Route:"); + long routeDistance = 0; + var index = routing.Start(0); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Route distance: {0}m", routeDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.Locations.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Define cost of each arc. + // [START transit_callback] + long[,] distanceMatrix = ComputeEuclideanDistanceMatrix(data.Locations); + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return distanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/TspCities.cs b/libs/or-tools-src-ubuntu/examples/dotnet/TspCities.cs new file mode 100644 index 0000000000000000000000000000000000000000..48a2a5bc3beeb4c61fc2313db05eb84cdaaf3284 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/TspCities.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. + +// [START program] +// [START import] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal TSP using distance matrix. +/// +public class TspCities { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 2451, 713, 1018, 1631, 1374, 2408, 213, 2571, 875, 1420, 2145, 1972}, + {2451, 0, 1745, 1524, 831, 1240, 959, 2596, 403, 1589, 1374, 357, 579}, + {713, 1745, 0, 355, 920, 803, 1737, 851, 1858, 262, 940, 1453, 1260}, + {1018, 1524, 355, 0, 700, 862, 1395, 1123, 1584, 466, 1056, 1280, 987}, + {1631, 831, 920, 700, 0, 663, 1021, 1769, 949, 796, 879, 586, 371}, + {1374, 1240, 803, 862, 663, 0, 1681, 1551, 1765, 547, 225, 887, 999}, + {2408, 959, 1737, 1395, 1021, 1681, 0, 2493, 678, 1724, 1891, 1114, 701}, + {213, 2596, 851, 1123, 1769, 1551, 2493, 0, 2699, 1038, 1605, 2300, 2099}, + {2571, 403, 1858, 1584, 949, 1765, 678, 2699, 0, 1744, 1645, 653, 600}, + {875, 1589, 262, 466, 796, 547, 1724, 1038, 1744, 0, 679, 1272, 1162}, + {1420, 1374, 940, 1056, 879, 225, 1891, 1605, 1645, 679, 0, 1017, 1200}, + {2145, 357, 1453, 1280, 586, 887, 1114, 2300, 653, 1272, 1017, 0, 504}, + {1972, 579, 1260, 987, 371, 999, 701, 2099, 600, 1162, 1200, 504, 0}, + }; + public int VehicleNumber = 1; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + Console.WriteLine("Objective: {0} miles", solution.ObjectiveValue()); + // Inspect solution. + Console.WriteLine("Route:"); + long routeDistance = 0; + var index = routing.Start(0); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Route distance: {0}miles", routeDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/TspDistanceMatrix.cs b/libs/or-tools-src-ubuntu/examples/dotnet/TspDistanceMatrix.cs new file mode 100644 index 0000000000000000000000000000000000000000..d4dda047d316dd40960811737066e01d03ed0b2d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/TspDistanceMatrix.cs @@ -0,0 +1,129 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal TSP using distance matrix. +/// +public class TspDistanceMatrix { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0} + }; + public int VehicleNumber = 1; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); + // Inspect solution. + Console.WriteLine("Route for Vehicle 0:"); + long routeDistance = 0; + var index = routing.Start(0); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/Vrp.cs b/libs/or-tools-src-ubuntu/examples/dotnet/Vrp.cs new file mode 100644 index 0000000000000000000000000000000000000000..89b9b598190734c00b86831d768f4d808a913dff --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/Vrp.cs @@ -0,0 +1,135 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +// [START import] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal TSP using distance matrix. +/// +public class Vrp { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0} + }; + public int VehicleNumber = 4; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + Console.WriteLine("Objective: {0}", solution.ObjectiveValue()); + // Inspect solution. + long totalDistance = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + long routeDistance = 0; + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + totalDistance += routeDistance; + } + Console.WriteLine("Total Distance of all routes: {0}m", totalDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpCapacity.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpCapacity.cs new file mode 100644 index 0000000000000000000000000000000000000000..cabe3a679e48ffc7e9198ad2316ea73dd995c86f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpCapacity.cs @@ -0,0 +1,159 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal TSP using distance matrix. +/// +public class VrpCapacity { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0} + }; + // [START demands_capacities] + public long[] Demands = {0, 1, 1, 2, 4, 2, 4, 8, 8, 1, 2, 1, 2, 4, 4, 8, 8}; + public long[] VehicleCapacities = {15, 15, 15, 15}; + // [END demands_capacities] + public int VehicleNumber = 4; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + // Inspect solution. + long totalDistance = 0; + long totalLoad = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + long routeDistance = 0; + long routeLoad = 0; + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + long nodeIndex = manager.IndexToNode(index); + routeLoad += data.Demands[nodeIndex]; + Console.Write("{0} Load({1}) -> ", nodeIndex, routeLoad); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + totalDistance += routeDistance; + totalLoad += routeLoad; + } + Console.WriteLine("Total distance of all routes: {0}m", totalDistance); + Console.WriteLine("Total load of all routes: {0}m", totalLoad); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Capacity constraint. + // [START capacity_constraint] + int demandCallbackIndex = routing.RegisterUnaryTransitCallback( + (long fromIndex) => { + // Convert from routing variable Index to demand NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + return data.Demands[fromNode]; } + ); + routing.AddDimensionWithVehicleCapacity( + demandCallbackIndex, 0, // null capacity slack + data.VehicleCapacities, // vehicle maximum capacities + true, // start cumul to zero + "Capacity"); + // [END capacity_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpDropNodes.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpDropNodes.cs new file mode 100644 index 0000000000000000000000000000000000000000..8ac22ea654f7ed84758c935a9ac8f9b6e8480cb6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpDropNodes.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. + +// [START program] +// [START import] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal Vrp with drop nodes. +/// +public class VrpDropNodes { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0} + }; + // [START demands_capacities] + public long[] Demands = {0, 1, 1, 3, 6, 3, 6, 8, 8, 1, 2, 1, 2, 6, 6, 8, 8}; + public long[] VehicleCapacities = {15, 15, 15, 15}; + // [END demands_capacities] + public int VehicleNumber = 4; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + // Display dropped nodes. + string droppedNodes = "Dropped nodes:"; + for (int index = 0; index < routing.Size(); ++index) { + if (routing.IsStart(index) || routing.IsEnd(index)) { + continue; + } + if (solution.Value(routing.NextVar(index)) == index) { + droppedNodes += " " + manager.IndexToNode(index); + } + } + Console.WriteLine("{0}", droppedNodes); + // Inspect solution. + long totalDistance = 0; + long totalLoad = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + long routeDistance = 0; + long routeLoad = 0; + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + long nodeIndex = manager.IndexToNode(index); + routeLoad += data.Demands[nodeIndex]; + Console.Write("{0} Load({1}) -> ", nodeIndex, routeLoad); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + totalDistance += routeDistance; + totalLoad += routeLoad; + } + Console.WriteLine("Total Distance of all routes: {0}m", totalDistance); + Console.WriteLine("Total Load of all routes: {0}m", totalLoad); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Capacity constraint. + // [START capacity_constraint] + int demandCallbackIndex = routing.RegisterUnaryTransitCallback( + (long fromIndex) => { + // Convert from routing variable Index to demand NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + return data.Demands[fromNode]; } + ); + routing.AddDimensionWithVehicleCapacity( + demandCallbackIndex, 0, // null capacity slack + data.VehicleCapacities, // vehicle maximum capacities + true, // start cumul to zero + "Capacity"); + // Allow to drop nodes. + long penalty = 1000; + for (int i = 1; i < data.DistanceMatrix.GetLength(0); ++i) { + routing.AddDisjunction( + new long[] {manager.NodeToIndex(i)}, penalty); + } + // [END capacity_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpGlobalSpan.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpGlobalSpan.cs new file mode 100644 index 0000000000000000000000000000000000000000..bf061ce509217a3e2f67612229e5c00a166313ec --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpGlobalSpan.cs @@ -0,0 +1,144 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal TSP using distance matrix. +/// +public class VrpGlobalSpan { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0} + }; + public int VehicleNumber = 4; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + // Inspect solution. + long maxRouteDistance = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + long routeDistance = 0; + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + maxRouteDistance = Math.Max(routeDistance, maxRouteDistance); + } + Console.WriteLine("Maximum distance of the routes: {0}m", maxRouteDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transitCallbackIndex, 0, 3000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.GetMutableDimension("Distance"); + distanceDimension.SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpInitialRoutes.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpInitialRoutes.cs new file mode 100644 index 0000000000000000000000000000000000000000..1e73f544494d1f6ec2982834421f8757179e0614 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpInitialRoutes.cs @@ -0,0 +1,159 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// VRP with initial routes. +/// +public class InitialRoutes { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + // [START initial_routes] + public long[][] InitialRoutes = { + new long[] {8, 16, 14, 13, 12, 11}, + new long[] {3, 4, 9, 10}, + new long[] {15, 1}, + new long[] {7, 5, 2, 6}, + }; + // [END initial_routes] + public int VehicleNumber = 4; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + // Inspect solution. + long maxRouteDistance = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + long routeDistance = 0; + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}", routeDistance); + maxRouteDistance = Math.Max(routeDistance, maxRouteDistance); + } + Console.WriteLine("Maximum distance of the routes: {0}", maxRouteDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transitCallbackIndex, 0, 3000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.GetMutableDimension("Distance"); + distanceDimension.SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Get inital solution from routes. + // [START print_initial_solution] + Assignment initialSolution = routing.ReadAssignmentFromRoutes( + data.InitialRoutes, true); + // Print initial solution on console. + Console.WriteLine("Initial solution:"); + PrintSolution(data, routing, manager, initialSolution); + // [END print_initial_solution] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + Console.WriteLine("Solution after search:"); + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpPickupDelivery.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpPickupDelivery.cs new file mode 100644 index 0000000000000000000000000000000000000000..72b380134e52419a4c0cd4e07d473e536d01c39f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpPickupDelivery.cs @@ -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. + +// [START program] +// [START import] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal Pickup & Delivery Problem (PDP). +/// +public class VrpPickupDelivery { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0} + }; + // [START pickups_deliveries] + public int[][] PickupsDeliveries = { + new int[] {1, 6}, + new int[] {2, 10}, + new int[] {4, 3}, + new int[] {5, 9}, + new int[] {7, 8}, + new int[] {15, 11}, + new int[] {13, 12}, + new int[] {16, 14}, + }; + // [END pickups_deliveries] + public int VehicleNumber = 4; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + long totalDistance = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + long routeDistance = 0; + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + totalDistance += routeDistance; + } + Console.WriteLine("Total Distance of all routes: {0}m", totalDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transitCallbackIndex, 0, 3000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.GetMutableDimension("Distance"); + distanceDimension.SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Define Transportation Requests. + // [START pickup_delivery_constraint] + Solver solver = routing.solver(); + for(int i=0; i < data.PickupsDeliveries.GetLength(0); i++) { + long pickupIndex = manager.NodeToIndex(data.PickupsDeliveries[i][0]); + long deliveryIndex = manager.NodeToIndex(data.PickupsDeliveries[i][1]); + routing.AddPickupAndDelivery(pickupIndex, deliveryIndex); + solver.Add(solver.MakeEquality( + routing.VehicleVar(pickupIndex), + routing.VehicleVar(deliveryIndex))); + solver.Add(solver.MakeLessOrEqual( + distanceDimension.CumulVar(pickupIndex), + distanceDimension.CumulVar(deliveryIndex))); + } + // [END pickup_delivery_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpPickupDeliveryFifo.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpPickupDeliveryFifo.cs new file mode 100644 index 0000000000000000000000000000000000000000..3dcd7b07fe4ce192e41da82f33688acf60123b2f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpPickupDeliveryFifo.cs @@ -0,0 +1,173 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal Pickup & Delivery Problem (PDP). +/// +public class VrpPickupDeliveryFifo { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0} + }; + // [START pickups_deliveries] + public int[][] PickupsDeliveries = { + new int[] {1, 6}, + new int[] {2, 10}, + new int[] {4, 3}, + new int[] {5, 9}, + new int[] {7, 8}, + new int[] {15, 11}, + new int[] {13, 12}, + new int[] {16, 14}, + }; + // [END pickups_deliveries] + public int VehicleNumber = 4; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + long totalDistance = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + long routeDistance = 0; + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + totalDistance += routeDistance; + } + Console.WriteLine("Total Distance of all routes: {0}m", totalDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transitCallbackIndex, 0, 3000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.GetMutableDimension("Distance"); + distanceDimension.SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Define Transportation Requests. + // [START pickup_delivery_constraint] + Solver solver = routing.solver(); + for(int i=0; i < data.PickupsDeliveries.GetLength(0); i++) { + long pickupIndex = manager.NodeToIndex(data.PickupsDeliveries[i][0]); + long deliveryIndex = manager.NodeToIndex(data.PickupsDeliveries[i][1]); + routing.AddPickupAndDelivery(pickupIndex, deliveryIndex); + solver.Add(solver.MakeEquality( + routing.VehicleVar(pickupIndex), + routing.VehicleVar(deliveryIndex))); + solver.Add(solver.MakeLessOrEqual( + distanceDimension.CumulVar(pickupIndex), + distanceDimension.CumulVar(deliveryIndex))); + } + routing.SetPickupAndDeliveryPolicyOfAllVehicles( + RoutingModel.PICKUP_AND_DELIVERY_FIFO); + // [END pickup_delivery_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpPickupDeliveryLifo.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpPickupDeliveryLifo.cs new file mode 100644 index 0000000000000000000000000000000000000000..aedf0c09ee0ef12ccc924caf3d7c12486b258b80 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpPickupDeliveryLifo.cs @@ -0,0 +1,172 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal Pickup & Delivery Problem (PDP). +/// +public class VrpPickupDeliveryLifo { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0} + }; + // [START pickups_deliveries] + public int[][] PickupsDeliveries = { + new int[] {1, 6}, + new int[] {2, 10}, + new int[] {4, 3}, + new int[] {5, 9}, + new int[] {7, 8}, + new int[] {15, 11}, + new int[] {13, 12}, + new int[] {16, 14}, + }; + // [END pickups_deliveries] + public int VehicleNumber = 4; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + long totalDistance = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + long routeDistance = 0; + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + totalDistance += routeDistance; + } + Console.WriteLine("Total Distance of all routes: {0}m", totalDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transitCallbackIndex, 0, 3000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.GetMutableDimension("Distance"); + distanceDimension.SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Define Transportation Requests. + // [START pickup_delivery_constraint] + Solver solver = routing.solver(); + for(int i=0; i < data.PickupsDeliveries.GetLength(0); i++) { + long pickupIndex = manager.NodeToIndex(data.PickupsDeliveries[i][0]); + long deliveryIndex = manager.NodeToIndex(data.PickupsDeliveries[i][1]); + routing.AddPickupAndDelivery(pickupIndex, deliveryIndex); + solver.Add(solver.MakeEquality( + routing.VehicleVar(pickupIndex), + routing.VehicleVar(deliveryIndex))); + solver.Add(solver.MakeLessOrEqual( + distanceDimension.CumulVar(pickupIndex), + distanceDimension.CumulVar(deliveryIndex))); + } + routing.SetPickupAndDeliveryPolicyOfAllVehicles( + RoutingModel.PICKUP_AND_DELIVERY_LIFO); + // [END pickup_delivery_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpResources.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpResources.cs new file mode 100644 index 0000000000000000000000000000000000000000..1d53bfc5b7c29d39ea71bd223c8ed5c3bd8831e3 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpResources.cs @@ -0,0 +1,222 @@ +// 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] +using System; +using System.Linq; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Vehicles Routing Problem (VRP) with Resource Constraints. +/// +public class VrpResources { + // [START data_model] + class DataModel { + public long[,] TimeMatrix = { + {0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7}, + {6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14}, + {9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9}, + {8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16}, + {7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14}, + {3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8}, + {6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5}, + {2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10}, + {3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6}, + {2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5}, + {6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4}, + {6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10}, + {4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8}, + {4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6}, + {5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2}, + {9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9}, + {7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0}, + }; + public long[,] TimeWindows = { + {0, 5}, // depot + {7, 12}, // 1 + {10, 15}, // 2 + {5, 14}, // 3 + {5, 13}, // 4 + {0, 5}, // 5 + {5, 10}, // 6 + {0, 10}, // 7 + {5, 10}, // 8 + {0, 5}, // 9 + {10, 16}, // 10 + {10, 15}, // 11 + {0, 5}, // 12 + {5, 10}, // 13 + {7, 12}, // 14 + {10, 15}, // 15 + {5, 15}, // 16 + }; + public int VehicleNumber = 4; + // [START resources_data] + public int VehicleLoadTime = 5; + public int VehicleUnloadTime = 5; + public int DepotCapacity = 2; + // [END resources_data] + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + RoutingDimension timeDimension = routing.GetMutableDimension("Time"); + // Inspect solution. + long totalTime = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + var timeVar = timeDimension.CumulVar(index); + Console.Write("{0} Time({1},{2}) -> ", + manager.IndexToNode(index), + solution.Min(timeVar), + solution.Max(timeVar)); + index = solution.Value(routing.NextVar(index)); + } + var endTimeVar = timeDimension.CumulVar(index); + Console.WriteLine("{0} Time({1},{2})", + manager.IndexToNode(index), + solution.Min(endTimeVar), + solution.Max(endTimeVar)); + Console.WriteLine("Time of the route: {0}min", solution.Min(endTimeVar)); + totalTime += solution.Min(endTimeVar); + } + Console.WriteLine("Total time of all routes: {0}min", totalTime); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.TimeMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.TimeMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START time_constraint] + routing.AddDimension( + transitCallbackIndex, // transit callback + 30, // allow waiting time + 30, // vehicle maximum capacities + false, // start cumul to zero + "Time"); + RoutingDimension timeDimension = routing.GetMutableDimension("Time"); + // Add time window constraints for each location except depot. + for (int i = 1; i < data.TimeWindows.GetLength(0); ++i) { + long index = manager.NodeToIndex(i); + timeDimension.CumulVar(index).SetRange( + data.TimeWindows[i, 0], + data.TimeWindows[i, 1]); + } + // Add time window constraints for each vehicle start node. + for (int i = 0; i < data.VehicleNumber; ++i) { + long index = routing.Start(i); + timeDimension.CumulVar(index).SetRange( + data.TimeWindows[0, 0], + data.TimeWindows[0, 1]); + } + // [END time_constraint] + + // Add resource constraints at the depot. + // [START depot_load_time] + Solver solver = routing.solver(); + IntervalVar[] intervals = new IntervalVar[ data.VehicleNumber * 2 ]; + for (int i = 0; i < data.VehicleNumber; ++i) { + // Add load duration at start of routes + intervals[2*i] = solver.MakeFixedDurationIntervalVar( + timeDimension.CumulVar(routing.Start(i)), data.VehicleLoadTime, + "depot_interval"); + // Add unload duration at end of routes. + intervals[2*i+1] = solver.MakeFixedDurationIntervalVar( + timeDimension.CumulVar(routing.End(i)), data.VehicleUnloadTime, + "depot_interval"); + } + // [END depot_load_time] + + // [START depot_capacity] + long[] depot_usage = Enumerable.Repeat(1, intervals.Length).ToArray(); + solver.Add(solver.MakeCumulative(intervals, depot_usage, + data.DepotCapacity, "depot")); + // [END depot_capacity] + + // Instantiate route start and end times to produce feasible times. + // [START depot_start_end_times] + for (int i = 0; i < data.VehicleNumber; ++i) { + routing.AddVariableMinimizedByFinalizer( + timeDimension.CumulVar(routing.Start(i))); + routing.AddVariableMinimizedByFinalizer( + timeDimension.CumulVar(routing.End(i))); + } + // [END depot_start_end_times] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpStartsEnds.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpStartsEnds.cs new file mode 100644 index 0000000000000000000000000000000000000000..3f29f4c7a009932f95644c0efa747a1ca6a4ccdb --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpStartsEnds.cs @@ -0,0 +1,147 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +/// +/// Minimal TSP using distance matrix. +/// +public class VrpStartsEnds { + // [START data_model] + class DataModel { + public long[,] DistanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0} + }; + public int VehicleNumber = 4; + // [START starts_ends] + public int[] Starts = {1, 2, 15, 16}; + public int[] Ends = {0, 0, 0, 0}; + // [END starts_ends] + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + // Inspect solution. + long maxRouteDistance = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + long routeDistance = 0; + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + maxRouteDistance = Math.Max(routeDistance, maxRouteDistance); + } + Console.WriteLine("Maximum distance of the routes: {0}m", maxRouteDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.DistanceMatrix.GetLength(0), + data.VehicleNumber, + data.Starts, + data.Ends); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.DistanceMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension(transitCallbackIndex, 0, 2000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.GetMutableDimension("Distance"); + distanceDimension.SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpTimeWindows.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpTimeWindows.cs new file mode 100644 index 0000000000000000000000000000000000000000..6da928d01e686fb207b4f8bca34a14afec326fa4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpTimeWindows.cs @@ -0,0 +1,196 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +// [END import] + +// [START program_part1] +/// +/// Vehicles Routing Problem (VRP) with Time Windows. +/// +public class VrpTimeWindows { + // [START data_model] + class DataModel { + public long[,] TimeMatrix = { + {0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7}, + {6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14}, + {9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9}, + {8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16}, + {7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14}, + {3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8}, + {6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5}, + {2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10}, + {3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6}, + {2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5}, + {6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4}, + {6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10}, + {4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8}, + {4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6}, + {5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2}, + {9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9}, + {7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0}, + }; + public long[,] TimeWindows = { + {0, 5}, // depot + {7, 12}, // 1 + {10, 15}, // 2 + {16, 18}, // 3 + {10, 13}, // 4 + {0, 5}, // 5 + {5, 10}, // 6 + {0, 4}, // 7 + {5, 10}, // 8 + {0, 3}, // 9 + {10, 16}, // 10 + {10, 15}, // 11 + {0, 5}, // 12 + {5, 10}, // 13 + {7, 8}, // 14 + {10, 15}, // 15 + {11, 15}, // 16 + }; + public int VehicleNumber = 4; + public int Depot = 0; + }; + // [END data_model] + + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in DataModel data, + in RoutingModel routing, + in RoutingIndexManager manager, + in Assignment solution) { + RoutingDimension timeDimension = routing.GetMutableDimension("Time"); + // Inspect solution. + long totalTime = 0; + for (int i = 0; i < data.VehicleNumber; ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + var timeVar = timeDimension.CumulVar(index); + Console.Write("{0} Time({1},{2}) -> ", + manager.IndexToNode(index), + solution.Min(timeVar), + solution.Max(timeVar)); + index = solution.Value(routing.NextVar(index)); + } + var endTimeVar = timeDimension.CumulVar(index); + Console.WriteLine("{0} Time({1},{2})", + manager.IndexToNode(index), + solution.Min(endTimeVar), + solution.Max(endTimeVar)); + Console.WriteLine("Time of the route: {0}min", solution.Min(endTimeVar)); + totalTime += solution.Min(endTimeVar); + } + Console.WriteLine("Total time of all routes: {0}min", totalTime); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.TimeMatrix.GetLength(0), + data.VehicleNumber, + data.Depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return data.TimeMatrix[fromNode, toNode]; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START time_constraint] + routing.AddDimension( + transitCallbackIndex, // transit callback + 30, // allow waiting time + 30, // vehicle maximum capacities + false, // start cumul to zero + "Time"); + RoutingDimension timeDimension = routing.GetMutableDimension("Time"); + // Add time window constraints for each location except depot. + for (int i = 1; i < data.TimeWindows.GetLength(0); ++i) { + long index = manager.NodeToIndex(i); + timeDimension.CumulVar(index).SetRange( + data.TimeWindows[i, 0], + data.TimeWindows[i, 1]); + } + // Add time window constraints for each vehicle start node. + for (int i = 0; i < data.VehicleNumber; ++i) { + long index = routing.Start(i); + timeDimension.CumulVar(index).SetRange( + data.TimeWindows[0, 0], + data.TimeWindows[0, 1]); + } + // [END time_constraint] + + // Instantiate route start and end times to produce feasible times. + // [START depot_start_end_times] + for (int i = 0; i < data.VehicleNumber; ++i) { + routing.AddVariableMinimizedByFinalizer( + timeDimension.CumulVar(routing.Start(i))); + routing.AddVariableMinimizedByFinalizer( + timeDimension.CumulVar(routing.End(i))); + } + // [END depot_start_end_times] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = + FirstSolutionStrategy.Types.Value.PathCheapestArc; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program_part1] +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/VrpWithTimeLimit.cs b/libs/or-tools-src-ubuntu/examples/dotnet/VrpWithTimeLimit.cs new file mode 100644 index 0000000000000000000000000000000000000000..1c381785ed3c9058d2435d89ed0580aa33f8c940 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/VrpWithTimeLimit.cs @@ -0,0 +1,125 @@ +// 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] +using System; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; +using Google.Protobuf.WellKnownTypes; // Duration +// [END import] + +/// +/// Minimal Vrp using a time limit. +/// +public class Vrp { + // [START solution_printer] + /// + /// Print the solution. + /// + static void PrintSolution( + in RoutingIndexManager manager, + in RoutingModel routing, + in Assignment solution) { + // Inspect solution. + long maxRouteDistance = 0; + for (int i = 0; i < manager.GetNumberOfVehicles(); ++i) { + Console.WriteLine("Route for Vehicle {0}:", i); + long routeDistance = 0; + var index = routing.Start(i); + while (routing.IsEnd(index) == false) { + Console.Write("{0} -> ", manager.IndexToNode((int)index)); + var previousIndex = index; + index = solution.Value(routing.NextVar(index)); + routeDistance += routing.GetArcCostForVehicle(previousIndex, index, 0); + } + Console.WriteLine("{0}", manager.IndexToNode((int)index)); + Console.WriteLine("Distance of the route: {0}m", routeDistance); + maxRouteDistance = Math.Max(routeDistance, maxRouteDistance); + } + Console.WriteLine("Maximum distance of the routes: {0}m", maxRouteDistance); + } + // [END solution_printer] + + public static void Main(String[] args) { + // Instantiate the data problem. + // [START data] + int locationNumber = 20; + int vehicleNumber = 5; + int depot = 0; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + locationNumber, + vehicleNumber, + depot); + + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + int transitCallbackIndex = routing.RegisterTransitCallback( + (long fromIndex, long toIndex) => { + // Convert from routing variable Index to distance matrix NodeIndex. + var fromNode = manager.IndexToNode(fromIndex); + var toNode = manager.IndexToNode(toIndex); + return 1; } + ); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.AddDimension( + transitCallbackIndex, + /*slack=*/0, + /*horizon=*/3000, + /*start_cumul_to_zero=*/true, + "Distance"); + RoutingDimension distanceDimension = routing.GetMutableDimension("Distance"); + distanceDimension.SetGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + operations_research_constraint_solver.DefaultRoutingSearchParameters(); + searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc; + searchParameters.LocalSearchMetaheuristic = LocalSearchMetaheuristic.Types.Value.GuidedLocalSearch; + searchParameters.LogSearch = true; + searchParameters.TimeLimit = new Duration { Seconds = 10 }; + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.SolveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + PrintSolution(manager, routing, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/a_puzzle.cs b/libs/or-tools-src-ubuntu/examples/dotnet/a_puzzle.cs new file mode 100644 index 0000000000000000000000000000000000000000..edbb511b8008fd213c89aab119e7436473ef8718 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/a_puzzle.cs @@ -0,0 +1,262 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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.Linq; +using Google.OrTools.ConstraintSolver; + +public class APuzzle +{ + /** + * + * From "God plays dice" + * "A puzzle" + * http://gottwurfelt.wordpress.com/2012/02/22/a-puzzle/ + * And the sequel "Answer to a puzzle" + * http://gottwurfelt.wordpress.com/2012/02/24/an-answer-to-a-puzzle/ + * + * This problem instance was taken from the latter blog post. + * (Problem 1) + * + * """ + * 8809 = 6 + * 7111 = 0 + * 2172 = 0 + * 6666 = 4 + * 1111 = 0 + * 3213 = 0 + * 7662 = 2 + * 9312 = 1 + * 0000 = 4 + * 2222 = 0 + * 3333 = 0 + * 5555 = 0 + * 8193 = 3 + * 8096 = 5 + * 7777 = 0 + * 9999 = 4 + * 7756 = 1 + * 6855 = 3 + * 9881 = 5 + * 5531 = 0 + * + * 2581 = ? + * """ + * + * Note: + * This model yields 10 solutions, since x4 is not + * restricted in the constraints. + * All solutions has x assigned to the correct result. + * + * + * (Problem 2) + * The problem stated in "A puzzle" + * http://gottwurfelt.wordpress.com/2012/02/22/a-puzzle/ + * is + * """ + * 8809 = 6 + * 7662 = 2 + * 9312 = 1 + * 8193 = 3 + * 8096 = 5 + * 7756 = 1 + * 6855 = 3 + * 9881 = 5 + * + * 2581 = ? + * """ + * This problem instance yields two different solutions of x, + * one is the same (correct) as for the above problem instance, + * and one is not. + * This is because here x0,x1,x4 and x9 are underdefined. + * + * + */ + private static void Solve(int p = 1) + { + Solver solver = new Solver("APuzzle"); + + Console.WriteLine("\nSolving p{0}", p); + + + // + // Data + // + int n = 10; + + // + // Decision variables + // + IntVar x0 = solver.MakeIntVar(0, n-1, "x0"); + IntVar x1 = solver.MakeIntVar(0, n-1, "x1"); + IntVar x2 = solver.MakeIntVar(0, n-1, "x2"); + IntVar x3 = solver.MakeIntVar(0, n-1, "x3"); + IntVar x4 = solver.MakeIntVar(0, n-1, "x4"); + IntVar x5 = solver.MakeIntVar(0, n-1, "x5"); + IntVar x6 = solver.MakeIntVar(0, n-1, "x6"); + IntVar x7 = solver.MakeIntVar(0, n-1, "x7"); + IntVar x8 = solver.MakeIntVar(0, n-1, "x8"); + IntVar x9 = solver.MakeIntVar(0, n-1, "x9"); + + IntVar[] all = {x0,x1,x2,x3,x4,x5,x6,x7,x8,x9}; + + // The unknown, i.e. 2581 = x + IntVar x = solver.MakeIntVar(0, n-1, "x"); + + + // + // Constraints + // + + // Both problem are here shown in two + // approaches: + // - using equations + // - using a a matrix and Sum of each row + + if (p == 1) { + + // Problem 1 + solver.Add(x8+x8+x0+x9 == 6); + solver.Add(x7+x1+x1+x1 == 0); + solver.Add(x2+x1+x7+x2 == 0); + solver.Add(x6+x6+x6+x6 == 4); + solver.Add(x1+x1+x1+x1 == 0); + solver.Add(x3+x2+x1+x3 == 0); + solver.Add(x7+x6+x6+x2 == 2); + solver.Add(x9+x3+x1+x2 == 1); + solver.Add(x0+x0+x0+x0 == 4); + solver.Add(x2+x2+x2+x2 == 0); + solver.Add(x3+x3+x3+x3 == 0); + solver.Add(x5+x5+x5+x5 == 0); + solver.Add(x8+x1+x9+x3 == 3); + solver.Add(x8+x0+x9+x6 == 5); + solver.Add(x7+x7+x7+x7 == 0); + solver.Add(x9+x9+x9+x9 == 4); + solver.Add(x7+x7+x5+x6 == 1); + solver.Add(x6+x8+x5+x5 == 3); + solver.Add(x9+x8+x8+x1 == 5); + solver.Add(x5+x5+x3+x1 == 0); + + // The unknown + solver.Add(x2+x5+x8+x1 == x); + + } else if (p == 2) { + + // Another representation of Problem 1 + int[,] problem1 = { + {8,8,0,9, 6}, + {7,1,1,1, 0}, + {2,1,7,2, 0}, + {6,6,6,6, 4}, + {1,1,1,1, 0}, + {3,2,1,3, 0}, + {7,6,6,2, 2}, + {9,3,1,2, 1}, + {0,0,0,0, 4}, + {2,2,2,2, 0}, + {3,3,3,3, 0}, + {5,5,5,5, 0}, + {8,1,9,3, 3}, + {8,0,9,6, 5}, + {7,7,7,7, 0}, + {9,9,9,9, 4}, + {7,7,5,6, 1}, + {6,8,5,5, 3}, + {9,8,8,1, 5}, + {5,5,3,1, 0} + }; + + for(int i = 0; i < problem1.GetLength(0); i++) { + solver.Add( (from j in Enumerable.Range(0, 4) + select all[problem1[i,j]] + ).ToArray().Sum() == problem1[i,4] ); + } + + solver.Add(all[2]+all[5]+all[8]+all[1] == x); + + } else if (p == 3) { + + // Problem 2 + solver.Add(x8+x8+x0+x9 == 6); + solver.Add(x7+x6+x6+x2 == 2); + solver.Add(x9+x3+x1+x2 == 1); + solver.Add(x8+x1+x9+x3 == 3); + solver.Add(x8+x0+x9+x6 == 5); + solver.Add(x7+x7+x5+x6 == 1); + solver.Add(x6+x8+x5+x5 == 3); + solver.Add(x9+x8+x8+x1 == 5); + + // The unknown + solver.Add(x2+x5+x8+x1 == x); + + } else { + + // Another representation of Problem 2 + int[,] problem2 = { + {8,8,0,9, 6}, + {7,6,6,2, 2}, + {9,3,1,2, 1}, + {8,1,9,3, 3}, + {8,0,9,6, 5}, + {7,7,5,6, 1}, + {6,8,5,5, 3}, + {9,8,8,1, 5} + }; + + for(int i = 0; i < problem2.GetLength(0); i++) { + solver.Add( (from j in Enumerable.Range(0, 4) + select all[problem2[i,j]] + ).ToArray().Sum() == problem2[i,4] ); + } + + + solver.Add(all[2]+all[5]+all[8]+all[1] == x); + } + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + + solver.NewSearch(db); + while (solver.NextSolution()) { + Console.Write("x: {0} x0..x9: ", x.Value()); + for(int i = 0; i < n; i++) { + Console.Write(all[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + for(int p = 1; p <= 4; p++) { + Solve(p); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/a_round_of_golf.cs b/libs/or-tools-src-ubuntu/examples/dotnet/a_round_of_golf.cs new file mode 100644 index 0000000000000000000000000000000000000000..fb1316d1251512fea63a29fbea06c2341b7496a6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/a_round_of_golf.cs @@ -0,0 +1,183 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class ARoundOfGolf +{ + + /** + * + * A Round of Golf puzzle (Dell Logic Puzzles) in Google CP Solver. + * + * From http://brownbuffalo.sourceforge.net/RoundOfGolfClues.html + * """ + * Title: A Round of Golf + * Author: Ellen K. Rodehorst + * Publication: Dell Favorite Logic Problems + * Issue: Summer, 2000 + * Puzzle #: 9 + * Stars: 1 + * + * When the Sunny Hills Country Club golf course isn't in use by club members, + * of course, it's open to the club's employees. Recently, Jack and three other + * workers at the golf course got together on their day off to play a round of + * eighteen holes of golf. + * Afterward, all four, including Mr. Green, went to the clubhouse to total + * their scorecards. Each man works at a different job (one is a short-order + * cook), and each shot a different score in the game. No one scored below + * 70 or above 85 strokes. From the clues below, can you discover each man's + * full name, job and golf score? + * + * 1. Bill, who is not the maintenance man, plays golf often and had the lowest + * score of the foursome. + * 2. Mr. Clubb, who isn't Paul, hit several balls into the woods and scored ten + * strokes more than the pro-shop clerk. + * 3. In some order, Frank and the caddy scored four and seven more strokes than + * Mr. Sands. + * 4. Mr. Carter thought his score of 78 was one of his better games, even + * though Frank's score was lower. + * 5. None of the four scored exactly 81 strokes. + * + * Determine: First Name - Last Name - Job - Score + * """ + * + * See http://www.hakank.org/google_or_tools/a_round_of_golf.py + * + */ + private static void Solve() + { + Solver solver = new Solver("ARoundOfGolf"); + + // number of speakers + int n = 4; + + int Jack = 0; + int Bill = 1; + int Paul = 2; + int Frank = 3; + + // + // Decision variables + // + IntVar[] last_name = solver.MakeIntVarArray(n, 0, n-1, "last_name"); + // IntVar Green = last_name[0]; // not used + IntVar Clubb = last_name[1]; + IntVar Sands = last_name[2]; + IntVar Carter = last_name[3]; + + IntVar[] job = solver.MakeIntVarArray(n, 0, n-1, "job"); + // IntVar cook = job[0]; // not used + IntVar maintenance_man = job[1]; + IntVar clerk = job[2]; + IntVar caddy = job[3]; + + IntVar[] score = solver.MakeIntVarArray(n, 70, 85, "score"); + + // for search + IntVar[] all = new IntVar[n*3]; + for(int i = 0; i < n; i++) { + all[i] = last_name[i]; + all[i+n] = job[i]; + all[i+2*n] = score[i]; + } + + // + // Constraints + // + solver.Add(last_name.AllDifferent()); + solver.Add(job.AllDifferent()); + solver.Add(score.AllDifferent()); + + // 1. Bill, who is not the maintenance man, plays golf often and had + // the lowest score of the foursome. + solver.Add(maintenance_man != Bill); + solver.Add(score[Bill] < score[Jack]); + solver.Add(score[Bill] < score[Paul]); + solver.Add(score[Bill] < score[Frank]); + + // 2. Mr. Clubb, who isn't Paul, hit several balls into the woods and + // scored ten strokes more than the pro-shop clerk. + solver.Add(Clubb != Paul); + solver.Add(score.Element(Clubb) == score.Element(clerk) + 10); + + // 3. In some order, Frank and the caddy scored four and seven more + // strokes than Mr. Sands. + solver.Add(caddy != Frank); + solver.Add(Sands != Frank); + solver.Add(caddy != Sands); + + IntVar b3_a_1 = score.Element(Sands) + 4 == score[Frank]; + IntVar b3_a_2 = score.Element(caddy) == score.Element(Sands) + 7; + IntVar b3_b_1 = score.Element(Sands) + 7 == score[Frank]; + IntVar b3_b_2 = score.Element(caddy) == score.Element(Sands) + 4; + solver.Add( (b3_a_1*b3_a_2) + (b3_b_1*b3_b_2) == 1); + + // 4. Mr. Carter thought his score of 78 was one of his better games, + // even though Frank's score was lower. + solver.Add(Carter != Frank); + solver.Add(score.Element(Carter) == 78); + solver.Add(score[Frank] < score.Element(Carter)); + + // 5. None of the four scored exactly 81 strokes. + for(int i = 0; i < n; i++) { + solver.Add(score[i] != 81); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine( + "Last name: " + + String.Join(", ", (from i in last_name + select i.Value().ToString()).ToArray())); + Console.WriteLine( + "Job : " + + String.Join(", ", (from i in job + select i.Value().ToString()).ToArray())); + Console.WriteLine( + "Score : " + + String.Join(", ", (from i in score + select i.Value().ToString()).ToArray())); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/all_interval.cs b/libs/or-tools-src-ubuntu/examples/dotnet/all_interval.cs new file mode 100644 index 0000000000000000000000000000000000000000..793db46ac025a6daa0b5a88cd89c62ba0bfdc48e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/all_interval.cs @@ -0,0 +1,100 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class AllInterval +{ + + /** + * + * Implements the all interval problem. + * See http://www.hakank.org/google_or_tools/all_interval.py + * + */ + private static void Solve(int n=12) + { + Solver solver = new Solver("AllInterval"); + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, n-1, "x"); + IntVar[] diffs = solver.MakeIntVarArray(n-1, 1, n-1, "diffs"); + + // + // Constraints + // + solver.Add(x.AllDifferent()); + solver.Add(diffs.AllDifferent()); + + for(int k = 0; k < n - 1; k++) { + // solver.Add(diffs[k] == (x[k + 1] - x[k]).Abs()); + solver.Add(diffs[k] == (x[k + 1] - x[k].Abs())); + } + + + // symmetry breaking + solver.Add(x[0] < x[n - 1]); + solver.Add(diffs[0] < diffs[1]); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", x[i].Value()); + } + Console.Write(" diffs: "); + for(int i = 0; i < n-1; i++) { + Console.Write("{0} ", diffs[i].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 12; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/alldifferent_except_0.cs b/libs/or-tools-src-ubuntu/examples/dotnet/alldifferent_except_0.cs new file mode 100644 index 0000000000000000000000000000000000000000..cbe0c6a13e430f956eded62d0defdea313734359 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/alldifferent_except_0.cs @@ -0,0 +1,103 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + + +public class AllDifferentExcept0Test +{ + + // + // Decomposition of alldifferent_except_0 + // + public static void AllDifferentExcept0(Solver solver, IntVar[] a) { + + int n = a.Length; + for(int i = 0; i < n; i++) { + for(int j = 0; j < i; j++) { + solver.Add((a[i] != 0) * (a[j] != 0) <= (a[i] != a[j])); + } + } + } + + + /** + * + * Decomposition of alldifferent_except_0 + * + * See http://www.hakank.org/google_or_tools/map.py + * + * + */ + private static void Solve() + { + Solver solver = new Solver("AllDifferentExcept0"); + + // + // data + // + int n = 6; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, n - 1 , "x"); + + // + // Constraints + // + AllDifferentExcept0(solver, x); + + // we also require at least 2 0's + IntVar[] z_tmp = new IntVar[n]; + for(int i = 0; i < n; i++) { + z_tmp[i] = x[i] == 0; + } + IntVar z = z_tmp.Sum().VarWithName("z"); + solver.Add(z == 2); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + while (solver.NextSolution()) { + Console.Write("z: {0} x: ", z.Value()); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", x[i].Value()); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/assignment.cs b/libs/or-tools-src-ubuntu/examples/dotnet/assignment.cs new file mode 100644 index 0000000000000000000000000000000000000000..17d9e0821d50c5b114c1b315226cc8051dd5f47a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/assignment.cs @@ -0,0 +1,137 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class Assignment +{ + + /** + * + * Assignment problem + * + * From Wayne Winston "Operations Research", + * Assignment Problems, page 393f + * (generalized version with added test column) + * + * See See http://www.hakank.org/or-tools/assignment.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("Assignment"); + + // + // data + // + + // Problem instance + // hakank: I added the fifth column to make it more + // interesting + int rows = 4; + int cols = 5; + int[,] cost = { + {14, 5, 8, 7, 15}, + { 2, 12, 6, 5, 3}, + { 7, 8, 3, 9, 7}, + { 2, 4, 6, 10, 1} + }; + + + // + // Decision variables + // + IntVar[,] x = solver.MakeBoolVarMatrix(rows, cols, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + + // Exacly one assignment per row (task), + // i.e. all rows must be assigned with one worker + for(int i = 0; i < rows; i++) { + solver.Add((from j in Enumerable.Range(0, cols) + select x[i,j]).ToArray().Sum() == 1); + } + + // At most one assignments per column (worker) + for(int j = 0; j < cols; j++) { + solver.Add((from i in Enumerable.Range(0, rows) + select x[i,j]).ToArray().Sum() <= 1); + } + + // Total cost + IntVar total_cost = (from i in Enumerable.Range(0, rows) + from j in Enumerable.Range(0, cols) + select (cost[i,j] * x[i,j])).ToArray().Sum().Var(); + + // + // objective + // + OptimizeVar objective = total_cost.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("total_cost: {0}", total_cost.Value()); + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + Console.WriteLine("Assignments:"); + for(int i = 0; i < rows; i++) { + Console.Write("Task " + i); + for(int j = 0; j < cols; j++) { + if (x[i,j].Value() == 1) { + Console.WriteLine(" is done by " + j); + } + } + } + Console.WriteLine(); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/broken_weights.cs b/libs/or-tools-src-ubuntu/examples/dotnet/broken_weights.cs new file mode 100644 index 0000000000000000000000000000000000000000..ff05dbb9a65d92e858513946ea6fd711d8426c53 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/broken_weights.cs @@ -0,0 +1,167 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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.Linq; +using Google.OrTools.ConstraintSolver; + + +public class BrokenWeights +{ + + /** + * + * Broken weights problem. + * + * From http://www.mathlesstraveled.com/?p=701 + * """ + * Here's a fantastic problem I recently heard. Apparently it was first + * posed by Claude Gaspard Bachet de Meziriac in a book of arithmetic problems + * published in 1612, and can also be found in Heinrich Dorrie's 100 + * Great Problems of Elementary Mathematics. + * + * A merchant had a forty pound measuring weight that broke + * into four pieces as the result of a fall. When the pieces were + * subsequently weighed, it was found that the weight of each piece + * was a whole number of pounds and that the four pieces could be + * used to weigh every integral weight between 1 and 40 pounds. What + * were the weights of the pieces? + * + * Note that since this was a 17th-century merchant, he of course used a + * balance scale to weigh things. So, for example, he could use a 1-pound + * weight and a 4-pound weight to weigh a 3-pound object, by placing the + * 3-pound object and 1-pound weight on one side of the scale, and + * the 4-pound weight on the other side. + * """ + * + * Also see http://www.hakank.org/or-tools/broken_weights.py + * + */ + private static void Solve(int m=40, int n=4) + { + Solver solver = new Solver("BrokenWeights"); + + Console.WriteLine("Total weight (m): {0}", m); + Console.WriteLine("Number of pieces (n): {0}", n); + Console.WriteLine(); + + + // + // Decision variables + // + + IntVar[] weights = solver.MakeIntVarArray(n, 1, m , "weights"); + IntVar[,] x = new IntVar[m, n]; + // Note: in x_flat we insert the weights array before x + IntVar[] x_flat = new IntVar[m*n + n]; + for(int j = 0; j < n; j++) { + x_flat[j] = weights[j]; + } + for(int i = 0; i < m; i++) { + for(int j = 0; j < n; j++) { + x[i,j] = solver.MakeIntVar(-1, 1, "x["+i+","+j+"]"); + x_flat[n+i*n+j] = x[i,j]; + } + } + + + + // + // Constraints + // + + + // symmetry breaking + for(int j = 1; j < n; j++) { + solver.Add(weights[j-1] < weights[j]); + } + + + solver.Add(weights.Sum() == m); + + // Check that all weights from 1 to n (default 40) can be made. + // + // Since all weights can be on either side + // of the side of the scale we allow either + // -1, 0, or 1 of the weights, assuming that + // -1 is the weights on the left and 1 is on the right. + // + for(int i = 0; i < m; i++) { + solver.Add( (from j in Enumerable.Range(0, n) + select weights[j] * x[i,j]).ToArray().Sum() == i+1); + } + + + // + // The objective is to minimize the last weight. + // + OptimizeVar obj = weights[n-1].Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.Write("weights: "); + for(int i = 0; i < n; i++) { + Console.Write("{0,3} ", weights[i].Value()); + } + Console.WriteLine(); + for(int i = 0; i < 10+n*4; i++) { + Console.Write("-"); + } + Console.WriteLine(); + for(int i = 0; i < m; i++) { + Console.Write("weight {0,2}:", i+1); + for(int j = 0; j < n; j++) { + Console.Write("{0,3} ", x[i,j].Value()); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + + int m = 40; + int n = 4; + + if (args.Length > 0) { + m = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + n = Convert.ToInt32(args[1]); + } + + Solve(m, n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/bus_schedule.cs b/libs/or-tools-src-ubuntu/examples/dotnet/bus_schedule.cs new file mode 100644 index 0000000000000000000000000000000000000000..5b23e569e37c4ca75b78016d1f3513a81e54aacc --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/bus_schedule.cs @@ -0,0 +1,131 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class BusSchedule +{ + + + /** + * + * Bus scheduling. + * + * Minimize number of buses in timeslots. + * + * Problem from Taha "Introduction to Operations Research", page 58. + * + * This is a slightly more general model than Taha's. + * + * Also see, http://www.hakank.org/or-tools/bus_schedule.py + * + */ + private static long Solve(long num_buses_check = 0) + { + + Solver solver = new Solver("BusSchedule"); + + // + // data + // + int time_slots = 6; + int[] demands = {8, 10, 7, 12, 4, 4}; + int max_num = demands.Sum(); + + // + // Decision variables + // + + // How many buses start the schedule at time slot t + IntVar[] x = solver.MakeIntVarArray(time_slots, 0, max_num, "x"); + // Total number of buses + IntVar num_buses = x.Sum().VarWithName("num_buses"); + + // + // Constraints + // + + // Meet the demands for this and the next time slot. + for(int i = 0; i < time_slots - 1; i++) { + solver.Add(x[i]+x[i+1] >= demands[i]); + } + + // The demand "around the clock" + solver.Add(x[time_slots-1] + x[0] - demands[time_slots-1] == 0); + + // For showing all solutions of minimal number of buses + if (num_buses_check > 0) { + solver.Add(num_buses == num_buses_check); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + if (num_buses_check == 0) { + + // Minimize num_buses + OptimizeVar obj = num_buses.Minimize(1); + solver.NewSearch(db, obj); + + } else { + + solver.NewSearch(db); + + } + + long result = 0; + while (solver.NextSolution()) { + result = num_buses.Value(); + Console.Write("x: "); + for(int i = 0; i < time_slots; i++) { + Console.Write("{0,2} ", x[i].Value()); + } + Console.WriteLine("num_buses: " + num_buses.Value()); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + return result; + + } + + + + public static void Main(String[] args) + { + + Console.WriteLine("Check for minimum number of buses: "); + long num_buses = Solve(); + Console.WriteLine("\n... got {0} as minimal value.", num_buses); + Console.WriteLine("\nAll solutions: ", num_buses); + num_buses = Solve(num_buses); + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/circuit.cs b/libs/or-tools-src-ubuntu/examples/dotnet/circuit.cs new file mode 100644 index 0000000000000000000000000000000000000000..c4026c21596d3c71dab008597b92251484992a46 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/circuit.cs @@ -0,0 +1,118 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class CircuitTest +{ + + + /** + * circuit(solver, x) + * + * A decomposition of the global constraint circuit, based + * on some observation of the orbits in an array. + * + * Note: The domain of x must be 0..n-1 (not 1..n) + * since C# is 0-based. + */ + public static void circuit(Solver solver, IntVar[] x) { + + int n = x.Length; + IntVar[] z = solver.MakeIntVarArray(n, 0, n - 1, "z"); + + solver.Add(x.AllDifferent()); + solver.Add(z.AllDifferent()); + + // put the orbit of x[0] in z[0..n-1] + solver.Add(z[0] == x[0]); + for(int i = 1; i < n-1; i++) { + solver.Add(z[i] == x.Element(z[i-1])); + } + + // z may not be 0 for i < n-1 + for(int i = 1; i < n - 1; i++) { + solver.Add(z[i] != 0); + } + + // when i = n-1 it must be 0 + solver.Add(z[n - 1] == 0); + + } + + /** + * + * Implements a (decomposition) of the global constraint circuit. + * See http://www.hakank.org/google_or_tools/circuit.py + * + */ + private static void Solve(int n = 5) + { + Solver solver = new Solver("Circuit"); + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, n-1, "x"); + + // + // Constraints + // + circuit(solver, x); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write("{0} ", x[i].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 5; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/circuit2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/circuit2.cs new file mode 100644 index 0000000000000000000000000000000000000000..4e2ed0a26b95f0bd38ea20b8cb79307aad4af525 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/circuit2.cs @@ -0,0 +1,129 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class CircuitTest2 +{ + + + /** + * circuit(solver, x, z) + * + * A decomposition of the global constraint circuit, based + * on some observation of the orbits in an array. + * + * This version also exposes z (the path) to the public. + * + * Note: The domain of x must be 0..n-1 (not 1..n) + * since C# is 0-based. + */ + public static void circuit(Solver solver, IntVar[] x, IntVar[] z) { + + int n = x.Length; + + solver.Add(x.AllDifferent()); + solver.Add(z.AllDifferent()); + + // put the orbit of x[0] in z[0..n-1] + solver.Add(z[0] == x[0]); + for(int i = 1; i < n-1; i++) { + solver.Add(z[i] == x.Element(z[i-1])); + } + + // z may not be 0 for i < n-1 + for(int i = 1; i < n - 1; i++) { + solver.Add(z[i] != 0); + } + + // when i = n-1 it must be 0 + solver.Add(z[n - 1] == 0); + + } + + /** + * + * Implements a (decomposition) of the global constraint circuit + * and extracting the path. + * + * One circuit for n = 5 is 3 0 4 2 1 + * Thus the extracted path is 0 -> 3 -> 2 -> 4 -> 1 -> 0 + * + */ + private static void Solve(int n = 5) + { + Solver solver = new Solver("CircuitTest2"); + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, n-1, "x"); + IntVar[] path = solver.MakeIntVarArray(n, 0, n-1, "path"); + + // + // Constraints + // + circuit(solver, x, path); + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x : "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", x[i].Value()); + } + Console.Write("\npath: "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", path[i].Value()); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 5; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/coins3.cs b/libs/or-tools-src-ubuntu/examples/dotnet/coins3.cs new file mode 100644 index 0000000000000000000000000000000000000000..de242f192de9dce8b3eeacb3d7d9131c0d3d9a97 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/coins3.cs @@ -0,0 +1,114 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class Coins3 +{ + + /** + * + * Coin application. + * + * From "Constraint Logic Programming using ECLiPSe" + * pages 99f and 234 ff. + * The solution in ECLiPSe is at page 236. + * + * """ + * What is the minimum number of coins that allows one to pay _exactly_ + * any amount smaller than one Euro? Recall that there are six different + * euro cents, of denomination 1, 2, 5, 10, 20, 50 + * """ + + * Also see http://www.hakank.org/or-tools/coins3.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("Coins3"); + + // + // Data + // + int n = 6; // number of different coins + int[] variables = {1, 2, 5, 10, 25, 50}; + + IEnumerable RANGE = Enumerable.Range(0, n); + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 99, "x"); + IntVar num_coins = x.Sum().VarWithName("num_coins"); + + + // + // Constraints + // + + // Check that all changes from 1 to 99 can be made. + for(int j = 1; j < 100; j++) { + IntVar[] tmp = solver.MakeIntVarArray(n, 0, 99, "tmp"); + solver.Add(tmp.ScalProd(variables) == j); + + foreach(int i in RANGE) { + solver.Add(tmp[i] <= x[i]); + } + + } + + // + // Objective + // + OptimizeVar obj = num_coins.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_MIN_SIZE_LOWEST_MAX, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("num_coins: {0}", num_coins.Value()); + Console.Write("x: "); + foreach(int i in RANGE) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/coins_grid.cs b/libs/or-tools-src-ubuntu/examples/dotnet/coins_grid.cs new file mode 100644 index 0000000000000000000000000000000000000000..06eb73548f4ee10bff266cb3db57c1e9a8a26427 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/coins_grid.cs @@ -0,0 +1,113 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class CoinsGrid +{ + + /** + * + * Solves the Coins Grid problm. + * See http://www.hakank.org/google_or_tools/coins_grid.py + * + */ + private static void Solve(int n = 31, int c = 14) + { + Solver solver = new Solver("CoinsGrid"); + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, 1 , "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + + // sum row/columns == c + for(int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + IntVar[] col = new IntVar[n]; + for(int j = 0; j < n; j++) { + row[j] = x[i,j]; + col[j] = x[j,i]; + } + solver.Add(row.Sum() == c); + solver.Add(col.Sum() == c); + } + + // quadratic horizonal distance + IntVar[] obj_tmp = new IntVar[n * n]; + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + obj_tmp[i * n + j] = (x[i,j] * (i - j) * (i - j)).Var(); + } + } + IntVar obj_var = obj_tmp.Sum().Var(); + + // + // Objective + // + OptimizeVar obj = obj_var.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("obj: " + obj_var.Value()); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int n = 31; + int c = 14; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + c = Convert.ToInt32(args[1]); + } + + + Solve(n, c); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/combinatorial_auction2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/combinatorial_auction2.cs new file mode 100644 index 0000000000000000000000000000000000000000..c85d2acec1f3921129b76829b0df78eeea3d143e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/combinatorial_auction2.cs @@ -0,0 +1,118 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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.Linq; +using System.Collections; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +public class CombinatorialAuction2 +{ + /** + * + * Combinatorial auction. + * + * This is a more general model for the combinatorial example + * in the Numberjack Tutorial, pages 9 and 24 (slides 19/175 and + * 51/175). + * + * See http://www.hakank.org/or-tools/combinatorial_auction2.py + * + * The original and more talkative model is here: + * http://www.hakank.org/numberjack/combinatorial_auction.py + * + */ + private static void Solve() + { + Solver solver = new Solver("CombinatorialAuction2"); + + // + // Data + // + int n = 5; + + // the items for each bid + int[][] items = { + new int[] {0,1}, // A,B + new int[] {0,2}, // A, C + new int[] {1,3}, // B,D + new int[] {1,2,3}, // B,C,D + new int[] {0} // A + }; + + int[] bid_ids = {0,1,2,3}; + int[] bid_amount = {10,20,30,40,14}; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 1, "x"); + IntVar z = x.ScalProd(bid_amount).VarWithName("z"); + + // + // Constraints + // + + foreach(int bid_id in bid_ids) { + + var tmp2 = (from item in Enumerable.Range(0, n) + from i in Enumerable.Range(0, items[item].Length) + where items[item][i] == bid_id + select x[item]); + + solver.Add(tmp2.ToArray().Sum() <= 1); + + } + + + + + // + // Objective + // + OptimizeVar obj = z.Maximize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.Write("z: {0,2} x: ", z.Value()); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/contiguity_regular.cs b/libs/or-tools-src-ubuntu/examples/dotnet/contiguity_regular.cs new file mode 100644 index 0000000000000000000000000000000000000000..a22cc5c2902863eb17b3b7f88f4c40ce75a9d5f0 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/contiguity_regular.cs @@ -0,0 +1,209 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class ContiguityRegular +{ + + + /* + * Global constraint regular + * + * This is a translation of MiniZinc's regular constraint (defined in + * lib/zinc/globals.mzn), via the Comet code refered above. + * All comments are from the MiniZinc code. + * """ + * The sequence of values in array 'x' (which must all be in the range 1..S) + * is accepted by the DFA of 'Q' states with input 1..S and transition + * function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' + * (which must be in 1..Q) and accepting states 'F' (which all must be in + * 1..Q). We reserve state 0 to be an always failing state. + * """ + * + * x : IntVar array + * Q : number of states + * S : input_max + * d : transition matrix + * q0: initial state + * F : accepting states + * + */ + static void MyRegular(Solver solver, + IntVar[] x, + int Q, + int S, + int[,] d, + int q0, + int[] F) { + + + + Debug.Assert(Q > 0, "regular: 'Q' must be greater than zero"); + Debug.Assert(S > 0, "regular: 'S' must be greater than zero"); + + // d2 is the same as d, except we add one extra transition for + // each possible input; each extra transition is from state zero + // to state zero. This allows us to continue even if we hit a + // non-accepted input. + int[][] d2 = new int[Q+1][]; + for(int i = 0; i <= Q; i++) { + int[] row = new int[S]; + for(int j = 0; j < S; j++) { + if (i == 0) { + row[j] = 0; + } else { + row[j] = d[i-1,j]; + } + } + d2[i] = row; + } + + int[] d2_flatten = (from i in Enumerable.Range(0, Q+1) + from j in Enumerable.Range(0, S) + select d2[i][j]).ToArray(); + + // If x has index set m..n, then a[m-1] holds the initial state + // (q0), and a[i+1] holds the state we're in after processing + // x[i]. If a[n] is in F, then we succeed (ie. accept the + // string). + int m = 0; + int n = x.Length; + + IntVar[] a = solver.MakeIntVarArray(n+1-m, 0,Q+1, "a"); + // Check that the final state is in F + solver.Add(a[a.Length-1].Member(F)); + // First state is q0 + solver.Add(a[m] == q0); + + for(int i = 0; i < n; i++) { + solver.Add(x[i] >= 1); + solver.Add(x[i] <= S); + // Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == d2_flatten.Element(((a[i]*S)+(x[i]-1)))); + + } + + } + + + static void MyContiguity(Solver solver, IntVar[] x) { + + // the DFA (for regular) + int n_states = 3; + int input_max = 2; + int initial_state = 1; // note: state 0 is used for the failing state + // in MyRegular + + // all states are accepting states + int[] accepting_states = {1,2,3}; + + // The regular expression 0*1*0* + int[,] transition_fn = + { + {1,2}, // state 1 (start): input 0 -> state 1, input 1 -> state 2 i.e. 0* + {3,2}, // state 2: 1* + {3,0}, // state 3: 0* + }; + + MyRegular(solver, x, n_states, input_max, transition_fn, + initial_state, accepting_states); + + + + } + + + /** + * + * Global constraint contiguity using regular + * + * This is a decomposition of the global constraint global contiguity. + * + * From Global Constraint Catalogue + * http://www.emn.fr/x-info/sdemasse/gccat/Cglobal_contiguity.html + * """ + * Enforce all variables of the VARIABLES collection to be assigned to 0 or 1. + * In addition, all variables assigned to value 1 appear contiguously. + * + * Example: + * (<0, 1, 1, 0>) + * + * The global_contiguity constraint holds since the sequence 0 1 1 0 contains + * no more than one group of contiguous 1. + * """ + * + * Also see http://www.hakank.org/or-tools/contiguity_regular.py + * + */ + private static void Solve() + { + Solver solver = new Solver("ContiguityRegular"); + + // + // Data + // + int n = 7; // length of the array + + + // + // Decision variables + // + + // Note: We use 1..2 (instead of 0..1) and subtract 1 in the solution + IntVar[] reg_input = solver.MakeIntVarArray(n, 1, 2, "reg_input"); + + + // + // Constraints + // + MyContiguity(solver, reg_input); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(reg_input, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + // Note: here we subtract 1 to get 0..1 + Console.Write((reg_input[i].Value()-1) + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/contiguity_transition.cs b/libs/or-tools-src-ubuntu/examples/dotnet/contiguity_transition.cs new file mode 100644 index 0000000000000000000000000000000000000000..3cb90d486d3c7d999ffdbd3d55599244b5711cbb --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/contiguity_transition.cs @@ -0,0 +1,125 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class ContiguityRegular +{ + + static void MyContiguity(Solver solver, IntVar[] x) { + + // the DFA (for regular) + int initial_state = 1; + + // all states are accepting states + int[] accepting_states = {1,2,3}; + + // The regular expression 0*1*0* {state, input, next state} + long[][] transition_tuples = { + new long[] {1, 0, 1}, + new long[] {1, 1, 2}, + new long[] {2, 0, 3}, + new long[] {2, 1, 2}, + new long[] {3, 0, 3} + }; + + IntTupleSet result = new IntTupleSet(3); + result.InsertAll(transition_tuples); + + solver.Add(x.Transition(result, + initial_state, + accepting_states)); + } + + + /** + * + * Global constraint contiguity using Transition + * + * This version use the built-in TransitionConstraint. + * + * From Global Constraint Catalogue + * http://www.emn.fr/x-info/sdemasse/gccat/Cglobal_contiguity.html + * """ + * Enforce all variables of the VARIABLES collection to be assigned to 0 or 1. + * In addition, all variables assigned to value 1 appear contiguously. + * + * Example: + * (<0, 1, 1, 0>) + * + * The global_contiguity constraint holds since the sequence 0 1 1 0 contains + * no more than one group of contiguous 1. + * """ + * + * Also see http://www.hakank.org/or-tools/contiguity_regular.py + * + */ + private static void Solve() + { + Solver solver = new Solver("ContiguityRegular"); + + // + // Data + // + int n = 7; // length of the array + + + // + // Decision variables + // + + IntVar[] reg_input = solver.MakeIntVarArray(n, 0, 1, "reg_input"); + + + // + // Constraints + // + MyContiguity(solver, reg_input); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(reg_input, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write((reg_input[i].Value()) + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/costas_array.cs b/libs/or-tools-src-ubuntu/examples/dotnet/costas_array.cs new file mode 100644 index 0000000000000000000000000000000000000000..060e72c46cecb40ee3cf820c1bc4f1dce037244a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/costas_array.cs @@ -0,0 +1,181 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class CostasArray +{ + + /** + * + * Costas array + * + * From http://mathworld.wolfram.com/CostasArray.html: + * """ + * An order-n Costas array is a permutation on {1,...,n} such + * that the distances in each row of the triangular difference + * table are distinct. For example, the permutation {1,3,4,2,5} + * has triangular difference table {2,1,-2,3}, {3,-1,1}, {1,2}, + * and {4}. Since each row contains no duplications, the permutation + * is therefore a Costas array. + * """ + * + * Also see + * http://en.wikipedia.org/wiki/Costas_array + * http://hakank.org/or-tools/costas_array.py + * + */ + private static void Solve(int n = 6) + { + + Solver solver = new Solver("CostasArray"); + + // + // Data + // + Console.WriteLine("n: {0}", n); + + // + // Decision variables + // + IntVar[] costas = solver.MakeIntVarArray(n, 1, n, "costas"); + IntVar[,] differences = solver.MakeIntVarMatrix(n, n, -n+1, n-1, + "differences"); + + // + // Constraints + // + + // Fix the values in the lower triangle in the + // difference matrix to -n+1. This removes variants + // of the difference matrix for the the same Costas array. + for(int i = 0; i < n; i++) { + for(int j = 0; j <= i; j++ ) { + solver.Add(differences[i,j] == -n+1); + } + } + + // hakank: All the following constraints are from + // Barry O'Sullivans's original model. + // + solver.Add(costas.AllDifferent()); + + + // "How do the positions in the Costas array relate + // to the elements of the distance triangle." + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (i < j) { + solver.Add( differences[i,j] - (costas[j] - costas[j-i-1]) == 0); + } + } + } + + + // "All entries in a particular row of the difference + // triangle must be distint." + for(int i = 0; i < n-2; i++) { + IntVar[] tmp = ( + from j in Enumerable.Range(0, n) + where j > i + select differences[i,j]).ToArray(); + solver.Add(tmp.AllDifferent()); + + } + + // + // "All the following are redundant - only here to speed up search." + // + + // "We can never place a 'token' in the same row as any other." + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (i < j) { + solver.Add(differences[i,j] != 0); + solver.Add(differences[i,j] != 0); + } + } + } + + for(int k = 2; k < n; k++) { + for(int l = 2; l < n; l++) { + if (k < l) { + solver.Add( + (differences[k-2,l-1] + differences[k,l]) - + (differences[k-1,l-1] + differences[k-1,l]) == 0 + ); + } + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(costas, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("costas: "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", costas[i].Value()); + } + Console.WriteLine("\ndifferences:"); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + long v = differences[i,j].Value(); + if (v == -n+1) { + Console.Write(" "); + } else { + Console.Write("{0,2} ", v); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + int n = 6; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/covering_opl.cs b/libs/or-tools-src-ubuntu/examples/dotnet/covering_opl.cs new file mode 100644 index 0000000000000000000000000000000000000000..39da44748bcea79bdd359d55144cd1e715114d14 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/covering_opl.cs @@ -0,0 +1,128 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCoveringOPL +{ + + /** + * + * Solves a set covering problem. + * See See http://www.hakank.org/or-tools/set_covering_opl.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCoveringOPL"); + + // + // data + // + int num_workers = 32; + int num_tasks = 15; + + // Which worker is qualified for each task. + // Note: This is 1-based and will be made 0-base below. + int[][] qualified = { + new int[] { 1, 9, 19, 22, 25, 28, 31 }, + new int[] { 2, 12, 15, 19, 21, 23, 27, 29, 30, 31, 32 }, + new int[] { 3, 10, 19, 24, 26, 30, 32 }, + new int[] { 4, 21, 25, 28, 32 }, + new int[] { 5, 11, 16, 22, 23, 27, 31 }, + new int[] { 6, 20, 24, 26, 30, 32 }, + new int[] { 7, 12, 17, 25, 30, 31 } , + new int[] { 8, 17, 20, 22, 23 }, + new int[] { 9, 13, 14, 26, 29, 30, 31 }, + new int[] { 10, 21, 25, 31, 32 }, + new int[] { 14, 15, 18, 23, 24, 27, 30, 32 }, + new int[] { 18, 19, 22, 24, 26, 29, 31 }, + new int[] { 11, 20, 25, 28, 30, 32 }, + new int[] { 16, 19, 23, 31 }, + new int[] { 9, 18, 26, 28, 31, 32 } + }; + + int[] cost = {1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9}; + + + // + // Decision variables + // + IntVar[] hire = solver.MakeIntVarArray(num_workers, 0, 1, "workers"); + IntVar total_cost = hire.ScalProd(cost).Var(); + + // + // Constraints + // + + for(int j = 0; j < num_tasks; j++) { + // Sum the cost for hiring the qualified workers + // (also, make 0-base). + int len = qualified[j].Length; + IntVar[] tmp = new IntVar[len]; + for(int c = 0; c < len; c++) { + tmp[c] = hire[qualified[j][c] - 1]; + } + solver.Add(tmp.Sum() >= 1); + } + + + // + // objective + // + OptimizeVar objective = total_cost.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(hire, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("Cost: " + total_cost.Value()); + Console.Write("Hire: "); + for(int i = 0; i < num_workers; i++) { + if (hire[i].Value() == 1) { + Console.Write(i + " "); + } + } + Console.WriteLine("\n"); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/crew.cs b/libs/or-tools-src-ubuntu/examples/dotnet/crew.cs new file mode 100644 index 0000000000000000000000000000000000000000..27965dafdf47a4860d275ad017c6aef1da68f530 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/crew.cs @@ -0,0 +1,272 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class Crew +{ + + /** + * + * Crew allocation problem in Google CP Solver. + * + * From Gecode example crew + * examples/crew.cc + * """ + * Example: Airline crew allocation + * + * Assign 20 flight attendants to 10 flights. Each flight needs a certain + * number of cabin crew, and they have to speak certain languages. + * Every cabin crew member has two flights off after an attended flight. + * """ + * + * Also see http://www.hakank.org/or-tools/crew.pl + * + */ + private static void Solve(int sols = 1, int minimize = 0) + { + Solver solver = new Solver("Crew"); + + // + // Data + // + string[] names = {"Tom", + "David", + "Jeremy", + "Ron", + "Joe", + "Bill", + "Fred", + "Bob", + "Mario", + "Ed", + "Carol", + "Janet", + "Tracy", + "Marilyn", + "Carolyn", + "Cathy", + "Inez", + "Jean", + "Heather", + "Juliet"}; + + int num_persons = names.Length; + + + // + // Attributes of the crew + // + int[,] attributes = { + // steward, hostess, french, spanish, german + {1,0,0,0,1}, // Tom = 0 + {1,0,0,0,0}, // David = 1 + {1,0,0,0,1}, // Jeremy = 2 + {1,0,0,0,0}, // Ron = 3 + {1,0,0,1,0}, // Joe = 4 + {1,0,1,1,0}, // Bill = 5 + {1,0,0,1,0}, // Fred = 6 + {1,0,0,0,0}, // Bob = 7 + {1,0,0,1,1}, // Mario = 8 + {1,0,0,0,0}, // Ed = 9 + {0,1,0,0,0}, // Carol = 10 + {0,1,0,0,0}, // Janet = 11 + {0,1,0,0,0}, // Tracy = 12 + {0,1,0,1,1}, // Marilyn = 13 + {0,1,0,0,0}, // Carolyn = 14 + {0,1,0,0,0}, // Cathy = 15 + {0,1,1,1,1}, // Inez = 16 + {0,1,1,0,0}, // Jean = 17 + {0,1,0,1,1}, // Heather = 18 + {0,1,1,0,0} // Juliet = 19 + }; + + + // + // Required number of crew members. + // + // The columns are in the following order: + // staff : Overall number of cabin crew needed + // stewards : How many stewards are required + // hostesses : How many hostesses are required + // french : How many French speaking employees are required + // spanish : How many Spanish speaking employees are required + // german : How many German speaking employees are required + // + int[,] required_crew = { + {4,1,1,1,1,1}, // Flight 1 + {5,1,1,1,1,1}, // Flight 2 + {5,1,1,1,1,1}, // .. + {6,2,2,1,1,1}, + {7,3,3,1,1,1}, + {4,1,1,1,1,1}, + {5,1,1,1,1,1}, + {6,1,1,1,1,1}, + {6,2,2,1,1,1}, // ... + {7,3,3,1,1,1} // Flight 10 + }; + + int num_flights = required_crew.GetLength(0); + + + // + // Decision variables + // + IntVar[,] crew = solver.MakeIntVarMatrix(num_flights, num_persons, + 0, 1, "crew"); + IntVar[] crew_flat = crew.Flatten(); + + // number of working persons + IntVar num_working = solver.MakeIntVar(1, num_persons, "num_working"); + + // + // Constraints + // + + // number of working persons + IntVar[] nw = new IntVar[num_persons]; + for(int p = 0; p < num_persons; p++) { + IntVar[] tmp = new IntVar[num_flights]; + for(int f = 0; f < num_flights; f++) { + tmp[f] = crew[f,p]; + } + nw[p] = tmp.Sum() > 0; + } + solver.Add(nw.Sum() == num_working); + + for(int f = 0; f < num_flights; f++) { + // size of crew + IntVar[] tmp = new IntVar[num_persons]; + for(int p = 0; p < num_persons; p++) { + tmp[p] = crew[f,p]; + } + solver.Add(tmp.Sum() == required_crew[f,0]); + + // attributes and requirements + for(int a = 0; a < 5; a++) { + IntVar[] tmp2 = new IntVar[num_persons]; + for(int p = 0; p < num_persons; p++) { + tmp2[p] = (crew[f,p]*attributes[p,a]).Var(); + } + solver.Add(tmp2.Sum() >= required_crew[f,a+1]); + } + } + + // after a flight, break for at least two flights + for(int f = 0; f < num_flights - 2; f++) { + for(int i = 0; i < num_persons; i++) { + solver.Add(crew[f,i] + crew[f+1,i] + crew[f+2,i] <= 1); + } + } + + // extra contraint: all must work at least two of the flights + /* + for(int p = 0; p < num_persons; p++) { + IntVar[] tmp = new IntVar[num_flights]; + for(int f = 0; f < num_flights; f++) { + tmp[f] = crew[f,p]; + } + solver.Add(tmp.Sum() >= 2); + } + */ + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(crew_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + if (minimize > 0) { + OptimizeVar obj = num_working.Minimize(1); + solver.NewSearch(db, obj); + } else { + solver.NewSearch(db); + } + + int num_solutions = 0; + while (solver.NextSolution()) { + num_solutions++; + Console.WriteLine("Solution #{0}", num_solutions); + Console.WriteLine("Number working: {0}", num_working.Value()); + + for(int f = 0; f < num_flights; f++) { + for(int p = 0; p < num_persons; p++) { + Console.Write(crew[f,p].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine("\nFlights: "); + for(int f = 0; f < num_flights; f++) { + Console.Write("Flight #{0}: ", f); + for(int p = 0; p < num_persons; p++) { + if (crew[f, p].Value() == 1) { + Console.Write(names[p] + " "); + } + } + Console.WriteLine(); + } + + Console.WriteLine("\nCrew:"); + for(int p = 0; p < num_persons; p++) { + Console.Write("{0,-10}", names[p]); + for(int f = 0; f < num_flights; f++) { + if (crew[f,p].Value() == 1) { + Console.Write(f + " "); + } + } + Console.WriteLine(); + } + + Console.WriteLine(); + + if (num_solutions >= sols) { + break; + } + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 1; + int min = 0; // > 0 -> minimize num_working + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + min = Convert.ToInt32(args[1]); + } + + Solve(n, min); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/crossword.cs b/libs/or-tools-src-ubuntu/examples/dotnet/crossword.cs new file mode 100644 index 0000000000000000000000000000000000000000..4c5e5ee2b8bd0755b47b8e61040e5a8f911c6f72 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/crossword.cs @@ -0,0 +1,188 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +// Note: During compilation, there are a couple of +// warnings about assigned but never used variables. +// It's the characters a..z so it's quite benign. + + +public class Crossword +{ + + /** + * + * Solving a simple crossword. + * See http://www.hakank.org/or-tools/crossword2.py + * + * + */ + private static void Solve() + { + Solver solver = new Solver("Crossword"); + + // + // data + // + String[] alpha = {"_","a","b","c","d","e","f", + "g","h","i","j","k","l","m", + "n","o","p","q","r","s","t", + "u","v","w","x","y","z"}; + + int a=1; int b=2; int c=3; int d=4; int e=5; int f=6; + int g=7; int h=8; int i=9; int j=10; int k=11; int l=12; + int m=13; int n=14; int o=15; int p=16; int q=17; int r=18; + int s=19; int t=20; int u=21; int v=22; int w=23; int x=24; + int y=25; int z=26; + + const int num_words = 15; + int word_len = 5; + + int[,] AA = {{h, o, s, e, s}, // HOSES + {l, a, s, e, r}, // LASER + {s, a, i, l, s}, // SAILS + {s, h, e, e, t}, // SHEET + {s, t, e, e, r}, // STEER + {h, e, e, l, 0}, // HEEL + {h, i, k, e, 0}, // HIKE + {k, e, e, l, 0}, // KEEL + {k, n, o, t, 0}, // KNOT + {l, i, n, e, 0}, // LINE + {a, f, t, 0, 0}, // AFT + {a, l, e, 0, 0}, // ALE + {e, e, l, 0, 0}, // EEL + {l, e, e, 0, 0}, // LEE + {t, i, e, 0, 0}}; // TIE + + int num_overlapping = 12; + int[,] overlapping = {{0, 2, 1, 0}, // s + {0, 4, 2, 0}, // s + + {3, 1, 1, 2}, // i + {3, 2, 4, 0}, // k + {3, 3, 2, 2}, // e + + {6, 0, 1, 3}, // l + {6, 1, 4, 1}, // e + {6, 2, 2, 3}, // e + + {7, 0, 5, 1}, // l + {7, 2, 1, 4}, // s + {7, 3, 4, 2}, // e + {7, 4, 2, 4}}; // r + + int N = 8; + + // + // Decision variables + // + // for labeling on A and E + IntVar[,] A = solver.MakeIntVarMatrix(num_words, word_len, + 0, 26, "A"); + IntVar[] A_flat = A.Flatten(); + IntVar[] all = new IntVar[(num_words * word_len) + N]; + for(int I = 0; I < num_words; I++) { + for(int J = 0; J < word_len; J++) { + all[I * word_len + J] = A[I,J]; + } + } + + + + IntVar[] E = solver.MakeIntVarArray(N, 0, num_words, "E"); + for(int I = 0; I < N; I++) { + all[num_words * word_len + I] = E[I]; + } + + + + // + // Constraints + // + solver.Add(E.AllDifferent()); + + for(int I = 0; I < num_words; I++) { + for(int J = 0; J < word_len; J++) { + solver.Add(A[I,J] == AA[I,J]); + } + } + + // This contraint handles the overlappings. + // + // It's coded in MiniZinc as + // + // forall(i in 1..num_overlapping) ( + // A[E[overlapping[i,1]], overlapping[i,2]] = + // A[E[overlapping[i,3]], overlapping[i,4]] + // ) + // and in or-tools/Python as + // solver.Add( + // solver.Element(A_flat,E[overlapping[I][0]]*word_len+overlapping[I][1]) + // == + // solver.Element(A_flat,E[overlapping[I][2]]*word_len+overlapping[I][3])) + // + for(int I = 0; I < num_overlapping; I++) { + solver.Add( + A_flat.Element(E[overlapping[I,0]] * word_len + overlapping[I,1]) == + A_flat.Element(E[overlapping[I,2]] * word_len + overlapping[I,3])); + } + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine("E: "); + for(int ee = 0; ee < N; ee++) { + int e_val = (int)E[ee].Value(); + Console.Write(ee + ": (" + e_val + ") "); + for(int ii = 0; ii < word_len; ii++) { + Console.Write(alpha[(int)A[ee,ii].Value()]); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/crypta.cs b/libs/or-tools-src-ubuntu/examples/dotnet/crypta.cs new file mode 100644 index 0000000000000000000000000000000000000000..eb6d9f45fad97bb8d1d9587cd0a9667b69f22ad2 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/crypta.cs @@ -0,0 +1,119 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class Crypta +{ + /** + * + * Cryptarithmetic puzzle. + * + * Prolog benchmark problem GNU Prolog (crypta.pl) + * """ + * Name : crypta.pl + * Title : crypt-arithmetic + * Original Source: P. Van Hentenryck's book + * Adapted by : Daniel Diaz - INRIA France + * Date : September 1992 + * + * Solve the operation: + * + * B A I J J A J I I A H F C F E B B J E A + * + D H F G A B C D I D B I F F A G F E J E + * ----------------------------------------- + * = G J E G A C D D H F A F J B F I H E E F + * """ + * + * + * Also see http://hakank.org/or-tools/crypta.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Crypta"); + + // + // Decision variables + // + IntVar A = solver.MakeIntVar(0, 9, "A"); + IntVar B = solver.MakeIntVar(0, 9, "B"); + IntVar C = solver.MakeIntVar(0, 9, "C"); + IntVar D = solver.MakeIntVar(0, 9, "D"); + IntVar E = solver.MakeIntVar(0, 9, "E"); + IntVar F = solver.MakeIntVar(0, 9, "F"); + IntVar G = solver.MakeIntVar(0, 9, "G"); + IntVar H = solver.MakeIntVar(0, 9, "H"); + IntVar I = solver.MakeIntVar(0, 9, "I"); + IntVar J = solver.MakeIntVar(0, 9, "J"); + + IntVar[] LD = new IntVar[] {A,B,C,D,E,F,G,H,I,J}; + + IntVar Sr1 = solver.MakeIntVar(0, 1, "Sr1"); + IntVar Sr2 = solver.MakeIntVar(0, 1, "Sr2"); + + + // + // Constraints + // + solver.Add(LD.AllDifferent()); + solver.Add(B >= 1); + solver.Add(D >= 1); + solver.Add(G >= 1); + + solver.Add((A+10*E+100*J+1000*B+10000*B+100000*E+1000000*F+ + E+10*J+100*E+1000*F+10000*G+100000*A+1000000*F) == + (F+10*E+100*E+1000*H+10000*I+100000*F+1000000*B+10000000*Sr1)); + + + solver.Add((C+10*F+100*H+1000*A+10000*I+100000*I+1000000*J+ + F+10*I+100*B+1000*D+10000*I+100000*D+1000000*C+Sr1) == + (J+10*F+100*A+1000*F+10000*H+100000*D+1000000*D+10000000*Sr2)); + + + solver.Add((A+10*J+100*J+1000*I+10000*A+100000*B+ + B+10*A+100*G+1000*F+10000*H+100000*D+Sr2) == + (C+10*A+100*G+1000*E+10000*J+100000*G)); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(LD, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < 10; i++) { + Console.Write(LD[i].ToString() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nWallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/crypto.cs b/libs/or-tools-src-ubuntu/examples/dotnet/crypto.cs new file mode 100644 index 0000000000000000000000000000000000000000..6d316772095c46b59521e3c11e7a3fab5751d821 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/crypto.cs @@ -0,0 +1,152 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class Crypto +{ + /** + * + * Crypto problem. + * + * This is the standard benchmark "crypto" problem. + * + * From GLPK:s model cryto.mod. + * + * """ + * This problem comes from the newsgroup rec.puzzle. + * The numbers from 1 to 26 are assigned to the letters of the alphabet. + * The numbers beside each word are the total of the values assigned to + * the letters in the word (e.g. for LYRE: L, Y, R, E might be to equal + * 5, 9, 20 and 13, or any other combination that add up to 47). + * Find the value of each letter under the equations: + * + * BALLET 45 GLEE 66 POLKA 59 SONG 61 + * CELLO 43 JAZZ 58 QUARTET 50 SOPRANO 82 + * CONCERT 74 LYRE 47 SAXOPHONE 134 THEME 72 + * FLUTE 30 OBOE 53 SCALE 51 VIOLIN 100 + * FUGUE 50 OPERA 65 SOLO 37 WALTZ 34 + * + * Solution: + * A, B,C, D, E,F, G, H, I, J, K,L,M, N, O, P,Q, R, S,T,U, V,W, X, Y, Z + * 5,13,9,16,20,4,24,21,25,17,23,2,8,12,10,19,7,11,15,3,1,26,6,22,14,18 + * + * Reference: + * Koalog Constraint Solver , + * Simple problems, the crypto-arithmetic puzzle ALPHACIPHER. + * """ + * + * Also see http://hakank.org/or-tools/crypto.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Crypto"); + + int num_letters = 26; + + int BALLET = 45; + int CELLO = 43; + int CONCERT = 74; + int FLUTE = 30; + int FUGUE = 50; + int GLEE = 66; + int JAZZ = 58; + int LYRE = 47; + int OBOE = 53; + int OPERA = 65; + int POLKA = 59; + int QUARTET = 50; + int SAXOPHONE = 134; + int SCALE = 51; + int SOLO = 37; + int SONG = 61; + int SOPRANO = 82; + int THEME = 72; + int VIOLIN = 100; + int WALTZ = 34; + + + // + // Decision variables + // + IntVar[] LD = solver.MakeIntVarArray(num_letters, 1, num_letters, "LD"); + + // Note D is not used in the constraints below + IntVar A = LD[0]; IntVar B = LD[1]; IntVar C = LD[2]; // IntVar D = LD[3]; + IntVar E = LD[4]; IntVar F = LD[5]; IntVar G = LD[6]; IntVar H = LD[7]; + IntVar I = LD[8]; IntVar J = LD[9]; IntVar K = LD[10]; IntVar L = LD[11]; + IntVar M = LD[12]; IntVar N = LD[13]; IntVar O = LD[14]; IntVar P = LD[15]; + IntVar Q = LD[16]; IntVar R = LD[17]; IntVar S = LD[18]; IntVar T = LD[19]; + IntVar U = LD[20]; IntVar V = LD[21]; IntVar W = LD[22]; IntVar X = LD[23]; + IntVar Y = LD[24]; IntVar Z = LD[25]; + + // + // Constraints + // + solver.Add(LD.AllDifferent()); + solver.Add( B + A + L + L + E + T == BALLET); + solver.Add( C + E + L + L + O == CELLO); + solver.Add( C + O + N + C + E + R + T == CONCERT); + solver.Add( F + L + U + T + E == FLUTE); + solver.Add( F + U + G + U + E == FUGUE); + solver.Add( G + L + E + E == GLEE); + solver.Add( J + A + Z + Z == JAZZ); + solver.Add( L + Y + R + E == LYRE); + solver.Add( O + B + O + E == OBOE); + solver.Add( O + P + E + R + A == OPERA); + solver.Add( P + O + L + K + A == POLKA); + solver.Add( Q + U + A + R + T + E + T == QUARTET); + solver.Add(S + A + X + O + P + H + O + N + E == SAXOPHONE); + solver.Add( S + C + A + L + E == SCALE); + solver.Add( S + O + L + O == SOLO); + solver.Add( S + O + N + G == SONG); + solver.Add( S + O + P + R + A + N + O == SOPRANO); + solver.Add( T + H + E + M + E == THEME); + solver.Add( V + I + O + L + I + N == VIOLIN); + solver.Add( W + A + L + T + Z == WALTZ); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(LD, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + + String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + while (solver.NextSolution()) { + for(int i = 0; i < num_letters; i++) { + Console.WriteLine("{0}: {1,2}", str[i], LD[i].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nWallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/cscvrptw.cs b/libs/or-tools-src-ubuntu/examples/dotnet/cscvrptw.cs new file mode 100644 index 0000000000000000000000000000000000000000..55be6c2dd6f5f61620acc7f7dd7aebe954662da3 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/csdiet.cs b/libs/or-tools-src-ubuntu/examples/dotnet/csdiet.cs new file mode 100644 index 0000000000000000000000000000000000000000..f8d993ff2572c8522b392dca3678f590e1216a9a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/csdiet.cs @@ -0,0 +1,101 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class Diet +{ + /** + * + * Solves the Diet problem + * + * See http://www.hakank.org/google_or_tools/diet1.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Diet"); + + int n = 4; + int[] price = { 50, 20, 30, 80}; // in cents + + // requirements for each nutrition type + int[] limits = {500, 6, 10, 8}; + string[] products = {"A", "B", "C", "D"}; + + // nutritions for each product + int[] calories = {400, 200, 150, 500}; + int[] chocolate = {3, 2, 0, 0}; + int[] sugar = {2, 2, 4, 4}; + int[] fat = {2, 4, 1, 5}; + + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 100, "x"); + IntVar cost = x.ScalProd(price).Var(); + + + + // + // Constraints + // + + // solver.Add(solver.MakeScalProdGreaterOrEqual(x, calories, limits[0])); + solver.Add(x.ScalProd(calories) >= limits[0]); + solver.Add(x.ScalProd(chocolate) >= limits[1]); + solver.Add(x.ScalProd(sugar) >= limits[2]); + solver.Add(x.ScalProd(fat) >= limits[3]); + + // + // Objective + // + OptimizeVar obj = cost.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_PATH, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + while (solver.NextSolution()) { + Console.WriteLine("cost: {0}", cost.Value()); + Console.WriteLine("Products: "); + for(int i = 0; i < n; i++) { + Console.WriteLine("{0}: {1}", products[i], x[i].Value()); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/csflow.cs b/libs/or-tools-src-ubuntu/examples/dotnet/csflow.cs new file mode 100644 index 0000000000000000000000000000000000000000..d327f639dfe182ee47ac2fc971d3f5c5f7ff2ccd --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/csknapsack.cs b/libs/or-tools-src-ubuntu/examples/dotnet/csknapsack.cs new file mode 100644 index 0000000000000000000000000000000000000000..445aa24dda49b0808c54025ffb4c9a89f926512e --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/csls_api.cs b/libs/or-tools-src-ubuntu/examples/dotnet/csls_api.cs new file mode 100644 index 0000000000000000000000000000000000000000..41e2a4bc58dd4d403923bc7c8030b4871d1f218f --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/csrabbitspheasants.cs b/libs/or-tools-src-ubuntu/examples/dotnet/csrabbitspheasants.cs new file mode 100644 index 0000000000000000000000000000000000000000..536a0b0d26fc101fdb9a7e58a23cfab521d03151 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/cstsp.cs b/libs/or-tools-src-ubuntu/examples/dotnet/cstsp.cs new file mode 100644 index 0000000000000000000000000000000000000000..76cf52cf7646eab0c791ee09b6e8df3423f80d16 --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/dotnet/curious_set_of_integers.cs b/libs/or-tools-src-ubuntu/examples/dotnet/curious_set_of_integers.cs new file mode 100644 index 0000000000000000000000000000000000000000..6e223c3baacbe4456cfc2abb0e9daec0e66ae144 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/curious_set_of_integers.cs @@ -0,0 +1,122 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class CuriousSetOfIntegers +{ + + + public static void Decreasing(Solver solver, IntVar[] x) { + for(int i = 0; i < x.Length - 1; i++) { + solver.Add(x[i] <= x[i+1]); + } + } + + + /** + * + * Crypto problem in Google CP Solver. + * + * Martin Gardner (February 1967): + * """ + * The integers 1,3,8, and 120 form a set with a remarkable property: the + * product of any two integers is one less than a perfect square. Find + * a fifth number that can be added to the set without destroying + * this property. + * """ + * + * Also see, http://www.hakank.org/or-tools/curious_set_of_integers.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("CuriousSetOfIntegers"); + + // + // data + // + int n = 5; + int max_val = 10000; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, max_val, "x"); + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + for(int i = 0; i < n - 1; i++) { + for(int j = i + 1; j < n; j++) { + IntVar p = solver.MakeIntVar(0, max_val); + solver.Add((p.Square() - 1) - (x[i] * x[j]) == 0); + } + } + + // Symmetry breaking + Decreasing(solver, x); + + // This is the original problem + // Which is the fifth number? + int[] v = {1,3,8,120}; + IntVar[] b = (from i in Enumerable.Range(0, n) + select x[i].IsMember(v)).ToArray(); + solver.Add(b.Sum() == 4); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + + Solve(); + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/debruijn.cs b/libs/or-tools-src-ubuntu/examples/dotnet/debruijn.cs new file mode 100644 index 0000000000000000000000000000000000000000..adc232e3b3af00bc427b8afd9cd93d9bcfcaefba --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/debruijn.cs @@ -0,0 +1,202 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class DeBruijn +{ + + + /** + * + * ToNum(solver, a, num, base) + * + * channelling between the array a and the number num. + * + */ + private static Constraint ToNum(IntVar[] a, IntVar num, int bbase) { + int len = a.Length; + + IntVar[] tmp = new IntVar[len]; + for(int i = 0; i < len; i++) { + tmp[i] = (a[i]*(int)Math.Pow(bbase,(len-i-1))).Var(); + } + return tmp.Sum() == num; + } + + + + /** + * + * Implements "arbitrary" de Bruijn sequences. + * See http://www.hakank.org/or-tools/debruijn_binary.py + * + */ + private static void Solve(int bbase, int n, int m) + { + Solver solver = new Solver("DeBruijn"); + + + // Ensure that the number of each digit in bin_code is + // the same. Nice feature, but it can slow things down... + bool check_same_gcc = false; // true; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(m, 0, (int)Math.Pow(bbase, n) - 1, "x"); + IntVar[,] binary = solver.MakeIntVarMatrix(m, n, 0, bbase - 1, "binary"); + + // this is the de Bruijn sequence + IntVar[] bin_code = + solver.MakeIntVarArray(m, 0, bbase - 1, "bin_code"); + + // occurences of each number in bin_code + IntVar[] gcc = solver.MakeIntVarArray(bbase, 0, m, "gcc"); + + // for the branching + IntVar[] all = new IntVar[2 * m + bbase]; + for(int i = 0; i < m; i++) { + all[i] = x[i]; + all[m + i] = bin_code[i]; + } + for(int i = 0; i < bbase; i++) { + all[2 * m + i] = gcc[i]; + } + + + // + // Constraints + // + + solver.Add(x.AllDifferent()); + + // converts x <-> binary + for(int i = 0; i < m; i++) { + IntVar[] t = new IntVar[n]; + for(int j = 0; j < n; j++) { + t[j] = binary[i,j]; + } + solver.Add(ToNum(t, x[i], bbase)); + } + + // the de Bruijn condition: + // the first elements in binary[i] is the same as the last + // elements in binary[i-1] + for(int i = 1; i < m; i++) { + for(int j = 1; j < n; j++) { + solver.Add(binary[i - 1,j] == binary[i,j - 1]); + } + } + + // ... and around the corner + for(int j = 1; j < n; j++) { + solver.Add(binary[m - 1,j] == binary[0,j - 1]); + } + + // converts binary -> bin_code (de Bruijn sequence) + for(int i = 0; i < m; i++) { + solver.Add(bin_code[i] == binary[i,0]); + + } + + + // extra: ensure that all the numbers in the de Bruijn sequence + // (bin_code) has the same occurrences (if check_same_gcc is True + // and mathematically possible) + solver.Add(bin_code.Distribute(gcc)); + if (check_same_gcc && m % bbase == 0) { + for(int i = 1; i < bbase; i++) { + solver.Add(gcc[i] == gcc[i - 1]); + } + } + + // symmetry breaking: + // the minimum value of x should be first + // solver.Add(x[0] == x.Min()); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.CHOOSE_MIN_SIZE_LOWEST_MAX, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + for(int i = 0; i < m; i++) { + Console.Write(x[i].Value() + " "); + } + + Console.Write("\nde Bruijn sequence:"); + for(int i = 0; i < m; i++) { + Console.Write(bin_code[i].Value() + " "); + } + + Console.Write("\ngcc: "); + for(int i = 0; i < bbase; i++) { + Console.Write(gcc[i].Value() + " "); + } + Console.WriteLine("\n"); + + + // for debugging etc: show the full binary table + /* + Console.Write("binary:"); + for(int i = 0; i < m; i++) { + for(int j = 0; j < n; j++) { + Console.Write(binary[i][j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + */ + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int bbase = 2; + int n = 3; + int m = 8; + + if (args.Length > 0) { + bbase = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + n = Convert.ToInt32(args[1]); + } + + if (args.Length > 2) { + m = Convert.ToInt32(args[2]); + } + + Solve(bbase, n, m); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/discrete_tomography.cs b/libs/or-tools-src-ubuntu/examples/dotnet/discrete_tomography.cs new file mode 100644 index 0000000000000000000000000000000000000000..713a4639cfa5e628f95d08eae24ee6e65dc49ded --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/discrete_tomography.cs @@ -0,0 +1,216 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class DiscreteTomography +{ + + // default problem + static int[] default_rowsums = {0,0,8,2,6,4,5,3,7,0,0}; + static int[] default_colsums = {0,0,7,1,6,3,4,5,2,7,0,0}; + + static int[] rowsums2; + static int[] colsums2; + + + /** + * + * Discrete tomography + * + * Problem from http://eclipse.crosscoreop.com/examples/tomo.ecl.txt + * """ + * This is a little 'tomography' problem, taken from an old issue + * of Scientific American. + * + * A matrix which contains zeroes and ones gets "x-rayed" vertically and + * horizontally, giving the total number of ones in each row and column. + * The problem is to reconstruct the contents of the matrix from this + * information. Sample run: + * + * ?- go. + * 0 0 7 1 6 3 4 5 2 7 0 0 + * 0 + * 0 + * 8 * * * * * * * * + * 2 * * + * 6 * * * * * * + * 4 * * * * + * 5 * * * * * + * 3 * * * + * 7 * * * * * * * + * 0 + * 0 + * + * Eclipse solution by Joachim Schimpf, IC-Parc + * """ + * + * See http://www.hakank.org/or-tools/discrete_tomography.py + * + */ + private static void Solve(int[] rowsums, int[] colsums) + { + + Solver solver = new Solver("DiscreteTomography"); + + // + // Data + // + int r = rowsums.Length; + int c = colsums.Length; + + Console.Write("rowsums: "); + for(int i = 0; i < r; i++) { + Console.Write(rowsums[i] + " "); + } + Console.Write("\ncolsums: "); + for(int j = 0; j < c; j++) { + Console.Write(colsums[j] + " "); + } + Console.WriteLine("\n"); + + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(r, c, 0, 1, "x"); + IntVar[] x_flat = x.Flatten(); + + + // + // Constraints + // + + // row sums + for(int i = 0; i < r; i++) { + var tmp = from j in Enumerable.Range(0, c) select x[i,j]; + solver.Add(tmp.ToArray().Sum() == rowsums[i]); + } + + // cols sums + for(int j = 0; j < c; j++) { + var tmp = from i in Enumerable.Range(0, r) select x[i,j]; + solver.Add(tmp.ToArray().Sum() == colsums[j]); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + Console.Write("{0} ", x[i,j].Value() == 1 ? "#" : "." ); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /** + * + * Reads a discrete tomography file. + * File format: + * # a comment which is ignored + * % a comment which also is ignored + * rowsums separated by [,\s] + * colsums separated by [,\s] + * + * e.g. + * """ + * 0,0,8,2,6,4,5,3,7,0,0 + * 0,0,7,1,6,3,4,5,2,7,0,0 + * # comment + * % another comment + * """ + * + */ + private static void readFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + + TextReader inr = new StreamReader(file); + String str; + int lineCount = 0; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + str = str.Trim(); + + // ignore comments + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + if (lineCount == 0) { + rowsums2 = ConvLine(str); + } else if (lineCount == 1) { + colsums2 = ConvLine(str); + break; + } + + lineCount++; + + } // end while + + inr.Close(); + + } // end readFile + + + private static int[] ConvLine(String str) { + String[] tmp = Regex.Split(str, "[,\\s]+"); + int len = tmp.Length; + int[] sums = new int[len]; + for(int i = 0; i < len; i++) { + sums[i] = Convert.ToInt32(tmp[i]); + } + + return sums; + + } + + public static void Main(String[] args) + { + + if(args.Length > 0) { + readFile(args[0]); + Solve(rowsums2, colsums2); + } else { + Solve(default_rowsums, default_colsums); + } + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/divisible_by_9_through_1.cs b/libs/or-tools-src-ubuntu/examples/dotnet/divisible_by_9_through_1.cs new file mode 100644 index 0000000000000000000000000000000000000000..ffa00fe6c23a67b8f3d05bea9daaedc83fbfa276 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/divisible_by_9_through_1.cs @@ -0,0 +1,192 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class DivisibleBy9Through1 +{ + + + /** + * + * A simple propagator for modulo constraint. + * + * This implementation is based on the ECLiPSe version + * mentioned in "A Modulo propagator for ECLiPSE" + * http://www.hakank.org/constraint_programming_blog/2010/05/a_modulo_propagator_for_eclips.html + * The ECLiPSe Prolog source code: + * http://www.hakank.org/eclipse/modulo_propagator.ecl + * + */ + public static void MyMod(Solver solver, IntVar x, IntVar y, IntVar r) { + + long lbx = x.Min(); + long ubx = x.Max(); + long ubx_neg = -ubx; + long lbx_neg = -lbx; + int min_x = (int)Math.Min(lbx, ubx_neg); + int max_x = (int)Math.Max(ubx, lbx_neg); + + IntVar d = solver.MakeIntVar(min_x, max_x, "d"); + + // r >= 0 + solver.Add(r >= 0); + + // x*r >= 0 + solver.Add( x*r >= 0); + + // -abs(y) < r + solver.Add(-y.Abs() < r); + + // r < abs(y) + solver.Add(r < y.Abs()); + + // min_x <= d, i.e. d > min_x + solver.Add(d > min_x); + + // d <= max_x + solver.Add(d <= max_x); + + // x == y*d+r + solver.Add(x - (y*d + r) == 0); + + } + + + /** + * + * ToNum(solver, a, num, base) + * + * channelling between the array a and the number num + * + */ + private static Constraint ToNum(IntVar[] a, IntVar num, int bbase) { + int len = a.Length; + + IntVar[] tmp = new IntVar[len]; + for(int i = 0; i < len; i++) { + tmp[i] = (a[i]*(int)Math.Pow(bbase,(len-i-1))).Var(); + } + return tmp.Sum() == num; + } + + /** + * + * Solves the divisible by 9 through 1 problem. + * See http://www.hakank.org/google_or_tools/divisible_by_9_through_1.py + * + */ + private static void Solve(int bbase) + { + + Solver solver = new Solver("DivisibleBy9Through1"); + + + int m = (int)Math.Pow(bbase,(bbase-1)) - 1; + int n = bbase - 1; + + String[] digits_str = {"_","0","1","2","3","4","5","6","7","8","9"}; + + Console.WriteLine("base: " + bbase); + + // + // Decision variables + // + // digits + IntVar[] x = solver.MakeIntVarArray(n, 1, bbase - 1, "x"); + + // the numbers. t[0] contains the answe + IntVar[] t = solver.MakeIntVarArray(n, 0, m, "t"); + + + // + // Constraints + // + + solver.Add(x.AllDifferent()); + + // Ensure the divisibility of base .. 1 + IntVar zero = solver.MakeIntConst(0); + for(int i = 0; i < n; i++) { + int mm = bbase - i - 1; + IntVar[] tt = new IntVar[mm]; + for(int j = 0; j < mm; j++) { + tt[j] = x[j]; + } + solver.Add(ToNum(tt, t[i], bbase)); + MyMod(solver, t[i], solver.MakeIntConst(mm), zero); + + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine("\nt: "); + for(int i = 0; i < n; i++) { + Console.Write(t[i].Value() + " "); + } + Console.WriteLine("\n"); + + if (bbase != 10) { + Console.Write("Number base 10: " + t[0].Value()); + Console.Write(" Base " + bbase + ": "); + for(int i = 0; i < n; i++) { + Console.Write(digits_str[(int)x[i].Value() + 1]); + } + Console.WriteLine("\n"); + + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + + int bbase = 10; + if (args.Length > 0) { + bbase = Convert.ToInt32(args[0]); + if (bbase > 12) { + // Though base = 12 has no solution... + Console.WriteLine("Sorry, max relevant base is 12. Setting base to 12."); + bbase = 10; + } + } + + Solve(bbase); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/dudeney.cs b/libs/or-tools-src-ubuntu/examples/dotnet/dudeney.cs new file mode 100644 index 0000000000000000000000000000000000000000..0ed1ca0d03be6a821eac512a2e73ef5916fde739 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/dudeney.cs @@ -0,0 +1,122 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class DudeneyNumbers +{ + + + private static Constraint ToNum(IntVar[] a, IntVar num, int bbase) { + int len = a.Length; + + IntVar[] tmp = new IntVar[len]; + for(int i = 0; i < len; i++) { + tmp[i] = (a[i]*(int)Math.Pow(bbase,(len-i-1))).Var(); + } + return tmp.Sum() == num; + } + + + /** + * + * Dudeney numbers + * From Pierre Schaus blog post + * Dudeney number + * http://cp-is-fun.blogspot.com/2010/09/test-python.html + * """ + * I discovered yesterday Dudeney Numbers + * A Dudeney Numbers is a positive integer that is a perfect cube such that the sum + * of its decimal digits is equal to the cube root of the number. There are only six + * Dudeney Numbers and those are very easy to find with CP. + * I made my first experience with google cp solver so find these numbers (model below) + * and must say that I found it very convenient to build CP models in python! + * When you take a close look at the line: + * solver.Add(sum([10**(n-i-1)*x[i] for i in range(n)]) == nb) + * It is difficult to argue that it is very far from dedicated + * optimization languages! + * """ + * + * Also see: http://en.wikipedia.org/wiki/Dudeney_number + * + */ + private static void Solve() + { + + Solver solver = new Solver("DudeneyNumbers"); + + // + // data + // + int n = 6; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 9, "x"); + IntVar nb = solver.MakeIntVar(3, (int)Math.Pow(10,n), "nb"); + IntVar s = solver.MakeIntVar(1,9*n+1,"s"); + + // + // Constraints + // + solver.Add(nb == s*s*s); + solver.Add(x.Sum() == s); + + // solver.Add(ToNum(x, nb, 10)); + + // alternative + solver.Add((from i in Enumerable.Range(0, n) + select (x[i]*(int)Math.Pow(10,n-i-1)).Var()). + ToArray().Sum() == nb); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine(nb.Value()); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + + Solve(); + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/einav_puzzle2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/einav_puzzle2.cs new file mode 100644 index 0000000000000000000000000000000000000000..7390755e8b18b377ef31c1618f855b64254c915d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/einav_puzzle2.cs @@ -0,0 +1,228 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class EinavPuzzle2 +{ + /** + * + * A programming puzzle from Einav. + * + * From + * "A programming puzzle from Einav" + * http://gcanyon.wordpress.com/2009/10/28/a-programming-puzzle-from-einav/ + * """ + * My friend Einav gave me this programming puzzle to work on. Given + * this array of positive and negative numbers: + * 33 30 -10 -6 18 7 -11 -23 6 + * ... + * -25 4 16 30 33 -23 -4 4 -23 + * + * You can flip the sign of entire rows and columns, as many of them + * as you like. The goal is to make all the rows and columns sum to positive + * numbers (or zero), and then to find the solution (there are more than one) + * that has the smallest overall sum. So for example, for this array: + * 33 30 -10 + * -16 19 9 + * -17 -12 -14 + * You could flip the sign for the bottom row to get this array: + * 33 30 -10 + * -16 19 9 + * 17 12 14 + * Now all the rows and columns have positive sums, and the overall total is + * 108. + * But you could instead flip the second and third columns, and the second + * row, to get this array: + * 33 -30 10 + * 16 19 9 + * -17 12 14 + * All the rows and columns still total positive, and the overall sum is just + * 66. So this solution is better (I don't know if it's the best) + * A pure brute force solution would have to try over 30 billion solutions. + * I wrote code to solve this in J. I'll post that separately. + * """ + * + * Note: + * This is a port of Larent Perrons's Python version of my own einav_puzzle.py. + * He removed some of the decision variables and made it more efficient. + * Thanks! + * + * Also see http://www.hakank.org/or-tools/einav_puzzle2.py + * + */ + private static void Solve() + { + Solver solver = new Solver("EinavPuzzle2"); + + // + // Data + // + + // Small problem + // int rows = 3; + // int cols = 3; + // int[,] data = { + // { 33, 30, -10}, + // {-16, 19, 9}, + // {-17, -12, -14} + // }; + + + // Full problem + int rows = 27; + int cols = 9; + int[,] data = { + {33,30,10,-6,18,-7,-11,23,-6}, + {16,-19,9,-26,-8,-19,-8,-21,-14}, + {17,12,-14,31,-30,13,-13,19,16}, + {-6,-11,1,17,-12,-4,-7,14,-21}, + {18,-31,34,-22,17,-19,20,24,6}, + {33,-18,17,-15,31,-5,3,27,-3}, + {-18,-20,-18,31,6,4,-2,-12,24}, + {27,14,4,-29,-3,5,-29,8,-12}, + {-15,-7,-23,23,-9,-8,6,8,-12}, + {33,-23,-19,-4,-8,-7,11,-12,31}, + {-20,19,-15,-30,11,32,7,14,-5}, + {-23,18,-32,-2,-31,-7,8,24,16}, + {32,-4,-10,-14,-6,-1,0,23,23}, + {25,0,-23,22,12,28,-27,15,4}, + {-30,-13,-16,-3,-3,-32,-3,27,-31}, + {22,1,26,4,-2,-13,26,17,14}, + {-9,-18,3,-20,-27,-32,-11,27,13}, + {-17,33,-7,19,-32,13,-31,-2,-24}, + {-31,27,-31,-29,15,2,29,-15,33}, + {-18,-23,15,28,0,30,-4,12,-32}, + {-3,34,27,-25,-18,26,1,34,26}, + {-21,-31,-10,-13,-30,-17,-12,-26,31}, + {23,-31,-19,21,-17,-10,2,-23,23}, + {-3,6,0,-3,-32,0,-10,-25,14}, + {-19,9,14,-27,20,15,-5,-27,18}, + {11,-6,24,7,-17,26,20,-31,-25}, + {-25,4,-16,30,33,23,-4,-4,23} + }; + + + IEnumerable ROWS = Enumerable.Range(0, rows); + IEnumerable COLS = Enumerable.Range(0, cols); + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(rows, cols, -100, 100, "x"); + IntVar[] x_flat = x.Flatten(); + + int[] signs_domain = {-1,1}; + // This don't work at the moment... + IntVar[] row_signs = solver.MakeIntVarArray(rows, signs_domain, "row_signs"); + IntVar[] col_signs = solver.MakeIntVarArray(cols, signs_domain, "col_signs"); + + + + // To optimize + IntVar total_sum = x_flat.Sum().VarWithName("total_sum"); + + // + // Constraints + // + foreach(int i in ROWS) { + foreach(int j in COLS) { + solver.Add(x[i,j] == data[i,j] * row_signs[i] * col_signs[j]); + } + } + + // row sums + IntVar[] row_sums = (from i in ROWS + select (from j in COLS + select x[i,j] + ).ToArray().Sum().Var()).ToArray(); + + foreach(int i in ROWS) { + row_sums[i].SetMin(0); + } + + // col sums + IntVar[] col_sums = (from j in COLS + select (from i in ROWS + select x[i,j] + ).ToArray().Sum().Var()).ToArray(); + + foreach(int j in COLS) { + col_sums[j].SetMin(0); + } + + + // + // Objective + // + OptimizeVar obj = total_sum.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(col_signs.Concat(row_signs).ToArray(), + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MAX_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("Sum: {0}",total_sum.Value()); + Console.Write("row_sums: "); + foreach(int i in ROWS) { + Console.Write(row_sums[i].Value() + " "); + } + Console.Write("\nrow_signs: "); + foreach(int i in ROWS) { + Console.Write(row_signs[i].Value() + " "); + } + + Console.Write("\ncol_sums: "); + foreach(int j in COLS) { + Console.Write(col_sums[j].Value() + " "); + } + Console.Write("\ncol_signs: "); + foreach(int j in COLS) { + Console.Write(col_signs[j].Value() + " "); + } + Console.WriteLine("\n"); + foreach(int i in ROWS) { + foreach(int j in COLS) { + Console.Write("{0,3} ", x[i,j].Value()); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/eq10.cs b/libs/or-tools-src-ubuntu/examples/dotnet/eq10.cs new file mode 100644 index 0000000000000000000000000000000000000000..e91bb604921cb6aeba3b99e566205be0334b1769 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/eq10.cs @@ -0,0 +1,112 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class Eq10 +{ + /** + * + * Eq 10 in Google CP Solver. + * + * Standard benchmark problem. + * + * Also see http://hakank.org/or-tools/eq10.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Eq10"); + + int n = 7; + + // + // Decision variables + // + IntVar X1 = solver.MakeIntVar(0, 10, "X1"); + IntVar X2 = solver.MakeIntVar(0, 10, "X2"); + IntVar X3 = solver.MakeIntVar(0, 10, "X3"); + IntVar X4 = solver.MakeIntVar(0, 10, "X4"); + IntVar X5 = solver.MakeIntVar(0, 10, "X5"); + IntVar X6 = solver.MakeIntVar(0, 10, "X6"); + IntVar X7 = solver.MakeIntVar(0, 10, "X7"); + + IntVar[] X = {X1,X2,X3,X4,X5,X6,X7}; + + + // + // Constraints + // + solver.Add(0+98527*X1+34588*X2+5872*X3+59422*X5+65159*X7 + == 1547604+30704*X4+29649*X6); + + solver.Add(0+98957*X2+83634*X3+69966*X4+62038*X5+37164*X6+85413*X7 + == 1823553+93989*X1); + + solver.Add(900032+10949*X1+77761*X2+67052*X5 + == 0+80197*X3+61944*X4+92964*X6+44550*X7); + + solver.Add(0+73947*X1+84391*X3+81310*X5 + == 1164380+96253*X2+44247*X4+70582*X6+33054*X7); + + solver.Add(0+13057*X3+42253*X4+77527*X5+96552*X7 + == 1185471+60152*X1+21103*X2+97932*X6); + + solver.Add(1394152+66920*X1+55679*X4 + == 0+64234*X2+65337*X3+45581*X5+67707*X6+98038*X7); + + solver.Add(0+68550*X1+27886*X2+31716*X3+73597*X4+38835*X7 + == 279091+88963*X5+76391*X6); + + solver.Add(0+76132*X2+71860*X3+22770*X4+68211*X5+78587*X6 + == 480923+48224*X1+82817*X7); + + solver.Add(519878+94198*X2+87234*X3+37498*X4 + == 0+71583*X1+25728*X5+25495*X6+70023*X7); + + solver.Add(361921+78693*X1+38592*X5+38478*X6 + == 0+94129*X2+43188*X3+82528*X4+69025*X7); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(X, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(X[i].ToString() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/eq20.cs b/libs/or-tools-src-ubuntu/examples/dotnet/eq20.cs new file mode 100644 index 0000000000000000000000000000000000000000..147768944d2a65100657a0fa75a3ac272ceb51cc --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/eq20.cs @@ -0,0 +1,124 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class Eq20 +{ + /** + * + * Eq 20 in Google CP Solver. + * + * Standard benchmark problem. + * + * Also see http://hakank.org/or-tools/eq20.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Eq20"); + + int n = 7; + + // + // Decision variables + // + IntVar X0 = solver.MakeIntVar(0, 10, "X0"); + IntVar X1 = solver.MakeIntVar(0, 10, "X1"); + IntVar X2 = solver.MakeIntVar(0, 10, "X2"); + IntVar X3 = solver.MakeIntVar(0, 10, "X3"); + IntVar X4 = solver.MakeIntVar(0, 10, "X4"); + IntVar X5 = solver.MakeIntVar(0, 10, "X5"); + IntVar X6 = solver.MakeIntVar(0, 10, "X6"); + + IntVar[] X = {X0,X1,X2,X3,X4,X5,X6}; + + + // + // Constraints + // + solver.Add(-76706*X0 + 98205*X1 + 23445*X2 + 67921*X3 + 24111*X4 + + -48614*X5 + -41906*X6 == 821228); + solver.Add(87059*X0 + -29101*X1 + -5513*X2 + -21219*X3 + 22128*X4 + + 7276*X5 + 57308*X6 == 22167); + solver.Add(-60113*X0 + 29475*X1 + 34421*X2 + -76870*X3 + 62646*X4 + + 29278*X5 + -15212*X6 == 251591); + solver.Add(49149*X0 + 52871*X1 + -7132*X2 + 56728*X3 + -33576*X4 + + -49530*X5 + -62089*X6 == 146074); + solver.Add(-10343*X0 + 87758*X1 + -11782*X2 + 19346*X3 + 70072*X4 + + -36991*X5 + 44529*X6 == 740061); + solver.Add(85176*X0 + -95332*X1 + -1268*X2 + 57898*X3 + 15883*X4 + + 50547*X5 + 83287*X6 == 373854); + solver.Add(-85698*X0 + 29958*X1 + 57308*X2 + 48789*X3 + -78219*X4 + + 4657*X5 + 34539*X6 == 249912); + solver.Add(-67456*X0 + 84750*X1 + -51553*X2 + 21239*X3 + 81675*X4 + + -99395*X5 + -4254*X6 == 277271); + solver.Add(94016*X0 + -82071*X1 + 35961*X2 + 66597*X3 + -30705*X4 + + -44404*X5 + -38304*X6 == 25334); + solver.Add(-60301*X0 + 31227*X1 + 93951*X2 + 73889*X3 + 81526*X4 + + -72702*X5 + 68026*X6 == 1410723); + solver.Add(-16835*X0 + 47385*X1 + 97715*X2 + -12640*X3 + 69028*X4 + + 76212*X5 + -81102*X6 == 1244857); + solver.Add(-43277*X0 + 43525*X1 + 92298*X2 + 58630*X3 + 92590*X4 + + -9372*X5 + -60227*X6 == 1503588); + solver.Add(-64919*X0 + 80460*X1 + 90840*X2 + -59624*X3 + -75542*X4 + + 25145*X5 + -47935*X6 == 18465); + solver.Add(-45086*X0 + 51830*X1 + -4578*X2 + 96120*X3 + 21231*X4 + + 97919*X5 + 65651*X6 == 1198280); + solver.Add(85268*X0 + 54180*X1 + -18810*X2 + -48219*X3 + 6013*X4 + + 78169*X5 + -79785*X6 == 90614); + solver.Add(8874*X0 + -58412*X1 + 73947*X2 + 17147*X3 + 62335*X4 + + 16005*X5 + 8632*X6 == 752447); + solver.Add(71202*X0 + -11119*X1 + 73017*X2 + -38875*X3 + -14413*X4 + + -29234*X5 + 72370*X6 == 129768); + solver.Add(1671*X0 + -34121*X1 + 10763*X2 + 80609*X3 + 42532*X4 + + 93520*X5 + -33488*X6 == 915683); + solver.Add(51637*X0 + 67761*X1 + 95951*X2 + 3834*X3 + -96722*X4 + + 59190*X5 + 15280*X6 == 533909); + solver.Add(-16105*X0 + 62397*X1 + -6704*X2 + 43340*X3 + 95100*X4 + + -68610*X5 + 58301*X6 == 876370); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(X, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(X[i].ToString() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fill_a_pix.cs b/libs/or-tools-src-ubuntu/examples/dotnet/fill_a_pix.cs new file mode 100644 index 0000000000000000000000000000000000000000..83e5c65963191463d27375e9c6fe7569863fa856 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fill_a_pix.cs @@ -0,0 +1,248 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class FillAPix +{ + + static int X = -1; + + // + // Default problem. + // Puzzle 1 from + // http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/rules + + // + static int default_n = 10; + static int[,] default_puzzle = {{X,X,X,X,X,X,X,X,0,X}, + {X,8,8,X,2,X,0,X,X,X}, + {5,X,8,X,X,X,X,X,X,X}, + {X,X,X,X,X,2,X,X,X,2}, + {1,X,X,X,4,5,6,X,X,X}, + {X,0,X,X,X,7,9,X,X,6}, + {X,X,X,6,X,X,9,X,X,6}, + {X,X,6,6,8,7,8,7,X,5}, + {X,4,X,6,6,6,X,6,X,4}, + {X,X,X,X,X,X,3,X,X,X}}; + + // for the actual problem + static int n; + static int[,] puzzle; + + + /** + * + * Fill-a-Pix problem + * + * From http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/basiclogic + * """ + * Each puzzle consists of a grid containing clues in various places. The + * object is to reveal a hidden picture by painting the squares around each + * clue so that the number of painted squares, including the square with + * the clue, matches the value of the clue. + * """ + * + * http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/rules + * """ + * Fill-a-Pix is a Minesweeper-like puzzle based on a grid with a pixilated + * picture hidden inside. Using logic alone, the solver determines which + * squares are painted and which should remain empty until the hidden picture + * is completely exposed. + * """ + * + * Fill-a-pix History: + * http://www.conceptispuzzles.com/index.aspx?uri=puzzle/fill-a-pix/history + * + * Also see http://www.hakank.org/google_or_tools/fill_a_pix.py + * + * + */ + private static void Solve() + { + Solver solver = new Solver("FillAPix"); + + // + // data + // + int[] S = {-1, 0, 1}; + + Console.WriteLine("Problem:"); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (puzzle[i,j] > X) { + Console.Write(puzzle[i,j] + " "); + } else { + Console.Write("X "); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + + + // + // Decision variables + // + IntVar[,] pict = solver.MakeIntVarMatrix(n, n, 0, 1, "pict"); + IntVar[] pict_flat = pict.Flatten(); // for branching + + // + // Constraints + // + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (puzzle[i,j] > X) { + + // this cell is the sum of all surrounding cells + var tmp = from a in S from b in S where + i + a >= 0 && + j + b >= 0 && + i + a < n && + j + b < n + select(pict[i+a,j+b]); + + solver.Add(tmp.ToArray().Sum() == puzzle[i,j]); + + } + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(pict_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + int sol = 0; + while (solver.NextSolution()) { + sol++; + Console.WriteLine("Solution #{0} ", sol + " "); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++){ + Console.Write(pict[i,j].Value() == 1 ? "#" : " "); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /** + * + * Reads a Fill-a-Pix file. + * File format: + * # a comment which is ignored + * % a comment which also is ignored + * number of rows and columns (n x n) + * < + * row number of neighbours lines... + * > + * + * 0..8 means number of neighbours, "." mean unknown (may be a mine) + * + * Example (from fill_a_pix1.txt): + * + * 10 + * ........0. + * .88.2.0... + * 5.8....... + * .....2...2 + * 1...456... + * .0...79..6 + * ...6..9..6 + * ..668787.5 + * .4.666.6.4 + * ......3... + * + */ + private static void readFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + int lineCount = 0; + + TextReader inr = new StreamReader(file); + String str; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + + str = str.Trim(); + + // ignore comments + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + Console.WriteLine(str); + if (lineCount == 0) { + n = Convert.ToInt32(str); // number of rows + puzzle = new int[n,n]; + } else { + // the problem matrix + String[] row = Regex.Split(str, ""); + for(int j = 1; j <= n; j++) { + String s = row[j]; + if (s.Equals(".")) { + puzzle[lineCount-1, j-1] = -1; + } else { + puzzle[lineCount-1, j-1] = Convert.ToInt32(s); + } + } + } + + lineCount++; + + } // end while + + inr.Close(); + + } // end readFile + + + + + public static void Main(String[] args) + { + String file = ""; + if (args.Length > 0) { + file = args[0]; + readFile(file); + } else { + puzzle = default_puzzle; + n = default_n; + } + + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsProgram.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsProgram.fs new file mode 100644 index 0000000000000000000000000000000000000000..9e8b1b8d40fa3c9646603206ae797eed4d1f2285 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsProgram.fs @@ -0,0 +1,96 @@ +// 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. + +open System +open Google.OrTools.FSharp +open Google.OrTools.LinearSolver + +let solver solverType = + let svr = Solver.CreateSolver("IntegerProgramming", solverType.ToString()) + + // x1, x2 and x3 are continuous non-negative variables. + let x1 = svr.MakeNumVar(0.0, Double.PositiveInfinity, "x1") + let x2 = svr.MakeNumVar(0.0, Double.PositiveInfinity, "x2") + let x3 = svr.MakeNumVar(0.0, Double.PositiveInfinity, "x3") + + // Maximize 10 * x1 + 6 * x2 + 4 * x3. + let objective = svr.Objective() + objective.SetCoefficient(x1, 10.0) + objective.SetCoefficient(x2, 6.0) + objective.SetCoefficient(x3, 4.0) + objective.SetMaximization() + + // x1 + x2 + x3 <= 100. + let c0 = svr.MakeConstraint(Double.NegativeInfinity, 100.0) + c0.SetCoefficient(x1, 1.0) + c0.SetCoefficient(x2, 1.0) + c0.SetCoefficient(x3, 1.0) + + // 10 * x1 + 4 * x2 + 5 * x3 <= 600. + let c1 = svr.MakeConstraint(Double.NegativeInfinity, 600.0) + c1.SetCoefficient(x1, 10.0) + c1.SetCoefficient(x2, 4.0) + c1.SetCoefficient(x3, 5.0) + + // 2 * x1 + 2 * x2 + 6 * x3 <= 300. + let c2 = svr.MakeConstraint(Double.NegativeInfinity, 300.0) + c2.SetCoefficient(x1, 2.0) + c2.SetCoefficient(x2, 2.0) + c2.SetCoefficient(x3, 6.0) + + printfn "Number of variables = %i" (svr.NumVariables()) + printfn "Number of constraints = %i" (svr.NumConstraints()) + + let resultStatus = svr.Solve() + + // Check that the problem has an optimal solution. + match resultStatus with + | status when status <> Solver.ResultStatus.OPTIMAL -> + printfn "The problem does not have an optimal solution!" + exit 0 + | _ -> + printfn "Problem solved in %i milliseconds" (svr.WallTime()) + + // The objective value of the solution. + printfn "Optimal objective value = %f" (svr.Objective().Value()) + + // The value of each variable in the solution. + printfn "x1 = %f" (x1.SolutionValue()) + printfn "x2 = %f" (x2.SolutionValue()) + printfn "x3 = %f" (x3.SolutionValue()) + + printfn "Advanced usage:" + let activities = svr.ComputeConstraintActivities(); + + printfn "Problem solved in %i iterations" (svr.Iterations()) + printfn "x1: reduced cost = %f" (x1.ReducedCost()) + printfn "x2: reduced cost = %f" (x2.ReducedCost()) + printfn "x3: reduced cost = %f" (x3.ReducedCost()) + printfn "c0: dual value = %f" (c0.DualValue()) + printfn " activity = %f" (activities.[c0.Index()]) + printfn "c1: dual value = %f" (c1.DualValue()) + printfn " activity = %f" (activities.[c1.Index()]) + printfn "c2: dual value = %f" (c2.DualValue()) + printfn " activity = %f" (activities.[c2.Index()]) + +[] +let main = + printfn "---- Linear programming example with %A ----" LinearProgramming.GLOP + solver LinearProgramming.GLOP + + // printfn "---- Linear programming example with %A ----" LinearProgramming.GLPK + // solver LinearProgramming.GLPK + + printfn "---- Linear programming example with %A ----" LinearProgramming.CLP + solver LinearProgramming.CLP + exit 0 diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsequality-inequality.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsequality-inequality.fs new file mode 100644 index 0000000000000000000000000000000000000000..2add18610a6f60d1c2274d3b6950a42488c89868 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsequality-inequality.fs @@ -0,0 +1,35 @@ +(* + Minimize: -3 * x[0] + 1 * x[1] + 1 * x[2] + + Subject to: 1 * x[0] - 2 * x[1] + 1 * x[2] <= 11 + -4 * x[0] + 1 * x[1] + 2 * x[2] >= 3 + -2 * x[0] + 0 * x[1] - 1 * x[2] = -1 + + And x[0] >= 0, x[1] >= 0, x[2] >= 0 + + + Answer: + Objective: 2.000000 + var[0] : 0.000000 + var[1] : 1.000000 + var[2] : 1.000000 + var[3] : 12.000000 + var[4] : 0.000000 + + Note: When entering the matrix, it is reduced to standard form with appropriate slack variables. +*) + +open System +open Google.OrTools.FSharp + +let opts = SolverOpts.Default + .Name("Equality/Inequality Constraints") + .Goal(Minimize) + .Objective([-3.0;1.0;1.0;0.0;0.0]) + .MatrixEq([[1.0;-4.0;2.0]; [-2.0;1.0;0.0]; [1.0;2.0;1.0]; [1.0;0.0;0.0]; [0.0;-1.0;0.0]]) + .VectorEq([11.0; 3.0; 1.0]) + .VarLowerBound(0. |> List.replicate 5) + .VarUpperBound(Double.PositiveInfinity |> List.replicate 5) + +let slvrCLP = opts.Algorithm(LP CLP) |> lpSolve |> SolverSummary +let slvrGLOP = opts.Algorithm(LP GLOP) |> lpSolve |> SolverSummary diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsequality.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsequality.fs new file mode 100644 index 0000000000000000000000000000000000000000..9948735233e625d8499a24c68aaf6ea291d08d0b --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsequality.fs @@ -0,0 +1,34 @@ +(* + Minimize: 2 * x[0] + 1 * x[1] + + Subject to: 3 * x[0] + 1 * x[1] - 1 * x[2] = 3 + 4 * x[0] + 3 * x[1] - 1 * x[3] = 6 + 1 * x[0] + 2 * x[1] - 1 * x[4] = 2 + + And x[j] >= 0 for j in [0..4] + + + Answer: + Objective: 2.400000 + x[0] : 0.600000 + x[1] : 1.200000 + x[2] : 0.000000 + x[3] : 0.000000 + x[4] : 1.000000 +*) + +open System +open Google.OrTools.FSharp + +let opts = SolverOpts.Default + .Name("Equality Constraints") + .Goal(Minimize) + .Objective([2.0;1.0;0.0;0.0;0.0]) + .MatrixEq([[3.0;4.0;1.0]; [1.0;3.0;2.0]; [-1.0;0.0;0.0]; [0.0;-1.0;0.0]; [0.0;0.0;-1.0]]) + .VectorEq([3.0; 6.0; 2.0]) + .VarLowerBound([0.0; 0.0; 0.0; 0.0; 0.0]) + .VarUpperBound([Double.PositiveInfinity; Double.PositiveInfinity; Double.PositiveInfinity; Double.PositiveInfinity; Double.PositiveInfinity]) + .Algorithm(LP CLP) + +let slvr = opts |> lpSolve |> SolverSummary +slvr |> ignore diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsinteger-linear-program.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsinteger-linear-program.fs new file mode 100644 index 0000000000000000000000000000000000000000..9d8fe60051e7b9ea3806addee549e7e56431d014 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsinteger-linear-program.fs @@ -0,0 +1,54 @@ +(* + Minimize: 6 * x[0] + 3 * x[1] + 1 * x[2] + 2 * x[3] + + Subject to: 1 * x[0] + 1 * x[1] + 1 * x[2] + 1 * x[3] <= 8 + 2 * x[0] + 1 * x[1] + 3 * x[2] + 0 * x[3] <= 12 + 0 * x[0] + 5 * x[1] + 1 * x[2] + 3 * x[3] <= 6 + + And x[0] <= 1, x[1] <= 1, x[2] <= 4, x[3] <= 2 + + Answer: + Integer Program Solution + + Problem solved in 51 milliseconds + Iterations: 1 + + Objective: 11.000000 + var[0] : 1.000000 + var[1] : 0.000000 + var[2] : 3.000000 + var[3] : 1.000000 + Advanced usage: + Problem solved in 0 branch-and-bound nodes + + Linear Program Solution + + Problem solved in 2 milliseconds + Iterations: 2 + + Objective: 11.111111 + var[0] : 1.000000 + var[1] : 0.000000 + var[2] : 3.333333 + var[3] : 0.888889 +*) + +open Google.OrTools.FSharp + +let opts = SolverOpts.Default + .Goal(Maximize) + .Objective([6.;3.;1.;2.]) + .Matrix([[1.;2.;0.]; [1.;1.;5.]; [1.;3.;1.]; [1.;0.;3.]]) + .VectorUpperBound([8.; 12.; 6.]) + .VarLowerBound([0.; 0.; 0.; 0.]) + .VarUpperBound([1.; 1.; 4.; 2.]) + + +printfn "\n\nInteger Program Solution" +let slvrIP = opts.Name("IP Solution").Algorithm(IP CBC) |> lpSolve |> SolverSummary +printfn "Advanced usage:" +printfn "Problem solved in %i branch-and-bound nodes" (slvrIP.Nodes()) + +printfn "\n\nLinear Program Solution" +let slvrLP = opts.Name("LP Solution").Algorithm(LP CLP) |> lpSolve |> SolverSummary + diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsintegerprogramming.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsintegerprogramming.fs new file mode 100644 index 0000000000000000000000000000000000000000..d6a5e9f21b02d85fd389cd068b9ae10e75d7e27c --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsintegerprogramming.fs @@ -0,0 +1,64 @@ +// Copyright 2010-2014 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. + +open System +open Google.OrTools.FSharp +open Google.OrTools.LinearSolver + +let solver solverType = + let svr = Solver.CreateSolver("IntegerProgramming", solverType.ToString()) + + // x1 and x2 are integer non-negative variables. + let x1 = svr.MakeIntVar(0.0, Double.PositiveInfinity, "x1") + let x2 = svr.MakeIntVar(0.0, Double.PositiveInfinity, "x2") + + // Minimize x1 + 2 * x2. + let objective = svr.Objective() + objective.SetMinimization() + objective.SetCoefficient(x1, 1.0) + objective.SetCoefficient(x2, 2.0) + + // 2 * x2 + 3 * x1 >= 17. + let ct = svr.MakeConstraint(17.0, Double.PositiveInfinity) + ct.SetCoefficient(x1, 3.0) + ct.SetCoefficient(x2, 2.0) + + let resultStatus = svr.Solve() + + // Check that the problem has an optimal solution. + match resultStatus with + | status when status <> Solver.ResultStatus.OPTIMAL -> + printfn "The problem does not have an optimal solution!" + exit 0 + | _ -> + printfn "Problem solved in %i milliseconds" (svr.WallTime()) + + // The objective value of the solution. + printfn "Optimal objective value = %f" (objective.Value()) + + // The value of each variable in the solution. + printfn "x1 = %f" (x1.SolutionValue()) + printfn "x2 = %f" (x2.SolutionValue()) + + printfn "Advanced usage:" + printfn "Problem solved in %i branch-and-bound nodes" (svr.Nodes()) + + +// printfn "---- Integer programming example with %A ----" IntegerProgramming.GLPK +// solver IntegerProgramming.GLPK + +printfn "---- Linear programming example with %A ----" IntegerProgramming.CBC +solver IntegerProgramming.CBC + +// printfn "---- Linear programming example with %A ----" IntegerProgramming.SCIP +// solver IntegerProgramming.SCIP diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsknapsack.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsknapsack.fs new file mode 100644 index 0000000000000000000000000000000000000000..e07ddc2fd3a32f0ac5b459a6efbd7bf520b33615 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsknapsack.fs @@ -0,0 +1,30 @@ +(* +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. +*) + +open Google.OrTools.FSharp + +let profits = [ 360L; 83L; 59L; 130L; 431L; 67L; 230L; 52L; 93L; 125L; 670L; 892L; 600L; 38L; 48L; 147L; 78L; 256L; 63L; 17L; 120L; 164L; 432L; 35L; 92L; 110L; 22L; 42L; 50L; 323L; 514L; 28L; 87L; 73L; 78L; 15L; 26L; 78L; 210L; 36L; 85L; 189L; 274L; 43L; 33L; 10L; 19L; 389L; 276L; 312L ] +let weights = array2D [ [ 7L; 0L; 30L; 22L; 80L; 94L; 11L; 81L; 70L; 64L; 59L; 18L; 0L; +36L; 3L; 8L; 15L; 42L; 9L; 0L; 42L; 47L; 52L; 32L; 26L; 48L; 55L; 6L; 29L; 84L; +2L; 4L; 18L; 56L; 7L; 29L; 93L; 44L; 71L; 3L; 86L; 66L; 31L; 65L; 0L; 79L; 20L; +65L; 52L; 13L ] ] +let capacities = [ 850L ] + +printfn "Solving knapsack with %i items and %i dimensions" (profits.Length) (weights.Length) + +let ks = knapsackSolve "test" KnapsackSolverAlgorithm.MultidimensionBranchAndBound profits weights capacities +let computedProfit = ks.Solve(); +let expectedProfit = 7534L +printfn "Optimal Profit = %d \nexpected = %d \nOptimal Soltion = %b " computedProfit expectedProfit (ks.IsSolutionOptimal()) diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fslinearprogramming.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fslinearprogramming.fs new file mode 100644 index 0000000000000000000000000000000000000000..6d46128025fed2211b338739f6dbaecabf7f6fe2 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fslinearprogramming.fs @@ -0,0 +1,94 @@ +// 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. + +open System +open Google.OrTools.FSharp +open Google.OrTools.LinearSolver + +let solver solverType = + let svr = Solver.CreateSolver("IntegerProgramming", solverType.ToString()) + + // x1, x2 and x3 are continuous non-negative variables. + let x1 = svr.MakeNumVar(0.0, Double.PositiveInfinity, "x1") + let x2 = svr.MakeNumVar(0.0, Double.PositiveInfinity, "x2") + let x3 = svr.MakeNumVar(0.0, Double.PositiveInfinity, "x3") + + // Maximize 10 * x1 + 6 * x2 + 4 * x3. + let objective = svr.Objective() + objective.SetCoefficient(x1, 10.0) + objective.SetCoefficient(x2, 6.0) + objective.SetCoefficient(x3, 4.0) + objective.SetMaximization() + + // x1 + x2 + x3 <= 100. + let c0 = svr.MakeConstraint(Double.NegativeInfinity, 100.0) + c0.SetCoefficient(x1, 1.0) + c0.SetCoefficient(x2, 1.0) + c0.SetCoefficient(x3, 1.0) + + // 10 * x1 + 4 * x2 + 5 * x3 <= 600. + let c1 = svr.MakeConstraint(Double.NegativeInfinity, 600.0) + c1.SetCoefficient(x1, 10.0) + c1.SetCoefficient(x2, 4.0) + c1.SetCoefficient(x3, 5.0) + + // 2 * x1 + 2 * x2 + 6 * x3 <= 300. + let c2 = svr.MakeConstraint(Double.NegativeInfinity, 300.0) + c2.SetCoefficient(x1, 2.0) + c2.SetCoefficient(x2, 2.0) + c2.SetCoefficient(x3, 6.0) + + printfn "Number of variables = %i" (svr.NumVariables()) + printfn "Number of constraints = %i" (svr.NumConstraints()) + + let resultStatus = svr.Solve() + + // Check that the problem has an optimal solution. + match resultStatus with + | status when status <> Solver.ResultStatus.OPTIMAL -> + printfn "The problem does not have an optimal solution!" + exit 0 + | _ -> + printfn "Problem solved in %i milliseconds" (svr.WallTime()) + + // The objective value of the solution. + printfn "Optimal objective value = %f" (svr.Objective().Value()) + + // The value of each variable in the solution. + printfn "x1 = %f" (x1.SolutionValue()) + printfn "x2 = %f" (x2.SolutionValue()) + printfn "x3 = %f" (x3.SolutionValue()) + + printfn "Advanced usage:" + let activities = svr.ComputeConstraintActivities(); + + printfn "Problem solved in %i iterations" (svr.Iterations()) + printfn "x1: reduced cost = %f" (x1.ReducedCost()) + printfn "x2: reduced cost = %f" (x2.ReducedCost()) + printfn "x3: reduced cost = %f" (x3.ReducedCost()) + printfn "c0: dual value = %f" (c0.DualValue()) + printfn " activity = %f" (activities.[c0.Index()]) + printfn "c1: dual value = %f" (c1.DualValue()) + printfn " activity = %f" (activities.[c1.Index()]) + printfn "c2: dual value = %f" (c2.DualValue()) + printfn " activity = %f" (activities.[c2.Index()]) + + +printfn "---- Linear programming example with %A ----" LinearProgramming.GLOP +solver LinearProgramming.GLOP + +// printfn "---- Linear programming example with %A ----" LinearProgramming.GLPK +// solver LinearProgramming.GLPK + +printfn "---- Linear programming example with %A ----" LinearProgramming.CLP +solver LinearProgramming.CLP diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsnetwork-max-flow-lpSolve.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsnetwork-max-flow-lpSolve.fs new file mode 100644 index 0000000000000000000000000000000000000000..0d3a039ea6d0faa0aec2fd0843acb33747448da1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsnetwork-max-flow-lpSolve.fs @@ -0,0 +1,38 @@ +(* + The matrix in the problem represent the arcs versus the number of nodes + (not including the source and sink nodes). In this problem we have 6 nodes + and nine arcs. For maximal flow, we want to maximize the flow in the arcs + connected to the source (in this case node 6). The answer represent the arcs + flows. + + The objective function vector identify the arcs we want to maximize and the lower + and upper bounds are the arc capacities. + + Answer: + Objective: 12.000000 + var[0] : 1.000000 + var[1] : 4.000000 + var[2] : 5.000000 + var[3] : 2.000000 + var[4] : 4.000000 + var[5] : 0.000000 + var[6] : 6.000000 + var[7] : 6.000000 + var[8] : 1.000000 + +*) + +open Google.OrTools.FSharp + +let opts = SolverOpts.Default + .Name("Max Flow with Linear Programming") + .Goal(Maximize) + .Objective([0.; 0.; 0.; 0.; 0.; 0.; 1.; 1.; 0.]) + .MatrixEq([[1.; 0.; 0. ;0.]; [0.; 1.; 0.; 0.]; [0.; 0.; 1.; 0.]; [0.; 0.; 0.; 1.]; [0.; -1.; 0.; 1.]; [0.; 0.; -1.; 1.]; [0.; 0.; 0.; -1.]; [0.; 0.; -1.; 0.]; [-1.; 0.; 1.; 0.]]) + .VectorEq([0.; 0.; 0.; 0.]) + .VarLowerBound([0.; 0.; 0.; 0.; 0.; 0.; 0.; 0.; 0.]) + .VarUpperBound([5.; 8.; 5.; 3.; 4.; 5.; 6.; 6.; 4.]) + .Algorithm(LP CLP) + +let slvr = opts |> lpSolve +slvr |> SolverSummary diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsnetwork-max-flow.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsnetwork-max-flow.fs new file mode 100644 index 0000000000000000000000000000000000000000..3e12fee80edc93945f0cee8443d65727d4fdb94e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsnetwork-max-flow.fs @@ -0,0 +1,60 @@ +(* + 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. + + Answer: + Solving max flow with 6 nodes, and 9 arcs, source=0, sink=5 + total computed flow 10, expected = 10 + Arc 1 (1 -> 0), capacity=5, computed=4, expected=4 + Arc 2 (2 -> 0), capacity=8, computed=4, expected=4 + Arc 3 (3 -> 0), capacity=5, computed=2, expected=2 + Arc 4 (4 -> 0), capacity=3, computed=0, expected=0 + Arc 5 (3 -> 1), capacity=4, computed=4, expected=4 + Arc 6 (4 -> 2), capacity=5, computed=4, expected=4 + Arc 7 (4 -> 3), capacity=6, computed=0, expected=0 + Arc 8 (5 -> 3), capacity=6, computed=6, expected=6 + Arc 9 (5 -> 4), capacity=4, computed=4, expected=4 +*) + +open Google.OrTools.Graph +open Google.OrTools.FSharp + +printfn "Max Flow Problem" +let numNodes = 6; +let numArcs = 9; +let tails = [0; 0; 0; 0; 1; 2; 3; 3; 4] +let heads = [1; 2; 3; 4; 3; 4; 4; 5; 5] +let capacities = [5L; 8L; 5L; 3L; 4L; 5L; 6L; 6L; 4L] +let expectedFlows = [4; 4; 2; 0; 4; 4; 0; 6; 4] +let expectedTotalFlow = 10; +let maxFlow = new MaxFlow() + +for i=0 to (numArcs-1) do + let arc = maxFlow.AddArcWithCapacity(tails.[i], heads.[i], capacities.[i]) + if (arc <> i) then + failwith "Internal error" + +let source = 0; +let sink = numNodes - 1; +printfn "Solving max flow with %i nodes, and %i arcs, source=%i, sink=%i " numNodes numArcs source sink +let solveStatus = maxFlow.Solve(source, sink) + +match solveStatus with +| x when x = MaximumFlowResult.Optimal.Id -> + let totalFlow = maxFlow.OptimalFlow(); + printfn "total computed flow %i, expected = %i" totalFlow expectedTotalFlow + + for i=1 to numArcs do + printfn "Arc %i (%i -> %i), capacity=%i, computed=%i, expected=%i" i (maxFlow.Head(i-1)) (maxFlow.Tail(i-1)) (maxFlow.Capacity(i-1)) (maxFlow.Flow(i-1)) (expectedFlows.[i-1]) +| _ -> + printfn "Solving the max flow problem failed. Solver status: %s" (solveStatus.ToString("g")) diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsnetwork-min-cost-flow.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsnetwork-min-cost-flow.fs new file mode 100644 index 0000000000000000000000000000000000000000..d81f4ca353972040a41b01e3e55fb27222eb6ba0 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsnetwork-min-cost-flow.fs @@ -0,0 +1,49 @@ +(* + 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. +*) + +open Google.OrTools.Graph +open Google.OrTools.FSharp + +printfn "Min Cost Flow Problem" +let numSources = 4 +let numTargets = 4 +let costs = array2D [ + [90L; 75L; 75L; 80L]; + [35L; 85L; 55L; 65L]; + [125L; 95L; 90L; 105L]; + [45L; 110L; 95L; 115L] + ] + +let expectedCost = 275 +let minCostFlow = new MinCostFlow() + +for source=0 to (numSources-1) do + for target=0 to (numTargets-1) do + minCostFlow.AddArcWithCapacityAndUnitCost(source, numSources + target, 1L, costs.[source, target]) |> ignore + +for source=0 to (numSources-1) do + minCostFlow.SetNodeSupply(source, 1L) + +for target=0 to (numTargets-1) do + minCostFlow.SetNodeSupply(numSources + target, -1L); + +printfn "Solving min cost flow with %i sources, and %i targets." numSources numTargets +let solveStatus = minCostFlow.Solve(); + +match solveStatus with +| x when x = MinimumCostFlowResult.Optimal.Id -> + printfn "Total computed flow cost = %i, expected = %i" (minCostFlow.OptimalCost()) expectedCost +| _ -> + printfn "Solving the min cost flow problem failed. Solver status: %s" (solveStatus.ToString("g")) diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsrabbit-pheasant.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsrabbit-pheasant.fs new file mode 100644 index 0000000000000000000000000000000000000000..0e0a40fc7598ea3377ca6e598c5ec111a27a93c5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsrabbit-pheasant.fs @@ -0,0 +1,34 @@ +(* Here is a very simple Constraint Programming problem: + + Knowing that we see 56 legs and 20 heads, how many pheasants and rabbits + are we looking at ? +*) + +open Google.OrTools.ConstraintSolver + +let solver = new Solver("pheasant") + +// Variables +let p = solver.MakeIntVar(0L, 20L, "pheasant") +let r = solver.MakeIntVar(0L, 20L, "rabbit") + +// Constraints +let legs = solver.MakeSum(solver.MakeProd(p, 2L), solver.MakeProd(r, 4L)) +let heads = solver.MakeSum(p, r) + +let constraintLegs = solver.MakeEquality(legs, 56) +let constraintHeads = solver.MakeEquality(heads, 20) + +solver.Add(constraintLegs) +solver.Add(constraintHeads) + +let vars = new IntVarVector([|p; r|]) +let db = solver.MakePhase(vars, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE) + +// Solve +solver.NewSearch(db) + +while (solver.NextSolution()) do + printfn "rabbits: %d, pheasants: %d" (r.Value()) (p.Value()) + +solver.EndSearch() diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsvolsay.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsvolsay.fs new file mode 100644 index 0000000000000000000000000000000000000000..24c30bd5e03da5dcfc925cfb1afd50c542aa049a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsvolsay.fs @@ -0,0 +1,54 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +open System +open Google.OrTools.FSharp +open Google.OrTools.LinearSolver + +let solver (solverType:LinearProgramming) = + let svr = new Solver( + "Volsay", enum(solverType.Id)); + + let gas = svr.MakeNumVar(0.0, 100000.0, "Gas") + let chloride = svr.MakeNumVar(0.0, 100000.0, "Cloride") + + let c1 = svr.MakeConstraint(Double.NegativeInfinity, 50.0) + c1.SetCoefficient(gas, 1.0) + c1.SetCoefficient(chloride, 1.0) + + let c2 = svr.MakeConstraint(Double.NegativeInfinity, 180.0) + c1.SetCoefficient(gas, 3.0) + c1.SetCoefficient(chloride, 4.0) + + svr.Maximize(40.0 * gas + 50.0 * chloride) + + let resultStatus = svr.Solve(); + + // Check that the problem has an optimal solution. + match resultStatus with + | status when status <> Solver.ResultStatus.OPTIMAL -> + printfn "The problem does not have an optimal solution!" + exit 0 + | _ -> + printfn "Problem solved in %i milliseconds" (svr.WallTime()) + printfn "Iterations: %i" (svr.Iterations()) + + printfn "Objective: %f" (svr.Objective().Value()) + printfn "Gas : %f ReducedCost: %f" (gas.SolutionValue()) (gas.ReducedCost()) + printfn "Chloride : %f ReducedCost: %f" (chloride.SolutionValue()) (chloride.ReducedCost()) + printfn "c1 : DualValue: %f" (c1.DualValue()) + printfn "c2 : DualValue: %f" (c2.DualValue()) + +solver LinearProgramming.CLP diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsvolsay3-lpSolve.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsvolsay3-lpSolve.fs new file mode 100644 index 0000000000000000000000000000000000000000..0e2293b2d82891776451ae0550b0ecba83808c1f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsvolsay3-lpSolve.fs @@ -0,0 +1,28 @@ +(* + 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. +*) + +open Google.OrTools.FSharp + +let opts = SolverOpts.Default + .Name("Volsay3") + .Goal(Maximize) + .Objective([30.0;40.0]) + .Matrix([[1.0;3.0;0.0]; [1.0;4.0;1.0]]) + .VectorUpperBound([50.0; 180.0; 40.0]) + .VarLowerBound([0.0; 0.0]) + .VarUpperBound([10000.0; 10000.0]) + .Algorithm(LP CLP) + +let slvr = opts |> lpSolve |> SolverSummary +slvr |> ignore diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/fsvolsay3.fs b/libs/or-tools-src-ubuntu/examples/dotnet/fsvolsay3.fs new file mode 100644 index 0000000000000000000000000000000000000000..f2850faac76142bfbe2204d0e6ee04d99a76e8f6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/fsvolsay3.fs @@ -0,0 +1,67 @@ +// Copyright 2012 Hakan Kjellerstrand +// +// 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. + +open System +open Google.OrTools.FSharp +open Google.OrTools.LinearSolver + + +let solver (solverType:LinearProgramming) = + let svr = new Solver( + "Volsay3", enum(solverType.Id)); + + let products = ["Gas"; "Chloride"] + let components = ["nitrogen"; "hydrogen"; "chlorine"] + + let demand = [[1.0;3.0;0.0]; [1.0;4.0;1.0]] // column vectors + let stock = [50.0;180.0;40.0] // upper bound constraints for each component + let profit = [30.0;40.0] + + // Variables + let production = [ for i in 0 .. (products.Length-1) -> svr.MakeNumVar(0.0, 10000.0, products.[i]) ] + + // Constraints + + // generate column index selectors + let cols = [ for i in 0 .. (demand.Length-1) -> i ] + + for row = 0 to (components.Length-1) do + // generate constraint operands based on indices + let constraintOperands = List.map (fun c -> production.[c] * demand.[c].[row]) cols + let linearExp = List.reduce (+) constraintOperands + + // create the constraint + let rc = RangeConstraint(linearExp, Double.NegativeInfinity, stock.[row]) + svr.Add(rc) |> ignore + + + // Objective + let objectiveOperands = List.map (fun c -> profit.[c] * production.[c]) cols + let objectiveExp = List.reduce (+) objectiveOperands + svr.Maximize(objectiveExp) + + let resultStatus = svr.Solve(); + + match resultStatus with + | status when status <> Solver.ResultStatus.OPTIMAL -> + printfn "The problem does not have an optimal solution!" + exit 0 + | _ -> + printfn "\nProblem solved in %d milliseconds" (svr.WallTime()) + printfn "Iterations: %i\n" (svr.Iterations()) + + printfn "Objective: %f" (svr.Objective().Value()) + products |> List.iteri (fun i x -> printfn "%-10s: %f ReducedCost: %f" x ((production.[i]).SolutionValue()) ((production.[i]).ReducedCost()) ) + +solver LinearProgramming.CLP diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/furniture_moving.cs b/libs/or-tools-src-ubuntu/examples/dotnet/furniture_moving.cs new file mode 100644 index 0000000000000000000000000000000000000000..2c96c43f5425d5040be5349db681e53ffb358f8c --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/furniture_moving.cs @@ -0,0 +1,180 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class FurnitureMoving +{ + + /* + * Decompositon of cumulative. + * + * Inspired by the MiniZinc implementation: + * http://www.g12.csse.unimelb.edu.au/wiki/doku.php?id=g12:zinc:lib:minizinc:std:cumulative.mzn&s[]=cumulative + * The MiniZinc decomposition is discussed in the paper: + * A. Schutt, T. Feydy, P.J. Stuckey, and M. G. Wallace. + * "Why cumulative decomposition is not as bad as it sounds." + * Download: + * http://www.cs.mu.oz.au/%7Epjs/rcpsp/papers/cp09-cu.pdf + * http://www.cs.mu.oz.au/%7Epjs/rcpsp/cumu_lazyfd.pdf + * + * + * Parameters: + * + * s: start_times assumption: IntVar[] + * d: durations assumption: int[] + * r: resources assumption: int[] + * b: resource limit assumption: IntVar or int + * + * + */ + static void MyCumulative(Solver solver, + IntVar[] s, + int[] d, + int[] r, + IntVar b) { + + int[] tasks = (from i in Enumerable.Range(0, s.Length) + where r[i] > 0 && d[i] > 0 + select i).ToArray(); + int times_min = tasks.Min(i => (int)s[i].Min()); + int d_max = d.Max(); + int times_max = tasks.Max(i => (int)s[i].Max() + d_max); + for(int t = times_min; t <= times_max; t++) { + ArrayList bb = new ArrayList(); + foreach(int i in tasks) { + bb.Add(((s[i] <= t) * (s[i] + d[i]> t) * r[i]).Var()); + } + solver.Add((bb.ToArray(typeof(IntVar)) as IntVar[]).Sum() <= b); + } + + // Somewhat experimental: + // This constraint is needed to constrain the upper limit of b. + if (b is IntVar) { + solver.Add(b <= r.Sum()); + } + + } + + + /** + * + * Moving furnitures (scheduling) problem in Google CP Solver. + * + * Marriott & Stukey: 'Programming with constraints', page 112f + * + * The model implements an decomposition of the global constraint + * cumulative (see above). + * + * Also see http://www.hakank.org/or-tools/furniture_moving.py + * + */ + private static void Solve() + { + Solver solver = new Solver("FurnitureMoving"); + + int n = 4; + int[] duration = {30,10,15,15}; + int[] demand = { 3, 1, 3, 2}; + int upper_limit = 160; + + + // + // Decision variables + // + IntVar[] start_times = solver.MakeIntVarArray(n, 0, upper_limit, "start_times"); + IntVar[] end_times = solver.MakeIntVarArray(n, 0, upper_limit * 2, "end_times"); + IntVar end_time = solver.MakeIntVar(0, upper_limit * 2, "end_time"); + + // number of needed resources, to be minimized or constrained + IntVar num_resources = solver.MakeIntVar(0, 10, "num_resources"); + + + // + // Constraints + // + for(int i = 0; i < n; i++) { + solver.Add(end_times[i] == start_times[i] + duration[i]); + } + + solver.Add(end_time == end_times.Max()); + MyCumulative(solver, start_times, duration, demand, num_resources); + + + // + // Some extra constraints to play with + // + + // all tasks must end within an hour + // solver.Add(end_time <= 60); + + // All tasks should start at time 0 + // for(int i = 0; i < n; i++) { + // solver.Add(start_times[i] == 0); + // } + + + // limitation of the number of people + // solver.Add(num_resources <= 3); + solver.Add(num_resources <= 4); + + + // + // Objective + // + + // OptimizeVar obj = num_resources.Minimize(1); + OptimizeVar obj = end_time.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(start_times, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("num_resources: {0} end_time: {1}", + num_resources.Value(), end_time.Value()); + for(int i = 0; i < n; i++) { + Console.WriteLine("Task {0,1}: {1,2} -> {2,2} -> {3,2} (demand: {4})", + i, + start_times[i].Value(), + duration[i], + end_times[i].Value(), + demand[i]); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/furniture_moving_intervals.cs b/libs/or-tools-src-ubuntu/examples/dotnet/furniture_moving_intervals.cs new file mode 100644 index 0000000000000000000000000000000000000000..0c5dbdc49e087c59a009cb78447e5dc9990ac021 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/furniture_moving_intervals.cs @@ -0,0 +1,150 @@ +// 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; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class FurnitureMovingIntervals +{ + /** + * + * Moving furnitures (scheduling) problem in Google CP Solver. + * + * Marriott & Stukey: 'Programming with constraints', page 112f + * + * Also see http://www.hakank.org/or-tools/furniture_moving.py + * + */ + private static void Solve() + { + Solver solver = new Solver("FurnitureMovingIntervals"); + + const int n = 4; + int[] durations = {30,10,15,15}; + int[] demand = {3, 1, 3, 2}; + const int upper_limit = 160; + const int max_num_workers = 5; + + // + // Decision variables + // + IntervalVar[] tasks = new IntervalVar[n]; + for (int i = 0; i < n; ++i) + { + tasks[i] = solver.MakeFixedDurationIntervalVar(0, + upper_limit - durations[i], + durations[i], + false, + "task_" + i); + } + + // Fillers that span the whole resource and limit the available + // number of workers. + IntervalVar[] fillers = new IntervalVar[max_num_workers]; + for (int i = 0; i < max_num_workers; ++i) + { + fillers[i] = solver.MakeFixedDurationIntervalVar(0, + 0, + upper_limit, + true, + "filler_" + i); + } + + // Number of needed resources, to be minimized or constrained. + IntVar num_workers = solver.MakeIntVar(0, max_num_workers, "num_workers"); + // Links fillers and num_workers. + for (int i = 0; i < max_num_workers; ++i) + { + solver.Add((num_workers > i) + fillers[i].PerformedExpr() == 1); + } + + // Creates makespan. + IntVar[] ends = new IntVar[n]; + for (int i = 0; i < n; ++i) + { + ends[i] = tasks[i].EndExpr().Var(); + } + IntVar end_time = ends.Max().VarWithName("end_time"); + + // + // Constraints + // + IntervalVar[] all_tasks = new IntervalVar[n + max_num_workers]; + int[] all_demands = new int[n + max_num_workers]; + for (int i = 0; i < n; ++i) + { + all_tasks[i] = tasks[i]; + all_demands[i] = demand[i]; + } + for (int i = 0; i < max_num_workers; ++i) + { + all_tasks[i + n] = fillers[i]; + all_demands[i + n] = 1; + } + solver.Add(all_tasks.Cumulative(all_demands, max_num_workers, "workers")); + + // + // Some extra constraints to play with + // + + // all tasks must end within an hour + // solver.Add(end_time <= 60); + + // All tasks should start at time 0 + // for(int i = 0; i < n; i++) { + // solver.Add(tasks[i].StartAt(0)); + // } + + + // limitation of the number of people + // solver.Add(num_workers <= 3); + solver.Add(num_workers <= 4); + + // + // Objective + // + + // OptimizeVar obj = num_workers.Minimize(1); + OptimizeVar obj = end_time.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all_tasks, Solver.INTERVAL_DEFAULT); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine(num_workers.ToString() + ", " + end_time.ToString()); + for(int i = 0; i < n; i++) { + Console.WriteLine("{0} (demand:{1})", tasks[i].ToString(), demand[i]); + } + Console.WriteLine(); + } + + Console.WriteLine("Solutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0} ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/futoshiki.cs b/libs/or-tools-src-ubuntu/examples/dotnet/futoshiki.cs new file mode 100644 index 0000000000000000000000000000000000000000..9a9b90ebc8ededb6aefa10abb4a0c5e4b3f55a88 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/futoshiki.cs @@ -0,0 +1,200 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class Futoshiki +{ + + /** + * + * Futoshiki problem. + * + * From http://en.wikipedia.org/wiki/Futoshiki + * """ + * The puzzle is played on a square grid, such as 5 x 5. The objective + * is to place the numbers 1 to 5 (or whatever the dimensions are) + * such that each row, and column contains each of the digits 1 to 5. + * Some digits may be given at the start. In addition, inequality + * constraints are also initially specifed between some of the squares, + * such that one must be higher or lower than its neighbour. These + * constraints must be honoured as the grid is filled out. + * """ + * + * Also see http://www.hakank.org/or-tools/futoshiki.py + * + */ + private static void Solve(int[,] values, int[,] lt) + { + + Solver solver = new Solver("Futoshiki"); + + int size = values.GetLength(0); + IEnumerable RANGE = Enumerable.Range(0, size); + IEnumerable NUMQD = Enumerable.Range(0, lt.GetLength(0)); + + // + // Decision variables + // + IntVar[,] field = solver.MakeIntVarMatrix(size, size, 1, size, "field"); + IntVar[] field_flat = field.Flatten(); + + // + // Constraints + // + + + // set initial values + foreach(int row in RANGE) { + foreach(int col in RANGE) { + if (values[row,col] > 0) { + solver.Add(field[row,col] == values[row,col]); + } + } + } + + + // all rows have to be different + foreach(int row in RANGE) { + solver.Add((from col in RANGE + select field[row,col]).ToArray().AllDifferent()); + } + + + // all columns have to be different + foreach(int col in RANGE) { + solver.Add((from row in RANGE + select field[row,col]).ToArray().AllDifferent()); + } + + + // all < constraints are satisfied + // Also: make 0-based + foreach(int i in NUMQD) { + solver.Add(field[ lt[i,0]-1, lt[i,1]-1 ] < + field[ lt[i,2]-1, lt[i,3]-1 ] ); + } + + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(field_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + foreach(int i in RANGE) { + foreach(int j in RANGE) { + Console.Write("{0} ", field[i,j].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + + // + // Example from Tailor model futoshiki.param/futoshiki.param + // Solution: + // 5 1 3 2 4 + // 1 4 2 5 3 + // 2 3 1 4 5 + // 3 5 4 1 2 + // 4 2 5 3 1 + // + // Futoshiki instance, by Andras Salamon + // specify the numbers in the grid + // + int[,] values1 = { + {0, 0, 3, 2, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}}; + + + // [i1,j1, i2,j2] requires that values[i1,j1] < values[i2,j2] + // Note: 1-based + int [,] lt1 = { + {1,2, 1,1}, + {1,4, 1,5}, + {2,3, 1,3}, + {3,3, 2,3}, + {3,4, 2,4}, + {2,5, 3,5}, + {3,2, 4,2}, + {4,4, 4,3}, + {5,2, 5,1}, + {5,4, 5,3}, + {5,5, 4,5}}; + + + // + // Example from http://en.wikipedia.org/wiki/Futoshiki + // Solution: + // 5 4 3 2 1 + // 4 3 1 5 2 + // 2 1 4 3 5 + // 3 5 2 1 4 + // 1 2 5 4 3 + // + int[,] values2 = { + {0, 0, 0, 0, 0}, + {4, 0, 0, 0, 2}, + {0, 0, 4, 0, 0}, + {0, 0, 0, 0, 4}, + {0, 0, 0, 0, 0}}; + + // Note: 1-based + int[,] lt2 = { + {1,2, 1,1}, + {1,4, 1,3}, + {1,5, 1,4}, + {4,4, 4,5}, + {5,1, 5,2}, + {5,2, 5,3} + }; + + Console.WriteLine("Problem 1"); + Solve(values1, lt1); + + Console.WriteLine("\nProblem 2"); + Solve(values2, lt2); + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/golomb_ruler.cs b/libs/or-tools-src-ubuntu/examples/dotnet/golomb_ruler.cs new file mode 100644 index 0000000000000000000000000000000000000000..f7cbb9a8a963922121b82b8a41054be83d98ae7f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/golomb_ruler.cs @@ -0,0 +1,128 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class GolombRuler +{ + + /** + * + * Golomb Ruler problem. + * + * This C# implementation is based on Charles Prud'homme's + * or-tools/Java model: + * http://code.google.com/p/or-tools/source/browse/trunk/com/google/ortools/constraintsolver/samples/GolombRuler.java + * + */ + private static void Solve(int m = 8) + { + Solver solver = new Solver("GolombRuler"); + + + // + // Decision variables + // + IntVar[] ticks = solver.MakeIntVarArray(m, + 0, + ((m < 31) ? (1 << (m + 1)) - 1 : 9999), + "ticks"); + + IntVar[] diff = new IntVar[(m * m - m) / 2]; + + + // + // Constraints + // + solver.Add(ticks[0] == 0); + + for(int i = 0; i < ticks.Length - 1; i++) { + solver.Add(ticks[i] < ticks[i+1]); + } + + + for (int k = 0, i = 0; i < m - 1; i++) { + for (int j = i + 1; j < m; j++, k++) { + diff[k] = (ticks[j]-ticks[i]).Var(); + solver.Add(diff[k] >= (j - i) * (j - i + 1) / 2); + } + } + + solver.Add(diff.AllDifferent()); + + // break symetries + if (m > 2) { + solver.Add(diff[0] < diff[diff.Length - 1]); + } + + + // + // Optimization + // + OptimizeVar opt = ticks[m - 1].Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(ticks, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + + // We just want the debug info for larger instances. + if (m >= 11) { + + SearchMonitor log = solver.MakeSearchLog(10000, opt); + solver.NewSearch(db, opt, log); + + } else { + + solver.NewSearch(db, opt); + } + + + while (solver.NextSolution()) { + Console.Write("opt: {0} [ ", ticks[m-1].Value()); + for(int i = 0; i < m; i++) { + Console.Write("{0} ", ticks[i].Value()); + } + Console.WriteLine("]"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 8; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/grocery.cs b/libs/or-tools-src-ubuntu/examples/dotnet/grocery.cs new file mode 100644 index 0000000000000000000000000000000000000000..75d6c1dc0a97da3a8989824ec26258a83be19ec9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/grocery.cs @@ -0,0 +1,112 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + + +public class Grocery +{ + + public static void Decreasing(Solver solver, IntVar[] x) { + for(int i = 0; i < x.Length - 1; i++) { + solver.Add(x[i] <= x[i+1]); + } + } + + // + // Simple decomposition of Prod() for an IntVar array + // + private static Constraint MyProd(IntVar[] x, int prod) { + int len = x.Length; + IntVar[] tmp = new IntVar[len]; + tmp[0] = x[0]; + for(int i = 1; i < len; i++) { + tmp[i] = (tmp[i-1]*x[i]).Var(); + } + + return tmp[len-1] == prod; + } + + + /** + * + * Grocery problem. + * + * From Christian Schulte, Gert Smolka, Finite Domain + * http://www.mozart-oz.org/documentation/fdt/ + * Constraint Programming in Oz. A Tutorial. 2001. + * """ + * A kid goes into a grocery store and buys four items. The cashier + * charges $7.11, the kid pays and is about to leave when the cashier + * calls the kid back, and says 'Hold on, I multiplied the four items + * instead of adding them; I'll try again; Hah, with adding them the + * price still comes to $7.11'. What were the prices of the four items? + * """ + * + */ + private static void Solve() + { + Solver solver = new Solver("Grocery"); + + int n = 4; + int c = 711; + + // + // Decision variables + // + + IntVar[] item = solver.MakeIntVarArray(n, 0, c / 2, "item"); + + // + // Constraints + // + solver.Add(item.Sum() == c); + // solver.Add(item[0] * item[1] * item[2] * item[3] == c * 100*100*100); + // solver.Add(item.Prod() == c * 100*100*100); + solver.Add(MyProd(item, c * 100*100*100)); + + + // Symmetry breaking + Decreasing(solver, item); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(item, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(item[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nWallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/hidato_table.cs b/libs/or-tools-src-ubuntu/examples/dotnet/hidato_table.cs new file mode 100644 index 0000000000000000000000000000000000000000..3bb4554c3ec1965eef4531def473975fbbef1525 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/hidato_table.cs @@ -0,0 +1,287 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class HidatoTable +{ + + + /* + * Build closeness pairs for consecutive numbers. + * + * Build set of allowed pairs such that two consecutive numbers touch + * each other in the grid. + * + * Returns: + * A list of pairs for allowed consecutive position of numbers. + * + * Args: + * rows: the number of rows in the grid + * cols: the number of columns in the grid + */ + public static IntTupleSet BuildPairs(int rows, int cols) + { + int[] ix = {-1, 0, 1}; + var result_tmp = (from x in Enumerable.Range(0, rows) + from y in Enumerable.Range(0, cols) + from dx in ix + from dy in ix + where + x + dx >= 0 && + x + dx < rows && + y + dy >= 0 && + y + dy < cols && + (dx != 0 || dy != 0) + select new int[] {x * cols + y, (x + dx) * cols + (y + dy)} + ).ToArray(); + + // Convert to len x 2 matrix + int len = result_tmp.Length; + IntTupleSet result = new IntTupleSet(2); + foreach(int[] r in result_tmp) { + result.Insert(r); + } + return result; + } + + + /** + * + * Hidato puzzle in Google CP Solver. + * + * http://www.hidato.com/ + * """ + * Puzzles start semi-filled with numbered tiles. + * The first and last numbers are circled. + * Connect the numbers together to win. Consecutive + * number must touch horizontally, vertically, or + * diagonally. + * """ + * + * This is a port of the Python model hidato_table.py + * made by Laurent Perron (using AllowedAssignments), + * based on my (much slower) model hidato.py. + * + */ + private static void Solve(int model = 1) + { + Solver solver = new Solver("HidatoTable"); + + // + // models, a 0 indicates an open cell which number is not yet known. + // + + int[,] puzzle = null; + if (model == 1) { + + // Simple problem + + // Solution 1: + // 6 7 9 + // 5 2 8 + // 1 4 3 + int[,] puzzle1 = {{6, 0, 9}, + {0, 2, 8}, + {1, 0, 0}}; + puzzle = puzzle1; + + } else if (model == 2) { + + int[,] puzzle2 = {{0, 44, 41, 0, 0, 0, 0}, + {0, 43, 0, 28, 29, 0, 0}, + {0, 1, 0, 0, 0, 33, 0}, + {0, 2, 25, 4, 34, 0, 36}, + {49, 16, 0, 23, 0, 0, 0}, + {0, 19, 0, 0, 12, 7, 0}, + {0, 0, 0, 14, 0, 0, 0}}; + puzzle = puzzle2; + + } else if (model == 3) { + // Problems from the book: + // Gyora Bededek: "Hidato: 2000 Pure Logic Puzzles" + // Problem 1 (Practice) + int[,] puzzle3 = {{0, 0, 20, 0, 0}, + {0, 0, 0, 16, 18}, + {22, 0, 15, 0, 0}, + {23, 0, 1, 14, 11}, + {0, 25, 0, 0, 12}}; + puzzle = puzzle3; + + } else if (model == 4) { + // problem 2 (Practice) + int[,] puzzle4 = {{0, 0, 0, 0, 14}, + {0, 18, 12, 0, 0}, + {0, 0, 17, 4, 5}, + {0, 0, 7, 0, 0}, + {9, 8, 25, 1, 0}}; + puzzle = puzzle4; + + } else if (model == 5) { + // problem 3 (Beginner) + int[,] puzzle5 = {{0, 26, 0, 0, 0, 18}, + {0, 0, 27, 0, 0, 19}, + {31, 23, 0, 0, 14, 0}, + {0, 33, 8, 0, 15, 1}, + {0, 0, 0, 5, 0, 0}, + {35, 36, 0, 10, 0, 0}}; + puzzle = puzzle5; + + } else if (model == 6) { + // Problem 15 (Intermediate) + int[,] puzzle6 = {{64, 0, 0, 0, 0, 0, 0, 0}, + {1, 63, 0, 59, 15, 57, 53, 0}, + {0, 4, 0, 14, 0, 0, 0, 0}, + {3, 0, 11, 0, 20, 19, 0, 50}, + {0, 0, 0, 0, 22, 0, 48, 40}, + {9, 0, 0, 32, 23, 0, 0, 41}, + {27, 0, 0, 0, 36, 0, 46, 0}, + {28, 30, 0, 35, 0, 0, 0, 0}}; + puzzle = puzzle6; + } + + int r = puzzle.GetLength(0); + int c = puzzle.GetLength(1); + + Console.WriteLine(); + Console.WriteLine("----- Solving problem {0} -----", model); + Console.WriteLine(); + + PrintMatrix(puzzle); + + // + // Decision variables + // + IntVar[] positions = solver.MakeIntVarArray(r*c, 0, r * c - 1, "p"); + + + // + // Constraints + // + solver.Add(positions.AllDifferent()); + + // + // Fill in the clues + // + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + if (puzzle[i,j] > 0) { + solver.Add(positions[puzzle[i,j] - 1] == i * c + j); + } + } + } + + // Consecutive numbers much touch each other in the grid. + // We use an allowed assignment constraint to model it. + IntTupleSet close_tuples = BuildPairs(r, c); + for(int k = 1; k < r * c - 1; k++) { + IntVar[] tmp = new IntVar[] {positions[k], positions[k + 1]}; + solver.Add(tmp.AllowedAssignments(close_tuples)); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(positions, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int num_solution = 0; + while (solver.NextSolution()) { + num_solution++; + PrintOneSolution(positions, r, c, num_solution); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + // Print the current solution + public static void PrintOneSolution(IntVar[] positions, + int rows, + int cols, + int num_solution) + { + + Console.WriteLine("Solution {0}", num_solution); + + // Create empty board + int[,] board = new int[rows, cols]; + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + board[i,j] = 0; + } + } + + // Fill board with solution value + for(int k = 0; k < rows*cols; k++) { + int position = (int)positions[k].Value(); + board[position / cols, position % cols] = k + 1; + } + + PrintMatrix(board); + + } + + + // Pretty print of the matrix + public static void PrintMatrix(int[,] game) + { + int rows = game.GetLength(0); + int cols = game.GetLength(1); + + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + if (game[i,j] == 0) { + Console.Write(" ."); + } else { + Console.Write(" {0,2}", game[i,j] ); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + + + public static void Main(String[] args) + { + int model = 1; + if (args.Length > 0) { + + model = Convert.ToInt32(args[0]); + Solve(model); + + } else { + + for(int m = 1; m <= 6; m++) { + Solve(m); + } + } + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/just_forgotten.cs b/libs/or-tools-src-ubuntu/examples/dotnet/just_forgotten.cs new file mode 100644 index 0000000000000000000000000000000000000000..6281ec5d5b4092a39eb9882b2d7f8a8b40d3848f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/just_forgotten.cs @@ -0,0 +1,129 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class JustForgotten +{ + + + + /** + * + * Just forgotten puzzle (Enigma 1517) in Google CP Solver. + * + * From http://www.f1compiler.com/samples/Enigma 201517.f1.html + * """ + * Enigma 1517 Bob Walker, New Scientist magazine, October 25, 2008. + * + * Joe was furious when he forgot one of his bank account numbers. + * He remembered that it had all the digits 0 to 9 in some order, + * so he tried the following four sets without success: + * + * 9 4 6 2 1 5 7 8 3 0 + * 8 6 0 4 3 9 1 2 5 7 + * 1 6 4 0 2 9 7 8 5 3 + * 6 8 2 4 3 1 9 0 7 5 + * + * When Joe finally remembered his account number, he realised that + * in each set just four of the digits were in their correct position + * and that, if one knew that, it was possible to work out his + * account number. What was it? + * """ + * + * Also see http://www.hakank.org/google_or_tools/just_forgotten.py + * + */ + private static void Solve() + { + Solver solver = new Solver("JustForgotten"); + + + int rows = 4; + int cols = 10; + + // The four tries + int[,] a = {{9,4,6,2,1,5,7,8,3,0}, + {8,6,0,4,3,9,1,2,5,7}, + {1,6,4,0,2,9,7,8,5,3}, + {6,8,2,4,3,1,9,0,7,5}}; + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(cols, 0, 9, "x"); + + // + // Constraints + // + solver.Add(x.AllDifferent()); + for(int r = 0; r < rows; r++) { + solver.Add( (from c in Enumerable.Range(0, cols) + select x[c] == a[r,c]).ToArray().Sum() == 4); + } + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine("Account number:"); + for(int j = 0; j < cols; j++) { + Console.Write(x[j].Value() + " "); + } + Console.WriteLine("\n"); + Console.WriteLine("The four tries, where '!' represents a correct digit:"); + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + String c = " "; + if (a[i,j] == x[j].Value()) { + c = "!"; + } + Console.Write("{0}{1} ", a[i,j], c); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/kakuro.cs b/libs/or-tools-src-ubuntu/examples/dotnet/kakuro.cs new file mode 100644 index 0000000000000000000000000000000000000000..e7a53b672dd0e27682b715b653812ebafb6bdf7d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/kakuro.cs @@ -0,0 +1,219 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class Kakuro +{ + + /** + * Ensure that the sum of the segments + * in cc == res + * + */ + public static void calc(Solver solver, + int[] cc, + IntVar[,] x, + int res) + { + + // ensure that the values are positive + int len = cc.Length / 2; + for(int i = 0; i < len; i++) { + solver.Add(x[cc[i*2]-1,cc[i*2+1]-1] >= 1); + } + + // sum the numbers + solver.Add( (from i in Enumerable.Range(0, len) + select x[cc[i*2]-1,cc[i*2+1]-1]) + .ToArray().Sum() == res); + } + + + + /** + * + * Kakuru puzzle. + * + * http://en.wikipedia.org/wiki/Kakuro + * """ + * The object of the puzzle is to insert a digit from 1 to 9 inclusive + * into each white cell such that the sum of the numbers in each entry + * matches the clue associated with it and that no digit is duplicated in + * any entry. It is that lack of duplication that makes creating Kakuro + * puzzles with unique solutions possible, and which means solving a Kakuro + * puzzle involves investigating combinations more, compared to Sudoku in + * which the focus is on permutations. There is an unwritten rule for + * making Kakuro puzzles that each clue must have at least two numbers + * that add up to it. This is because including one number is mathematically + * trivial when solving Kakuro puzzles; one can simply disregard the + * number entirely and subtract it from the clue it indicates. + * """ + * + * This model solves the problem at the Wikipedia page. + * For a larger picture, see + * http://en.wikipedia.org/wiki/File:Kakuro_black_box.svg + * + * The solution: + * 9 7 0 0 8 7 9 + * 8 9 0 8 9 5 7 + * 6 8 5 9 7 0 0 + * 0 6 1 0 2 6 0 + * 0 0 4 6 1 3 2 + * 8 9 3 1 0 1 4 + * 3 1 2 0 0 2 1 + * + * Also see http://www.hakank.org/or-tools/kakuro.py + * though this C# model has another representation of + * the problem instance. + * + */ + private static void Solve() + { + + Solver solver = new Solver("Kakuro"); + + // size of matrix + int n = 7; + + // segments: + // sum, the segments + // Note: this is 1-based + int[][] problem = + { + new int[] {16, 1,1, 1,2}, + new int[] {24, 1,5, 1,6, 1,7}, + new int[] {17, 2,1, 2,2}, + new int[] {29, 2,4, 2,5, 2,6, 2,7}, + new int[] {35, 3,1, 3,2, 3,3, 3,4, 3,5}, + new int[] { 7, 4,2, 4,3}, + new int[] { 8, 4,5, 4,6}, + new int[] {16, 5,3, 5,4, 5,5, 5,6, 5,7}, + new int[] {21, 6,1, 6,2, 6,3, 6,4}, + new int[] { 5, 6,6, 6,7}, + new int[] { 6, 7,1, 7,2, 7,3}, + new int[] { 3, 7,6, 7,7}, + + new int[] {23, 1,1, 2,1, 3,1}, + new int[] {30, 1,2, 2,2, 3,2, 4,2}, + new int[] {27, 1,5, 2,5, 3,5, 4,5, 5,5}, + new int[] {12, 1,6, 2,6}, + new int[] {16, 1,7, 2,7}, + new int[] {17, 2,4, 3,4}, + new int[] {15, 3,3, 4,3, 5,3, 6,3, 7,3}, + new int[] {12, 4,6, 5,6, 6,6, 7,6}, + new int[] { 7, 5,4, 6,4}, + new int[] { 7, 5,7, 6,7, 7,7}, + new int[] {11, 6,1, 7,1}, + new int[] {10, 6,2, 7,2} + + }; + + + int num_p = 24; // Number of segments + + // The blanks + // Note: 1-based + int[,] blanks = { + {1,3}, {1,4}, + {2,3}, + {3,6}, {3,7}, + {4,1}, {4,4}, {4,7}, + {5,1}, {5,2}, + {6,5}, + {7,4}, {7,5} + }; + + int num_blanks = blanks.GetLength(0); + + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, 9, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + + // fill the blanks with 0 + for(int i = 0; i < num_blanks; i++) { + solver.Add(x[blanks[i,0]-1,blanks[i,1]-1]==0); + } + + for(int i = 0; i < num_p; i++) { + int[] segment = problem[i]; + + // Remove the sum from the segment + int[] s2 = new int[segment.Length-1]; + for(int j = 1; j < segment.Length; j++) { + s2[j-1] = segment[j]; + } + + // sum this segment + calc(solver, s2, x, segment[0]); + + // all numbers in this segment must be distinct + int len = segment.Length / 2; + solver.Add( (from j in Enumerable.Range(0, len) + select x[s2[j * 2] - 1, s2[j * 2 + 1] - 1]) + .ToArray().AllDifferent()); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + int v = (int)x[i,j].Value(); + if (v > 0) { + Console.Write(v + " "); + } else { + Console.Write(" "); + } + } + Console.WriteLine(); + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/kenken2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/kenken2.cs new file mode 100644 index 0000000000000000000000000000000000000000..a322f15a388f209504bc34cad1722b71d2a1e9dc --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/kenken2.cs @@ -0,0 +1,241 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class KenKen2 +{ + + /** + * Ensure that the sum of the segments + * in cc == res + * + */ + public static void calc(Solver solver, + int[] cc, + IntVar[,] x, + int res) + { + + int ccLen = cc.Length; + if (ccLen == 4) { + + // for two operands there's + // a lot of possible variants + IntVar a = x[cc[0]-1, cc[1]-1]; + IntVar b = x[cc[2]-1, cc[3]-1]; + + IntVar r1 = a + b == res; + IntVar r2 = a * b == res; + IntVar r3 = a * res == b; + IntVar r4 = b * res == a; + IntVar r5 = a - b == res; + IntVar r6 = b - a == res; + + solver.Add(r1+r2+r3+r4+r5+r6 >= 1); + + } else { + + // For length > 2 then res is either the sum + // the the product of the segment + + // sum the numbers + int len = cc.Length / 2; + IntVar[] xx = (from i in Enumerable.Range(0, len) + select x[cc[i*2]-1,cc[i*2+1]-1]).ToArray(); + + // Sum + IntVar this_sum = xx.Sum() == res; + + // Product + // IntVar this_prod = (xx.Prod() == res).Var(); // don't work + IntVar this_prod; + if (xx.Length == 3) { + this_prod = (x[cc[0]-1,cc[1]-1] * + x[cc[2]-1,cc[3]-1] * + x[cc[4]-1,cc[5]-1]) == res; + } else { + this_prod = (x[cc[0]-1,cc[1]-1] * + x[cc[2]-1,cc[3]-1] * + x[cc[4]-1,cc[5]-1] * + x[cc[6]-1,cc[7]-1]) == res; + + } + + solver.Add(this_sum + this_prod >= 1); + + + } + } + + + + /** + * + * KenKen puzzle. + * + * http://en.wikipedia.org/wiki/KenKen + * """ + * KenKen or KEN-KEN is a style of arithmetic and logical puzzle sharing + * several characteristics with sudoku. The name comes from Japanese and + * is translated as 'square wisdom' or 'cleverness squared'. + * ... + * The objective is to fill the grid in with the digits 1 through 6 such that: + * + * * Each row contains exactly one of each digit + * * Each column contains exactly one of each digit + * * Each bold-outlined group of cells is a cage containing digits which + * achieve the specified result using the specified mathematical operation: + * addition (+), + * subtraction (-), + * multiplication (x), + * and division (/). + * (Unlike in Killer sudoku, digits may repeat within a group.) + * + * ... + * More complex KenKen problems are formed using the principles described + * above but omitting the symbols +, -, x and /, thus leaving them as + * yet another unknown to be determined. + * """ + * + * The solution is: + * + * 5 6 3 4 1 2 + * 6 1 4 5 2 3 + * 4 5 2 3 6 1 + * 3 4 1 2 5 6 + * 2 3 6 1 4 5 + * 1 2 5 6 3 4 + * + * + * Also see http://www.hakank.org/or-tools/kenken2.py + * though this C# model has another representation of + * the problem instance. + * + */ + private static void Solve() + { + + Solver solver = new Solver("KenKen2"); + + // size of matrix + int n = 6; + IEnumerable RANGE = Enumerable.Range(0, n); + + // For a better view of the problem, see + // http://en.wikipedia.org/wiki/File:KenKenProblem.svg + + // hints + // sum, the hints + // Note: this is 1-based + int[][] problem = + { + new int[] { 11, 1,1, 2,1}, + new int[] { 2, 1,2, 1,3}, + new int[] { 20, 1,4, 2,4}, + new int[] { 6, 1,5, 1,6, 2,6, 3,6}, + new int[] { 3, 2,2, 2,3}, + new int[] { 3, 2,5, 3,5}, + new int[] {240, 3,1, 3,2, 4,1, 4,2}, + new int[] { 6, 3,3, 3,4}, + new int[] { 6, 4,3, 5,3}, + new int[] { 7, 4,4, 5,4, 5,5}, + new int[] { 30, 4,5, 4,6}, + new int[] { 6, 5,1, 5,2}, + new int[] { 9, 5,6, 6,6}, + new int[] { 8, 6,1, 6,2, 6,3}, + new int[] { 2, 6,4, 6,5} + }; + + + int num_p = problem.GetLength(0); // Number of segments + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + + // + // alldifferent rows and columns + foreach(int i in RANGE) { + // rows + solver.Add( (from j in RANGE select x[i,j]).ToArray().AllDifferent()); + + // cols + solver.Add( (from j in RANGE select x[j,i]).ToArray().AllDifferent()); + + } + + + // Calculate the segments + for(int i = 0; i < num_p; i++) { + + int[] segment = problem[i]; + + // Remove the sum from the segment + int len = segment.Length-1; + int[] s2 = new int[len]; + Array.Copy(segment, 1, s2, 0, len); + + // sum this segment + calc(solver, s2, x, segment[0]); + + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/killer_sudoku.cs b/libs/or-tools-src-ubuntu/examples/dotnet/killer_sudoku.cs new file mode 100644 index 0000000000000000000000000000000000000000..390ec05146c14ffdd3e18b3c7b9d25614fbd4033 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/killer_sudoku.cs @@ -0,0 +1,240 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class KillerSudoku +{ + + /** + * Ensure that the sum of the segments + * in cc == res + * + */ + public static void calc(Solver solver, + int[] cc, + IntVar[,] x, + int res) + { + + // sum the numbers + int len = cc.Length / 2; + solver.Add( (from i in Enumerable.Range(0, len) + select x[cc[i*2]-1,cc[i*2+1]-1]).ToArray().Sum() == res); + } + + + + /** + * + * Killer Sudoku. + * + * http://en.wikipedia.org/wiki/Killer_Sudoku + * """ + * Killer sudoku (also killer su doku, sumdoku, sum doku, addoku, or + * samunamupure) is a puzzle that combines elements of sudoku and kakuro. + * Despite the name, the simpler killer sudokus can be easier to solve + * than regular sudokus, depending on the solver's skill at mental arithmetic; + * the hardest ones, however, can take hours to crack. + * + * ... + * + * The objective is to fill the grid with numbers from 1 to 9 in a way that + * the following conditions are met: + * + * - Each row, column, and nonet contains each number exactly once. + * - The sum of all numbers in a cage must match the small number printed + * in its corner. + * - No number appears more than once in a cage. (This is the standard rule + * for killer sudokus, and implies that no cage can include more + * than 9 cells.) + * + * In 'Killer X', an additional rule is that each of the long diagonals + * contains each number once. + * """ + * + * Here we solve the problem from the Wikipedia page, also shown here + * http://en.wikipedia.org/wiki/File:Killersudoku_color.svg + * + * The output is: + * 2 1 5 6 4 7 3 9 8 + * 3 6 8 9 5 2 1 7 4 + * 7 9 4 3 8 1 6 5 2 + * 5 8 6 2 7 4 9 3 1 + * 1 4 2 5 9 3 8 6 7 + * 9 7 3 8 1 6 4 2 5 + * 8 2 1 7 3 9 5 4 6 + * 6 5 9 4 2 8 7 1 3 + * 4 3 7 1 6 5 2 8 9 + * + * Also see http://www.hakank.org/or-tools/killer_sudoku.py + * though this C# model has another representation of + * the problem instance. + * + */ + private static void Solve() + { + + Solver solver = new Solver("KillerSudoku"); + + // size of matrix + int cell_size = 3; + IEnumerable CELL = Enumerable.Range(0, cell_size); + int n = cell_size*cell_size; + IEnumerable RANGE = Enumerable.Range(0, n); + + // For a better view of the problem, see + // http://en.wikipedia.org/wiki/File:Killersudoku_color.svg + + // hints + // sum, the hints + // Note: this is 1-based + int[][] problem = + { + new int[] { 3, 1,1, 1,2}, + new int[] {15, 1,3, 1,4, 1,5}, + new int[] {22, 1,6, 2,5, 2,6, 3,5}, + new int[] {4, 1,7, 2,7}, + new int[] {16, 1,8, 2,8}, + new int[] {15, 1,9, 2,9, 3,9, 4,9}, + new int[] {25, 2,1, 2,2, 3,1, 3,2}, + new int[] {17, 2,3, 2,4}, + new int[] { 9, 3,3, 3,4, 4,4}, + new int[] { 8, 3,6, 4,6, 5,6}, + new int[] {20, 3,7, 3,8, 4,7}, + new int[] { 6, 4,1, 5,1}, + new int[] {14, 4,2, 4,3}, + new int[] {17, 4,5, 5,5, 6,5}, + new int[] {17, 4,8, 5,7, 5,8}, + new int[] {13, 5,2, 5,3, 6,2}, + new int[] {20, 5,4, 6,4, 7,4}, + new int[] {12, 5,9, 6,9}, + new int[] {27, 6,1, 7,1, 8,1, 9,1}, + new int[] { 6, 6,3, 7,2, 7,3}, + new int[] {20, 6,6, 7,6, 7,7}, + new int[] { 6, 6,7, 6,8}, + new int[] {10, 7,5, 8,4, 8,5, 9,4}, + new int[] {14, 7,8, 7,9, 8,8, 8,9}, + new int[] { 8, 8,2, 9,2}, + new int[] {16, 8,3, 9,3}, + new int[] {15, 8,6, 8,7}, + new int[] {13, 9,5, 9,6, 9,7}, + new int[] {17, 9,8, 9,9} + + }; + + + int num_p = 29; // Number of segments + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, 9, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + + // + // The first three constraints is the same as for sudokus.cs + // + // alldifferent rows and columns + foreach(int i in RANGE) { + // rows + solver.Add( (from j in RANGE + select x[i,j]).ToArray().AllDifferent()); + + // cols + solver.Add( (from j in RANGE + select x[j,i]).ToArray().AllDifferent()); + + } + + // cells + foreach(int i in CELL) { + foreach(int j in CELL) { + solver.Add( (from di in CELL + from dj in CELL + select x[i*cell_size+di, j*cell_size+dj] + ).ToArray().AllDifferent()); + } + } + + + // Sum the segments and ensure alldifferent + for(int i = 0; i < num_p; i++) { + int[] segment = problem[i]; + + // Remove the sum from the segment + int[] s2 = new int[segment.Length-1]; + for(int j = 1; j < segment.Length; j++) { + s2[j-1] = segment[j]; + } + + // sum this segment + calc(solver, s2, x, segment[0]); + + // all numbers in this segment must be distinct + int len = segment.Length / 2; + solver.Add( (from j in Enumerable.Range(0, len) + select x[s2[j*2]-1, s2[j*2+1]-1]) + .ToArray().AllDifferent()); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + int v = (int)x[i,j].Value(); + if (v > 0) { + Console.Write(v + " "); + } else { + Console.Write(" "); + } + } + Console.WriteLine(); + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/labeled_dice.cs b/libs/or-tools-src-ubuntu/examples/dotnet/labeled_dice.cs new file mode 100644 index 0000000000000000000000000000000000000000..5b273cfc2db9f10586262a73db64b1f4ab927ba8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/labeled_dice.cs @@ -0,0 +1,184 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class LabeledDice +{ + + /** + * + * Labeled dice problem. + * + * From Jim Orlin 'Colored letters, labeled dice: a logic puzzle' + * http://jimorlin.wordpress.com/2009/02/17/colored-letters-labeled-dice-a-logic-puzzle/ + * """ + * My daughter Jenn bough a puzzle book, and showed me a cute puzzle. There + * are 13 words as follows: BUOY, CAVE, CELT, FLUB, FORK, HEMP, JUDY, + * JUNK, LIMN, QUIP, SWAG, VISA, WISH. + * + * There are 24 different letters that appear in the 13 words. The question + * is: can one assign the 24 letters to 4 different cubes so that the + * four letters of each word appears on different cubes. (There is one + * letter from each word on each cube.) It might be fun for you to try + * it. I'll give a small hint at the end of this post. The puzzle was + * created by Humphrey Dudley. + * """ + * + * Jim Orlin's followup 'Update on Logic Puzzle': + * http://jimorlin.wordpress.com/2009/02/21/update-on-logic-puzzle/ + * + * + * Also see http://www.hakank.org/or-tools/labeled_dice.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("LabeledDice"); + + // + // Data + // + int n = 4; + int m = 24; + + int A = 0; + int B = 1; + int C = 2; + int D = 3; + int E = 4; + int F = 5; + int G = 6; + int H = 7; + int I = 8; + int J = 9; + int K = 10; + int L = 11; + int M = 12; + int N = 13; + int O = 14; + int P = 15; + int Q = 16; + int R = 17; + int S = 18; + int T = 19; + int U = 20; + int V = 21; + int W = 22; + int Y = 23; + + + String[] letters_str = {"A","B","C","D","E","F","G","H","I","J","K","L","M", + "N","O","P","Q","R","S","T","U","V","W","Y"}; + + int num_words = 13; + int[,] words = + { + {B,U,O,Y}, + {C,A,V,E}, + {C,E,L,T}, + {F,L,U,B}, + {F,O,R,K}, + {H,E,M,P}, + {J,U,D,Y}, + {J,U,N,K}, + {L,I,M,N}, + {Q,U,I,P}, + {S,W,A,G}, + {V,I,S,A}, + {W,I,S,H} + }; + + + // + // Decision variables + // + IntVar[] dice = solver.MakeIntVarArray(m, 0, n-1, "dice"); + IntVar[] gcc = solver.MakeIntVarArray(n, 6, 6, "gcc"); + + // + // Constraints + // + + + // the letters in a word must be on a different die + for(int i = 0; i < num_words; i++) { + solver.Add( (from j in Enumerable.Range(0, n) + select dice[words[i,j]] + ).ToArray().AllDifferent()); + } + + // there must be exactly 6 letters of each die + /* + for(int i = 0; i < n; i++) { + solver.Add( ( from j in Enumerable.Range(0, m) + select (dice[j] == i) + ).ToArray().Sum() == 6 ); + } + */ + // Use Distribute (Global Cardinality Count) instead. + solver.Add(dice.Distribute(gcc)); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(dice, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int d = 0; d < n; d++) { + Console.Write("die {0}: ", d); + for(int i = 0; i < m; i++) { + if (dice[i].Value() == d) { + Console.Write(letters_str[i]); + } + } + Console.WriteLine(); + } + + Console.WriteLine("The words with the cube label:"); + for(int i = 0; i < num_words; i++) { + for(int j = 0; j < n; j++) { + Console.Write("{0} ({1})", letters_str[words[i,j]], dice[words[i,j]].Value()); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/langford.cs b/libs/or-tools-src-ubuntu/examples/dotnet/langford.cs new file mode 100644 index 0000000000000000000000000000000000000000..d29d56e3be013a43511ed754cd56b87c11a7de08 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/langford.cs @@ -0,0 +1,113 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class Langford +{ + + /** + * + * Langford number problem. + * See http://www.hakank.org/or-tools/langford.py + * + */ + private static void Solve(int k = 8, int num_sol = 0) + { + + Solver solver = new Solver("Langford"); + + Console.WriteLine("k: {0}", k); + + // + // data + // + int p = 2*k; + + // + // Decision variables + // + IntVar[] position = solver.MakeIntVarArray(p, 0, p-1, "position"); + IntVar[] solution = solver.MakeIntVarArray(p, 1, k, "solution"); + + // + // Constraints + // + solver.Add(position.AllDifferent()); + + for(int i = 1; i <= k; i++) { + solver.Add(position[i+k-1] - (position[i-1] + solver.MakeIntVar(i+1,i+1)) == 0); + solver.Add(solution.Element(position[i-1]) == i); + solver.Add(solution.Element(position[k+i-1]) == i); + } + + // Symmetry breaking + solver.Add(solution[0] < solution[2*k-1]); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(position, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db); + + int num_solutions = 0; + while (solver.NextSolution()) { + Console.Write("solution : "); + for(int i = 0; i < p; i++) { + Console.Write(solution[i].Value() + " "); + } + Console.WriteLine(); + num_solutions++; + if (num_sol > 0 && num_solutions >= num_sol) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + int k = 8; + int num_sol = 0; // 0: print all solutions + + if (args.Length > 0) { + k = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + num_sol = Convert.ToInt32(args[1]); + } + + Solve(k, num_sol); + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/least_diff.cs b/libs/or-tools-src-ubuntu/examples/dotnet/least_diff.cs new file mode 100644 index 0000000000000000000000000000000000000000..f525265e8c77e2fb117081e4e25de447b0f3d53a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/least_diff.cs @@ -0,0 +1,91 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class LeastDiff +{ + /** + * + * Solve the Least diff problem + * For more info, see http://www.hakank.org/google_or_tools/least_diff.py + * + */ + private static void Solve() + { + Solver solver = new Solver("LeastDiff"); + + // + // Decision variables + // + IntVar A = solver.MakeIntVar(0, 9, "A"); + IntVar B = solver.MakeIntVar(0, 9, "B"); + IntVar C = solver.MakeIntVar(0, 9, "C"); + IntVar D = solver.MakeIntVar(0, 9, "D"); + IntVar E = solver.MakeIntVar(0, 9, "E"); + IntVar F = solver.MakeIntVar(0, 9, "F"); + IntVar G = solver.MakeIntVar(0, 9, "G"); + IntVar H = solver.MakeIntVar(0, 9, "H"); + IntVar I = solver.MakeIntVar(0, 9, "I"); + IntVar J = solver.MakeIntVar(0, 9, "J"); + + IntVar[] all = new IntVar[] {A,B,C,D,E,F,G,H,I,J}; + int[] coeffs = {10000,1000,100,10,1}; + IntVar x = new IntVar[]{A,B,C,D,E}.ScalProd(coeffs).Var(); + IntVar y = new IntVar[]{F,G,H,I,J}.ScalProd(coeffs).Var(); + IntVar diff = (x - y).VarWithName("diff"); + + + // + // Constraints + // + solver.Add(all.AllDifferent()); + solver.Add(A > 0); + solver.Add(F > 0); + solver.Add(diff > 0); + + + // + // Objective + // + OptimizeVar obj = diff.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.CHOOSE_PATH, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + while (solver.NextSolution()) { + Console.WriteLine("{0} - {1} = {2} ({3}",x.Value(), y.Value(), diff.Value(), diff.ToString()); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/lectures.cs b/libs/or-tools-src-ubuntu/examples/dotnet/lectures.cs new file mode 100644 index 0000000000000000000000000000000000000000..1427233fd62a5658316ddff60595b0eb08ccc1f4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/lectures.cs @@ -0,0 +1,193 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class Lectures +{ + + /** + * + * Lectures problem in Google CP Solver. + * + * Biggs: Discrete Mathematics (2nd ed), page 187. + * """ + * Suppose we wish to schedule six one-hour lectures, v1, v2, v3, v4, v5, v6. + * Among the the potential audience there are people who wish to hear both + * + * - v1 and v2 + * - v1 and v4 + * - v3 and v5 + * - v2 and v6 + * - v4 and v5 + * - v5 and v6 + * - v1 and v6 + * + * How many hours are necessary in order that the lectures can be given + * without clashes? + * """ + * + * Note: This can be seen as a coloring problem. + * + * Also see http://www.hakank.org/or-tools/lectures.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Lectures"); + + // + // The schedule requirements: + // lecture a cannot be held at the same time as b + // Note: 1-based (compensated in the constraints). + int[,] g = + { + {1, 2}, + {1, 4}, + {3, 5}, + {2, 6}, + {4, 5}, + {5, 6}, + {1, 6} + }; + + // number of nodes + int n = 6; + + // number of edges + int edges = g.GetLength(0); + + // + // Decision variables + // + // + // declare variables + // + IntVar[] v = solver.MakeIntVarArray(n, 0, n-1,"v"); + + // Maximum color (hour) to minimize. + // Note: since C# is 0-based, the + // number of colors is max_c+1. + IntVar max_c = v.Max().VarWithName("max_c"); + + + // + // Constraints + // + + // Ensure that there are no clashes + // also, adjust to 0-base. + for(int i = 0; i < edges; i++) { + solver.Add(v[g[i,0]-1] != v[g[i,1]-1]); + } + + // Symmetry breaking: + // - v0 has the color 0, + // - v1 has either color 0 or 1 + solver.Add(v[0] == 0); + solver.Add(v[1] <= 1); + + + // + // Objective + // + OptimizeVar obj = max_c.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(v, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("\nmax hours: {0}", max_c.Value()+1); + Console.WriteLine("v: " + + String.Join(" ", (from i in Enumerable.Range(0, n) + select v[i].Value()).ToArray())); + for(int i = 0; i < n; i++) { + Console.WriteLine("Lecture {0} at {1}h", i, v[i].Value()); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + // Print the current solution + public static void PrintOneSolution(IntVar[] positions, + int rows, + int cols, + int num_solution) + { + + Console.WriteLine("Solution {0}", num_solution); + + // Create empty board + int[,] board = new int[rows, cols]; + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + board[i,j] = 0; + } + } + + // Fill board with solution value + for(int k = 0; k < rows*cols; k++) { + int position = (int)positions[k].Value(); + board[position / cols, position % cols] = k + 1; + } + + PrintMatrix(board); + + } + + + // Pretty print of the matrix + public static void PrintMatrix(int[,] game) + { + int rows = game.GetLength(0); + int cols = game.GetLength(1); + + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + if (game[i,j] == 0) { + Console.Write(" ."); + } else { + Console.Write(" {0,2}", game[i,j] ); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/magic_sequence.cs b/libs/or-tools-src-ubuntu/examples/dotnet/magic_sequence.cs new file mode 100644 index 0000000000000000000000000000000000000000..609fcca2d3ec418a524b74f3e063102e06b4e046 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/magic_sequence.cs @@ -0,0 +1,112 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Linq; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class MagicSequence +{ + + /** + * + * Magic sequence problem. + * + * This is a port of the Python model + * https://code.google.com/p/or-tools/source/browse/trunk/python/magic_sequence_distribute.py + * """ + * This models aims at building a sequence of numbers such that the number of + * occurrences of i in this sequence is equal to the value of the ith number. + * It uses an aggregated formulation of the count expression called + * distribute(). + * """ + * + */ + private static void Solve(int size) + { + + Solver solver = new Solver("MagicSequence"); + + Console.WriteLine("\nSize: {0}", size); + + // + // data + // + int[] all_values = new int[size]; + for (int i = 0; i < size; i++) { + all_values[i] = i; + } + + // + // Decision variables + // + IntVar[] all_vars = solver.MakeIntVarArray(size, 0, size - 1, "vars"); + + // + // Constraints + // + solver.Add(all_vars.Distribute(all_values, all_vars)); + solver.Add(all_vars.Sum() == size); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all_vars, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < size; i++) { + Console.Write(all_vars[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + + if (args.Length > 0) { + + int size = Convert.ToInt32(args[0]); + Solve(size); + + } else { + // Let's test some diferent sizes + foreach(int i in new int[] {2, 10, 100, 200, 500}) { + Solve(i); + } + + } + + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/magic_square.cs b/libs/or-tools-src-ubuntu/examples/dotnet/magic_square.cs new file mode 100644 index 0000000000000000000000000000000000000000..99e679e3f49839d4fc7b9e17b0473f2da6a4dfbd --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/magic_square.cs @@ -0,0 +1,140 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class MagicSquare +{ + + /** + * + * Solves the Magic Square problem. + * See http://www.hakank.org/or-tools/magic_square.py + * + */ + private static void Solve(int n = 4, int num = 0, int print = 1) + { + Solver solver = new Solver("MagicSquare"); + + Console.WriteLine("n: {0}", n); + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n*n, "x"); + // for the branching + IntVar[] x_flat = x.Flatten(); + + + // + // Constraints + // + long s = (n * (n * n + 1)) / 2; + Console.WriteLine("s: " + s); + + IntVar[] diag1 = new IntVar[n]; + IntVar[] diag2 = new IntVar[n]; + for(int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + for(int j = 0; j < n; j++) { + row[j] = x[i,j]; + } + // sum row to s + solver.Add(row.Sum() == s); + + diag1[i] = x[i,i]; + diag2[i] = x[i,n - i - 1]; + } + + // sum diagonals to s + solver.Add(diag1.Sum() == s); + solver.Add(diag2.Sum() == s); + + // sum columns to s + for(int j = 0; j < n; j++) { + IntVar[] col = new IntVar[n]; + for(int i = 0; i < n; i++) { + col[i] = x[i,j]; + } + solver.Add(col.Sum() == s); + } + + // all are different + solver.Add(x_flat.AllDifferent()); + + // symmetry breaking: upper left is 1 + // solver.Add(x[0,0] == 1); + + + // + // Search + // + + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_CENTER_VALUE); + + + solver.NewSearch(db); + + int c = 0; + while (solver.NextSolution()) { + if (print != 0) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + c++; + if (num > 0 && c >= num) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int n = 4; + int num = 0; + int print = 1; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + num = Convert.ToInt32(args[1]); + } + + if (args.Length > 2) { + print = Convert.ToInt32(args[2]); + } + + Solve(n, num, print); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/magic_square_and_cards.cs b/libs/or-tools-src-ubuntu/examples/dotnet/magic_square_and_cards.cs new file mode 100644 index 0000000000000000000000000000000000000000..a76039f2d3b20e28e16840b45d64b48c71aa1f27 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/magic_square_and_cards.cs @@ -0,0 +1,131 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class MagicSquareAndCards +{ + + /** + * + * Magic squares and cards problem. + * + * Martin Gardner (July 1971) + * """ + * Allowing duplicates values, what is the largest constant sum for an order-3 + * magic square that can be formed with nine cards from the deck. + * """ + * + * + * Also see http://www.hakank.org/or-tools/magic_square_and_cards.py + * + */ + private static void Solve(int n=3) + { + + Solver solver = new Solver("MagicSquareAndCards"); + + IEnumerable RANGE = Enumerable.Range(0, n); + + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, 13, "x"); + IntVar[] x_flat = x.Flatten(); + + IntVar s = solver.MakeIntVar(1, 13*4, "s"); + IntVar[] counts = solver.MakeIntVarArray(14, 0, 4, "counts"); + + // + // Constraints + // + + solver.Add(x_flat.Distribute(counts)); + + // the standard magic square constraints (sans all_different) + foreach(int i in RANGE) { + // rows + solver.Add( (from j in RANGE select x[i,j]).ToArray().Sum() == s); + + // columns + solver.Add( (from j in RANGE select x[j,i]).ToArray().Sum() == s); + } + + // diagonals + solver.Add( (from i in RANGE select x[i,i]).ToArray().Sum() == s); + solver.Add( (from i in RANGE select x[i,n-i-1]).ToArray().Sum() == s); + + + // redundant constraint + solver.Add(counts.Sum() == n*n); + + + // + // Objective + // + OptimizeVar obj = s.Maximize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("s: {0}", s.Value()); + Console.Write("counts:"); + for(int i = 0; i < 14; i++) { + Console.Write(counts[i].Value() + " "); + } + Console.WriteLine(); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + int n = 3; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/map.cs b/libs/or-tools-src-ubuntu/examples/dotnet/map.cs new file mode 100644 index 0000000000000000000000000000000000000000..bafba79858ac46214bf497ce8fbd040942e39d8f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/map.cs @@ -0,0 +1,96 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class Map +{ + /** + * + * Solves a simple map coloring problem. + * + * See http://www.hakank.org/google_or_tools/map.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Map"); + + // + // data + // + int Belgium = 0; + int Denmark = 1; + int France = 2; + int Germany = 3; + int Netherlands = 4; + int Luxembourg = 5; + + int n = 6; + int max_num_colors = 4; + + // + // Decision variables + // + IntVar[] color = solver.MakeIntVarArray(n, 1, max_num_colors, "color"); + + // + // Constraints + // + solver.Add(color[France] != color[Belgium]); + solver.Add(color[France] != color[Luxembourg]); + solver.Add(color[France] != color[Germany]); + solver.Add(color[Luxembourg] != color[Germany]); + solver.Add(color[Luxembourg] != color[Belgium]); + solver.Add(color[Belgium] != color[Netherlands]); + solver.Add(color[Belgium] != color[Germany]); + solver.Add(color[Germany] != color[Netherlands]); + solver.Add(color[Germany] != color[Denmark]); + + // Symmetry breaking + solver.Add(color[Belgium] == 1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(color, + Solver.CHOOSE_MIN_SIZE_LOWEST_MAX, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + Console.Write("colors: "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", color[i].Value()); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0} ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/map2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/map2.cs new file mode 100644 index 0000000000000000000000000000000000000000..b58eaa1411647648e2b7488321d3458b9d1c9814 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/map2.cs @@ -0,0 +1,107 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class Map2 +{ + /** + * + * Solves a simple map coloring problem. + * + * Alternative version, using a matrix to represent + * the neighbours. + * + * See http://www.hakank.org/google_or_tools/map.py + * + * + */ + private static void Solve() + { + Solver solver = new Solver("Map2"); + + // + // data + // + int Belgium = 0; + int Denmark = 1; + int France = 2; + int Germany = 3; + int Netherlands = 4; + int Luxembourg = 5; + + int n = 6; + int max_num_colors = 4; + + int[,] neighbours = {{France, Belgium}, + {France, Luxembourg}, + {France, Germany}, + {Luxembourg, Germany}, + {Luxembourg, Belgium}, + {Belgium, Netherlands}, + {Belgium, Germany}, + {Germany, Netherlands}, + {Germany, Denmark}}; + + + + // + // Decision variables + // + IntVar[] color = solver.MakeIntVarArray(n, 1, max_num_colors, "color"); + + // + // Constraints + // + for(int i = 0; i < neighbours.GetLength(0); i++) { + solver.Add(color[neighbours[i,0]] != color[neighbours[i,1]]); + } + + // Symmetry breaking + solver.Add(color[Belgium] == 1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(color, + Solver.CHOOSE_MIN_SIZE_LOWEST_MAX, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + Console.Write("colors: "); + for(int i = 0; i < n; i++) { + Console.Write("{0} ", color[i].Value()); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/marathon2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/marathon2.cs new file mode 100644 index 0000000000000000000000000000000000000000..4c63f39da1556f354c4fa5e1747d8edfcf20ae8f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/marathon2.cs @@ -0,0 +1,144 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class Marathon2 +{ + /** + * + * Marathon puzzle. + * + * From Xpress example + * http://www.dashoptimization.com/home/cgi-bin/example.pl?id=mosel_puzzle_5_3 + * """ + * Dominique, Ignace, Naren, Olivier, Philippe, and Pascal + * have arrived as the first six at the Paris marathon. + * Reconstruct their arrival order from the following + * information: + * a) Olivier has not arrived last + * b) Dominique, Pascal and Ignace have arrived before Naren + * and Olivier + * c) Dominique who was third last year has improved this year. + * d) Philippe is among the first four. + * e) Ignace has arrived neither in second nor third position. + * f) Pascal has beaten Naren by three positions. + * g) Neither Ignace nor Dominique are on the fourth position. + * + * (c) 2002 Dash Associates + * author: S. Heipcke, Mar. 2002 + * """ + * + * Also see http://www.hakank.org/or-tools/marathon2.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Marathon2"); + + // + // Data + // + int n = 6; + String[] runners_str = {"Dominique", "Ignace", "Naren", + "Olivier", "Philippe", "Pascal"}; + + + // + // Decision variables + // + IntVar[] runners = solver.MakeIntVarArray(n, 1, n, "runners"); + IntVar Dominique = runners[0]; + IntVar Ignace = runners[1]; + IntVar Naren = runners[2]; + IntVar Olivier = runners[3]; + IntVar Philippe = runners[4]; + IntVar Pascal = runners[5]; + + // + // Constraints + // + solver.Add(runners.AllDifferent()); + + // a: Olivier not last + solver.Add(Olivier != n); + + // b: Dominique, Pascal and Ignace before Naren and Olivier + solver.Add(Dominique < Naren); + solver.Add(Dominique < Olivier); + solver.Add(Pascal < Naren); + solver.Add(Pascal < Olivier); + solver.Add(Ignace < Naren); + solver.Add(Ignace < Olivier); + + // c: Dominique better than third + solver.Add(Dominique < 3); + + // d: Philippe is among the first four + solver.Add(Philippe <= 4); + + // e: Ignace neither second nor third + solver.Add(Ignace != 2); + solver.Add(Ignace != 3); + + // f: Pascal three places earlier than Naren + solver.Add(Pascal + 3 == Naren); + + // g: Neither Ignace nor Dominique on fourth position + solver.Add(Ignace != 4); + solver.Add(Dominique != 4); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(runners, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + int[] runners_val = new int[n]; + Console.Write("runners: "); + for(int i = 0; i < n; i++) { + runners_val[i] = (int)runners[i].Value(); + Console.Write(runners_val[i] + " "); + } + Console.WriteLine("\nPlaces:"); + for(int i = 1; i < n+1; i++) { + for(int j = 0; j < n; j++) { + if (runners_val[j] == i) { + Console.WriteLine("{0}: {1}", i, runners_str[j]); + } + } + } + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/max_flow_taha.cs b/libs/or-tools-src-ubuntu/examples/dotnet/max_flow_taha.cs new file mode 100644 index 0000000000000000000000000000000000000000..8bfc2a473ac4d52df5dbda56e96656b33ec40801 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/max_flow_taha.cs @@ -0,0 +1,170 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class MaxFlowTaha +{ + /** + * + * Max flow problem. + * + * From Taha "Introduction to Operations Research", Example 6.4-2 + * + * Translated from the AMPL code at + * http://taha.ineg.uark.edu/maxflo.txt + * + * Also see http://www.hakank.org/or-tools/max_flow_taha.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("MaxFlowTaha"); + + // + // Data + // + int n = 5; + int start = 0; + int end = n-1; + + IEnumerable NODES = Enumerable.Range(0, n); + + // cost matrix + int[,] c = { + {0, 20, 30, 10, 0}, + {0, 0, 40, 0, 30}, + {0, 0, 0, 10, 20}, + {0, 0, 5, 0, 20}, + {0, 0, 0, 0, 0} + }; + + + + // + // Decision variables + // + IntVar[,] x = new IntVar[n,n]; + foreach(int i in NODES) { + foreach(int j in NODES) { + x[i,j] = solver.MakeIntVar(0, c[i,j], "x"); + } + } + + IntVar[] x_flat = x.Flatten(); + + IntVar[] out_flow = solver.MakeIntVarArray(n, 0, 1000, "out_flow"); + IntVar[] in_flow = solver.MakeIntVarArray(n, 0, 1000, "in_flow"); + IntVar total = solver.MakeIntVar(0, 10000, "total"); + + // + // Constraints + // + solver.Add( (from j in NODES + where c[start,j] > 0 + select x[start,j] + ).ToArray().Sum() == total); + + foreach(int i in NODES) { + + var in_flow_sum = (from j in NODES + where c[j,i] > 0 + select x[j,i] + ); + if (in_flow_sum.Count() > 0) { + solver.Add(in_flow_sum.ToArray().Sum() == in_flow[i]); + } + + var out_flow_sum = (from j in NODES + where c[i,j] > 0 + select x[i,j] + ); + if (out_flow_sum.Count() > 0) { + solver.Add(out_flow_sum.ToArray().Sum() == out_flow[i]); + } + + } + + // in_flow == out_flow + foreach(int i in NODES) { + if (i != start && i != end) { + solver.Add(out_flow[i] == in_flow[i]); + } + } + + var s1 = (from i in NODES where c[i,start] > 0 select x[i,start]); + if (s1.Count() > 0) { + solver.Add(s1.ToArray().Sum() == 0); + } + + var s2 = (from j in NODES where c[end, j] > 0 select x[end,j]); + if (s2.Count() > 0) { + solver.Add(s2.ToArray().Sum() == 0); + } + + + // + // Objective + // + OptimizeVar obj = total.Maximize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat.Concat(in_flow).Concat(out_flow).ToArray(), + Solver.INT_VAR_DEFAULT, + Solver.ASSIGN_MAX_VALUE); + + solver.NewSearch(db, obj); + while (solver.NextSolution()) { + Console.WriteLine("total: {0}",total.Value()); + Console.Write("in_flow : "); + foreach(int i in NODES) { + Console.Write(in_flow[i].Value() + " "); + } + Console.Write("\nout_flow: "); + foreach(int i in NODES) { + Console.Write(out_flow[i].Value() + " "); + } + Console.WriteLine(); + foreach(int i in NODES) { + foreach(int j in NODES) { + Console.Write("{0,2} ", x[i,j].Value()); + } + Console.WriteLine(); + } + Console.WriteLine(); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/max_flow_winston1.cs b/libs/or-tools-src-ubuntu/examples/dotnet/max_flow_winston1.cs new file mode 100644 index 0000000000000000000000000000000000000000..0fa4573b112338b4abcf2c1ef2c725dde4c1e17a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/max_flow_winston1.cs @@ -0,0 +1,165 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class MaxFlowWinston1 +{ + /** + * + * Max flow problem. + * + * From Winston 'Operations Research', page 420f, 423f + * Sunco Oil example. + * + * + * Also see http://www.hakank.org/or-tools/max_flow_winston1.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("MaxFlowWinston1"); + + // + // Data + // + int n = 5; + IEnumerable NODES = Enumerable.Range(0, n); + + // The arcs + // Note: + // This is 1-based to be compatible with other implementations. + // + int[,] arcs1 = { + {1, 2}, + {1, 3}, + {2, 3}, + {2, 4}, + {3, 5}, + {4, 5}, + {5, 1} + }; + + // Capacities + int [] cap = {2,3,3,4,2,1,100}; + + // Convert arcs to 0-based + int num_arcs = arcs1.GetLength(0); + IEnumerable ARCS = Enumerable.Range(0, num_arcs); + int[,] arcs = new int[num_arcs, 2]; + foreach(int i in ARCS) { + for(int j = 0; j < 2; j++) { + arcs[i,j] = arcs1[i,j] - 1; + } + } + + // Convert arcs to matrix (for sanity checking below) + int[,] mat = new int[num_arcs, num_arcs]; + foreach(int i in NODES) { + foreach(int j in NODES) { + int c = 0; + foreach(int k in ARCS) { + if (arcs[k,0] == i && arcs[k,1] == j) { + c = 1; + } + } + mat[i,j] = c; + } + } + + // + // Decision variables + // + IntVar[,] flow = solver.MakeIntVarMatrix(n, n, 0, 200, "flow"); + IntVar z = flow[n-1, 0].VarWithName("z"); + + // + // Constraints + // + + // capacity of arcs + foreach(int i in ARCS) { + solver.Add(flow[arcs[i,0], arcs[i,1]] <= cap[i]); + } + + // inflows == outflows + foreach(int i in NODES) { + var s1 = (from k in ARCS + where arcs[k,1] == i + select flow[arcs[k,0], arcs[k,1]] + ).ToArray().Sum(); + + var s2 = (from k in ARCS + where arcs[k,0] == i + select flow[arcs[k,0], arcs[k,1]] + ).ToArray().Sum(); + + solver.Add(s1 == s2); + + } + + // Sanity check: just arcs with connections can have a flow. + foreach(int i in NODES) { + foreach(int j in NODES) { + if (mat[i,j] == 0) { + solver.Add(flow[i,j] == 0); + } + } + } + + + // + // Objective + // + OptimizeVar obj = z.Maximize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(flow.Flatten(), + Solver.INT_VAR_DEFAULT, + Solver.ASSIGN_MAX_VALUE); + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}",z.Value()); + foreach(int i in NODES) { + foreach(int j in NODES) { + Console.Write(flow[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/minesweeper.cs b/libs/or-tools-src-ubuntu/examples/dotnet/minesweeper.cs new file mode 100644 index 0000000000000000000000000000000000000000..2f51764932baeeb2ef6ec19f0d2fc92b4bc4cff2 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/minesweeper.cs @@ -0,0 +1,232 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class Minesweeper +{ + + static int X = -1; + + // + // Default problem. + // It has 4 solutions. + // + static int default_r = 8; + static int default_c = 8; + static int[,] default_game = {{2, 3, X, 2, 2, X, 2, 1}, + {X, X, 4, X, X, 4, X, 2}, + {X, X, X, X, X, X, 4, X}, + {X, 5, X, 6, X, X, X, 2}, + {2, X, X, X, 5, 5, X, 2}, + {1, 3, 4, X, X, X, 4, X}, + {0, 1, X, 4, X, X, X, 3}, + {0, 1, 2, X, 2, 3, X, 2}}; + + // for the actual problem + static int r; + static int c; + static int[,] game; + + + /** + * + * Solves the Minesweeper problems. + * + * See http://www.hakank.org/google_or_tools/minesweeper.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Minesweeper"); + + // + // data + // + int[] S = {-1, 0, 1}; + + Console.WriteLine("Problem:"); + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + if (game[i,j] > X) { + Console.Write(game[i,j] + " "); + } else { + Console.Write("X "); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + + + // + // Decision variables + // + IntVar[,] mines = solver.MakeIntVarMatrix(r, c, 0, 1, "mines"); + // for branching + IntVar[] mines_flat = mines.Flatten(); + + // + // Constraints + // + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + if (game[i,j] >= 0) { + solver.Add( mines[i,j] == 0); + + // this cell is the sum of all its neighbours + var tmp = from a in S from b in S where + i + a >= 0 && + j + b >= 0 && + i + a < r && + j + b < c + select(mines[i+a,j+b]); + + solver.Add(tmp.ToArray().Sum() == game[i,j]); + + } + + if (game[i,j] > X) { + // This cell cannot be a mine since it + // has some value assigned to it + solver.Add(mines[i,j] == 0); + } + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(mines_flat, + Solver.CHOOSE_PATH, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int sol = 0; + while (solver.NextSolution()) { + sol++; + Console.WriteLine("Solution #{0} ", sol + " "); + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++){ + Console.Write("{0} ", mines[i,j].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + /** + * + * Reads a minesweeper file. + * File format: + * # a comment which is ignored + * % a comment which also is ignored + * number of rows + * number of columns + * < + * row number of neighbours lines... + * > + * + * 0..8 means number of neighbours, "." mean unknown (may be a mine) + * + * Example (from minesweeper0.txt) + * # Problem from Gecode/examples/minesweeper.cc problem 0 + * 6 + * 6 + * ..2.3. + * 2..... + * ..24.3 + * 1.34.. + * .....3 + * .3.3.. + * + */ + private static void readFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + int lineCount = 0; + + TextReader inr = new StreamReader(file); + String str; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + + str = str.Trim(); + + // ignore comments + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + Console.WriteLine(str); + if (lineCount == 0) { + r = Convert.ToInt32(str); // number of rows + } else if (lineCount == 1) { + c = Convert.ToInt32(str); // number of columns + game = new int[r,c]; + } else { + // the problem matrix + String[] row = Regex.Split(str, ""); + for(int j = 1; j <= c; j++) { + String s = row[j]; + if (s.Equals(".")) { + game[lineCount-2,j-1] = -1; + } else { + game[lineCount-2,j-1] = Convert.ToInt32(s); + } + } + } + + lineCount++; + + } // end while + + inr.Close(); + + } // end readFile + + + public static void Main(String[] args) + { + String file = ""; + if (args.Length > 0) { + file = args[0]; + readFile(file); + } else { + game = default_game; + r = default_r; + c = default_c; + } + + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/mr_smith.cs b/libs/or-tools-src-ubuntu/examples/dotnet/mr_smith.cs new file mode 100644 index 0000000000000000000000000000000000000000..16f32f1bb01b294d69668de79ccdd390e0907b1c --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/mr_smith.cs @@ -0,0 +1,136 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class MrSmith +{ + /** + * + * Mr Smith problem. + * + * From an IF Prolog example (http://www.ifcomputer.de/) + * """ + * The Smith family and their three children want to pay a visit but they + * do not all have the time to do so. Following are few hints who will go + * and who will not: + * o If Mr Smith comes, his wife will come too. + * o At least one of their two sons Matt and John will come. + * o Either Mrs Smith or Tim will come, but not both. + * o Either Tim and John will come, or neither will come. + * o If Matt comes, then John and his father will + * also come. + * """ + * + * The answer should be: + * Mr_Smith_comes = 0 + * Mrs_Smith_comes = 0 + * Matt_comes = 0 + * John_comes = 1 + * Tim_comes = 1 + * + * + * Also see http://www.hakank.org/or-tools/mr_smith.py + * + */ + private static void Solve() + { + Solver solver = new Solver("MrSmith"); + + // + // Data + // + int n = 5; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 1, "x"); + IntVar Mr_Smith = x[0]; + IntVar Mrs_Smith = x[1]; + IntVar Matt = x[2]; + IntVar John = x[3]; + IntVar Tim = x[4]; + + + // + // Constraints + // + + // + // I've kept the MiniZinc constraints for clarity + // and debugging. + // + + // If Mr Smith comes then his wife will come too. + // (Mr_Smith -> Mrs_Smith) + solver.Add(Mr_Smith - Mrs_Smith <= 0); + + // At least one of their two sons Matt and John will come. + // (Matt \/ John) + solver.Add(Matt+John >= 1); + + // Either Mrs Smith or Tim will come but not both. + // bool2int(Mrs_Smith) + bool2int(Tim) = 1 + // (Mrs_Smith xor Tim) + solver.Add(Mrs_Smith + Tim == 1); + + // Either Tim and John will come or neither will come. + // (Tim = John) + solver.Add(Tim == John); + + // If Matt comes /\ then John and his father will also come. + // (Matt -> (John /\ Mr_Smith)) + solver.Add(Matt - (John*Mr_Smith) <= 0); + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine("\n"); + Console.WriteLine("Mr Smith : {0}", Mr_Smith.Value()); + Console.WriteLine("Mrs Smith: {0}", Mrs_Smith.Value()); + Console.WriteLine("Matt : {0}", Matt.Value()); + Console.WriteLine("John : {0}", John.Value()); + Console.WriteLine("Tim : {0}", Tim.Value()); + + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/nontransitive_dice.cs b/libs/or-tools-src-ubuntu/examples/dotnet/nontransitive_dice.cs new file mode 100644 index 0000000000000000000000000000000000000000..a4284496f0c1230c21af940de481361c8c019666 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/nontransitive_dice.cs @@ -0,0 +1,210 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class NonTransitiveDice +{ + + /** + * + * Nontransitive dice. + * + * From + * http://en.wikipedia.org/wiki/Nontransitive_dice + * """ + * A set of nontransitive dice is a set of dice for which the relation + * 'is more likely to roll a higher number' is not transitive. See also + * intransitivity. + * + * This situation is similar to that in the game Rock, Paper, Scissors, + * in which each element has an advantage over one choice and a + * disadvantage to the other. + * """ + * + * Also see http://www.hakank.org/or-tools/nontransitive_dice.py + * + * + */ + private static void Solve(int m=3, int n=6, int minimize_val=0) + { + + Solver solver = new Solver("Nontransitive_dice"); + + Console.WriteLine("Number of dice: {0}", m); + Console.WriteLine("Number of sides: {0}", n); + Console.WriteLine("minimize_val: {0}\n", minimize_val); + + // + // Decision variables + // + + // The dice + IntVar[,] dice = solver.MakeIntVarMatrix(m, n, 1, n*2, "dice"); + IntVar[] dice_flat = dice.Flatten(); + + // For comparison (probability) + IntVar[,] comp = solver.MakeIntVarMatrix(m, 2, 0, n*n, "dice"); + IntVar[] comp_flat = comp.Flatten(); + + // For branching + IntVar[] all = dice_flat.Concat(comp_flat).ToArray(); + + // The following variables are for summaries or objectives + IntVar[] gap = solver.MakeIntVarArray(m, 0, n*n, "gap"); + IntVar gap_sum = gap.Sum().Var(); + + IntVar max_val = dice_flat.Max().Var(); + IntVar max_win = comp_flat.Max().Var(); + + // number of occurrences of each value of the dice + IntVar[] counts = solver.MakeIntVarArray(n*2+1, 0, n*m, "counts"); + + + // + // Constraints + // + + // Number of occurrences for each number + solver.Add(dice_flat.Distribute(counts)); + + // Order of the number of each die, lowest first + for(int i = 0; i < m; i++) { + for(int j = 0; j < n-1; j++) { + solver.Add(dice[i,j] <= dice[i,j+1]); + } + } + + // Nontransitivity + for(int i = 0; i < m; i++) { + solver.Add(comp[i,0] > comp[i,1]); + } + + // Probability gap + for(int i = 0; i < m; i++) { + solver.Add(gap[i] == comp[i,0] - comp[i,1]); + solver.Add(gap[i] > 0); + } + + // And now we roll... + // comp[] is the number of wins for [A vs B, B vs A] + for(int d = 0; d < m; d++) { + IntVar sum1 = ( from r1 in Enumerable.Range(0, n) + from r2 in Enumerable.Range(0, n) + select (dice[d % m, r1] > dice[(d+1) % m, r2]) + ).ToArray().Sum().Var(); + + solver.Add(comp[d%m,0] == sum1); + + IntVar sum2 = ( from r1 in Enumerable.Range(0, n) + from r2 in Enumerable.Range(0, n) + select (dice[(d+1) % m, r1] > dice[d % m, r2]) + ).ToArray().Sum().Var(); + + solver.Add(comp[d%m,1] == sum2); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.INT_VAR_DEFAULT, + Solver.ASSIGN_MIN_VALUE); + + if (minimize_val > 0) { + Console.WriteLine("Minimizing max_val"); + + OptimizeVar obj = max_val.Minimize(1); + + // Other experiments: + // OptimizeVar obj = max_win.Maximize(1); + // OptimizeVar obj = gap_sum.Maximize(1); + + solver.NewSearch(db, obj); + + } else { + solver.NewSearch(db); + } + + while (solver.NextSolution()) { + Console.WriteLine("gap_sum: {0}", gap_sum.Value()); + Console.WriteLine("gap: {0}", (from i in Enumerable.Range(0, m) + select gap[i].Value().ToString() + ).ToArray() + ); + Console.WriteLine("max_val: {0}", max_val.Value()); + Console.WriteLine("max_win: {0}", max_win.Value()); + Console.WriteLine("dice:"); + for(int i = 0; i < m; i++) { + for(int j = 0; j < n; j++) { + Console.Write(dice[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine("comp:"); + for(int i = 0; i < m; i++) { + for(int j = 0; j < 2; j++) { + Console.Write(comp[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine("counts:"); + for(int i = 1; i < n*2+1; i++) { + int c = (int)counts[i].Value(); + if (c > 0) { + Console.Write("{0}({1}) ", i, c); + } + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + int m = 3; // number of dice + int n = 6; // number of sides of each die + int minimize_val = 0; // minimizing max_max (0: no, 1: yes) + + if (args.Length > 0) { + m = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + n = Convert.ToInt32(args[1]); + } + + if (args.Length > 2) { + minimize_val = Convert.ToInt32(args[2]); + } + + Solve(m, n, minimize_val); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/nqueens.cs b/libs/or-tools-src-ubuntu/examples/dotnet/nqueens.cs new file mode 100644 index 0000000000000000000000000000000000000000..ff14865aa06062f7890fdc870f716793e52b3be4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/nqueens.cs @@ -0,0 +1,119 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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.Linq; +using Google.OrTools.ConstraintSolver; + +public class NQueens +{ + /** + * + * Solves the N-Queens problem. + * + * Syntax: nqueens.exe n num print + * where + * n : size of board + * num : number of solutions to calculate + * print: print the results (if > 0) + * + */ + private static void Solve(int n=8, int num=0, int print=1) + { + Solver solver = new Solver("N-Queens"); + + // + // Decision variables + // + IntVar[] q = solver.MakeIntVarArray(n, 0, n-1, "q"); + + + // + // Constraints + // + solver.Add(q.AllDifferent()); + + IntVar[] q1 = new IntVar[n]; + IntVar[] q2 = new IntVar[n]; + for(int i = 0; i < n; i++) { + q1[i] = (q[i] + i).Var(); + q2[i] = (q[i] - i).Var(); + } + solver.Add(q1.AllDifferent()); + solver.Add(q2.AllDifferent()); + + // Alternative version: it works as well but are not that clear + /* + solver.Add((from i in Enumerable.Range(0, n) + select (q[i] + i).Var()).ToArray().AllDifferent()); + + solver.Add((from i in Enumerable.Range(0, n) + select (q[i] - i).Var()).ToArray().AllDifferent()); + */ + + // + // Search + // + DecisionBuilder db = solver.MakePhase(q, + Solver.CHOOSE_MIN_SIZE_LOWEST_MAX, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db); + int c = 0; + while (solver.NextSolution()) { + if (print > 0) { + for(int i = 0; i < n; i++) { + Console.Write("{0} ", q[i].Value()); + } + + Console.WriteLine(); + } + c++; + if (num > 0 && c >= num) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int n = 8; + int num = 0; + int print = 1; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + num = Convert.ToInt32(args[1]); + } + if (args.Length > 2) { + print = Convert.ToInt32(args[2]); + } + + Console.WriteLine("n: {0} num: {1} print: {2}", n, num, print); + + Solve(n, num, print); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/nurse_rostering_regular.cs b/libs/or-tools-src-ubuntu/examples/dotnet/nurse_rostering_regular.cs new file mode 100644 index 0000000000000000000000000000000000000000..35654a186a378c4d44a6c6388bf71b0308fa3702 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/nurse_rostering_regular.cs @@ -0,0 +1,323 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class NurseRostering +{ + + + /* + * Global constraint regular + * + * This is a translation of MiniZinc's regular constraint (defined in + * lib/zinc/globals.mzn), via the Comet code refered above. + * All comments are from the MiniZinc code. + * """ + * The sequence of values in array 'x' (which must all be in the range 1..S) + * is accepted by the DFA of 'Q' states with input 1..S and transition + * function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' + * (which must be in 1..Q) and accepting states 'F' (which all must be in + * 1..Q). We reserve state 0 to be an always failing state. + * """ + * + * x : IntVar array + * Q : number of states + * S : input_max + * d : transition matrix + * q0: initial state + * F : accepting states + * + */ + static void MyRegular(Solver solver, + IntVar[] x, + int Q, + int S, + int[,] d, + int q0, + int[] F) { + + + + Debug.Assert(Q > 0, "regular: 'Q' must be greater than zero"); + Debug.Assert(S > 0, "regular: 'S' must be greater than zero"); + + // d2 is the same as d, except we add one extra transition for + // each possible input; each extra transition is from state zero + // to state zero. This allows us to continue even if we hit a + // non-accepted input. + int[][] d2 = new int[Q+1][]; + for(int i = 0; i <= Q; i++) { + int[] row = new int[S]; + for(int j = 0; j < S; j++) { + if (i == 0) { + row[j] = 0; + } else { + row[j] = d[i-1,j]; + } + } + d2[i] = row; + } + + int[] d2_flatten = (from i in Enumerable.Range(0, Q+1) + from j in Enumerable.Range(0, S) + select d2[i][j]).ToArray(); + + // If x has index set m..n, then a[m-1] holds the initial state + // (q0), and a[i+1] holds the state we're in after processing + // x[i]. If a[n] is in F, then we succeed (ie. accept the + // string). + int m = 0; + int n = x.Length; + + IntVar[] a = solver.MakeIntVarArray(n+1-m, 0,Q+1, "a"); + // Check that the final state is in F + solver.Add(a[a.Length-1].Member(F)); + // First state is q0 + solver.Add(a[m] == q0); + + for(int i = 0; i < n; i++) { + solver.Add(x[i] >= 1); + solver.Add(x[i] <= S); + // Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == d2_flatten.Element(((a[i])*S)+(x[i]-1))); + + } + + } + + + /** + * + * Nurse rostering + * + * This is a simple nurse rostering model using a DFA and + * my decomposition of regular constraint. + * + * The DFA is from MiniZinc Tutorial, Nurse Rostering example: + * - one day off every 4 days + * - no 3 nights in a row. + * + * Also see http://www.hakank.org/or-tools/nurse_rostering.py + * + */ + private static void Solve() + { + Solver solver = new Solver("NurseRostering"); + + // + // Data + // + + // Note: If you change num_nurses or num_days, + // please also change the constraints + // on nurse_stat and/or day_stat. + int num_nurses = 7; + int num_days = 14; + + // Note: I had to add a dummy shift. + int dummy_shift = 0; + int day_shift = 1; + int night_shift = 2; + int off_shift = 3; + int[] shifts = {dummy_shift, day_shift, night_shift, off_shift}; + int[] valid_shifts = {day_shift, night_shift, off_shift}; + + // the DFA (for regular) + int n_states = 6; + int input_max = 3; + int initial_state = 1; // 0 is for the failing state + int[] accepting_states = {1,2,3,4,5,6}; + + int[,] transition_fn = { + // d,n,o + {2,3,1}, // state 1 + {4,4,1}, // state 2 + {4,5,1}, // state 3 + {6,6,1}, // state 4 + {6,0,1}, // state 5 + {0,0,1} // state 6 + }; + + string[] days = {"d","n","o"}; // for presentation + + // + // Decision variables + // + + // For regular + IntVar[,] x = + solver.MakeIntVarMatrix(num_nurses, num_days, valid_shifts, "x"); + IntVar[] x_flat = x.Flatten(); + + // summary of the nurses + IntVar[] nurse_stat = + solver.MakeIntVarArray(num_nurses, 0, num_days, "nurse_stat"); + + // summary of the shifts per day + int num_shifts = shifts.Length; + IntVar[,] day_stat = new IntVar[num_days, num_shifts]; + for(int i = 0; i < num_days; i++) { + for(int j = 0; j < num_shifts; j++) { + day_stat[i,j] = solver.MakeIntVar(0, num_nurses, "day_stat"); + } + } + + + // + // Constraints + // + for(int i = 0; i < num_nurses; i++) { + IntVar[] reg_input = new IntVar[num_days]; + for(int j = 0; j < num_days; j++) { + reg_input[j] = x[i,j]; + } + MyRegular(solver, reg_input, n_states, input_max, transition_fn, + initial_state, accepting_states); + + + + } + + // + // Statistics and constraints for each nurse + // + for(int i = 0; i < num_nurses; i++) { + + // Number of worked days (either day or night shift) + IntVar[] b = new IntVar[num_days]; + for(int j = 0; j < num_days; j++) { + b[j] = ((x[i,j] == day_shift) + (x[i,j] == night_shift)).Var(); + } + solver.Add(b.Sum() == nurse_stat[i]); + + // Each nurse must work between 7 and 10 + // days/nights during this period + solver.Add(nurse_stat[i] >= 7); + solver.Add(nurse_stat[i] <= 10); + + } + + + // + // Statistics and constraints for each day + // + for(int j = 0; j < num_days; j++) { + for(int t = 0; t < num_shifts; t++) { + IntVar[] b = new IntVar[num_nurses]; + for(int i = 0; i < num_nurses; i++) { + b[i] = x[i,j] == t; + } + solver.Add(b.Sum() == day_stat[j,t]); + } + + // + // Some constraints for each day: + // + // Note: We have a strict requirements of + // the number of shifts. + // Using atleast constraints is harder + // in this model. + // + if (j % 7 == 5 || j % 7 == 6) { + // special constraints for the weekends + solver.Add(day_stat[j,day_shift] == 2); + solver.Add(day_stat[j,night_shift] == 1); + solver.Add(day_stat[j,off_shift] == 4 ); + } else { + // for workdays: + + // - exactly 3 on day shift + solver.Add(day_stat[j,day_shift] == 3); + // - exactly 2 on night + solver.Add(day_stat[j,night_shift] == 2); + // - exactly 2 off duty + solver.Add(day_stat[j,off_shift] == 2 ); + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int num_solutions = 0; + while (solver.NextSolution()) { + num_solutions++; + for(int i = 0; i < num_nurses; i++) { + Console.Write("Nurse #{0,-2}: ", i); + var occ = new Dictionary(); + for(int j = 0; j < num_days; j++) { + int v = (int)x[i,j].Value()-1; + if (!occ.ContainsKey(v)) { + occ[v] = 0; + } + occ[v]++; + Console.Write(days[v] + " "); + } + + Console.Write(" #workdays: {0,2}", nurse_stat[i].Value()); + foreach(int s in valid_shifts) { + int v = 0; + if (occ.ContainsKey(s-1)) { + v = occ[s-1]; + } + Console.Write(" {0}:{1}", days[s-1], v); + } + Console.WriteLine(); + + } + Console.WriteLine(); + + Console.WriteLine("Statistics per day:\nDay d n o"); + for(int j = 0; j < num_days; j++) { + Console.Write("Day #{0,2}: ", j); + foreach(int t in valid_shifts) { + Console.Write(day_stat[j,t].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + + // We just show 2 solutions + if (num_solutions > 1) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/nurse_rostering_transition.cs b/libs/or-tools-src-ubuntu/examples/dotnet/nurse_rostering_transition.cs new file mode 100644 index 0000000000000000000000000000000000000000..dc39b431a65eadfcadf1190cb3fda99ff0447317 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/nurse_rostering_transition.cs @@ -0,0 +1,287 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class NurseRostering +{ + + /** + * + * Nurse rostering + * + * This is a simple nurse rostering model using a DFA and + * the built-in TransitionConstraint. + * + * The DFA is from MiniZinc Tutorial, Nurse Rostering example: + * - one day off every 4 days + * - no 3 nights in a row. + * + * Also see: + * - http://www.hakank.org/or-tools/nurse_rostering.py + * - http://www.hakank.org/or-tools/nurse_rostering_regular.cs + * which use (a decomposition of) regular constraint + * + */ + private static void Solve(int nurse_multiplier, int week_multiplier) + { + Console.WriteLine("Starting Nurse Rostering"); + Console.WriteLine(" - {0} teams of 7 nurses", nurse_multiplier); + Console.WriteLine(" - {0} blocks of 14 days", week_multiplier); + + Solver solver = new Solver("NurseRostering"); + + // + // Data + // + + // Note: If you change num_nurses or num_days, + // please also change the constraints + // on nurse_stat and/or day_stat. + int num_nurses = 7 * nurse_multiplier; + int num_days = 14 * week_multiplier; + + // Note: I had to add a dummy shift. + int dummy_shift = 0; + int day_shift = 1; + int night_shift = 2; + int off_shift = 3; + int[] shifts = {dummy_shift, day_shift, night_shift, off_shift}; + int[] valid_shifts = {day_shift, night_shift, off_shift}; + + // the DFA (for regular) + int initial_state = 1; + int[] accepting_states = {1,2,3,4,5,6}; + + /* + // This is the transition function + // used in nurse_rostering_regular.cs + int[,] transition_fn = { + // d,n,o + {2,3,1}, // state 1 + {4,4,1}, // state 2 + {4,5,1}, // state 3 + {6,6,1}, // state 4 + {6,0,1}, // state 5 + {0,0,1} // state 6 + }; + */ + + // For TransitionConstraint + IntTupleSet transition_tuples = new IntTupleSet(3); + // state, input, next state + transition_tuples.InsertAll(new long[][] { + new long[] {1,1,2}, + new long[] {1,2,3}, + new long[] {1,3,1}, + new long[] {2,1,4}, + new long[] {2,2,4}, + new long[] {2,3,1}, + new long[] {3,1,4}, + new long[] {3,2,5}, + new long[] {3,3,1}, + new long[] {4,1,6}, + new long[] {4,2,6}, + new long[] {4,3,1}, + new long[] {5,1,6}, + new long[] {5,3,1}, + new long[] {6,3,1} }); + + string[] days = {"d","n","o"}; // for presentation + + // + // Decision variables + // + + // + // For TransitionConstraint + // + IntVar[,] x = + solver.MakeIntVarMatrix(num_nurses, num_days, valid_shifts, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // summary of the nurses + // + IntVar[] nurse_stat = new IntVar[num_nurses]; + + // + // summary of the shifts per day + // + int num_shifts = shifts.Length; + IntVar[,] day_stat = new IntVar[num_days, num_shifts]; + for(int i = 0; i < num_days; i++) { + for(int j = 0; j < num_shifts; j++) { + day_stat[i,j] = solver.MakeIntVar(0, num_nurses, "day_stat"); + } + } + + // + // Constraints + // + for(int i = 0; i < num_nurses; i++) { + IntVar[] reg_input = new IntVar[num_days]; + for(int j = 0; j < num_days; j++) { + reg_input[j] = x[i,j]; + } + + solver.Add(reg_input.Transition(transition_tuples, + initial_state, + accepting_states)); + } + + // + // Statistics and constraints for each nurse + // + for(int nurse = 0; nurse < num_nurses; nurse++) { + + // Number of worked days (either day or night shift) + IntVar[] nurse_days = new IntVar[num_days]; + for(int day = 0; day < num_days; day++) { + nurse_days[day] = + x[nurse, day].IsMember(new int[] { day_shift, night_shift }); + } + nurse_stat[nurse] = nurse_days.Sum().Var(); + + // Each nurse must work between 7 and 10 + // days/nights during this period + solver.Add(nurse_stat[nurse] >= 7 * week_multiplier / nurse_multiplier); + solver.Add(nurse_stat[nurse] <= 10 * week_multiplier / nurse_multiplier); + + } + + // + // Statistics and constraints for each day + // + for(int day = 0; day < num_days; day++) { + IntVar[] nurses = new IntVar[num_nurses]; + for(int nurse = 0; nurse < num_nurses; nurse++) { + nurses[nurse] = x[nurse, day]; + } + IntVar[] stats = new IntVar[num_shifts]; + for (int shift = 0; shift < num_shifts; ++shift) + { + stats[shift] = day_stat[day, shift]; + } + solver.Add(nurses.Distribute(stats)); + + // + // Some constraints for each day: + // + // Note: We have a strict requirements of + // the number of shifts. + // Using atleast constraints is harder + // in this model. + // + if (day % 7 == 5 || day % 7 == 6) { + // special constraints for the weekends + solver.Add(day_stat[day, day_shift] == 2 * nurse_multiplier); + solver.Add(day_stat[day, night_shift] == nurse_multiplier); + solver.Add(day_stat[day, off_shift] == 4 * nurse_multiplier); + } else { + // for workdays: + + // - exactly 3 on day shift + solver.Add(day_stat[day, day_shift] == 3 * nurse_multiplier); + // - exactly 2 on night + solver.Add(day_stat[day, night_shift] == 2 * nurse_multiplier); + // - exactly 2 off duty + solver.Add(day_stat[day, off_shift] == 2 * nurse_multiplier); + } + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + SearchMonitor log = solver.MakeSearchLog(1000000); + + solver.NewSearch(db, log); + + int num_solutions = 0; + while (solver.NextSolution()) { + num_solutions++; + for(int i = 0; i < num_nurses; i++) { + Console.Write("Nurse #{0,-2}: ", i); + var occ = new Dictionary(); + for(int j = 0; j < num_days; j++) { + int v = (int)x[i,j].Value()-1; + if (!occ.ContainsKey(v)) { + occ[v] = 0; + } + occ[v]++; + Console.Write(days[v] + " "); + } + + Console.Write(" #workdays: {0,2}", nurse_stat[i].Value()); + foreach(int s in valid_shifts) { + int v = 0; + if (occ.ContainsKey(s-1)) { + v = occ[s-1]; + } + Console.Write(" {0}:{1}", days[s-1], v); + } + Console.WriteLine(); + + } + Console.WriteLine(); + + Console.WriteLine("Statistics per day:\nDay d n o"); + for(int j = 0; j < num_days; j++) { + Console.Write("Day #{0,2}: ", j); + foreach(int t in valid_shifts) { + Console.Write(day_stat[j,t].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + + // We just show 2 solutions + if (num_solutions > 1) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int nurse_multiplier = 1; + int week_multiplier = 1; + if (args.Length > 0) { + nurse_multiplier = Convert.ToInt32(args[0]); + } + if (args.Length > 1) { + week_multiplier = Convert.ToInt32(args[1]); + } + + Solve(nurse_multiplier, week_multiplier); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/olympic.cs b/libs/or-tools-src-ubuntu/examples/dotnet/olympic.cs new file mode 100644 index 0000000000000000000000000000000000000000..9e1df76e4bf31a57723f42350ab7324959d25862 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/olympic.cs @@ -0,0 +1,137 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class Olympic +{ + + public static void minus(Solver solver, + IntVar x, + IntVar y, + IntVar z) + { + solver.Add(z == (x - y).Abs()); + } + + + + /** + * + * Olympic puzzle. + * + * Benchmark for Prolog (BProlog) + * """ + * File : olympic.pl + * Author : Neng-Fa ZHOU + * Date : 1993 + * + * Purpose: solve a puzzle taken from Olympic Arithmetic Contest + * + * Given ten variables with the following configuration: + * + * X7 X8 X9 X10 + * + * X4 X5 X6 + * + * X2 X3 + * + * X1 + * + * We already know that X1 is equal to 3 and want to assign each variable + * with a different integer from {1,2,...,10} such that for any three + * variables + * Xi Xj + * + * Xk + * + * the following constraint is satisfied: + * + * |Xi-Xj| = Xk + * """ + * + * Also see http://www.hakank.org/or-tools/olympic.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Olympic"); + + // + // Data + // + int n = 10; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 1, n, "x"); + IntVar X1 = x[0]; + IntVar X2 = x[1]; + IntVar X3 = x[2]; + IntVar X4 = x[3]; + IntVar X5 = x[4]; + IntVar X6 = x[5]; + IntVar X7 = x[6]; + IntVar X8 = x[7]; + IntVar X9 = x[8]; + IntVar X10 = x[9]; + + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + solver.Add(X1 == 3); + minus(solver, X2, X3, X1); + minus(solver, X4, X5, X2); + minus(solver, X5, X6, X3); + minus(solver, X7, X8, X4); + minus(solver, X8, X9, X5); + minus(solver, X9, X10, X6); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_SIMPLE, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write("{0,2} ", x[i].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/organize_day.cs b/libs/or-tools-src-ubuntu/examples/dotnet/organize_day.cs new file mode 100644 index 0000000000000000000000000000000000000000..f41c4647d51769523141b858bce1065d602334a6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/organize_day.cs @@ -0,0 +1,148 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class OrganizeDay +{ + + + // + // No overlapping of tasks s1 and s2 + // + public static void NoOverlap(Solver solver, + IntVar s1, int d1, + IntVar s2, int d2) + { + solver.Add((s1 + d1 <= s2) + (s2 + d2 <= s1) == 1); + } + + + + /** + * + * + * Organizing a day. + * + * Simple scheduling problem. + * + * Problem formulation from ECLiPSe: + * Slides on (Finite Domain) Constraint Logic Programming, page 38f + * http://eclipseclp.org/reports/eclipse.ppt + * + * + * Also see http://www.hakank.org/google_or_tools/organize_day.py + * + */ + private static void Solve() + { + Solver solver = new Solver("OrganizeDay"); + + + int n = 4; + + + int work = 0; + int mail = 1; + int shop = 2; + int bank = 3; + int[] tasks = {work, mail, shop, bank}; + int[] durations = {4,1,2,1}; + + // task [i,0] must be finished before task [i,1] + int[,] before_tasks = { + {bank, shop}, + {mail, work} + }; + + // the valid times of the day + int begin = 9; + int end = 17; + + + // + // Decision variables + // + IntVar[] begins = solver.MakeIntVarArray(n, begin, end, "begins"); + IntVar[] ends = solver.MakeIntVarArray(n, begin, end, "ends"); + + // + // Constraints + // + foreach(int t in tasks) { + solver.Add(ends[t] == begins[t] + durations[t]); + } + + foreach(int i in tasks) { + foreach(int j in tasks) { + if (i < j) { + NoOverlap(solver, + begins[i], durations[i], + begins[j], durations[j]); + } + } + } + + // specific constraints + for(int t = 0; t < before_tasks.GetLength(0); t++) { + solver.Add(ends[before_tasks[t,0]] <= begins[before_tasks[t,1]]); + } + + solver.Add(begins[work] >= 11); + + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(begins, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + foreach(int t in tasks) { + Console.WriteLine("Task {0}: {1,2} .. ({2}) .. {3,2}", + t, + begins[t].Value(), + durations[t], + ends[t].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/organize_day_intervals.cs b/libs/or-tools-src-ubuntu/examples/dotnet/organize_day_intervals.cs new file mode 100644 index 0000000000000000000000000000000000000000..09f7e75ab0dafd1a0f259c4fb2f7d19c1f46f103 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/organize_day_intervals.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 System.Collections; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class OrganizeDay +{ + /** + * + * + * Organizing a day. + * + * Simple scheduling problem. + * + * Problem formulation from ECLiPSe: + * Slides on (Finite Domain) Constraint Logic Programming, page 38f + * http://eclipseclp.org/reports/eclipse.ppt + * + * + * Also see http://www.hakank.org/google_or_tools/organize_day.py + * + */ + private static void Solve() + { + Solver solver = new Solver("OrganizeDayIntervals"); + + + int n = 4; + + + int work = 0; + int mail = 1; + int shop = 2; + int bank = 3; + // the valid times of the day + int begin = 9; + int end = 17; + // tasks + int[] tasks = {work, mail, shop, bank}; + // durations + int[] durations = {4,1,2,1}; + // Arrays for interval variables. + int[] starts_max = { begin,begin,begin,begin }; + int[] ends_max = { end -4, end - 1, end - 2, end - 1 }; + + // task [i,0] must be finished before task [i,1] + int[,] before_tasks = { + {bank, shop}, + {mail, work} + }; + + + + // + // Decision variables + // + IntervalVar[] intervals = + solver.MakeFixedDurationIntervalVarArray(n, + starts_max, + ends_max, + durations, + false, + "task"); + // + // Constraints + // + DisjunctiveConstraint disjunctive = intervals.Disjunctive("Sequence"); + solver.Add(disjunctive); + + // specific constraints + for(int t = 0; t < before_tasks.GetLength(0); t++) { + int before = before_tasks[t, 0]; + int after = before_tasks[t, 1]; + solver.Add(intervals[after].StartsAfterEnd(intervals[before])); + } + + solver.Add(intervals[work].StartsAfter(11)); + + // + // Search + // + SequenceVar var = disjunctive.SequenceVar(); + SequenceVar[] seq_array = new SequenceVar[] { var }; + DecisionBuilder db = solver.MakePhase(seq_array, Solver.SEQUENCE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + foreach(int t in tasks) { + Console.WriteLine(intervals[t].ToString()); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/p_median.cs b/libs/or-tools-src-ubuntu/examples/dotnet/p_median.cs new file mode 100644 index 0000000000000000000000000000000000000000..53a2f72c0b7bbe3d072669038c045f17148463b9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/p_median.cs @@ -0,0 +1,137 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class PMedian +{ + /** + * + * P-median problem. + * + * Model and data from the OPL Manual, which describes the problem: + * """ + * The P-Median problem is a well known problem in Operations Research. + * The problem can be stated very simply, like this: given a set of customers + * with known amounts of demand, a set of candidate locations for warehouses, + * and the distance between each pair of customer-warehouse, choose P + * warehouses to open that minimize the demand-weighted distance of serving + * all customers from those P warehouses. + * """ + * + * Also see http://www.hakank.org/or-tools/p_median.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("PMedian"); + + // + // Data + // + int p = 2; + int num_customers = 4; + IEnumerable CUSTOMERS = Enumerable.Range(0, num_customers); + + int num_warehouses = 3; + IEnumerable WAREHOUSES = Enumerable.Range(0, num_warehouses); + + int[] demand = {100,80,80,70}; + int [,] distance = { + { 2, 10, 50}, + { 2, 10, 52}, + {50, 60, 3}, + {40, 60, 1} + }; + + // + // Decision variables + // + + IntVar[] open = solver.MakeIntVarArray(num_warehouses, 0, num_warehouses, "open"); + IntVar[,] ship = solver.MakeIntVarMatrix(num_customers, num_warehouses, + 0, 1, "ship"); + IntVar z = solver.MakeIntVar(0, 1000, "z"); + + + // + // Constraints + // + + solver.Add((from c in CUSTOMERS + from w in WAREHOUSES + select (demand[c]*distance[c,w]*ship[c,w]) + ).ToArray().Sum() == z); + + solver.Add(open.Sum() == p); + + foreach(int c in CUSTOMERS) { + foreach(int w in WAREHOUSES) { + solver.Add(ship[c,w] <= open[w]); + } + + solver.Add((from w in WAREHOUSES select ship[c,w]).ToArray().Sum() == 1); + } + + + // + // Objective + // + OptimizeVar obj = z.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(open.Concat(ship.Flatten()).ToArray(), + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}",z.Value()); + Console.Write("open:"); + foreach(int w in WAREHOUSES) { + Console.Write(open[w].Value() + " "); + } + Console.WriteLine(); + foreach(int c in CUSTOMERS) { + foreach(int w in WAREHOUSES) { + Console.Write(ship[c,w].Value()+ " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/pandigital_numbers.cs b/libs/or-tools-src-ubuntu/examples/dotnet/pandigital_numbers.cs new file mode 100644 index 0000000000000000000000000000000000000000..117b6b786e57d47f876ccf05e47fbfc620d25ce6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/pandigital_numbers.cs @@ -0,0 +1,193 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class PandigitalNumbers +{ + + /** + * + * toNum(solver, a, num, base) + * + * channelling between the array a and the number num. + * + */ + private static Constraint ToNum(IntVar[] a, + IntVar num, + int bbase) { + int len = a.Length; + IntVar[] tmp = new IntVar[len]; + for(int i = 0; i < len; i++) { + tmp[i] = (a[i]*(int)Math.Pow(bbase,len-i-1)).Var(); + } + return tmp.Sum() == num; + } + + + /** + * + * Pandigital numbers in Google CP Solver. + * + * From Albert H. Beiler 'Recreations in the Theory of Numbers', + * quoted from http://www.worldofnumbers.com/ninedig1.htm + * """ + * Chapter VIII : Digits - and the magic of 9 + * + * The following curious table shows how to arrange the 9 digits so that + * the product of 2 groups is equal to a number represented by the + * remaining digits. + * + * 12 x 483 = 5796 + * 42 x 138 = 5796 + * 18 x 297 = 5346 + * 27 x 198 = 5346 + * 39 x 186 = 7254 + * 48 x 159 = 7632 + * 28 x 157 = 4396 + * 4 x 1738 = 6952 + * 4 x 1963 = 7852 + * """ + * + * Also see MathWorld http://mathworld.wolfram.com/PandigitalNumber.html + * """ + * A number is said to be pandigital if it contains each of the digits + * from 0 to 9 (and whose leading digit must be nonzero). However, + * "zeroless" pandigital quantities contain the digits 1 through 9. + * Sometimes exclusivity is also required so that each digit is + * restricted to appear exactly once. + * """ + * + * Wikipedia: http://en.wikipedia.org/wiki/Pandigital_number + * + * + * Also see http://www.hakank.org/or-tools/pandigital_numbers.py + * + */ + private static void Solve(int bbase=10, int start=1, int len1=1, int len2=4) + { + + Solver solver = new Solver("PandigitalNumbers"); + + // + // Data + // + int max_d = bbase-1; + int x_len = max_d + 1 - start; + int max_num = (int)Math.Pow(bbase,4)-1; + + // + // Decision variables + // + IntVar num1 = solver.MakeIntVar(1, max_num, "num1"); + IntVar num2 = solver.MakeIntVar(1, max_num, "num2"); + IntVar res = solver.MakeIntVar(1, max_num, "res"); + + IntVar[] x = solver.MakeIntVarArray(x_len, start, max_d, "x"); + + // for labeling + IntVar[] all = new IntVar[x_len+3]; + for(int i = 0; i < x_len; i++) { + all[i] = x[i]; + } + all[x_len] = num1; + all[x_len+1] = num2; + all[x_len+2] = res; + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + solver.Add(ToNum(( from i in Enumerable.Range(0, len1) + select x[i]).ToArray(), + num1, + bbase)); + + solver.Add(ToNum(( from i in Enumerable.Range(len1, len2) + select x[i]).ToArray(), + num2, + bbase)); + + solver.Add(ToNum(( from i in Enumerable.Range(len1+len2, x_len-(len1+len2)) + select x[i]).ToArray(), + res, + bbase)); + + + solver.Add(num1*num2 == res); + + // no number must start with 0 + solver.Add(x[0] > 0); + solver.Add(x[len1] > 0); + solver.Add(x[len1+len2] > 0); + + // symmetry breaking + solver.Add(num1 < num2); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.INT_VAR_SIMPLE, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine("{0} * {1} = {2}", num1.Value(), num2.Value(), res.Value()); + } + + /* + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + */ + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int bbase = 10; + int start = 1; + + if(args.Length > 0) { + bbase = Convert.ToInt32(args[0]); + } + + if(args.Length > 1) { + start = Convert.ToInt32(args[1]); + } + + int x_len = bbase - 1 + 1-start; + for(int len1 = 0; len1 <= x_len; len1++) { + for(int len2 = 0; len2 <= x_len; len2++) { + if (x_len > len1 + len2 + && len1 > 0 && len2 > 0 + ) { + Solve(bbase, start, len1, len2); + } + } + } + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/partition.cs b/libs/or-tools-src-ubuntu/examples/dotnet/partition.cs new file mode 100644 index 0000000000000000000000000000000000000000..3826c4ebe764ec7794df322b7d1db563068cb12c --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/partition.cs @@ -0,0 +1,130 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class Partition +{ + + /** + * + * This is a port of Charles Prud'homme's Java model + * Partition.java + * """ + * Partition n numbers into two groups, so that + * - the sum of the first group equals the sum of the second, + * - and the sum of the squares of the first group equals the sum of + * the squares of the second + * """ + * + */ + private static void Solve(int m) + { + + Solver solver = new Solver("Partition"); + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(m, 1, 2 * m, "x"); + IntVar[] y = solver.MakeIntVarArray(m, 1, 2 * m, "y"); + + + // + // Constraints + // + // break symmetries + for (int i = 0; i < m - 1; i++) { + solver.Add(x[i] < x[i + 1]); + solver.Add(y[i] < y[i + 1]); + } + solver.Add(x[0] < y[0]); + + IntVar[] xy = new IntVar[2 * m]; + for (int i = m - 1; i >= 0; i--) { + xy[i] = x[i]; + xy[m + i] = y[i]; + } + + solver.Add(xy.AllDifferent()); + + int[] coeffs = new int[2 * m]; + for (int i = m - 1; i >= 0; i--) { + coeffs[i] = 1; + coeffs[m + i] = -1; + } + solver.Add(xy.ScalProd(coeffs) == 0); + + IntVar[] sxy, sx, sy; + sxy = new IntVar[2 * m]; + sx = new IntVar[m]; + sy = new IntVar[m]; + for (int i = m - 1; i >= 0; i--) { + sx[i] = x[i].Square().Var(); + sxy[i] = sx[i]; + sy[i] = y[i].Square().Var(); + sxy[m + i] = sy[i]; + } + solver.Add(sxy.ScalProd(coeffs) == 0); + + solver.Add(x.Sum() == 2 * m * (2 * m + 1) / 4); + solver.Add(y.Sum() == 2 * m * (2 * m + 1) / 4); + solver.Add(sx.Sum() == 2 * m * (2 * m + 1) * (4 * m + 1) / 12); + solver.Add(sy.Sum() == 2 * m * (2 * m + 1) * (4 * m + 1) / 12); + + // + // Search + // + DecisionBuilder db = solver.MakeDefaultPhase(xy); + + SearchMonitor log = solver.MakeSearchLog(10000); + solver.NewSearch(db, log); + + while (solver.NextSolution()) { + for(int i = 0; i < m; i++) { + Console.Write("[" + xy[i].Value() + "] "); + } + Console.WriteLine(); + for(int i = 0; i < m; i++) { + Console.Write("[" + xy[m+i].Value() + "] "); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + + int m = 32; + if (args.Length > 0) { + m = Convert.ToInt32(args[0]); + } + + Solve(m); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/perfect_square_sequence.cs b/libs/or-tools-src-ubuntu/examples/dotnet/perfect_square_sequence.cs new file mode 100644 index 0000000000000000000000000000000000000000..d51bc9a5922b15fa1e6f58888f801907ed4dda45 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/perfect_square_sequence.cs @@ -0,0 +1,144 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class PerfectSquareSequence +{ + + /** + * + * Perfect square sequence. + * + * From 'Fun with num3ers' + * "Sequence" + * http://benvitale-funwithnum3ers.blogspot.com/2010/11/sequence.html + * """ + * If we take the numbers from 1 to 15 + * (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) + * and rearrange them in such an order that any two consecutive + * numbers in the sequence add up to a perfect square, we get, + * + * 8 1 15 10 6 3 13 12 4 5 11 14 2 7 9 + * 9 16 25 16 9 16 25 16 9 16 25 16 9 16 + * + * + * I ask the readers the following: + * + * Can you take the numbers from 1 to 25 to produce such an arrangement? + * How about the numbers from 1 to 100? + * """ + * + * Via http://wildaboutmath.com/2010/11/26/wild-about-math-bloggers-111910 + * + * + * Also see http://www.hakank.org/or-tools/perfect_square_sequence.py + * + */ + private static int Solve(int n = 15, int print_solutions=1, int show_num_sols=0) + { + + Solver solver = new Solver("PerfectSquareSequence"); + + IEnumerable RANGE = Enumerable.Range(0, n); + + // create the table of possible squares + int[] squares = new int[n-1]; + for(int i = 1; i < n; i++) { + squares[i-1] = i*i; + } + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 1, n, "x"); + + + // + // Constraints + // + + solver.Add(x.AllDifferent()); + + for(int i = 1; i < n; i++) { + solver.Add((x[i-1]+x[i]).Member(squares)); + } + + // symmetry breaking + solver.Add(x[0] < x[n-1]); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + int num_solutions = 0; + while (solver.NextSolution()) { + num_solutions++; + if (print_solutions > 0) { + Console.Write("x: "); + foreach(int i in RANGE) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + if (show_num_sols > 0 && num_solutions >= show_num_sols) { + break; + } + } + + if (print_solutions > 0) { + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + } + + solver.EndSearch(); + + return num_solutions; + + } + + public static void Main(String[] args) + { + int n = 15; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (n == 0) { + for(int i = 2; i < 100; i++) { + int num_solutions = Solve(i, 0, 0); + Console.WriteLine("{0}: {1} solution(s)", i, num_solutions); + } + + } else { + int num_solutions = Solve(n); + Console.WriteLine("{0}: {1} solution(s)", n, num_solutions); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/photo_problem.cs b/libs/or-tools-src-ubuntu/examples/dotnet/photo_problem.cs new file mode 100644 index 0000000000000000000000000000000000000000..4137f6c32bc7181972aa351ce226665fba94cf2a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/photo_problem.cs @@ -0,0 +1,178 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class PhotoProblem +{ + + + + /** + * + * Photo problem. + * + * Problem statement from Mozart/Oz tutorial: + * http://www.mozart-oz.org/home/doc/fdt/node37.html#section.reified.photo + * """ + * Betty, Chris, Donald, Fred, Gary, Mary, and Paul want to align in one + * row for taking a photo. Some of them have preferences next to whom + * they want to stand: + * + * 1. Betty wants to stand next to Gary and Mary. + * 2. Chris wants to stand next to Betty and Gary. + * 3. Fred wants to stand next to Mary and Donald. + * 4. Paul wants to stand next to Fred and Donald. + * + * Obviously, it is impossible to satisfy all preferences. Can you find + * an alignment that maximizes the number of satisfied preferences? + * """ + * + * Oz solution: + * 6 # alignment(betty:5 chris:6 donald:1 fred:3 gary:7 mary:4 paul:2) + * [5, 6, 1, 3, 7, 4, 2] + * + * + * Also see http://www.hakank.org/or-tools/photo_problem.py + * + */ + private static void Solve(int show_all_max=0) + { + + Solver solver = new Solver("PhotoProblem"); + + // + // Data + // + String[] persons = {"Betty", "Chris", "Donald", "Fred", "Gary", "Mary", "Paul"}; + int n = persons.Length; + IEnumerable RANGE = Enumerable.Range(0, n); + + int[,] preferences = { + // 0 1 2 3 4 5 6 + // B C D F G M P + { 0,0,0,0,1,1,0 }, // Betty 0 + { 1,0,0,0,1,0,0 }, // Chris 1 + { 0,0,0,0,0,0,0 }, // Donald 2 + { 0,0,1,0,0,1,0 }, // Fred 3 + { 0,0,0,0,0,0,0 }, // Gary 4 + { 0,0,0,0,0,0,0 }, // Mary 5 + { 0,0,1,1,0,0,0 } // Paul 6 + }; + + Console.WriteLine("Preferences:"); + Console.WriteLine("1. Betty wants to stand next to Gary and Mary."); + Console.WriteLine("2. Chris wants to stand next to Betty and Gary."); + Console.WriteLine("3. Fred wants to stand next to Mary and Donald."); + Console.WriteLine("4. Paul wants to stand next to Fred and Donald.\n"); + + + // + // Decision variables + // + IntVar[] positions = solver.MakeIntVarArray(n, 0, n-1, "positions"); + // successful preferences (to Maximize) + IntVar z = solver.MakeIntVar(0, n*n, "z"); + + // + // Constraints + // + solver.Add(positions.AllDifferent()); + + // calculate all the successful preferences + solver.Add( ( from i in RANGE + from j in RANGE + where preferences[i,j] == 1 + select (positions[i] - positions[j]).Abs() == 1 + ).ToArray().Sum() == z); + + // + // Symmetry breaking (from the Oz page): + // Fred is somewhere left of Betty + solver.Add(positions[3] < positions[0]); + + + // + // Objective + // + OptimizeVar obj = z.Maximize(1); + + if (show_all_max > 0) { + Console.WriteLine("Showing all maximum solutions (z == 6).\n"); + solver.Add(z == 6); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(positions, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MAX_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}", z.Value()); + int[] p = new int[n]; + Console.Write("p: "); + for(int i = 0; i < n; i++) { + p[i] = (int)positions[i].Value(); + Console.Write(p[i] + " "); + } + Console.WriteLine(); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (p[j] == i) { + Console.Write(persons[j] + " "); + } + } + } + Console.WriteLine(); + Console.WriteLine("Successful preferences:"); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (preferences[i,j] == 1 && + Math.Abs(p[i]-p[j])==1) { + Console.WriteLine("\t{0} {1}", persons[i], persons[j]); + } + } + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: " + solver.Solutions()); + Console.WriteLine("WallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int show_all_max = 0; + if (args.Length > 0) { + show_all_max = Convert.ToInt32(args[0]); + } + + Solve(show_all_max); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/place_number_puzzle.cs b/libs/or-tools-src-ubuntu/examples/dotnet/place_number_puzzle.cs new file mode 100644 index 0000000000000000000000000000000000000000..624ac5f99b378229f1b33e16026ac07e537ae8f9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/place_number_puzzle.cs @@ -0,0 +1,141 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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.Linq; +using System.Collections; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +public class PlaceNumberPuzzle +{ + /** + * + * Place number puzzle. + * + * From + * http://ai.uwaterloo.ca/~vanbeek/Courses/Slides/introduction.pdf + * """ + * Place numbers 1 through 8 on nodes + * - each number appears exactly once + * - no connected nodes have consecutive numbers + * 2 - 5 + * / | X | \ + * 1 - 3 - 6 - 8 + * \ | X | / + * 4 - 7 + * """ + * + * Also see http://www.hakank.org/or-tools/place_number_puzzle.py + * + + */ + private static void Solve() + { + Solver solver = new Solver("PlaceNumberPuzzle"); + + // + // Data + // + int m = 32; + int n = 8; + + // Note: this is 1-based for compatibility (and lazyness) + int[,] graph = { + {1,2}, + {1,3}, + {1,4}, + {2,1}, + {2,3}, + {2,5}, + {2,6}, + {3,2}, + {3,4}, + {3,6}, + {3,7}, + {4,1}, + {4,3}, + {4,6}, + {4,7}, + {5,2}, + {5,3}, + {5,6}, + {5,8}, + {6,2}, + {6,3}, + {6,4}, + {6,5}, + {6,7}, + {6,8}, + {7,3}, + {7,4}, + {7,6}, + {7,8}, + {8,5}, + {8,6}, + {8,7} + }; + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 1, n, "x"); + + + // + // Constraints + // + solver.Add(x.AllDifferent()); + for(int i = 0; i < m; i++) { + // (also base 0-base) + solver.Add( (x[graph[i,0]-1]-x[graph[i,1]-1]).Abs() > 1); + } + + // symmetry breaking + solver.Add(x[0] < x[n-1]); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/post_office_problem2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/post_office_problem2.cs new file mode 100644 index 0000000000000000000000000000000000000000..2730ed60f5efcd67377ace31d8a94beb9f7a95fb --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/post_office_problem2.cs @@ -0,0 +1,148 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class PostOfficeProblem2 +{ + + /** + * + * Post office problem. + * + * Problem statement: + * http://www-128.ibm.com/developerworks/linux/library/l-glpk2/ + * + * From Winston 'Operations Research: Applications and Algorithms': + * """ + * A post office requires a different number of full-time employees working + * on different days of the week [summarized below]. Union rules state that + * each full-time employee must work for 5 consecutive days and then receive + * two days off. For example, an employee who works on Monday to Friday + * must be off on Saturday and Sunday. The post office wants to meet its + * daily requirements using only full-time employees. Minimize the number + * of employees that must be hired. + * + * To summarize the important information about the problem: + * + * Every full-time worker works for 5 consecutive days and takes 2 days off + * - Day 1 (Monday): 17 workers needed + * - Day 2 : 13 workers needed + * - Day 3 : 15 workers needed + * - Day 4 : 19 workers needed + * - Day 5 : 14 workers needed + * - Day 6 : 16 workers needed + * - Day 7 (Sunday) : 11 workers needed + * + * The post office needs to minimize the number of employees it needs + * to hire to meet its demand. + * """ + * + * Also see http://www.hakank.org/or-tools/post_office_problem2.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("PostOfficeProblem2"); + + // + // Data + // + + // days 0..6, monday 0 + int n = 7; + int[] need = {17, 13, 15, 19, 14, 16, 11}; + + // Total cost for the 5 day schedule. + // Base cost per day is 100. + // Working saturday is 100 extra + // Working sunday is 200 extra. + int[] cost = {500, 600, 800, 800, 800, 800, 700}; + + + // + // Decision variables + // + + // No. of workers starting at day i + IntVar[] x = solver.MakeIntVarArray(n, 0, 100, "x"); + + IntVar total_cost = x.ScalProd(cost).Var(); + IntVar num_workers = x.Sum().Var(); + + // + // Constraints + // + for(int i = 0; i < n; i++) { + IntVar s = (from j in Enumerable.Range(0, n) + where j != (i+5) % n && j != (i+6) % n + select x[j]).ToArray().Sum().Var(); + solver.Add(s >= need[i]); + } + + // Add a limit for the cost + solver.Add(total_cost <= 20000); + + // + + // objective + // + // + OptimizeVar obj = total_cost.Minimize(100); + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("num_workers: {0}", num_workers.Value()); + Console.WriteLine("total_cost: {0}", total_cost.Value()); + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/quasigroup_completion.cs b/libs/or-tools-src-ubuntu/examples/dotnet/quasigroup_completion.cs new file mode 100644 index 0000000000000000000000000000000000000000..26846e6a938feb81de44cfa32a6329ed30cb1d0b --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/quasigroup_completion.cs @@ -0,0 +1,229 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class QuasigroupCompletion +{ + + static int X = 0; + + /* + * default problem + * + * Example from Ruben Martins and Inès Lynce + * Breaking Local Symmetries in Quasigroup Completion Problems, page 3 + * The solution is unique: + * + * 1 3 2 5 4 + * 2 5 4 1 3 + * 4 1 3 2 5 + * 5 4 1 3 2 + * 3 2 5 4 1 + */ + static int default_n = 5; + static int[,] default_problem = {{1, X, X, X, 4}, + {X, 5, X, X, X}, + {4, X, X, 2, X}, + {X, 4, X, X, X}, + {X, X, 5, X, 1}}; + + + // for the actual problem + static int n; + static int[,] problem; + + + /** + * + * Solves the Quasigroup Completion problem. + * See http://www.hakank.org/or-tools/quasigroup_completion.py + * + */ + private static void Solve() + { + Solver solver = new Solver("QuasigroupCompletion"); + + // + // data + // + Console.WriteLine("Problem:"); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(problem[i,j] + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (problem[i,j] > X) { + solver.Add(x[i,j] == problem[i,j]); + } + } + } + + // + // rows and columns must be different + // + + // rows + for(int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + for(int j = 0; j < n; j++) { + row[j] = x[i,j]; + } + solver.Add(row.AllDifferent()); + } + + // columns + for(int j = 0; j < n; j++) { + IntVar[] col = new IntVar[n]; + for(int i = 0; i < n; i++) { + col[i] = x[i,j]; + } + solver.Add(col.AllDifferent()); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_SIMPLE, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int sol = 0; + while (solver.NextSolution()) { + sol++; + Console.WriteLine("Solution #{0} ", sol + " "); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++){ + Console.Write("{0} ", x[i,j].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /** + * + * Reads a Quasigroup completion file. + * File format: + * # a comment which is ignored + * % a comment which also is ignored + * number of rows (n) + * < + * row number of space separated entries + * > + * + * "." or "0" means unknown, integer 1..n means known value + * + * Example + * 5 + * 1 . . . 4 + * . 5 . . . + * 4 . . 2 . + * . 4 . . . + * . . 5 . 1 + * + */ + private static void readFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + int lineCount = 0; + + TextReader inr = new StreamReader(file); + String str; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + + str = str.Trim(); + + // ignore comments + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + Console.WriteLine(str); + if (lineCount == 0) { + n = Convert.ToInt32(str); // number of rows + problem = new int[n,n]; + } else { + // the problem matrix + String[] row = Regex.Split(str, " "); + for(int i = 0; i < n; i++) { + String s = row[i]; + if (s.Equals(".")) { + problem[lineCount - 1, i] = 0; + } else { + problem[lineCount - 1, i] = Convert.ToInt32(s); + } + } + } + + lineCount++; + + } // end while + + inr.Close(); + + + } // end readFile + + + + public static void Main(String[] args) + { + String file = ""; + if (args.Length > 0) { + file = args[0]; + readFile(file); + } else { + problem = default_problem; + n = default_n; + } + + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/regex.cs b/libs/or-tools-src-ubuntu/examples/dotnet/regex.cs new file mode 100644 index 0000000000000000000000000000000000000000..d7cc0dc101c8e44f5ba4ea0dd16b55896a9bb300 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/regex.cs @@ -0,0 +1,202 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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.ConstraintSolver; + +public class RegexGeneration +{ + + /* + * Global constraint regular + * + * This is a translation of MiniZinc's regular constraint (defined in + * lib/zinc/globals.mzn), via the Comet code refered above. + * All comments are from the MiniZinc code. + * """ + * The sequence of values in array 'x' (which must all be in the range 1..S) + * is accepted by the DFA of 'Q' states with input 1..S and transition + * function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' + * (which must be in 1..Q) and accepting states 'F' (which all must be in + * 1..Q). We reserve state 0 to be an always failing state. + * """ + * + * x : IntVar array + * Q : number of states + * S : input_max + * d : transition matrix + * q0: initial state + * F : accepting states + * + */ + static void MyRegular(Solver solver, + IntVar[] x, + int Q, + int S, + int[,] d, + int q0, + int[] F) { + + + + // d2 is the same as d, except we add one extra transition for + // each possible input; each extra transition is from state zero + // to state zero. This allows us to continue even if we hit a + // non-accepted input. + int[][] d2 = new int[Q+1][]; + for(int i = 0; i <= Q; i++) { + int[] row = new int[S]; + for(int j = 0; j < S; j++) { + if (i == 0) { + row[j] = 0; + } else { + row[j] = d[i-1,j]; + } + } + d2[i] = row; + } + + int[] d2_flatten = (from i in Enumerable.Range(0, Q+1) + from j in Enumerable.Range(0, S) + select d2[i][j]).ToArray(); + + // If x has index set m..n, then a[m-1] holds the initial state + // (q0), and a[i+1] holds the state we're in after processing + // x[i]. If a[n] is in F, then we succeed (ie. accept the + // string). + int m = 0; + int n = x.Length; + + IntVar[] a = solver.MakeIntVarArray(n+1-m, 0,Q+1, "a"); + // Check that the final state is in F + solver.Add(a[a.Length-1].Member(F)); + // First state is q0 + solver.Add(a[m] == q0); + + for(int i = 0; i < n; i++) { + solver.Add(x[i] >= 1); + solver.Add(x[i] <= S); + // Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == d2_flatten.Element(((a[i]*S)+(x[i]-1)))); + } + + } + + + + /** + * + * Simple regular expression. + * + * My last name (Kjellerstrand) is quite often misspelled + * in ways that this regular expression shows: + * k(je|ä)ll(er|ar)?(st|b)r?an?d + * + * This model generates all the words that can be construed + * by this regular expression. + * + * + * Also see http://www.hakank.org/or-tools/regex.py + * + */ + private static void Solve(int n, List res) + { + Solver solver = new Solver("RegexGeneration"); + + Console.WriteLine("\nn: {0}", n); + + // The DFS (for regular) + int n_states = 11; + int input_max = 12; + int initial_state = 1; // 0 is for the failing state + int[] accepting_states = {12}; + + // The DFA + int [,] transition_fn = { + // 1 2 3 4 5 6 7 8 9 0 1 2 // + {0,2,3,0,0,0,0,0,0,0,0,0}, // 1 k + {0,0,0,4,0,0,0,0,0,0,0,0}, // 2 je + {0,0,0,4,0,0,0,0,0,0,0,0}, // 3 ä + {0,0,0,0,5,6,7,8,0,0,0,0}, // 4 ll + {0,0,0,0,0,0,7,8,0,0,0,0}, // 5 er + {0,0,0,0,0,0,7,8,0,0,0,0}, // 6 ar + {0,0,0,0,0,0,0,0,9,10,0,0}, // 7 st + {0,0,0,0,0,0,0,0,9,10,0,0}, // 8 b + {0,0,0,0,0,0,0,0,0,10,0,0}, // 9 r + {0,0,0,0,0,0,0,0,0,0,11,12}, // 10 a + {0,0,0,0,0,0,0,0,0,0,0,12}, // 11 n + // 12 d + }; + + // Name of the states + String[] s = {"k","je","ä","ll","er","ar","st","b","r","a","n","d"}; + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 1, input_max, "x"); + + // + // Constraints + // + MyRegular(solver, x, n_states, input_max, transition_fn, + initial_state, accepting_states); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + List res2 = new List(); + // State 1 (the start state) is not included in the + // state array (x) so we add it first. + res2.Add(s[0]); + for(int i = 0; i < n; i++) { + res2.Add(s[x[i].Value()-1]); + } + res.Add(String.Join("", res2.ToArray())); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + List res = new List(); + for(int n = 4; n <= 9; n++) { + Solve(n, res); + } + Console.WriteLine("\nThe following {0} words where generated", res.Count); + foreach(string r in res) { + Console.WriteLine(r); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/rogo2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/rogo2.cs new file mode 100644 index 0000000000000000000000000000000000000000..af48fc89e9f975fdf8e6ff2759a1bf8c74b4b92b --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/rogo2.cs @@ -0,0 +1,356 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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.IO; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class Rogo2 +{ + + static int W = 0; + static int B = -1; + + // Default problem + // Data from + // Mike Trick: "Operations Research, Sudoko, Rogo, and Puzzles" + // http://mat.tepper.cmu.edu/blog/?p=1302 + // + // This has 48 solutions with symmetries; + // 4 when the path symmetry is removed. + // + static int default_rows = 5; + static int default_cols = 9; + static int default_max_steps = 12; + static int default_best = 8; + static int[,] default_problem = { + {2,W,W,W,W,W,W,W,W}, + {W,3,W,W,1,W,W,2,W}, + {W,W,W,W,W,W,B,W,2}, + {W,W,2,B,W,W,W,W,W}, + {W,W,W,W,2,W,W,1,W} + }; + static String default_problem_name = "Problem Mike Trick"; + + + // The actual problem + static int rows; + static int cols; + static int max_steps; + static int best; + static int[,] problem; + static string problem_name; + + + /** + * + * Build the table of valid connections of the grid. + * + */ + public static IntTupleSet ValidConnections(int rows, int cols) + { + + IEnumerable ROWS = Enumerable.Range(0, rows); + IEnumerable COLS = Enumerable.Range(0, cols); + var result_tmp = ( + from i1 in ROWS + from j1 in COLS + from i2 in ROWS + from j2 in COLS + where + (Math.Abs(j1-j2) == 1 && i1 == i2) || + (Math.Abs(i1-i2) == 1 && j1 % cols == j2 % cols) + select new int[] {i1*cols+j1, i2*cols+j2} + ).ToArray(); + + // Convert to len x 2 matrix + int len = result_tmp.Length; + IntTupleSet result = new IntTupleSet(2); + foreach(int[] r in result_tmp) { + result.Insert(r); + } + return result; + + } + + + + /** + * + * Rogo puzzle solver. + * + * From http://www.rogopuzzle.co.nz/ + * """ + * The object is to collect the biggest score possible using a given + * number of steps in a loop around a grid. The best possible score + * for a puzzle is given with it, so you can easily check that you have + * solved the puzzle. Rogo puzzles can also include forbidden squares, + * which must be avoided in your loop. + * """ + * + * Also see Mike Trick: + * "Operations Research, Sudoko, Rogo, and Puzzles" + * http://mat.tepper.cmu.edu/blog/?p=1302 + * + * + * Also see, http://www.hakank.org/or-tools/rogo2.py + * though this model differs in a couple of central points + * which makes it much faster: + * + * - it use a table ( +AllowedAssignments) with the valid connections + * - instead of two coordinates arrays, it use a single path array + * + */ + private static void Solve() + { + + Solver solver = new Solver("Rogo2"); + + + Console.WriteLine("\n"); + Console.WriteLine("**********************************************"); + Console.WriteLine(" {0}", problem_name); + Console.WriteLine("**********************************************\n"); + + // + // Data + // + int B = -1; + + Console.WriteLine("Rows: {0} Cols: {1} Max Steps: {2}", rows, cols, max_steps); + + int[] problem_flatten = problem.Cast().ToArray(); + int max_point = problem_flatten.Max(); + int max_sum = problem_flatten.Sum(); + Console.WriteLine("max_point: {0} max_sum: {1} best: {2}", max_point, max_sum, best); + + IEnumerable STEPS = Enumerable.Range(0, max_steps); + IEnumerable STEPS1 = Enumerable.Range(0, max_steps-1); + + // the valid connections, to be used with AllowedAssignments + IntTupleSet valid_connections = ValidConnections(rows, cols); + + + // + // Decision variables + // + IntVar[] path = solver.MakeIntVarArray(max_steps, 0, rows*cols-1, "path"); + IntVar[] points = solver.MakeIntVarArray(max_steps, 0, best, "points"); + IntVar sum_points = points.Sum().VarWithName("sum_points"); + + + // + // Constraints + // + + foreach(int s in STEPS) { + // calculate the points (to maximize) + solver.Add(points[s] == problem_flatten.Element(path[s])); + + // ensure that there are no black cells in + // the path + solver.Add(problem_flatten.Element(path[s]) != B); + } + + solver.Add(path.AllDifferent()); + + + // valid connections + foreach(int s in STEPS1) { + solver.Add(new IntVar[] {path[s], path[s+1]}. + AllowedAssignments(valid_connections)); + } + // around the corner + solver.Add(new IntVar[] {path[max_steps-1], path[0]}. + AllowedAssignments(valid_connections)); + + + // Symmetry breaking + for(int s = 1; s < max_steps; s++) { + solver.Add(path[0] < path[s]); + } + + + // + // Objective + // + OptimizeVar obj = sum_points.Maximize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(path, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("sum_points: {0}", sum_points.Value()); + Console.Write("path: "); + foreach(int s in STEPS) { + Console.Write("{0} ", path[s].Value()); + } + Console.WriteLine(); + Console.WriteLine("(Adding 1 to coords...)"); + int[,] sol = new int[rows, cols]; + foreach(int s in STEPS) { + int p = (int) path[s].Value(); + int x = (int) (p / cols); + int y = (int) (p % cols); + Console.WriteLine("{0,2},{1,2} ({2} points)", x+1, y+1, points[s].Value()); + sol[x, y] = 1; + } + Console.WriteLine("\nThe path is marked by 'X's:"); + for(int i = 0; i < rows; i++) { + for(int j = 0; j < cols; j++) { + String p = sol[i,j] == 1 ? "X" : " "; + String q = problem[i,j] == B ? "B" : + problem[i,j] == 0 ? "." : problem[i,j].ToString(); + Console.Write("{0,2}{1} ", q, p); + } + Console.WriteLine(); + } + Console.WriteLine(); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /** + * + * Reads a Rogo problem instance file. + * + * File format: + * # a comment which is ignored + * % a comment which also is ignored + * rows + * cols + * max_step + * best + * + * + * Where is a rows x cols matrix of + * digits (points), W (white), B (black) + * + * """ + * # comment + * % another comment + * 5 + * 9 + * 12 + * 8 + * 2 W W W W W W W W + * W 3 W W 1 W W 2 W + * W W W W W W B W 2 + * W W 2 B W W W W W + * W W W W 2 W W 1 W + * """ + * + */ + + private static void ReadFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + + TextReader inr = new StreamReader(file); + String str; + int lineCount = 0; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + str = str.Trim(); + + Console.WriteLine(str); + + // ignore comments + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + if (lineCount == 0) { + rows = Convert.ToInt32(str); + + } else if (lineCount == 1) { + cols = Convert.ToInt32(str); + problem = new int[rows, cols]; + + } else if (lineCount == 2) { + max_steps = Convert.ToInt32(str); + + } else if (lineCount == 3) { + best = Convert.ToInt32(str); + + } else { + + String[] tmp = Regex.Split(str, "[,\\s]+"); + for(int j = 0; j < cols; j++) { + int val = 0; + if (tmp[j] == "B") { + val = B; + } else if (tmp[j] == "W") { + val = W; + } else { + val = Convert.ToInt32(tmp[j]); + } + problem[lineCount-4, j] = val; + } + } + + lineCount++; + + } // end while + + inr.Close(); + + } // end readFile + + + + + public static void Main(String[] args) + { + + rows = default_rows; + cols = default_cols; + max_steps = default_max_steps; + best = default_best; + problem = default_problem; + problem_name = default_problem_name; + + String file = ""; + + if (args.Length > 0) { + file = args[0]; + problem_name = "Problem " + file; + ReadFile(file); + } + + Solve(); + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/scheduling_speakers.cs b/libs/or-tools-src-ubuntu/examples/dotnet/scheduling_speakers.cs new file mode 100644 index 0000000000000000000000000000000000000000..cb9afb7bc6bfcc7b2e9caa7f02240053e899e8b8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/scheduling_speakers.cs @@ -0,0 +1,99 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class SchedulingSpeakers +{ + + /** + * + * Scheduling speakers problem + * + * From Rina Dechter, Constraint Processing, page 72 + * Scheduling of 6 speakers in 6 slots. + * + * See http://www.hakank.org/google_or_tools/scheduling_speakers.py + * + */ + private static void Solve() + { + Solver solver = new Solver("SchedulingSpeakers"); + + + // number of speakers + int n = 6; + + // slots available to speak + int[][] available = { + // Reasoning: + new int[] {3,4,5,6}, // 2) the only one with 6 after speaker F -> 1 + new int[] {3,4}, // 5) 3 or 4 + new int[] {2,3,4,5}, // 3) only with 5 after F -> 1 and A -> 6 + new int[] {2,3,4}, // 4) only with 2 after C -> 5 and F -> 1 + new int[] {3,4}, // 5) 3 or 4 + new int[] {1,2,3,4,5,6} // 1) the only with 1 + }; + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 1, n, "x"); + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + for(int i = 0; i < n; i++) { + solver.Add(x[i].Member(available[i])); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine(string.Join(",", (from i in x select i.Value()))); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/secret_santa.cs b/libs/or-tools-src-ubuntu/examples/dotnet/secret_santa.cs new file mode 100644 index 0000000000000000000000000000000000000000..417789d1866c28fab6408499a4f87b97fdc048f0 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/secret_santa.cs @@ -0,0 +1,129 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class SecretSanta +{ + + /** + * + * Secret Santa problem in Google CP Solver. + * + * From Ruby Quiz Secret Santa + * http://www.rubyquiz.com/quiz2.html + * """ + * Honoring a long standing tradition started by my wife's dad, my friends + * all play a Secret Santa game around Christmas time. We draw names and + * spend a week sneaking that person gifts and clues to our identity. On the + * last night of the game, we get together, have dinner, share stories, and, + * most importantly, try to guess who our Secret Santa was. It's a crazily + * fun way to enjoy each other's company during the holidays. + * + * To choose Santas, we use to draw names out of a hat. This system was + * tedious, prone to many 'Wait, I got myself...' problems. This year, we + * made a change to the rules that further complicated picking and we knew + * the hat draw would not stand up to the challenge. Naturally, to solve + * this problem, I scripted the process. Since that turned out to be more + * interesting than I had expected, I decided to share. + * + * This weeks Ruby Quiz is to implement a Secret Santa selection script. + * * Your script will be fed a list of names on STDIN. + * ... + * Your script should then choose a Secret Santa for every name in the list. + * Obviously, a person cannot be their own Secret Santa. In addition, my friends + * no longer allow people in the same family to be Santas for each other and your + * script should take this into account. + * """ + * + * Comment: This model skips the file input and mail parts. We + * assume that the friends are identified with a number from 1..n, + * and the families is identified with a number 1..num_families. + * + * Also see http://www.hakank.org/or-tools/secret_santa.py + * Also see http://www.hakank.org/or-tools/secret_santa2.cs + * + */ + private static void Solve() + { + + Solver solver = new Solver("SecretSanta"); + + int[] family = {1,1,1,1, 2, 3,3,3,3,3, 4,4}; + int n = family.Length; + + Console.WriteLine("n = {0}", n); + + IEnumerable RANGE = Enumerable.Range(0, n); + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, n-1, "x"); + + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + // Can't be one own"s Secret Santa + // (i.e. ensure that there are no fix-point in the array.) + foreach(int i in RANGE) { + solver.Add(x[i] != i); + } + + + // No Secret Santa to a person in the same family + foreach(int i in RANGE) { + solver.Add(solver.MakeIntConst(family[i]) != family.Element(x[i])); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_SIMPLE, + Solver.INT_VALUE_SIMPLE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + foreach(int i in RANGE) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/secret_santa2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/secret_santa2.cs new file mode 100644 index 0000000000000000000000000000000000000000..9467c995b5571a0eabda9873fcf83cf7d7da8197 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/secret_santa2.cs @@ -0,0 +1,248 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + + +public class SecretSanta2 +{ + + /** + * + * Secret Santa problem II in Google CP Solver. + * + * From Maple Primes: 'Secret Santa Graph Theory' + * http://www.mapleprimes.com/blog/jpmay/secretsantagraphtheory + * """ + * Every year my extended family does a 'secret santa' gift exchange. + * Each person draws another person at random and then gets a gift for + * them. At first, none of my siblings were married, and so the draw was + * completely random. Then, as people got married, we added the restriction + * that spouses should not draw each others names. This restriction meant + * that we moved from using slips of paper on a hat to using a simple + * computer program to choose names. Then people began to complain when + * they would get the same person two years in a row, so the program was + * modified to keep some history and avoid giving anyone a name in their + * recent history. This year, not everyone was participating, and so after + * removing names, and limiting the number of exclusions to four per person, + * I had data something like this: + * + * Name: Spouse, Recent Picks + * + * Noah: Ava. Ella, Evan, Ryan, John + * Ava: Noah, Evan, Mia, John, Ryan + * Ryan: Mia, Ella, Ava, Lily, Evan + * Mia: Ryan, Ava, Ella, Lily, Evan + * Ella: John, Lily, Evan, Mia, Ava + * John: Ella, Noah, Lily, Ryan, Ava + * Lily: Evan, John, Mia, Ava, Ella + * Evan: Lily, Mia, John, Ryan, Noah + * """ + * + * Note: I interpret this as the following three constraints: + * 1) One cannot be a Secret Santa of one's spouse + * 2) One cannot be a Secret Santa for somebody two years in a row + * 3) Optimization: maximize the time since the last time + * + * This model also handle single persons, something the original + * problem don't mention. + * + * + * Also see http://www.hakank.org/or-tools/secret_santa2.py + * + */ + private static void Solve(int single=0) + { + + Solver solver = new Solver("SecretSanta2"); + + Console.WriteLine("\nSingle: {0}", single); + + // + // The matrix version of earlier rounds. + // M means that no earlier Santa has been assigned. + // Note: Ryan and Mia has the same recipient for years 3 and 4, + // and Ella and John has for year 4. + // This seems to be caused by modification of + // original data. + // + int n_no_single = 8; + int M = n_no_single + 1; + int[][] rounds_no_single = { + // N A R M El J L Ev + new int[] {0, M, 3, M, 1, 4, M, 2}, // Noah + new int[] {M, 0, 4, 2, M, 3, M, 1}, // Ava + new int[] {M, 2, 0, M, 1, M, 3, 4}, // Ryan + new int[] {M, 1, M, 0, 2, M, 3, 4}, // Mia + new int[] {M, 4, M, 3, 0, M, 1, 2}, // Ella + new int[] {1, 4, 3, M, M, 0, 2, M}, // John + new int[] {M, 3, M, 2, 4, 1, 0, M}, // Lily + new int[] {4, M, 3, 1, M, 2, M, 0} // Evan + }; + + // + // Rounds with a single person (fake data) + // + int n_with_single = 9; + M = n_with_single + 1; + int[][] rounds_single = { + // N A R M El J L Ev S + new int[] {0, M, 3, M, 1, 4, M, 2, 2}, // Noah + new int[] {M, 0, 4, 2, M, 3, M, 1, 1}, // Ava + new int[] {M, 2, 0, M, 1, M, 3, 4, 4}, // Ryan + new int[] {M, 1, M, 0, 2, M, 3, 4, 3}, // Mia + new int[] {M, 4, M, 3, 0, M, 1, 2, M}, // Ella + new int[] {1, 4, 3, M, M, 0, 2, M, M}, // John + new int[] {M, 3, M, 2, 4, 1, 0, M, M}, // Lily + new int[] {4, M, 3, 1, M, 2, M, 0, M}, // Evan + new int[] {1, 2, 3, 4, M, 2, M, M, 0} // Single + }; + + + int Noah = 0; + int Ava = 1; + int Ryan = 2; + int Mia = 3; + int Ella = 4; + int John = 5; + int Lily = 6; + int Evan = 7; + + int n = n_no_single; + + int[][] rounds = rounds_no_single; + if (single == 1) { + n = n_with_single; + rounds = rounds_single; + } + M = n + 1; + + IEnumerable RANGE = Enumerable.Range(0, n); + + + + String[] persons = {"Noah", "Ava", "Ryan", "Mia", "Ella", + "John", "Lily", "Evan", "Single"}; + + int[] spouses = { + Ava, // Noah + Noah, // Ava + Mia, // Rya + Ryan, // Mia + John, // Ella + Ella, // John + Evan, // Lily + Lily, // Evan + -1 // Single has no spouse + }; + + + // + // Decision variables + // + IntVar[] santas = solver.MakeIntVarArray(n, 0, n-1, "santas"); + IntVar[] santa_distance = solver.MakeIntVarArray(n, 0, M, "santa_distance"); + + // total of "distance", to maximize + IntVar z = santa_distance.Sum().VarWithName("z"); + + + // + // Constraints + // + solver.Add(santas.AllDifferent()); + + // Can't be one own"s Secret Santa + // (i.e. ensure that there are no fix-point in the array.) + foreach(int i in RANGE) { + solver.Add(santas[i] != i); + } + + + // no Santa for a spouses + foreach(int i in RANGE) { + if (spouses[i] > -1) { + solver.Add(santas[i] != spouses[i]); + } + } + + // optimize "distance" to earlier rounds: + foreach(int i in RANGE) { + solver.Add(santa_distance[i] == rounds[i].Element(santas[i])); + } + + + // cannot be a Secret Santa for the same person + // two years in a row. + foreach(int i in RANGE) { + foreach(int j in RANGE) { + if (rounds[i][j] == 1) { + solver.Add(santas[i] != j); + } + } + } + + + // + // Objective (minimize the distances) + // + OptimizeVar obj = z.Maximize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(santas, + Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, + Solver.ASSIGN_CENTER_VALUE); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("\ntotal distances: {0}", z.Value()); + Console.Write("santas: "); + for(int i = 0; i < n; i++) { + Console.Write(santas[i].Value() + " "); + } + Console.WriteLine(); + foreach(int i in RANGE) { + Console.WriteLine("{0}\tis a Santa to {1} (distance {2})", + persons[i], + persons[santas[i].Value()], + santa_distance[i].Value()); + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int single = 0; + Solve(single); + single = 1; + Solve(single); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/send_more_money.cs b/libs/or-tools-src-ubuntu/examples/dotnet/send_more_money.cs new file mode 100644 index 0000000000000000000000000000000000000000..e047ddb703cc5d75e0bd8693ded8c54ff956def0 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/send_more_money.cs @@ -0,0 +1,82 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class SendMoreMoney +{ + /** + * + * Solve the SEND+MORE=MONEY problem + * + */ + private static void Solve() + { + Solver solver = new Solver("SendMoreMoney"); + + // + // Decision variables + // + IntVar S = solver.MakeIntVar(0, 9, "S"); + IntVar E = solver.MakeIntVar(0, 9, "E"); + IntVar N = solver.MakeIntVar(0, 9, "N"); + IntVar D = solver.MakeIntVar(0, 9, "D"); + IntVar M = solver.MakeIntVar(0, 9, "M"); + IntVar O = solver.MakeIntVar(0, 9, "O"); + IntVar R = solver.MakeIntVar(0, 9, "R"); + IntVar Y = solver.MakeIntVar(0, 9, "Y"); + + // for AllDifferent() + IntVar[] x = new IntVar[] {S,E,N,D,M,O,R,Y}; + + // + // Constraints + // + solver.Add(x.AllDifferent()); + solver.Add(S*1000 + E*100 + N*10 + D + M*1000 + O*100 + R*10 + E == + M*10000 + O*1000 + N*100 + E*10 + Y); + + solver.Add(S > 0); + solver.Add(M > 0); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + for(int i = 0; i < 8; i++) { + Console.Write(x[i].ToString() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nWallTime: " + solver.WallTime() + "ms "); + Console.WriteLine("Failures: " + solver.Failures()); + Console.WriteLine("Branches: " + solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/send_more_money2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/send_more_money2.cs new file mode 100644 index 0000000000000000000000000000000000000000..2141d5ce94b2d88916feecdc8838e9d1d69647df --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/send_more_money2.cs @@ -0,0 +1,94 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class SendMoreMoney +{ + /** + * + * Solve the SEND+MORE=MONEY problem + * using scalar product. + * + */ + private static void Solve() + { + Solver solver = new Solver("SendMoreMoney"); + + // + // Decision variables + // + IntVar S = solver.MakeIntVar(0, 9, "S"); + IntVar E = solver.MakeIntVar(0, 9, "E"); + IntVar N = solver.MakeIntVar(0, 9, "N"); + IntVar D = solver.MakeIntVar(0, 9, "D"); + IntVar M = solver.MakeIntVar(0, 9, "M"); + IntVar O = solver.MakeIntVar(0, 9, "O"); + IntVar R = solver.MakeIntVar(0, 9, "R"); + IntVar Y = solver.MakeIntVar(0, 9, "Y"); + + // for AllDifferent() + IntVar[] x = new IntVar[] {S,E,N,D,M,O,R,Y}; + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + /* + solver.Add(S*1000 + E*100 + N*10 + D + M*1000 + O*100 + R*10 + E == + M*10000 + O*1000 + N*100 + E*10 + Y); + */ + + // Here we use scalar product instead. + int[] s1 = new int[] {1000,100,10,1}; + int[] s2 = new int[] {10000,1000,100,10,1}; + solver.Add(new IntVar[] {S,E,N,D}.ScalProd(s1) + + new IntVar[] {M,O,R,E}.ScalProd(s1) == + new IntVar[] {M,O,N,E,Y}.ScalProd(s2)); + + solver.Add(S > 0); + solver.Add(M > 0); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + for(int i = 0; i < 8; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0}", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/send_most_money.cs b/libs/or-tools-src-ubuntu/examples/dotnet/send_most_money.cs new file mode 100644 index 0000000000000000000000000000000000000000..1fdf31edc2c22a14e3e9eb1624dd895b9cef88da --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/send_most_money.cs @@ -0,0 +1,113 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class SendMostMoney +{ + /** + * + * Solve the SEND+MOST=MONEY problem + * where the object is to maximize MONEY + * See http://www.hakank.org/google_or_tools/send_most_money.py + * + */ + private static long Solve(long MONEY) + { + + Solver solver = new Solver("SendMostMoney"); + + // + // Decision variables + // + IntVar S = solver.MakeIntVar(0, 9, "S"); + IntVar E = solver.MakeIntVar(0, 9, "E"); + IntVar N = solver.MakeIntVar(0, 9, "N"); + IntVar D = solver.MakeIntVar(0, 9, "D"); + IntVar M = solver.MakeIntVar(0, 9, "M"); + IntVar O = solver.MakeIntVar(0, 9, "O"); + IntVar T = solver.MakeIntVar(0, 9, "T"); + IntVar Y = solver.MakeIntVar(0, 9, "Y"); + + // for AllDifferent() + IntVar[] x = new IntVar[] {S,E,N,D,M,O,T,Y}; + + IntVar[] eq = {S,E,N,D, M,O,S,T, M,O,N,E,Y}; + int[] coeffs = { 1000, 100, 10, 1, // S E N D + + 1000, 100, 10, 1, // M O S T + -10000,-1000, -100,-10,-1 // == M O N E Y + }; + solver.Add(eq.ScalProd(coeffs) == 0); + + // IntVar money = solver.MakeScalProd(new IntVar[] {M, O, N, E, Y}, + // new int[] {10000, 1000, 100, 10, 1}).Var(); + IntVar money = (new IntVar[] {M, O, N, E, Y}). + ScalProd(new int[] {10000, 1000, 100, 10, 1}).Var(); + + // + // Constraints + // + solver.Add(x.AllDifferent()); + solver.Add(S > 0); + solver.Add(M > 0); + + if (MONEY > 0) { + solver.Add(money == MONEY); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + if (MONEY == 0) { + OptimizeVar obj = money.Maximize(1); + solver.NewSearch(db, obj); + } else { + solver.NewSearch(db); + } + + long money_ret = 0; + while (solver.NextSolution()) { + money_ret = money.Value(); + Console.WriteLine("money: {0}", money.Value() ); + for(int i = 0; i < x.Length; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + return money_ret; + + } + + public static void Main(String[] args) + { + Console.WriteLine("First get the max value of money:"); + long this_money = Solve(0); + Console.WriteLine("\nThen we find all solutions for MONEY = {0}:", this_money); + long tmp = Solve(this_money); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/seseman.cs b/libs/or-tools-src-ubuntu/examples/dotnet/seseman.cs new file mode 100644 index 0000000000000000000000000000000000000000..51e041343427caa04c0d04c83850812f0a9e5444 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/seseman.cs @@ -0,0 +1,122 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + + +public class Seseman +{ + + /** + * + * Solves the Seseman convent problem. + * See http://www.hakank.org/google_or_tools/seseman.py + * + */ + private static void Solve(int n = 3) + { + Solver solver = new Solver("Seseman"); + + // + // data + // + int border_sum = n * n; + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 0, n*n, "x"); + IntVar[] x_flat = x.Flatten(); + IntVar total_sum = x_flat.Sum().Var(); + + + // + // Constraints + // + + // zero in all middle cells + for(int i = 1; i < n-1; i++) { + for(int j = 1; j < n-1; j++) { + solver.Add(x[i,j] == 0); + } + } + + // all borders must be >= 1 + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (i == 0 || j == 0 || i == n - 1 || j == n - 1) { + solver.Add(x[i,j] >= 1); + } + } + } + + // sum the four borders + IntVar[] border1 = new IntVar[n]; + IntVar[] border2 = new IntVar[n]; + IntVar[] border3 = new IntVar[n]; + IntVar[] border4 = new IntVar[n]; + for(int i = 0; i < n; i++) { + border1[i] = x[i,0]; + border2[i] = x[i,n-1]; + border3[i] = x[0,i]; + border4[i] = x[n-1,i]; + } + solver.Add(border1.Sum() == border_sum); + solver.Add(border2.Sum() == border_sum); + solver.Add(border3.Sum() == border_sum); + solver.Add(border4.Sum() == border_sum); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_PATH, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + Console.WriteLine("total_sum: {0} ", total_sum.Value()); + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++){ + Console.Write("{0} ", x[i,j].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int n = 3; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + Solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/set_covering.cs b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering.cs new file mode 100644 index 0000000000000000000000000000000000000000..3c2de49f9dee840cd8456531522753fbcea64eb4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering.cs @@ -0,0 +1,109 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCovering +{ + + /** + * + * Solves a set covering problem. + * See See http://www.hakank.org/or-tools/set_covering.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCovering"); + + // + // data + // + + // Placing of firestations, from Winston 'Operations Research', + // page 486. + int min_distance = 15; + int num_cities = 6; + + int[,] distance = {{ 0,10,20,30,30,20}, + {10, 0,25,35,20,10}, + {20,25, 0,15,30,20}, + {30,35,15, 0,15,25}, + {30,20,30,15, 0,14}, + {20,10,20,25,14, 0}}; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(num_cities, 0, 1, "x"); + IntVar z = x.Sum().Var(); + + // + // Constraints + // + + // ensure that all cities are covered + for(int i = 0; i < num_cities; i++) { + IntVar[] b = (from j in Enumerable.Range(0, num_cities) + where distance[i,j] <= min_distance + select x[j]).ToArray(); + solver.Add(b.Sum() >= 1); + + } + + // + // objective + // + OptimizeVar objective = z.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}", z.Value()); + Console.Write("x: "); + for(int i = 0; i < num_cities; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/set_covering2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering2.cs new file mode 100644 index 0000000000000000000000000000000000000000..8899c046acd400fc2a23e85c15af1b2bf8ea29f6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering2.cs @@ -0,0 +1,115 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCovering2 +{ + + /** + * + * Solves a set covering problem. + * See See http://www.hakank.org/or-tools/set_covering2.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCovering2"); + + // + // data + // + + // Example 9.1-2 from + // Taha "Operations Research - An Introduction", + // page 354ff. + // Minimize the number of security telephones in street + // corners on a campus. + + int n = 8; // maximum number of corners + int num_streets = 11; // number of connected streets + + // corners of each street + // Note: 1-based (handled below) + int[,] corner = {{1,2}, + {2,3}, + {4,5}, + {7,8}, + {6,7}, + {2,6}, + {1,6}, + {4,7}, + {2,4}, + {5,8}, + {3,5}}; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 1, "x"); + // number of telephones, to be minimized + IntVar z = x.Sum().Var(); + + // + // Constraints + // + + // ensure that all streets are covered + for(int i = 0; i < num_streets; i++) { + solver.Add(x[corner[i,0] - 1] + x[corner[i,1] - 1] >= 1); + } + + // + // objective + // + OptimizeVar objective = z.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}", z.Value()); + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/set_covering3.cs b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering3.cs new file mode 100644 index 0000000000000000000000000000000000000000..012a496c8d8e00404f592784e42178a6618e8ad9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering3.cs @@ -0,0 +1,131 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCovering3 +{ + + /** + * + * Solves a set covering problem. + * See See http://www.hakank.org/or-tools/set_covering3.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCovering3"); + + // + // data + // + + // Set covering problem from + // Katta G. Murty: 'Optimization Models for Decision Making', + // page 302f + // http://ioe.engin.umich.edu/people/fac/books/murty/opti_model/junior-7.pdf + int num_groups = 6; + int num_senators = 10; + + // which group does a senator belong to? + int[,] belongs = {{1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, // 1 southern + {0, 0, 0, 0, 0, 1, 1, 1, 1, 1}, // 2 northern + {0, 1, 1, 0, 0, 0, 0, 1, 1, 1}, // 3 liberals + {1, 0, 0, 0, 1, 1, 1, 0, 0, 0}, // 4 conservative + {0, 0, 1, 1, 1, 1, 1, 0, 1, 0}, // 5 democrats + {1, 1, 0, 0, 0, 0, 0, 1, 0, 1}}; // 6 republicans + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(num_senators, 0, 1, "x"); + // number of assigned senators, to be minimized + IntVar z = x.Sum().Var(); + + // + // Constraints + // + + // ensure that each group is covered by at least + // one senator + for(int i = 0; i < num_groups; i++) { + IntVar[] b = new IntVar[num_senators]; + for(int j = 0; j < num_senators; j++) { + b[j] = (x[j]*belongs[i,j]).Var(); + } + solver.Add(b.Sum() >= 1); + } + + + // + // objective + // + OptimizeVar objective = z.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("z: " + z.Value()); + Console.Write("x: "); + for(int j = 0; j < num_senators; j++) { + Console.Write(x[j].Value() + " "); + } + Console.WriteLine(); + + // More details + for(int j = 0; j < num_senators; j++) { + if (x[j].Value() == 1) { + Console.Write("Senator " + (1 + j) + + " belongs to these groups: "); + for(int i = 0; i < num_groups; i++) { + if (belongs[i,j] == 1) { + Console.Write((1 + i) + " "); + } + } + Console.WriteLine(); + } + + } + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/set_covering4.cs b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering4.cs new file mode 100644 index 0000000000000000000000000000000000000000..1fcc4435f9e5a93b42643ace9336e4bf2f5cefe2 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering4.cs @@ -0,0 +1,134 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCovering4 +{ + + /** + * + * Solves a set covering problem. + * See See http://www.hakank.org/or-tools/set_covering4.py + * + */ + private static void Solve(int set_partition) + { + + Solver solver = new Solver("SetCovering4"); + + // + // data + // + + // Set partition and set covering problem from + // Example from the Swedish book + // Lundgren, Roennqvist, Vaebrand + // 'Optimeringslaera' (translation: 'Optimization theory'), + // page 408. + int num_alternatives = 10; + int num_objects = 8; + + // costs for the alternatives + int[] costs = {19, 16, 18, 13, 15, 19, 15, 17, 16, 15}; + + // the alternatives, and their objects + int[,] a = { + // 1 2 3 4 5 6 7 8 the objects + {1,0,0,0,0,1,0,0}, // alternative 1 + {0,1,0,0,0,1,0,1}, // alternative 2 + {1,0,0,1,0,0,1,0}, // alternative 3 + {0,1,1,0,1,0,0,0}, // alternative 4 + {0,1,0,0,1,0,0,0}, // alternative 5 + {0,1,1,0,0,0,0,0}, // alternative 6 + {0,1,1,1,0,0,0,0}, // alternative 7 + {0,0,0,1,1,0,0,1}, // alternative 8 + {0,0,1,0,0,1,0,1}, // alternative 9 + {1,0,0,0,0,1,1,0}}; // alternative 10 + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(num_alternatives, 0, 1, "x"); + // number of assigned senators, to be minimized + IntVar z = x.ScalProd(costs).VarWithName("z"); + + // + // Constraints + // + + + for(int j = 0; j < num_objects; j++) { + IntVar[] b = new IntVar[num_alternatives]; + for(int i = 0; i < num_alternatives; i++) { + b[i] = (x[i] * a[i,j]).Var(); + } + + if (set_partition == 1) { + solver.Add(b.Sum() >= 1); + } else { + solver.Add(b.Sum() == 1); + } + } + + + // + // objective + // + OptimizeVar objective = z.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("z: " + z.Value()); + Console.Write("Selected alternatives: "); + for(int i = 0; i < num_alternatives; i++) { + if (x[i].Value() == 1) { + Console.Write((i+1) + " "); + } + } + Console.WriteLine("\n"); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Console.WriteLine("Set partition:"); + Solve(1); + Console.WriteLine("\nSet covering:"); + Solve(0); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/set_covering_deployment.cs b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering_deployment.cs new file mode 100644 index 0000000000000000000000000000000000000000..3135fb402a1bdc4d967abc6e8630fd28b6a95a17 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering_deployment.cs @@ -0,0 +1,149 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class SetCoveringDeployment +{ + + /** + * + * Solves a set covering deployment problem. + * See See http://www.hakank.org/or-tools/set_covering_deployment.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCoveringDeployment"); + + // + // data + // + + // From http://mathworld.wolfram.com/SetCoveringDeployment.html + string[] countries = {"Alexandria", + "Asia Minor", + "Britain", + "Byzantium", + "Gaul", + "Iberia", + "Rome", + "Tunis"}; + + int n = countries.Length; + + // the incidence matrix (neighbours) + int[,] mat = {{0, 1, 0, 1, 0, 0, 1, 1}, + {1, 0, 0, 1, 0, 0, 0, 0}, + {0, 0, 0, 0, 1, 1, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 0}, + {0, 0, 1, 0, 0, 1, 1, 0}, + {0, 0, 1, 0, 1, 0, 1, 1}, + {1, 0, 0, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 1, 1, 0}}; + + // + // Decision variables + // + + // First army + IntVar[] x = solver.MakeIntVarArray(n, 0, 1, "x"); + + // Second (reserve) army + IntVar[] y = solver.MakeIntVarArray(n, 0, 1, "y"); + + // total number of armies + IntVar num_armies = (x.Sum() + y.Sum()).Var(); + + + // + // Constraints + // + + // + // Constraint 1: There is always an army in a city + // (+ maybe a backup) + // Or rather: Is there a backup, there + // must be an an army + // + for(int i = 0; i < n; i++) { + solver.Add(x[i] >= y[i]); + } + + // + // Constraint 2: There should always be an backup + // army near every city + // + for(int i = 0; i < n; i++) { + IntVar[] count_neighbours = ( + from j in Enumerable.Range(0, n) + where mat[i,j] == 1 + select(y[j])).ToArray(); + + solver.Add((x[i] + count_neighbours.Sum()) >= 1); + + } + + + // + // objective + // + OptimizeVar objective = num_armies.Minimize(1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, objective); + + while (solver.NextSolution()) { + Console.WriteLine("num_armies: " + num_armies.Value()); + for(int i = 0; i < n; i++) { + if (x[i].Value() == 1) { + Console.Write("Army: " + countries[i] + " "); + } + + if (y[i].Value() == 1) { + Console.WriteLine(" Reverse army: " + countries[i]); + } + } + Console.WriteLine("\n"); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/set_covering_skiena.cs b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering_skiena.cs new file mode 100644 index 0000000000000000000000000000000000000000..5446e003496c934fe4602803b3b5a78d8cf28d74 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/set_covering_skiena.cs @@ -0,0 +1,126 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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.ConstraintSolver; + +public class SetCoveringSkiena +{ + /** + * + * Set covering. + * + * Example from Steven Skiena, The Stony Brook Algorithm Repository + * http://www.cs.sunysb.edu/~algorith/files/set-cover.shtml + * """ + * Input Description: A set of subsets S_1, ..., S_m of the + * universal set U = {1,...,n}. + * + * Problem: What is the smallest subset of subsets T subset S such + * that \cup_{t_i in T} t_i = U? + * """ + * Data is from the pictures INPUT/OUTPUT. + * + * + * Also see http://www.hakank.org/or-tools/set_covering_skiena.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("SetCoveringSkiena"); + + int num_sets = 7; + int num_elements = 12; + IEnumerable Sets = Enumerable.Range(0, num_sets); + IEnumerable Elements = Enumerable.Range(0, num_elements); + + // Which element belongs to which set + int[,] belongs = + { + // 1 2 3 4 5 6 7 8 9 0 1 2 elements + {1,1,0,0,0,0,0,0,0,0,0,0}, // Set 1 + {0,1,0,0,0,0,0,1,0,0,0,0}, // 2 + {0,0,0,0,1,1,0,0,0,0,0,0}, // 3 + {0,0,0,0,0,1,1,0,0,1,1,0}, // 4 + {0,0,0,0,0,0,0,0,1,1,0,0}, // 5 + {1,1,1,0,1,0,0,0,1,1,1,0}, // 6 + {0,0,1,1,0,0,1,1,0,0,1,1} // 7 + }; + + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(num_sets, 0, 1, "x"); + IntVar z = x.Sum().VarWithName("z"); + // total number of elements in the choosen sets + IntVar tot_elements = solver.MakeIntVar(0, num_sets*num_elements, "tot_elements"); + + + // + // Constraints + // + + // all sets must be used + foreach(int j in Elements) { + solver.Add( (from i in Sets select belongs[i,j] * x[i]) + .ToArray().Sum() >= 1); + } + + // number of used elements + solver.Add((from i in Sets from j in Elements select x[i] * belongs[i,j]) + .ToArray().Sum() == tot_elements); + + // + // Objective + // + OptimizeVar obj = z.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.WriteLine("z: {0}", z.Value()); + Console.WriteLine("tot_elements: {0}", tot_elements.Value()); + Console.WriteLine( + "x: {0}", + String.Join(" ", (from i in Enumerable.Range(0, num_sets) + select x[i].Value().ToString()).ToArray())); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/set_partition.cs b/libs/or-tools-src-ubuntu/examples/dotnet/set_partition.cs new file mode 100644 index 0000000000000000000000000000000000000000..e7900f3f1cb620b79a592aa04206c208434a9e06 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/set_partition.cs @@ -0,0 +1,204 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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 System.Diagnostics; +using Google.OrTools.ConstraintSolver; + +public class SetPartition +{ + + + // + // Partition the sets (binary matrix representation). + // + public static void partition_sets(Solver solver, + IntVar[,] x, int num_sets, int n) + { + + for(int i = 0; i Sets = Enumerable.Range(0, num_sets); + IEnumerable NRange = Enumerable.Range(0, n); + + + // + // Decision variables + // + IntVar[,] a = solver.MakeIntVarMatrix(num_sets, n, 0, 1, "a"); + IntVar[] a_flat = a.Flatten(); + + + // + // Constraints + // + + // partition set + partition_sets(solver, a, num_sets, n); + + foreach(int i in Sets) { + foreach(int j in Sets) { + + // same cardinality + solver.Add( + (from k in NRange select a[i,k]).ToArray().Sum() + == + (from k in NRange select a[j,k]).ToArray().Sum()); + + // same sum + solver.Add( + (from k in NRange select (k*a[i,k])).ToArray().Sum() + == + (from k in NRange select (k*a[j,k])).ToArray().Sum()); + + + // same sum squared + solver.Add( + (from k in NRange select (k*a[i,k]*k*a[i,k])).ToArray().Sum() + == + (from k in NRange select (k*a[j,k]*k*a[j,k])).ToArray().Sum()); + } + } + + + // symmetry breaking for num_sets == 2 + if (num_sets == 2) { + solver.Add(a[0,0] == 1); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(a_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + + int[,] a_val = new int[num_sets, n]; + foreach(int i in Sets) { + foreach(int j in NRange) { + a_val[i,j] = (int)a[i,j].Value(); + } + } + Console.WriteLine("sums: {0}", + (from j in NRange + select (j+1)*a_val[0,j]).ToArray().Sum()); + + Console.WriteLine("sums squared: {0}", + (from j in NRange + select (int)Math.Pow((j+1)*a_val[0,j],2)).ToArray().Sum()); + + // Show the numbers in each set + foreach(int i in Sets) { + if ( (from j in NRange select a_val[i,j]).ToArray().Sum() > 0 ) { + Console.Write(i+1 + ": "); + foreach(int j in NRange) { + if (a_val[i,j] == 1) { + Console.Write((j+1) + " "); + } + } + Console.WriteLine(); + } + } + Console.WriteLine(); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + int n = 16; + int num_sets = 2; + + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + + if (args.Length > 1) { + num_sets = Convert.ToInt32(args[1]); + } + + if (n % num_sets == 0) { + + Solve(n, num_sets); + } else { + Console.WriteLine("n {0} num_sets {1}: Equal sets is not possible!", + n, num_sets); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/sicherman_dice.cs b/libs/or-tools-src-ubuntu/examples/dotnet/sicherman_dice.cs new file mode 100644 index 0000000000000000000000000000000000000000..d0cc8e573e637c2d8d239b7b0ea38320aecaf5d8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/sicherman_dice.cs @@ -0,0 +1,145 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class SichermanDice +{ + /** + * + * Sicherman Dice. + * + * From http://en.wikipedia.org/wiki/Sicherman_dice + * "" + * Sicherman dice are the only pair of 6-sided dice which are not normal dice, + * bear only positive integers, and have the same probability distribution for + * the sum as normal dice. + * + * The faces on the dice are numbered 1, 2, 2, 3, 3, 4 and 1, 3, 4, 5, 6, 8. + * "" + * + * I read about this problem in a book/column by Martin Gardner long + * time ago, and got inspired to model it now by the WolframBlog post + * "Sicherman Dice": http://blog.wolfram.com/2010/07/13/sicherman-dice/ + * + * This model gets the two different ways, first the standard way and + * then the Sicherman dice: + * + * x1 = [1, 2, 3, 4, 5, 6] + * x2 = [1, 2, 3, 4, 5, 6] + * ---------- + * x1 = [1, 2, 2, 3, 3, 4] + * x2 = [1, 3, 4, 5, 6, 8] + * + * + * Extra: If we also allow 0 (zero) as a valid value then the + * following two solutions are also valid: + * + * x1 = [0, 1, 1, 2, 2, 3] + * x2 = [2, 4, 5, 6, 7, 9] + * ---------- + * x1 = [0, 1, 2, 3, 4, 5] + * x2 = [2, 3, 4, 5, 6, 7] + * + * These two extra cases are mentioned here: + * http://mathworld.wolfram.com/SichermanDice.html + * + * + * Also see http://www.hakank.org/or-tools/sicherman_dice.py + * + */ + private static void Solve() + { + Solver solver = new Solver("SichermanDice"); + + // + // Data + // + int n = 6; + int m = 10; + int lowest_value = 0; + + // standard distribution + int[] standard_dist = {1,2,3,4,5,6,5,4,3,2,1}; + + + IEnumerable RANGE = Enumerable.Range(0, n); + IEnumerable RANGE1 = Enumerable.Range(0, n-1); + + + // + // Decision variables + // + + IntVar[] x1 = solver.MakeIntVarArray(n, lowest_value, m, "x1"); + IntVar[] x2 = solver.MakeIntVarArray(n, lowest_value, m, "x2"); + + // + // Constraints + // + for(int k = 0; k < standard_dist.Length; k++) { + solver.Add((from i in RANGE + from j in RANGE + select x1[i] + x2[j] == k + 2 + ).ToArray().Sum() == standard_dist[k]); + } + + // symmetry breaking + foreach(int i in RANGE1) { + solver.Add(x1[i] <= x1[i+1]); + solver.Add(x2[i] <= x2[i+1]); + solver.Add(x1[i] <= x2[i]); + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x1.Concat(x2).ToArray(), + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x1: "); + foreach(int i in RANGE) { + Console.Write(x1[i].Value() + " "); + } + Console.Write("\nx2: "); + foreach(int i in RANGE) { + Console.Write(x2[i].Value() + " "); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/ski_assignment.cs b/libs/or-tools-src-ubuntu/examples/dotnet/ski_assignment.cs new file mode 100644 index 0000000000000000000000000000000000000000..e3b520e5b6a1079778f2974fc6f718b7af4bf654 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/ski_assignment.cs @@ -0,0 +1,122 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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.Linq; +using System.Collections; +using System.Collections.Generic; +using Google.OrTools.ConstraintSolver; + +public class SkiAssignment +{ + /** + * + * Ski assignment in Google CP Solver. + * + * From Jeffrey Lee Hellrung, Jr.: + * PIC 60, Fall 2008 Final Review, December 12, 2008 + * http://www.math.ucla.edu/~jhellrun/course_files/Fall%25202008/PIC%252060%2520-%2520Data%2520Structures%2520and%2520Algorithms/final_review.pdf + * """ + * 5. Ski Optimization! Your job at Snapple is pleasant but in the winter + * you've decided to become a ski bum. You've hooked up with the Mount + * Baldy Ski Resort. They'll let you ski all winter for free in exchange + * for helping their ski rental shop with an algorithm to assign skis to + * skiers. Ideally, each skier should obtain a pair of skis whose height + * matches his or her own height exactly. Unfortunately, this is generally + * not possible. We define the disparity between a skier and his or her + * skis to be the absolute value of the difference between the height of + * the skier and the pair of skis. Our objective is to find an assignment + * of skis to skiers that minimizes the sum of the disparities. + * ... + * Illustrate your algorithm by explicitly filling out the A[i, j] table + * for the following sample data: + * - Ski heights : 1, 2, 5, 7, 13, 21. + * - Skier heights: 3, 4, 7, 11, 18. + * """ + * + * Also see http://www.hakank.org/or-tools/ski_assignment.py + * + + */ + private static void Solve() + { + Solver solver = new Solver("SkiAssignment"); + + // + // Data + // + int num_skis = 6; + int num_skiers = 5; + int[] ski_heights = {1, 2, 5, 7, 13, 21}; + int[] skier_heights = {3, 4, 7, 11, 18}; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(num_skiers, 0, num_skis-1, "x"); + + + // + // Constraints + // + solver.Add(x.AllDifferent()); + + IntVar[] z_tmp = new IntVar[num_skiers]; + for(int i = 0; i < num_skiers; i++) { + z_tmp[i] = (ski_heights.Element(x[i]) - skier_heights[i]).Abs().Var(); + } + + // IntVar z = solver.MakeIntVar(0, ski_heights.Sum(), "z"); + // solver.Add(z_tmp.Sum() == z); + // The direct cast from IntExpr to IntVar is potentially faster than + // the above code. + IntVar z = z_tmp.Sum().VarWithName("z"); + + // + // Objective + // + OptimizeVar obj = z.Minimize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, obj); + + while (solver.NextSolution()) { + Console.Write("z: {0} x: ", z.Value()); + for(int i = 0; i < num_skiers; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/stable_marriage.cs b/libs/or-tools-src-ubuntu/examples/dotnet/stable_marriage.cs new file mode 100644 index 0000000000000000000000000000000000000000..9a20b10a52822bb3c7211152160dbb0a22e39c36 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/stable_marriage.cs @@ -0,0 +1,232 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class StableMarriage +{ + + /** + * + * Solves some stable marriage problems. + * See http://www.hakank.org/or-tools/stable_marriage.py + * + */ + private static void Solve(int[][][] ranks, String problem_name) + { + + Solver solver = new Solver("StableMarriage"); + + // + // data + // + int n = ranks[0].Length; + + Console.WriteLine("\n#####################"); + Console.WriteLine("Problem: " + problem_name); + + int[][] rankWomen = ranks[0]; + int[][] rankMen = ranks[1]; + + // + // Decision variables + // + IntVar[] wife = solver.MakeIntVarArray(n, 0, n - 1, "wife"); + IntVar[] husband = solver.MakeIntVarArray(n, 0, n - 1, "husband"); + + // + // Constraints + // + // (The comments below are the Comet code) + + // + // forall(m in Men) + // cp.post(husband[wife[m]] == m); + for(int m = 0; m < n; m++) { + solver.Add(husband.Element(wife[m]) == m); + } + + // forall(w in Women) + // cp.post(wife[husband[w]] == w); + for(int w = 0; w < n; w++) { + solver.Add(wife.Element(husband[w]) == w); + } + + + // forall(m in Men, o in Women) + // cp.post(rankMen[m,o] < rankMen[m, wife[m]] => + // rankWomen[o,husband[o]] < rankWomen[o,m]); + for(int m = 0; m < n; m++) { + for(int o = 0; o < n; o++) { + IntVar b1 = rankMen[m].Element(wife[m]) > rankMen[m][o]; + IntVar b2 = rankWomen[o].Element(husband[o]) < rankWomen[o][m]; + solver.Add(b1 <= b2); + } + + } + + // forall(w in Women, o in Men) + // cp.post(rankWomen[w,o] < rankWomen[w,husband[w]] => + // rankMen[o,wife[o]] < rankMen[o,w]); + for(int w = 0; w < n; w++) { + for(int o = 0; o < n; o++) { + IntVar b1 = rankWomen[w].Element(husband[w]) > rankWomen[w][o]; + IntVar b2 = rankMen[o].Element(wife[o]) < rankMen[o][w]; + solver.Add(b1 <= b2); + } + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(wife, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("wife : "); + for(int i = 0; i < n; i++) { + Console.Write(wife[i].Value() + " "); + } + Console.Write("\nhusband: "); + for(int i = 0; i < n; i++) { + Console.Write(husband[i].Value() + " "); + } + Console.WriteLine("\n"); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + // + // From Pascal Van Hentenryck's OPL book + // + int[][][] van_hentenryck = { + // rankWomen + new int[][] { + new int[] {1, 2, 4, 3, 5}, + new int[] {3, 5, 1, 2, 4}, + new int[] {5, 4, 2, 1, 3}, + new int[] {1, 3, 5, 4, 2}, + new int[] {4, 2, 3, 5, 1}}, + + // rankMen + new int[][] { + new int[] {5, 1, 2, 4, 3}, + new int[] {4, 1, 3, 2, 5}, + new int[] {5, 3, 2, 4, 1}, + new int[] {1, 5, 4, 3, 2}, + new int[] {4, 3, 2, 1, 5}} + }; + + // + // Data from MathWorld + // http://mathworld.wolfram.com/StableMarriageProblem.html + // + int[][][] mathworld = { + // rankWomen + new int[][] { + new int[] {3, 1, 5, 2, 8, 7, 6, 9, 4}, + new int[] {9, 4, 8, 1, 7, 6, 3, 2, 5}, + new int[] {3, 1, 8, 9, 5, 4, 2, 6, 7}, + new int[] {8, 7, 5, 3, 2, 6, 4, 9, 1}, + new int[] {6, 9, 2, 5, 1, 4, 7, 3, 8}, + new int[] {2, 4, 5, 1, 6, 8, 3, 9, 7}, + new int[] {9, 3, 8, 2, 7, 5, 4, 6, 1}, + new int[] {6, 3, 2, 1, 8, 4, 5, 9, 7}, + new int[] {8, 2, 6, 4, 9, 1, 3, 7, 5}}, + + // rankMen + new int[][] { + new int[] {7, 3, 8, 9, 6, 4, 2, 1, 5}, + new int[] {5, 4, 8, 3, 1, 2, 6, 7, 9}, + new int[] {4, 8, 3, 9, 7, 5, 6, 1, 2}, + new int[] {9, 7, 4, 2, 5, 8, 3, 1, 6}, + new int[] {2, 6, 4, 9, 8, 7, 5, 1, 3}, + new int[] {2, 7, 8, 6, 5, 3, 4, 1, 9}, + new int[] {1, 6, 2, 3, 8, 5, 4, 9, 7}, + new int[] {5, 6, 9, 1, 2, 8, 4, 3, 7}, + new int[] {6, 1, 4, 7, 5, 8, 3, 9, 2}} + }; + + // + // Data from + // http://www.csee.wvu.edu/~ksmani/courses/fa01/random/lecnotes/lecture5.pdf + // + int[][][] problem3 = { + // rankWomen + new int[][] { + new int[] {1,2,3,4}, + new int[] {4,3,2,1}, + new int[] {1,2,3,4}, + new int[] {3,4,1,2}}, + + // rankMen + new int[][] { + new int[] {1,2,3,4}, + new int[] {2,1,3,4}, + new int[] {1,4,3,2}, + new int[] {4,3,1,2}} + }; + + + // + // Data from + // http://www.comp.rgu.ac.uk/staff/ha/ZCSP/additional_problems/stable_marriage/stable_marriage.pdf + // page 4 + // + int[][][] problem4 = { + // rankWomen + new int[][] { + new int[] {1,5,4,6,2,3}, + new int[] {4,1,5,2,6,3}, + new int[] {6,4,2,1,5,3}, + new int[] {1,5,2,4,3,6}, + new int[] {4,2,1,5,6,3}, + new int[] {2,6,3,5,1,4}}, + + // rankMen + new int[][] { + new int[] {1,4,2,5,6,3}, + new int[] {3,4,6,1,5,2}, + new int[] {1,6,4,2,3,5}, + new int[] {6,5,3,4,2,1}, + new int[] {3,1,2,4,5,6}, + new int[] {2,3,1,6,5,4}}}; + + + Solve(van_hentenryck, "Van Hentenryck"); + Solve(mathworld, "MathWorld"); + Solve(problem3, "Problem 3"); + Solve(problem4, "Problem 4"); + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/strimko2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/strimko2.cs new file mode 100644 index 0000000000000000000000000000000000000000..4b2c76701944b446df7de138b9f713729dd8f86e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/strimko2.cs @@ -0,0 +1,133 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class Strimko2 +{ + + /** + * + * Solves a Strimko problem. + * See http://www.hakank.org/google_or_tools/strimko2.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("Strimko2"); + + // + // data + // + int[,] streams = {{1,1,2,2,2,2,2}, + {1,1,2,3,3,3,2}, + {1,4,1,3,3,5,5}, + {4,4,3,1,3,5,5}, + {4,6,6,6,7,7,5}, + {6,4,6,4,5,5,7}, + {6,6,4,7,7,7,7}}; + + // Note: This is 1-based + int[,] placed = {{2,1,1}, + {2,3,7}, + {2,5,6}, + {2,7,4}, + {3,2,7}, + {3,6,1}, + {4,1,4}, + {4,7,5}, + {5,2,2}, + {5,6,6}}; + + int n = streams.GetLength(0); + int num_placed = placed.GetLength(0); + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n, "x"); + IntVar[] x_flat = x.Flatten(); + + // + // Constraints + // + // all rows and columns must be unique, i.e. a Latin Square + for(int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + IntVar[] col = new IntVar[n]; + for(int j = 0; j < n; j++) { + row[j] = x[i,j]; + col[j] = x[j,i]; + } + + solver.Add(row.AllDifferent()); + solver.Add(col.AllDifferent()); + } + + // streams + for(int s = 1; s <= n; s++) { + IntVar[] tmp = (from i in Enumerable.Range(0, n) + from j in Enumerable.Range(0, n) + where streams[i,j] == s + select x[i,j]).ToArray(); + solver.Add(tmp.AllDifferent()); + + } + + // placed + for(int i = 0; i < num_placed; i++) { + // note: also adjust to 0-based + solver.Add(x[placed[i,0] - 1,placed[i,1] - 1] == placed[i,2]); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + Console.Write(x[i,j].Value() + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/subset_sum.cs b/libs/or-tools-src-ubuntu/examples/dotnet/subset_sum.cs new file mode 100644 index 0000000000000000000000000000000000000000..8d223df73276d9066792038458d270ddcd057ca7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/subset_sum.cs @@ -0,0 +1,118 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class SubsetSum +{ + + public static IntVar[] subset_sum(Solver solver, + int[] values, + int total) { + int n = values.Length; + IntVar[] x = solver.MakeIntVarArray(n, 0, n, "x"); + solver.Add(x.ScalProd(values) == total); + + return x; + } + + + /** + * + * Subset sum problem. + * + * From Katta G. Murty: 'Optimization Models for Decision Making', page 340 + * http://ioe.engin.umich.edu/people/fac/books/murty/opti_model/junior-7.pdf + * """ + * Example 7.8.1 + * + * A bank van had several bags of coins, each containing either + * 16, 17, 23, 24, 39, or 40 coins. While the van was parked on the + * street, thieves stole some bags. A total of 100 coins were lost. + * It is required to find how many bags were stolen. + * """ + * + * Also see http://www.hakank.org/or-tools/subset_sum.py + * + */ + private static void Solve(int[] coins, int total) + { + + Solver solver = new Solver("SubsetSum"); + + int n = coins.Length; + Console.Write("Coins: "); + for(int i = 0; i < n; i++) { + Console.Write(coins[i] + " "); + } + Console.WriteLine("\nTotal: {0}", total); + + // + // Variables + // + // number of coins + IntVar s = solver.MakeIntVar(0, coins.Sum(), "s"); + + + // + // Constraints + // + IntVar[] x = subset_sum(solver, coins, total); + solver.Add(x.Sum() == s); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("x: "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(" s: {0}", s.Value()); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + + int[] coins = {16, 17, 23, 24, 39, 40}; + int total = 100; + + if (args.Length > 0) { + total = Convert.ToInt32(args[0]); + } + + Solve(coins, total); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/sudoku.cs b/libs/or-tools-src-ubuntu/examples/dotnet/sudoku.cs new file mode 100644 index 0000000000000000000000000000000000000000..d57b3bcd19c619f8652c0e8432e1e310a0976de5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/sudoku.cs @@ -0,0 +1,133 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class Sudoku +{ + + /** + * + * Solves a Sudoku problem. + * + */ + private static void Solve() + { + Solver solver = new Solver("Sudoku"); + + // + // data + // + int cell_size = 3; + IEnumerable CELL = Enumerable.Range(0, cell_size); + int n = cell_size * cell_size; + IEnumerable RANGE = Enumerable.Range(0, n); + + // 0 marks an unknown value + int[,] initial_grid = {{0, 6, 0, 0, 5, 0, 0, 2, 0}, + {0, 0, 0, 3, 0, 0, 0, 9, 0}, + {7, 0, 0, 6, 0, 0, 0, 1, 0}, + {0, 0, 6, 0, 3, 0, 4, 0, 0}, + {0, 0, 4, 0, 7, 0, 1, 0, 0}, + {0, 0, 5, 0, 9, 0, 8, 0, 0}, + {0, 4, 0, 0, 0, 1, 0, 0, 6}, + {0, 3, 0, 0, 0, 8, 0, 0, 0}, + {0, 2, 0, 0, 4, 0, 0, 5, 0}}; + + + // + // Decision variables + // + IntVar[,] grid = solver.MakeIntVarMatrix(n, n, 1, 9, "grid"); + IntVar[] grid_flat = grid.Flatten(); + + // + // Constraints + // + + // init + foreach(int i in RANGE) { + foreach(int j in RANGE) { + if (initial_grid[i,j] > 0) { + solver.Add(grid[i,j] == initial_grid[i,j]); + } + } + } + + + foreach(int i in RANGE) { + + // rows + solver.Add( (from j in RANGE + select grid[i,j]).ToArray().AllDifferent()); + + // cols + solver.Add( (from j in RANGE + select grid[j,i]).ToArray().AllDifferent()); + + } + + // cells + foreach(int i in CELL) { + foreach(int j in CELL) { + solver.Add( (from di in CELL + from dj in CELL + select grid[i*cell_size+di, j*cell_size+dj] + ).ToArray().AllDifferent()); + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(grid_flat, + Solver.INT_VAR_SIMPLE, + Solver.INT_VALUE_SIMPLE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++){ + Console.Write("{0} ", grid[i,j].Value()); + } + Console.WriteLine(); + } + + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/survo_puzzle.cs b/libs/or-tools-src-ubuntu/examples/dotnet/survo_puzzle.cs new file mode 100644 index 0000000000000000000000000000000000000000..645ec77951df94b195d0158c504ccd526c41029d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/survo_puzzle.cs @@ -0,0 +1,238 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class SurvoPuzzle +{ + + // + // default problem + // + static int default_r = 3; + static int default_c = 4; + static int[] default_rowsums = {30, 18, 30}; + static int[] default_colsums = {27, 16, 10, 25}; + static int[,] default_game = {{0, 6, 0, 0}, + {8, 0, 0, 0}, + {0, 0, 3, 0}}; + + + // for the actual problem + static int r; + static int c; + static int[] rowsums; + static int[] colsums; + static int[,] game; + + + + /** + * + * Solves the Survo puzzle. + * See http://www.hakank.org/or-tools/survo_puzzle.py + * + */ + private static void Solve() + { + Solver solver = new Solver("SurvoPuzzle"); + + // + // data + // + Console.WriteLine("Problem:"); + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + Console.Write(game[i,j] + " "); + } + Console.WriteLine(); + } + Console.WriteLine(); + + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(r, c, 1, r*c, "x"); + IntVar[] x_flat = x.Flatten(); + + + // + // Constraints + // + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++) { + if (game[i,j] > 0) { + solver.Add(x[i,j] == game[i,j]); + } + } + } + + solver.Add(x_flat.AllDifferent()); + + + // + // calculate rowsums and colsums + // + for(int i = 0; i < r; i++) { + IntVar[] row = new IntVar[c]; + for(int j = 0; j < c; j++) { + row[j] = x[i,j]; + } + solver.Add(row.Sum() == rowsums[i]); + } + + for(int j = 0; j < c; j++) { + IntVar[] col = new IntVar[r]; + for(int i = 0; i < r; i++) { + col[i] = x[i,j]; + } + solver.Add(col.Sum() == colsums[j]); + + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.INT_VAR_SIMPLE, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int sol = 0; + while (solver.NextSolution()) { + sol++; + Console.WriteLine("Solution #{0} ", sol + " "); + for(int i = 0; i < r; i++) { + for(int j = 0; j < c; j++){ + Console.Write("{0} ", x[i,j].Value()); + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /** + * + * readFile() + * + * Reads a Survo puzzle in the following format + * r + * c + * rowsums + * olsums + * data + * ... + * + * Example: + * 3 + * 4 + * 30,18,30 + * 27,16,10,25 + * 0,6,0,0 + * 8,0,0,0 + * 0,0,3,0 + * + */ + private static void readFile(String file) { + + Console.WriteLine("readFile(" + file + ")"); + + TextReader inr = new StreamReader(file); + + r = Convert.ToInt32(inr.ReadLine()); + c = Convert.ToInt32(inr.ReadLine()); + rowsums = new int[r]; + colsums = new int[c]; + Console.WriteLine("r: " + r + " c: " + c); + + String[] rowsums_str = Regex.Split(inr.ReadLine(),",\\s*"); + String[] colsums_str = Regex.Split(inr.ReadLine(),",\\s*"); + Console.WriteLine("rowsums:"); + for(int i = 0; i < r; i++) { + Console.Write(rowsums_str[i] + " "); + rowsums[i] = Convert.ToInt32(rowsums_str[i]); + } + Console.WriteLine("\ncolsums:"); + for(int j = 0; j < c; j++) { + Console.Write(colsums_str[j] + " "); + colsums[j] = Convert.ToInt32(colsums_str[j]); + } + Console.WriteLine(); + + // init the game matrix and read data from file + game = new int[r,c]; + String str; + int line_count = 0; + while ((str = inr.ReadLine()) != null && str.Length > 0) { + str = str.Trim(); + + // ignore comments + // starting with either # or % + if(str.StartsWith("#") || str.StartsWith("%")) { + continue; + } + + String[] this_row = Regex.Split(str, ",\\s*"); + for(int j = 0; j < this_row.Length; j++) { + game[line_count,j] = Convert.ToInt32(this_row[j]); + } + + line_count++; + + } // end while + + inr.Close(); + + + } // end readFile + + + + + public static void Main(String[] args) + { + String file = ""; + if (args.Length > 0) { + file = args[0]; + readFile(file); + } else { + r = default_r; + c = default_c; + game = default_game; + rowsums = default_rowsums; + colsums = default_colsums; + } + + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/to_num.cs b/libs/or-tools-src-ubuntu/examples/dotnet/to_num.cs new file mode 100644 index 0000000000000000000000000000000000000000..76ff19c64928a60c6ff89c89d3f4be5328d849c3 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/to_num.cs @@ -0,0 +1,101 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class ToNumTest +{ + + + /** + * + * toNum(solver, a, num, base) + * + * channelling between the array a and the number num. + * + */ + private static Constraint ToNum(IntVar[] a, IntVar num, int bbase) { + int len = a.Length; + + IntVar[] tmp = new IntVar[len]; + for(int i = 0; i < len; i++) { + tmp[i] = (a[i]*(int)Math.Pow(bbase,(len-i-1))).Var(); + } + return tmp.Sum() == num; + } + + + + /** + * + * Implements toNum: channeling between a number and an array. + * See http://www.hakank.org/or-tools/toNum.py + * + */ + private static void Solve() + { + Solver solver = new Solver("ToNum"); + + int n = 5; + int bbase = 10; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, bbase - 1, "x"); + IntVar num = solver.MakeIntVar(0, (int)Math.Pow(bbase, n) - 1, "num"); + + // + // Constraints + // + + solver.Add(x.AllDifferent()); + solver.Add(ToNum(x, num, bbase)); + + // extra constraint (just for fun) + // second digit should be 7 + // solver.Add(x[1] == 7); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("\n" + num.Value() + ": "); + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/traffic_lights.cs b/libs/or-tools-src-ubuntu/examples/dotnet/traffic_lights.cs new file mode 100644 index 0000000000000000000000000000000000000000..e8d2adcbb81c6a23edac7879af45d922b92e2906 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/traffic_lights.cs @@ -0,0 +1,143 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + +public class TrafficLights +{ + + /** + * + * Traffic lights problem. + * + * CSPLib problem 16 + * http://www.cs.st-andrews.ac.uk/~ianm/CSPLib/prob/prob016/index.html + * """ + * Specification: + * Consider a four way traffic junction with eight traffic lights. Four of the traffic + * lights are for the vehicles and can be represented by the variables V1 to V4 with domains + * {r,ry,g,y} (for red, red-yellow, green and yellow). The other four traffic lights are + * for the pedestrians and can be represented by the variables P1 to P4 with domains {r,g}. + * + * The constraints on these variables can be modelled by quaternary constraints on + * (Vi, Pi, Vj, Pj ) for 1<=i<=4, j=(1+i)mod 4 which allow just the tuples + * {(r,r,g,g), (ry,r,y,r), (g,g,r,r), (y,r,ry,r)}. + * + * It would be interesting to consider other types of junction (e.g. five roads + * intersecting) as well as modelling the evolution over time of the traffic light sequence. + * ... + * + * Results + * Only 2^2 out of the 2^12 possible assignments are solutions. + * + * (V1,P1,V2,P2,V3,P3,V4,P4) = + * {(r,r,g,g,r,r,g,g), (ry,r,y,r,ry,r,y,r), (g,g,r,r,g,g,r,r), (y,r,ry,r,y,r,ry,r)} + * [(1,1,3,3,1,1,3,3), ( 2,1,4,1, 2,1,4,1), (3,3,1,1,3,3,1,1), (4,1, 2,1,4,1, 2,1)} + * The problem has relative few constraints, but each is very + * tight. Local propagation appears to be rather ineffective on this + * problem. + * + * """ + * Note: In this model we use only the constraint + * solver.AllowedAssignments(). + * + * + * See http://www.hakank.org/or-tools/traffic_lights.py + * + */ + private static void Solve() + { + + Solver solver = new Solver("TrafficLights"); + + // + // data + // + int n = 4; + + int r = 0; + int ry = 1; + int g = 2; + int y = 3; + + string[] lights = {"r", "ry", "g", "y"}; + + // The allowed combinations + IntTupleSet allowed = new IntTupleSet(4); + allowed.InsertAll(new long[][] { + new long[] {r,r,g,g}, + new long[] {ry,r,y,r}, + new long[] {g,g,r,r}, + new long[] {y,r,ry,r}}); + // + // Decision variables + // + IntVar[] V = solver.MakeIntVarArray(n, 0, n-1, "V"); + IntVar[] P = solver.MakeIntVarArray(n, 0, n-1, "P"); + + // for search + IntVar[] VP = new IntVar[2 * n]; + for(int i = 0; i < n; i++) { + VP[i] = V[i]; + VP[i+n] = P[i]; + } + + // + // Constraints + // + for(int i = 0; i < n; i++) { + int j = (1+i) % n; + IntVar[] tmp = new IntVar[] {V[i],P[i],V[j],P[j]}; + solver.Add(tmp.AllowedAssignments(allowed)); + } + + // + // Search + // + DecisionBuilder db = solver.MakePhase(VP, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + + solver.NewSearch(db); + + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write("{0,2} {1,2} ", + lights[V[i].Value()], + lights[P[i].Value()]); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/volsay.cs b/libs/or-tools-src-ubuntu/examples/dotnet/volsay.cs new file mode 100644 index 0000000000000000000000000000000000000000..f01d88a9b282152c1b229a24c998b70080977819 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/volsay.cs @@ -0,0 +1,77 @@ +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.LinearSolver; + +public class Volsay { + /** + * Volsay problem. + * + * From the OPL model volsay.mod. + * + * Also see http://www.hakank.org/or-tools/volsay.py + */ + private static void Solve() { + Solver solver = new Solver( + "Volsay", Solver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING); + + // + // Variables + // + Variable Gas = solver.MakeNumVar(0, 100000, "Gas"); + Variable Chloride = solver.MakeNumVar(0, 100000, "Cloride"); + + Constraint c1 = solver.Add(Gas + Chloride <= 50); + Constraint c2 = solver.Add(3 * Gas + 4 * Chloride <= 180); + + solver.Maximize(40 * Gas + 50 * Chloride); + + Solver.ResultStatus resultStatus = solver.Solve(); + + if (resultStatus != Solver.ResultStatus.OPTIMAL) { + Console.WriteLine("The problem don't have an optimal solution."); + return; + } + + Console.WriteLine("Objective: {0}", solver.Objective().Value()); + + Console.WriteLine("Gas : {0} ReducedCost: {1}", + Gas.SolutionValue(), + Gas.ReducedCost()); + + Console.WriteLine("Chloride : {0} ReducedCost: {1}", + Chloride.SolutionValue(), + Chloride.ReducedCost()); + + double[] activities = solver.ComputeConstraintActivities(); + Console.WriteLine("c1 : DualValue: {0} Activity: {1}", + c1.DualValue(), + activities[c1.Index()]); + + Console.WriteLine("c2 : DualValue: {0} Activity: {1}", + c2.DualValue(), + activities[c2.Index()]); + + Console.WriteLine("\nWallTime: " + solver.WallTime()); + Console.WriteLine("Iterations: " + solver.Iterations()); + } + + public static void Main(String[] args) { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/volsay2.cs b/libs/or-tools-src-ubuntu/examples/dotnet/volsay2.cs new file mode 100644 index 0000000000000000000000000000000000000000..ee1c1c8345933d1649b863df985d9bbc658dc812 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/volsay2.cs @@ -0,0 +1,92 @@ +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.LinearSolver; + +public class Volsay2 { + /** + * Volsay problem. + * + * From the OPL model volsay.mod. + * + * Also see + * http://www.hakank.org/or-tools/volsay.cs + * http://www.hakank.org/or-tools/volsay2.py + */ + private static void Solve() { + Solver solver = new Solver( + "Volsay2", Solver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING); + + int num_products = 2; + IEnumerable PRODUCTS = Enumerable.Range(0, num_products); + int Gas = 0; + int Chloride = 1; + String[] products = {"Gas", "Chloride"}; + + // + // Variables + // + Variable[] production = new Variable[num_products]; + foreach(int p in PRODUCTS) { + production[p] = solver.MakeNumVar(0, 100000, products[p]); + } + + int num_constraints = 2; + IEnumerable CONSTRAINTS = Enumerable.Range(0, num_constraints); + Constraint[] cons = new Constraint[num_constraints]; + cons[0] = solver.Add(production[Gas] + production[Chloride] <= 50); + cons[1] = solver.Add(3 * production[Gas] + 4 * production[Chloride] <= 180); + + solver.Maximize(40 * production[Gas] + 50 * production[Chloride]); + + Console.WriteLine("NumConstraints: {0}", solver.NumConstraints()); + + Solver.ResultStatus resultStatus = solver.Solve(); + + if (resultStatus != Solver.ResultStatus.OPTIMAL) { + Console.WriteLine("The problem don't have an optimal solution."); + return; + } + + foreach(int p in PRODUCTS) { + Console.WriteLine("{0,-10}: {1} ReducedCost: {2}", + products[p], + production[p].SolutionValue(), + production[p].ReducedCost()); + } + + double[] activities = solver.ComputeConstraintActivities(); + foreach(int c in CONSTRAINTS) { + Console.WriteLine( + "Constraint {0} DualValue {1} Activity: {2} lb: {3} ub: {4}", + c.ToString(), + cons[c].DualValue(), + activities[cons[c].Index()], + cons[c].Lb(), + cons[c].Ub()); + } + + Console.WriteLine("\nWallTime: " + solver.WallTime()); + Console.WriteLine("Iterations: " + solver.Iterations()); + + } + + public static void Main(String[] args) { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/volsay3.cs b/libs/or-tools-src-ubuntu/examples/dotnet/volsay3.cs new file mode 100644 index 0000000000000000000000000000000000000000..a9bb502f9615816085e31b451790fc6eb1ee8a88 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/volsay3.cs @@ -0,0 +1,102 @@ +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.LinearSolver; + +public class Volsay3 { + /** + * Volsay problem. + * + * From the OPL model volsay.mod. + * This version use arrays and matrices + * + * Also see + * http://www.hakank.org/or-tools/volsay2.cs + * http://www.hakank.org/or-tools/volsay3.py + */ + private static void Solve() { + Solver solver = new Solver( + "Volsay3", Solver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING); + + int num_products = 2; + IEnumerable PRODUCTS = Enumerable.Range(0, num_products); + String[] products = {"Gas", "Chloride"}; + String[] components = {"nitrogen", "hydrogen", "chlorine"}; + + int[,] demand = { {1,3,0}, {1,4,1}}; + int[] profit = {30,40}; + int[] stock = {50,180,40}; + + // + // Variables + // + Variable[] production = new Variable[num_products]; + foreach(int p in PRODUCTS) { + production[p] = solver.MakeNumVar(0, 100000, products[p]); + } + + // + // Constraints + // + int c_len = components.Length; + Constraint[] cons = new Constraint[c_len]; + for(int c = 0; c < c_len; c++) { + cons[c] = solver.Add( (from p in PRODUCTS + select (demand[p,c]*production[p])). + ToArray().Sum() <= stock[c]); + } + + // + // Objective + // + solver.Maximize( (from p in PRODUCTS + select (profit[p]*production[p])). + ToArray().Sum() + ); + + if (solver.Solve() != Solver.ResultStatus.OPTIMAL) { + Console.WriteLine("The problem don't have an optimal solution."); + return; + } + + Console.WriteLine("Objective: {0}", solver.Objective().Value()); + foreach(int p in PRODUCTS) { + Console.WriteLine("{0,-10}: {1} ReducedCost: {2}", + products[p], + production[p].SolutionValue(), + production[p].ReducedCost()); + } + + double[] activities = solver.ComputeConstraintActivities(); + for(int c = 0; c < c_len; c++) { + Console.WriteLine("Constraint {0} DualValue {1} Activity: {2} lb: {3} ub: {4}", + c, + cons[c].DualValue(), + activities[cons[c].Index()], + cons[c].Lb(), + cons[c].Ub()); + } + + Console.WriteLine("\nWallTime: " + solver.WallTime()); + Console.WriteLine("Iterations: " + solver.Iterations()); + } + + public static void Main(String[] args) { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/wedding_optimal_chart.cs b/libs/or-tools-src-ubuntu/examples/dotnet/wedding_optimal_chart.cs new file mode 100644 index 0000000000000000000000000000000000000000..9eecf2e473570e67e5655072004f3e7cc22c580b --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/wedding_optimal_chart.cs @@ -0,0 +1,215 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using Google.OrTools.ConstraintSolver; + +public class WeddingOptimalChart +{ + /** + * + * Finding an optimal wedding seating chart. + * + * From + * Meghan L. Bellows and J. D. Luc Peterson + * "Finding an optimal seating chart for a wedding" + * http://www.improbable.com/news/2012/Optimal-seating-chart.pdf + * http://www.improbable.com/2012/02/12/finding-an-optimal-seating-chart-for-a-wedding + * + * """ + * Every year, millions of brides (not to mention their mothers, future + * mothers-in-law, and occasionally grooms) struggle with one of the + * most daunting tasks during the wedding-planning process: the + * seating chart. The guest responses are in, banquet hall is booked, + * menu choices have been made. You think the hard parts are over, + * but you have yet to embark upon the biggest headache of them all. + * In order to make this process easier, we present a mathematical + * formulation that models the seating chart problem. This model can + * be solved to find the optimal arrangement of guests at tables. + * At the very least, it can provide a starting point and hopefully + * minimize stress and arguments… + * """ + * + * + * Also see http://www.hakank.org/minizinc/wedding_optimal_chart.mzn + * + */ + private static void Solve() + { + + Solver solver = new Solver("WeddingOptimalChart"); + + // + // Data + // + + // Easy problem (from the paper) + int n = 2; // number of tables + int a = 10; // maximum number of guests a table can seat + int b = 1; // minimum number of people each guest knows at their table + + /* + // Sligthly harder problem (also from the paper) + int n = 5; // number of tables + int a = 4; // maximum number of guests a table can seat + int b = 1; // minimum number of people each guest knows at their table + */ + + // j Guest Relation + // ------------------------------------- + // 1 Deb mother of the bride + // 2 John father of the bride + // 3 Martha sister of the bride + // 4 Travis boyfriend of Martha + // 5 Allan grandfather of the bride + // 6 Lois wife of Allan + // 7 Jayne aunt of the bride + // 8 Brad uncle of the bride + // 9 Abby cousin of the bride + // 10 Mary Helen mother of the groom + // 11 Lee father of the groom + // 12 Annika sister of the groom + // 13 Carl brother of the groom + // 14 Colin brother of the groom + // 15 Shirley grandmother of the groom + // 16 DeAnn aunt of the groom + // 17 Lori aunt of the groom + + // Connection matrix: who knows who, and how strong + // is the relation + int[,] C = { + { 1,50, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + {50, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1, 1,50, 1, 1, 1, 1,10, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1,50, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1, 1, 1, 1,50, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1, 1, 1,50, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1, 1, 1, 1, 1, 1,50, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1, 1, 1, 1, 1,50, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 1, 1,10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,50, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0,50, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1} + }; + + // Names of the guests. B: Bride side, G: Groom side + String[] names = {"Deb (B)", + "John (B)", + "Martha (B)", + "Travis (B)", + "Allan (B)", + "Lois (B)", + "Jayne (B)", + "Brad (B)", + "Abby (B)", + "Mary Helen (G)", + "Lee (G)", + "Annika (G)", + "Carl (G)", + "Colin (G)", + "Shirley (G)", + "DeAnn (G)", + "Lori (G)"}; + + + int m = C.GetLength(0); // number of quests + + IEnumerable NRANGE = Enumerable.Range(0, n); + IEnumerable MRANGE = Enumerable.Range(0, m); + + + // + // Decision variables + // + IntVar[] tables = solver.MakeIntVarArray(m, 0, n-1, "tables"); + IntVar z = (from j in MRANGE + from k in MRANGE + where j < k + select C[j,k] * tables[j] == tables[k] + ).ToArray().Sum().VarWithName("z"); + + // + // Constraints + // + foreach(int i in NRANGE) { + + solver.Add((from j in MRANGE + from k in MRANGE + where j < k && C[j, k] > 0 + select (tables[j] == i) * (tables[k] == i) + ).ToArray().Sum() >= b); + + + solver.Add((from j in MRANGE select tables[j] == i).ToArray().Sum() <= a); + } + + // Symmetry breaking + solver.Add(tables[0] == 0); + + // + // Objective + // + OptimizeVar obj = z.Maximize(1); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(tables, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db, obj); + while (solver.NextSolution()) { + Console.WriteLine("z: {0}",z.Value()); + Console.Write("Table: "); + foreach(int j in MRANGE) { + Console.Write(tables[j].Value() + " "); + } + Console.WriteLine(); + + foreach(int i in NRANGE) { + Console.Write("Table {0}: ", i); + foreach(int j in MRANGE) { + if (tables[j].Value() == i) { + Console.Write(names[j] + " "); + } + } + Console.WriteLine(); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/who_killed_agatha.cs b/libs/or-tools-src-ubuntu/examples/dotnet/who_killed_agatha.cs new file mode 100644 index 0000000000000000000000000000000000000000..ca2bc76045565dbc35a6778f3cb878adfb14f2a1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/who_killed_agatha.cs @@ -0,0 +1,154 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections; +using System.Linq; + +public class WhoKilledAgatha +{ + + /** + * + * Implements the Who killed Agatha problem. + * See http://www.hakank.org/google_or_tools/who_killed_agatha.py + * + */ + private static void Solve() + { + Solver solver = new Solver("WhoKilledAgatha"); + + int n = 3; + int agatha = 0; + int butler = 1; + int charles = 2; + + // + // Decision variables + // + IntVar the_killer = solver.MakeIntVar(0, 2, "the_killer"); + IntVar the_victim = solver.MakeIntVar(0, 2, "the_victim"); + IntVar[,] hates = solver.MakeIntVarMatrix(n, n, 0, 1, "hates"); + IntVar[] hates_flat = hates.Flatten(); + IntVar[,] richer = solver.MakeIntVarMatrix(n, n, 0, 1, "richer"); + IntVar[] richer_flat = richer.Flatten(); + + IntVar[] all = new IntVar[2 * n * n]; // for branching + for(int i = 0; i < n*n; i++) { + all[i] = hates_flat[i]; + all[(n*n)+i] = richer_flat[i]; + } + + // + // Constraints + // + + // Agatha, the butler, and Charles live in Dreadsbury Mansion, and + // are the only ones to live there. + + // A killer always hates, and is no richer than his victim. + // hates[the_killer, the_victim] == 1 + // hates_flat[the_killer * n + the_victim] == 1 + solver.Add(hates_flat.Element(the_killer * n + the_victim) == 1); + + // richer[the_killer, the_victim] == 0 + solver.Add(richer_flat.Element(the_killer * n + the_victim) == 0); + + // define the concept of richer: + // no one is richer than him-/herself... + for(int i = 0; i < n; i++) { + solver.Add(richer[i,i] == 0); + } + + // (contd...) if i is richer than j then j is not richer than i + // if (i != j) => + // ((richer[i,j] = 1) <=> (richer[j,i] = 0)) + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + if (i != j) { + solver.Add((richer[i, j]==1) - (richer[j, i]==0) == 0); + } + } + } + + // Charles hates no one that Agatha hates. + // forall i in 0..2: + // (hates[agatha, i] = 1) => (hates[charles, i] = 0) + for(int i = 0; i < n; i++) { + solver.Add((hates[agatha,i]==1) - (hates[charles,i]==0) <= 0); + + } + + // Agatha hates everybody except the butler. + solver.Add(hates[agatha,charles] == 1); + solver.Add(hates[agatha,agatha] == 1); + solver.Add(hates[agatha,butler] == 0); + + // The butler hates everyone not richer than Aunt Agatha. + // forall i in 0..2: + // (richer[i, agatha] = 0) => (hates[butler, i] = 1) + for(int i = 0; i < n; i++) { + solver.Add((richer[i,agatha] == 0)-(hates[butler,i] == 1)<=0); + } + + // The butler hates everyone whom Agatha hates. + // forall i : 0..2: + // (hates[agatha, i] = 1) => (hates[butler, i] = 1) + for(int i = 0; i < n; i++) { + solver.Add((hates[agatha,i] == 1)-(hates[butler,i] == 1)<=0); + } + + // Noone hates everyone. + // forall i in 0..2: + // (sum j in 0..2: hates[i,j]) <= 2 + for(int i = 0; i < n; i++) { + solver.Add((from j in Enumerable.Range(0, n) + select hates[i,j] + ).ToArray().Sum() <= 2 ); + } + + + // Who killed Agatha? + solver.Add(the_victim == agatha); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.WriteLine("the_killer: " + the_killer.Value()); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/word_square.cs b/libs/or-tools-src-ubuntu/examples/dotnet/word_square.cs new file mode 100644 index 0000000000000000000000000000000000000000..1bbdd4cf7aaa7679e11a4dd36b503f55cf3b1f5d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/word_square.cs @@ -0,0 +1,199 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class WordSquare +{ + + /** + * + * Word square. + * + * From http://en.wikipedia.org/wiki/Word_square + * """ + * A word square is a special case of acrostic. It consists of a set of words, + * all having the same number of letters as the total number of words (the + * 'order' of the square); when the words are written out in a square grid + * horizontally, the same set of words can be read vertically. + * """ + * + * See http://www.hakank.org/or-tools/word_square.py + * + */ + private static void Solve(String[] words, int word_len, int num_answers) + { + + Solver solver = new Solver("WordSquare"); + + int num_words = words.Length; + Console.WriteLine("num_words: " + num_words); + int n = word_len; + IEnumerable WORDLEN = Enumerable.Range(0, word_len); + + // + // convert a character to integer + // + + String alpha = "abcdefghijklmnopqrstuvwxyz"; + Hashtable d = new Hashtable(); + Hashtable rev = new Hashtable(); + int count = 1; + for(int a = 0; a < alpha.Length; a++) { + d[alpha[a]] = count; + rev[count] = a; + count++; + } + + int num_letters = alpha.Length; + + // + // Decision variables + // + IntVar[,] A = solver.MakeIntVarMatrix(num_words, word_len, + 0, num_letters, "A"); + IntVar[] A_flat = A.Flatten(); + IntVar[] E = solver.MakeIntVarArray(n, 0, num_words, "E"); + + + + // + // Constraints + // + solver.Add(E.AllDifferent()); + + // copy the words to a matrix + for(int i = 0; i < num_words; i++) { + char[] s = words[i].ToArray(); + foreach(int j in WORDLEN) { + int t = (int)d[s[j]]; + solver.Add(A[i,j] == t); + } + } + + foreach(int i in WORDLEN) { + foreach(int j in WORDLEN) { + solver.Add(A_flat.Element(E[i]*word_len+j) == + A_flat.Element(E[j]*word_len+i)); + } + } + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(E.Concat(A_flat).ToArray(), + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + int num_sols = 0; + while (solver.NextSolution()) { + num_sols++; + for(int i = 0; i < n; i++) { + Console.WriteLine(words[E[i].Value()] + " "); + } + Console.WriteLine(); + + if (num_answers > 0 && num_sols >= num_answers) { + break; + } + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + /* + * + * Read the words from a word list with a specific word length. + * + */ + public static String[] ReadWords(String word_list, int word_len) + { + + Console.WriteLine("ReadWords {0} {1}", word_list, word_len); + List all_words = new List(); + + TextReader inr = new StreamReader(word_list); + String str; + int count = 0; + Hashtable d = new Hashtable(); + while ((str = inr.ReadLine()) != null) { + str = str.Trim().ToLower(); + // skip weird words + if(Regex.Match(str, @"[^a-z]").Success + || + d.Contains(str) + || + str.Length == 0 + || + str.Length != word_len + ) { + continue; + } + + d[str] = 1; + all_words.Add(str); + count++; + + + } // end while + + inr.Close(); + + return all_words.ToArray(); + + } + + + public static void Main(String[] args) + { + + String word_list = "/usr/share/dict/words"; + int word_len = 4; + int num_answers = 20; + + if (args.Length > 0) { + word_list = args[0]; + } + + if (args.Length > 1) { + word_len = Convert.ToInt32(args[1]); + } + + if (args.Length > 2) { + num_answers = Convert.ToInt32(args[2]); + } + + String[] words = ReadWords(word_list, word_len); + + Solve(words, word_len, num_answers); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/xkcd.cs b/libs/or-tools-src-ubuntu/examples/dotnet/xkcd.cs new file mode 100644 index 0000000000000000000000000000000000000000..5ab7b29d94f716476ea96d2f3a8e54e63e5a45cb --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/xkcd.cs @@ -0,0 +1,77 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; + +public class Xkcd +{ + /** + * + * Solve the xkcd problem + * See http://www.hakank.org/google_or_tools/xkcd.py + * + */ + private static void Solve() + { + Solver solver = new Solver("Xkcd"); + + // + // Constants, inits + // + int n = 6; + // for price and total: multiplied by 100 to be able to use integers + int[] price = {215, 275, 335, 355, 420, 580}; + int total = 1505; + + // + // Decision variables + // + IntVar[] x = solver.MakeIntVarArray(n, 0, 10, "x"); + + // + // Constraints + // + solver.Add(x.ScalProd(price) == total); + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + while (solver.NextSolution()) { + for(int i = 0; i < n; i++) { + Console.Write(x[i].Value() + " "); + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0} ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0}", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/young_tableaux.cs b/libs/or-tools-src-ubuntu/examples/dotnet/young_tableaux.cs new file mode 100644 index 0000000000000000000000000000000000000000..bfa3404cb3990abc2d861100fca51cdcf193499e --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/young_tableaux.cs @@ -0,0 +1,141 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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; +using System.IO; +using System.Text.RegularExpressions; +using Google.OrTools.ConstraintSolver; + + +public class YoungTableaux +{ + + + /** + * + * Implements Young tableaux and partitions. + * See http://www.hakank.org/or-tools/young_tableuax.py + * + */ + private static void Solve(int n) + { + Solver solver = new Solver("YoungTableaux"); + + // + // data + // + Console.WriteLine("n: {0}\n", n); + + // + // Decision variables + // + IntVar[,] x = solver.MakeIntVarMatrix(n, n, 1, n + 1, "x"); + IntVar[] x_flat = x.Flatten(); + + // partition structure + IntVar[] p = solver.MakeIntVarArray(n, 0, n + 1, "p"); + + // + // Constraints + // + // 1..n is used exactly once + for(int i = 1; i <= n; i++) { + solver.Add(x_flat.Count(i, 1)); + } + + solver.Add(x[0,0] == 1); + + // row wise + for(int i = 0; i < n; i++) { + for(int j = 1; j < n; j++) { + solver.Add(x[i,j] >= x[i,j - 1]); + } + } + + // column wise + for(int j = 0; j < n; j++) { + for(int i = 1; i < n; i++) { + solver.Add(x[i,j] >= x[i - 1, j]); + } + } + + // calculate the structure (i.e. the partition) + for(int i = 0; i < n; i++) { + IntVar[] b = new IntVar[n]; + for(int j = 0; j < n; j++) { + b[j] = x[i, j] <= n; + } + solver.Add(p[i] == b.Sum()); + } + + solver.Add(p.Sum() == n); + + for(int i = 1; i < n; i++) { + solver.Add(p[i - 1] >= p[i]); + } + + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(x_flat, + Solver.CHOOSE_FIRST_UNBOUND, + Solver.ASSIGN_MIN_VALUE); + + solver.NewSearch(db); + + while (solver.NextSolution()) { + Console.Write("p: "); + for(int i = 0; i < n; i++) { + Console.Write(p[i].Value() + " "); + } + Console.WriteLine("\nx:"); + + for(int i = 0; i < n; i++) { + for(int j = 0; j < n; j++) { + long val = x[i,j].Value(); + if (val <= n) { + Console.Write(val + " "); + } + } + if (p[i].Value() > 0) { + Console.WriteLine(); + } + } + Console.WriteLine(); + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + + + public static void Main(String[] args) + { + int n = 5; + if (args.Length > 0) { + n = Convert.ToInt32(args[0]); + } + Solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/dotnet/zebra.cs b/libs/or-tools-src-ubuntu/examples/dotnet/zebra.cs new file mode 100644 index 0000000000000000000000000000000000000000..f1d0ba6fd56ab3e2ddb6679541d7b7b7550e3e07 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/dotnet/zebra.cs @@ -0,0 +1,178 @@ +// +// Copyright 2012 Hakan Kjellerstrand +// +// 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.Linq; +using Google.OrTools.ConstraintSolver; + +public class NQueens +{ + /** + * + * Solves the Zebra problem. + * + * This is a port of the or-tools/Python model zebra.py + * + * + * """ + * This is the zebra problem as invented by Lewis Caroll. + * + * There are five houses. + * The Englishman lives in the red house. + * The Spaniard owns the dog. + * Coffee is drunk in the green house. + * The Ukrainian drinks tea. + * The green house is immediately to the right of the ivory house. + * The Old Gold smoker owns snails. + * Kools are smoked in the yellow house. + * Milk is drunk in the middle house. + * The Norwegian lives in the first house. + * The man who smokes Chesterfields lives in the house next to the man + * with the fox. + * Kools are smoked in the house next to the house where the horse is kept. + * The Lucky Strike smoker drinks orange juice. + * The Japanese smokes Parliaments. + * The Norwegian lives next to the blue house. + * + * Who owns a zebra and who drinks water? + * """ + * + */ + private static void Solve() + { + Solver solver = new Solver("Zebra"); + + int n = 5; + + // + // Decision variables + // + + // Colors + IntVar red = solver.MakeIntVar(1, n, "red"); + IntVar green = solver.MakeIntVar(1, n, "green"); + IntVar yellow = solver.MakeIntVar(1, n, "yellow"); + IntVar blue = solver.MakeIntVar(1, n, "blue"); + IntVar ivory = solver.MakeIntVar(1, n, "ivory"); + + // Nationality + IntVar englishman = solver.MakeIntVar(1, n, "englishman"); + IntVar spaniard = solver.MakeIntVar(1, n, "spaniard"); + IntVar japanese = solver.MakeIntVar(1, n, "japanese"); + IntVar ukrainian = solver.MakeIntVar(1, n, "ukrainian"); + IntVar norwegian = solver.MakeIntVar(1, n, "norwegian"); + + // Animal + IntVar dog = solver.MakeIntVar(1, n, "dog"); + IntVar snails = solver.MakeIntVar(1, n, "snails"); + IntVar fox = solver.MakeIntVar(1, n, "fox"); + IntVar zebra = solver.MakeIntVar(1, n, "zebra"); + IntVar horse = solver.MakeIntVar(1, n, "horse"); + + // Drink + IntVar tea = solver.MakeIntVar(1, n, "tea"); + IntVar coffee = solver.MakeIntVar(1, n, "coffee"); + IntVar water = solver.MakeIntVar(1, n, "water"); + IntVar milk = solver.MakeIntVar(1, n, "milk"); + IntVar fruit_juice = solver.MakeIntVar(1, n, "fruit juice"); + + // Smoke + IntVar old_gold = solver.MakeIntVar(1, n, "old gold"); + IntVar kools = solver.MakeIntVar(1, n, "kools"); + IntVar chesterfields = solver.MakeIntVar(1, n, "chesterfields"); + IntVar lucky_strike = solver.MakeIntVar(1, n, "lucky strike"); + IntVar parliaments = solver.MakeIntVar(1, n, "parliaments"); + + + // for search + IntVar[] all_vars = + {parliaments, kools, chesterfields, lucky_strike, old_gold, + englishman, spaniard, japanese, ukrainian, norwegian, + dog, snails, fox, zebra, horse, + tea, coffee, water, milk, fruit_juice, + red, green, yellow, blue, ivory}; + + // + // Constraints + // + + // Alldifferents + solver.Add(new IntVar[] + {red, green, yellow, blue, ivory}.AllDifferent()); + solver.Add(new IntVar[] + {englishman, spaniard, japanese, ukrainian, norwegian}.AllDifferent()); + solver.Add(new IntVar[] + {dog, snails, fox, zebra, horse}.AllDifferent()); + solver.Add(new IntVar[] + {tea, coffee, water, milk, fruit_juice}.AllDifferent()); + solver.Add(new IntVar[] + {parliaments, kools, chesterfields, lucky_strike, old_gold}.AllDifferent()); + + // + // The clues + // + solver.Add(englishman == red); + solver.Add(spaniard == dog); + solver.Add(coffee == green); + solver.Add(ukrainian == tea); + solver.Add(green == ivory + 1); + solver.Add(old_gold == snails); + solver.Add(kools == yellow); + solver.Add(milk == 3); + solver.Add(norwegian == 1); + solver.Add((fox - chesterfields).Abs() == 1); + solver.Add((horse - kools).Abs() == 1); + solver.Add(lucky_strike == fruit_juice); + solver.Add(japanese == parliaments); + solver.Add((norwegian - blue).Abs() == 1); + + + // + // Search + // + DecisionBuilder db = solver.MakePhase(all_vars, + Solver.INT_VAR_DEFAULT, + Solver.INT_VALUE_DEFAULT); + + solver.NewSearch(db); + + IntVar[] p = {englishman, spaniard, japanese, ukrainian, norwegian}; + int[] ix = {0,1,2,3,4}; + while (solver.NextSolution()) { + int water_drinker = (from i in ix + where p[i].Value() == water.Value() + select i).First(); + int zebra_owner = (from i in ix + where p[i].Value() == zebra.Value() + select i).First(); + Console.WriteLine("The {0} drinks water.", p[water_drinker].ToString()); + Console.WriteLine("The {0} owns the zebra", p[zebra_owner].ToString()); + + } + + Console.WriteLine("\nSolutions: {0}", solver.Solutions()); + Console.WriteLine("WallTime: {0}ms", solver.WallTime()); + Console.WriteLine("Failures: {0}", solver.Failures()); + Console.WriteLine("Branches: {0} ", solver.Branches()); + + solver.EndSearch(); + + } + + public static void Main(String[] args) + { + Solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/AllDifferentExcept0.java b/libs/or-tools-src-ubuntu/examples/java/AllDifferentExcept0.java new file mode 100644 index 0000000000000000000000000000000000000000..c351ed8d52b1fe465b4cf4ff3b6a2143627f56ce --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/AllDifferentExcept0.java @@ -0,0 +1,109 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class AllDifferentExcept0 { + + static { + System.loadLibrary("jniortools"); + } + + // + // alldifferent_except_0(solver, x) + // + // A decomposition of the global constraint + // alldifferent_except_0, i.e. all values + // must be either distinct, or 0. + // + public static void alldifferent_except_0(Solver solver, IntVar[] a) { + + int n = a.length; + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + IntVar bi = solver.makeIsDifferentCstVar(a[i], 0); + IntVar bj = solver.makeIsDifferentCstVar(a[j], 0); + IntVar bij = solver.makeIsDifferentCstVar(a[i], a[j]); + solver.addConstraint(solver.makeLessOrEqual(solver.makeProd(bi, bj).var(), bij)); + } + } + } + + /** + * Implements a (decomposition) of global constraint alldifferent_except_0. See + * http://www.hakank.org/google_or_tools/circuit.py + */ + private static void solve() { + + Solver solver = new Solver("AllDifferentExcept0"); + + // + // data + // + int n = 5; + + // + // variables + // + IntVar[] x = solver.makeIntVarArray(n, 0, n - 1, "x"); + + // + // constraints + // + alldifferent_except_0(solver, x); + + // we also require at least 2 0's + IntVar[] z_tmp = solver.makeBoolVarArray(n, "z_tmp"); + for (int i = 0; i < n; i++) { + solver.addConstraint(solver.makeIsEqualCstCt(x[i], 0, z_tmp[i])); + } + + IntVar z = solver.makeSum(z_tmp).var(); + solver.addConstraint(solver.makeEquality(z, 2)); + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + System.out.print("x: "); + for (int i = 0; i < n; i++) { + System.out.print(x[i].value() + " "); + } + System.out.println(" z: " + z.value()); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + AllDifferentExcept0.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/AllInterval.java b/libs/or-tools-src-ubuntu/examples/java/AllInterval.java new file mode 100644 index 0000000000000000000000000000000000000000..8d8e7e4e32fd83e17299f5577ca1034f390a66ba --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/AllInterval.java @@ -0,0 +1,98 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class AllInterval { + + static { + System.loadLibrary("jniortools"); + } + + /** + * Implements the all interval problem. See http://www.hakank.org/google_or_tools/all_interval.py + */ + private static void solve(int n) { + + Solver solver = new Solver("AllInterval"); + + // + // variables + // + IntVar[] x = solver.makeIntVarArray(n, 0, n - 1, "x"); + IntVar[] diffs = solver.makeIntVarArray(n - 1, 1, n - 1, "diffs"); + + // + // constraints + // + solver.addConstraint(solver.makeAllDifferent(x)); + solver.addConstraint(solver.makeAllDifferent(diffs)); + + for (int k = 0; k < n - 1; k++) { + solver.addConstraint( + solver.makeEquality( + diffs[k], solver.makeAbs(solver.makeDifference(x[k + 1], x[k])).var())); + } + + // symmetry breaking + solver.addConstraint(solver.makeLess(x[0], x[n - 1])); + solver.addConstraint(solver.makeLess(diffs[0], diffs[1])); + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); + + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + System.out.print("x : "); + for (int i = 0; i < n; i++) { + System.out.print(x[i].value() + " "); + } + System.out.print("\ndiffs: "); + + for (int i = 0; i < n - 1; i++) { + System.out.print(diffs[i].value() + " "); + } + System.out.println("\n"); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + + int n = 12; + if (args.length > 0) { + n = Integer.parseInt(args[0]); + } + + AllInterval.solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/AssignmentSat.java b/libs/or-tools-src-ubuntu/examples/java/AssignmentSat.java new file mode 100644 index 0000000000000000000000000000000000000000..081bafcb8190cf3f4903c91e5976982d80ef7741 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/AssignmentSat.java @@ -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. + +// CP-SAT example that solves an assignment problem. +// [START program] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.LinearExpr; +// [END import] + +/** Assignment problem. */ +public class AssignmentSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) { + // Data + // [START data_model] + int[][] costs = { + {90, 80, 75, 70}, + {35, 85, 55, 65}, + {125, 95, 90, 95}, + {45, 110, 95, 115}, + {50, 100, 90, 100}, + }; + final int numWorkers = costs.length; + final int numTasks = costs[0].length; + // [END data_model] + + // Model + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Variables + // [START variables] + IntVar[][] x = new IntVar[numWorkers][numTasks]; + // Variables in a 1-dim array. + IntVar[] xFlat = new IntVar[numWorkers * numTasks]; + int[] costsFlat = new int[numWorkers * numTasks]; + for (int i = 0; i < numWorkers; ++i) { + for (int j = 0; j < numTasks; ++j) { + x[i][j] = model.newIntVar(0, 1, ""); + int k = i * numTasks + j; + xFlat[k] = x[i][j]; + costsFlat[k] = costs[i][j]; + } + } + // [END variables] + + // Constraints + // [START constraints] + // Each worker is assigned to at most one task. + for (int i = 0; i < numWorkers; ++i) { + IntVar[] vars = new IntVar[numTasks]; + for (int j = 0; j < numTasks; ++j) { + vars[j] = x[i][j]; + } + model.addLessOrEqual(LinearExpr.sum(vars), 1); + } + // Each task is assigned to exactly one worker. + for (int j = 0; j < numTasks; ++j) { + // LinearExpr taskSum; + IntVar[] vars = new IntVar[numWorkers]; + for (int i = 0; i < numWorkers; ++i) { + vars[i] = x[i][j]; + } + model.addEquality(LinearExpr.sum(vars), 1); + } + // [END constraints] + + // Objective + // [START objective] + model.minimize(LinearExpr.scalProd(xFlat, costsFlat)); + // [END objective] + + // Solve + // [START solve] + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.solve(model); + // [END solve] + + // Print solution. + // [START print_solution] + // Check that the problem has a feasible solution. + if (status == CpSolverStatus.OPTIMAL || status == CpSolverStatus.FEASIBLE) { + System.out.println("Total cost: " + solver.objectiveValue() + "\n"); + for (int i = 0; i < numWorkers; ++i) { + for (int j = 0; j < numTasks; ++j) { + if (solver.value(x[i][j]) == 1) { + System.out.println( + "Worker " + i + " assigned to task " + j + ". Cost: " + costs[i][j]); + } + } + } + } else { + System.err.println("No solution found."); + } + // [END print_solution] + } + + private AssignmentSat() {} +} diff --git a/libs/or-tools-src-ubuntu/examples/java/BinPackingProblemSat.java b/libs/or-tools-src-ubuntu/examples/java/BinPackingProblemSat.java new file mode 100644 index 0000000000000000000000000000000000000000..52b0fa043faa7a37347d0d0897997d62e09eb1a7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/BinPackingProblemSat.java @@ -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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.LinearExpr; + +/** Solves a bin packing problem with the CP-SAT solver. */ +public class BinPackingProblemSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + // Data. + int binCapacity = 100; + int slackCapacity = 20; + int numBins = 5; + + int[][] items = new int[][] {{20, 6}, {15, 6}, {30, 4}, {45, 3}}; + int numItems = items.length; + + // Model. + CpModel model = new CpModel(); + + // Main variables. + IntVar[][] x = new IntVar[numItems][numBins]; + for (int i = 0; i < numItems; ++i) { + int numCopies = items[i][1]; + for (int b = 0; b < numBins; ++b) { + x[i][b] = model.newIntVar(0, numCopies, "x_" + i + "_" + b); + } + } + + // Load variables. + IntVar[] load = new IntVar[numBins]; + for (int b = 0; b < numBins; ++b) { + load[b] = model.newIntVar(0, binCapacity, "load_" + b); + } + + // Slack variables. + IntVar[] slacks = new IntVar[numBins]; + for (int b = 0; b < numBins; ++b) { + slacks[b] = model.newBoolVar("slack_" + b); + } + + // Links load and x. + int[] sizes = new int[numItems]; + for (int i = 0; i < numItems; ++i) { + sizes[i] = items[i][0]; + } + for (int b = 0; b < numBins; ++b) { + IntVar[] vars = new IntVar[numItems]; + for (int i = 0; i < numItems; ++i) { + vars[i] = x[i][b]; + } + model.addEquality(LinearExpr.scalProd(vars, sizes), load[b]); + } + + // Place all items. + for (int i = 0; i < numItems; ++i) { + IntVar[] vars = new IntVar[numBins]; + for (int b = 0; b < numBins; ++b) { + vars[b] = x[i][b]; + } + model.addEquality(LinearExpr.sum(vars), items[i][1]); + } + + // Links load and slack. + int safeCapacity = binCapacity - slackCapacity; + for (int b = 0; b < numBins; ++b) { + // slack[b] => load[b] <= safeCapacity. + model.addLessOrEqual(load[b], safeCapacity).onlyEnforceIf(slacks[b]); + // not(slack[b]) => load[b] > safeCapacity. + model.addGreaterOrEqual(load[b], safeCapacity + 1).onlyEnforceIf(slacks[b].not()); + } + + // Maximize sum of slacks. + model.maximize(LinearExpr.sum(slacks)); + + // Solves and prints out the solution. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.solve(model); + System.out.println("Solve status: " + status); + if (status == CpSolverStatus.OPTIMAL) { + System.out.printf("Optimal objective value: %f%n", solver.objectiveValue()); + for (int b = 0; b < numBins; ++b) { + System.out.printf("load_%d = %d%n", b, solver.value(load[b])); + for (int i = 0; i < numItems; ++i) { + System.out.printf(" item_%d_%d = %d%n", i, b, solver.value(x[i][b])); + } + } + } + System.out.println("Statistics"); + System.out.println(" - conflicts : " + solver.numConflicts()); + System.out.println(" - branches : " + solver.numBranches()); + System.out.println(" - wall time : " + solver.wallTime() + " s"); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/BoolOrSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/BoolOrSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..0d7f40170ff9ce70ab07d5f51333179b38b62c3d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/BoolOrSampleSat.java @@ -0,0 +1,32 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.Literal; + +/** Code sample to demonstrates a simple Boolean constraint. */ +public class BoolOrSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + CpModel model = new CpModel(); + IntVar x = model.newBoolVar("x"); + IntVar y = model.newBoolVar("y"); + model.addBoolOr(new Literal[] {x, y.not()}); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java b/libs/or-tools-src-ubuntu/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java new file mode 100644 index 0000000000000000000000000000000000000000..b897e5780090259275306773d4fd4f61abfe4c39 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/CapacitatedVehicleRoutingProblemWithTimeWindows.java @@ -0,0 +1,313 @@ +// +// 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. +package com.google.ortools.examples; + +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-src-ubuntu/examples/java/ChannelingSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/ChannelingSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..e8b67f364146c4a1a656e9ac2b982416de474586 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/ChannelingSampleSat.java @@ -0,0 +1,80 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.DecisionStrategyProto; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.LinearExpr; +import com.google.ortools.sat.SatParameters; + +/** Link integer constraints together. */ +public class ChannelingSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + // Create the CP-SAT model. + CpModel model = new CpModel(); + + // Declare our two primary variables. + IntVar x = model.newIntVar(0, 10, "x"); + IntVar y = model.newIntVar(0, 10, "y"); + + // Declare our intermediate boolean variable. + IntVar b = model.newBoolVar("b"); + + // Implement b == (x >= 5). + model.addGreaterOrEqual(x, 5).onlyEnforceIf(b); + model.addLessOrEqual(x, 4).onlyEnforceIf(b.not()); + + // Create our two half-reified constraints. + // First, b implies (y == 10 - x). + model.addEquality(LinearExpr.sum(new IntVar[] {x, y}), 10).onlyEnforceIf(b); + // Second, not(b) implies y == 0. + model.addEquality(y, 0).onlyEnforceIf(b.not()); + + // Search for x values in increasing order. + model.addDecisionStrategy(new IntVar[] {x}, + DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST, + DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE); + + // Create the solver. + CpSolver solver = new CpSolver(); + + // Force the solver to follow the decision strategy exactly. + solver.getParameters().setSearchBranching(SatParameters.SearchBranching.FIXED_SEARCH); + + // Solve the problem with the printer callback. + solver.searchAllSolutions(model, new CpSolverSolutionCallback() { + public CpSolverSolutionCallback init(IntVar[] variables) { + variableArray = variables; + return this; + } + + @Override + public void onSolutionCallback() { + for (IntVar v : variableArray) { + System.out.printf("%s=%d ", v.getName(), value(v)); + } + System.out.println(); + } + + private IntVar[] variableArray; + }.init(new IntVar[] {x, y, b})); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Circuit.java b/libs/or-tools-src-ubuntu/examples/java/Circuit.java new file mode 100644 index 0000000000000000000000000000000000000000..457477b2348c1fe05b0eba013b119f58cc55dca0 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Circuit.java @@ -0,0 +1,110 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class Circuit { + + static { + System.loadLibrary("jniortools"); + } + + /** + * circuit(solver, x) + * + *

A decomposition of the global constraint circuit, based on some observation of the orbits in + * an array. + * + *

Note: The domain of x must be 0..n-1 (not 1..n) since Java is 0-based. + */ + public static void circuit(Solver solver, IntVar[] x) { + + int n = x.length; + IntVar[] z = solver.makeIntVarArray(n, 0, n - 1, "z"); + + solver.addConstraint(solver.makeAllDifferent(x)); + solver.addConstraint(solver.makeAllDifferent(z)); + + // put the orbit of x[0] in z[0..n-1] + solver.addConstraint(solver.makeEquality(z[0], x[0])); + for (int i = 1; i < n - 1; i++) { + solver.addConstraint(solver.makeEquality(z[i], solver.makeElement(x, z[i - 1]).var())); + } + + // z may not be 0 for i < n-1 + for (int i = 1; i < n - 1; i++) { + solver.addConstraint(solver.makeNonEquality(z[i], 0)); + } + + // when i = n-1 it must be 0 + solver.addConstraint(solver.makeEquality(z[n - 1], 0)); + } + + /** + * Implements a (decomposition) of the global constraint circuit. See + * http://www.hakank.org/google_or_tools/circuit.py + */ + private static void solve(int n) { + + Solver solver = new Solver("Circuit"); + + // + // variables + // + IntVar[] x = solver.makeIntVarArray(n, 0, n - 1, "x"); + + // + // constraints + // + circuit(solver, x); + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + for (int i = 0; i < n; i++) { + System.out.print(x[i].value() + " "); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + + int n = 5; + if (args.length > 0) { + n = Integer.parseInt(args[0]); + } + Circuit.solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/CoinsGrid.java b/libs/or-tools-src-ubuntu/examples/java/CoinsGrid.java new file mode 100644 index 0000000000000000000000000000000000000000..6c0ee23ce830b0f7cbf7f3eab2a081a4f2f60a92 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/CoinsGrid.java @@ -0,0 +1,116 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class CoinsGrid { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves the Coins Grid problem. See http://www.hakank.org/google_or_tools/coins_grid.py */ + private static void solve() { + Solver solver = new Solver("CoinsGrid"); + + // + // data + // + int n = 31; + int c = 14; + + // + // variables + // + IntVar[][] x = new IntVar[n][n]; + IntVar[] x_flat = new IntVar[n * n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + x[i][j] = solver.makeIntVar(0, 1, "x[" + i + "," + j + "]"); + x_flat[i * n + j] = x[i][j]; + } + } + + // + // constraints + // + + // sum row/columns == c + for (int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + IntVar[] col = new IntVar[n]; + for (int j = 0; j < n; j++) { + row[j] = x[i][j]; + col[j] = x[j][i]; + } + solver.addConstraint(solver.makeSumEquality(row, c)); + solver.addConstraint(solver.makeSumEquality(col, c)); + } + + // quadratic horizonal distance + IntVar[] obj_tmp = new IntVar[n * n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + obj_tmp[i * n + j] = solver.makeProd(x[i][j], (i - j) * (i - j)).var(); + } + } + IntVar obj_var = solver.makeSum(obj_tmp).var(); + + // + // objective + // + OptimizeVar obj = solver.makeMinimize(obj_var, 1); + + // + // search + // + DecisionBuilder db = + solver.makePhase(x_flat, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MAX_VALUE); + + solver.newSearch(db, obj); + + // + // output + // + while (solver.nextSolution()) { + System.out.println("obj_var: " + obj_var.value()); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + System.out.print(x[i][j].value() + " "); + } + System.out.println(); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + CoinsGrid.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/CoveringOpl.java b/libs/or-tools-src-ubuntu/examples/java/CoveringOpl.java new file mode 100644 index 0000000000000000000000000000000000000000..1c98a501f9d3f5ad4f8f760ef24e4ef5f0c6383d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/CoveringOpl.java @@ -0,0 +1,125 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class CoveringOpl { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves a set covering problem. See http://www.hakank.org/google_or_tools/covering_opl.py */ + private static void solve() { + + Solver solver = new Solver("CoveringOpl"); + + // + // data + // + int num_workers = 32; + int num_tasks = 15; + + // Which worker is qualified for each task. + // Note: This is 1-based and will be made 0-base below. + int[][] qualified = { + {1, 9, 19, 22, 25, 28, 31}, + {2, 12, 15, 19, 21, 23, 27, 29, 30, 31, 32}, + {3, 10, 19, 24, 26, 30, 32}, + {4, 21, 25, 28, 32}, + {5, 11, 16, 22, 23, 27, 31}, + {6, 20, 24, 26, 30, 32}, + {7, 12, 17, 25, 30, 31}, + {8, 17, 20, 22, 23}, + {9, 13, 14, 26, 29, 30, 31}, + {10, 21, 25, 31, 32}, + {14, 15, 18, 23, 24, 27, 30, 32}, + {18, 19, 22, 24, 26, 29, 31}, + {11, 20, 25, 28, 30, 32}, + {16, 19, 23, 31}, + {9, 18, 26, 28, 31, 32} + }; + + int[] cost = { + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9 + }; + + // + // variables + // + IntVar[] hire = solver.makeIntVarArray(num_workers, 0, 1, "workers"); + IntVar total_cost = solver.makeScalProd(hire, cost).var(); + + // + // constraints + // + for (int j = 0; j < num_tasks; j++) { + // Sum the cost for hiring the qualified workers + // (also, make 0-base). + int len = qualified[j].length; + IntVar[] tmp = new IntVar[len]; + for (int c = 0; c < len; c++) { + tmp[c] = hire[qualified[j][c] - 1]; + } + IntVar b = solver.makeSum(tmp).var(); + solver.addConstraint(solver.makeGreaterOrEqual(b, 1)); + } + + // Objective: Minimize total cost + OptimizeVar objective = solver.makeMinimize(total_cost, 1); + + // + // search + // + DecisionBuilder db = + solver.makePhase(hire, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); + + solver.newSearch(db, objective); + + // + // output + // + while (solver.nextSolution()) { + System.out.println("Cost: " + total_cost.value()); + System.out.print("Hire: "); + for (int i = 0; i < num_workers; i++) { + if (hire[i].value() == 1) { + System.out.print(i + " "); + } + } + System.out.println("\n"); + } + + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + + CoveringOpl.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/CpIsFunSat.java b/libs/or-tools-src-ubuntu/examples/java/CpIsFunSat.java new file mode 100644 index 0000000000000000000000000000000000000000..0938e02e39447ee37173f6aea7019d3ccfec969a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/CpIsFunSat.java @@ -0,0 +1,99 @@ +// 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] +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.LinearExpr; + +/** Cryptarithmetic puzzle. */ +public class CpIsFunSat { + static { + System.loadLibrary("jniortools"); + } + + // [START solution_printing] + static class VarArraySolutionPrinter extends CpSolverSolutionCallback { + public VarArraySolutionPrinter(IntVar[] variables) { + variableArray = variables; + } + + @Override + public void onSolutionCallback() { + for (IntVar v : variableArray) { + System.out.printf(" %s = %d", v.getName(), value(v)); + } + System.out.println(); + solutionCount++; + } + + public int getSolutionCount() { + return solutionCount; + } + + private int solutionCount; + private final IntVar[] variableArray; + } + // [END solution_printing] + + public static void main(String[] args) throws Exception { + // Create the model. + CpModel model = new CpModel(); + + // [START variables] + int base = 10; + IntVar c = model.newIntVar(1, base - 1, "C"); + IntVar p = model.newIntVar(0, base - 1, "P"); + IntVar i = model.newIntVar(1, base - 1, "I"); + IntVar s = model.newIntVar(0, base - 1, "S"); + IntVar f = model.newIntVar(1, base - 1, "F"); + IntVar u = model.newIntVar(0, base - 1, "U"); + IntVar n = model.newIntVar(0, base - 1, "N"); + IntVar t = model.newIntVar(1, base - 1, "T"); + IntVar r = model.newIntVar(0, base - 1, "R"); + IntVar e = model.newIntVar(0, base - 1, "E"); + + // We need to group variables in a list to use the constraint AllDifferent. + IntVar[] letters = new IntVar[] {c, p, i, s, f, u, n, t, r, e}; + // [END variables] + + // [START constraints] + // Define constraints. + model.addAllDifferent(letters); + + // CP + IS + FUN = TRUE + model.addEquality(LinearExpr.scalProd(new IntVar[] {c, p, i, s, f, u, n, t, r, u, e}, + new long[] {base, 1, base, 1, base * base, base, 1, -base * base * base, + -base * base, -base, -1}), + 0); + // [END constraints] + + // [START solve] + // Create a solver and solve the model. + CpSolver solver = new CpSolver(); + VarArraySolutionPrinter cb = new VarArraySolutionPrinter(letters); + solver.searchAllSolutions(model, cb); + // [END solve] + + System.out.println("Statistics"); + System.out.println(" - conflicts : " + solver.numConflicts()); + System.out.println(" - branches : " + solver.numBranches()); + System.out.println(" - wall time : " + solver.wallTime() + " s"); + System.out.println(" - solutions : " + cb.getSolutionCount()); + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/Crossword.java b/libs/or-tools-src-ubuntu/examples/java/Crossword.java new file mode 100644 index 0000000000000000000000000000000000000000..212b068256f54570726769f82aea20d44cf8d224 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Crossword.java @@ -0,0 +1,202 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class Crossword { + + static { + System.loadLibrary("jniortools"); + } + + /** Solving a simple crossword. See http://www.hakank.org/google_or_tools/crossword2.py */ + private static void solve() { + + Solver solver = new Solver("Crossword"); + + // + // data + // + String[] alpha = { + "_", "a", "b", "c", "d", "e", "f", + "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", + "u", "v", "w", "x", "y", "z" + }; + + int a = 1; + int b = 2; + int c = 3; + int d = 4; + int e = 5; + int f = 6; + int g = 7; + int h = 8; + int i = 9; + int j = 10; + int k = 11; + int l = 12; + int m = 13; + int n = 14; + int o = 15; + int p = 16; + int q = 17; + int r = 18; + int s = 19; + int t = 20; + int u = 21; + int v = 22; + int w = 23; + int x = 24; + int y = 25; + int z = 26; + + int num_words = 15; + int word_len = 5; + + int[][] AA = { + {h, o, s, e, s}, // HOSES + {l, a, s, e, r}, // LASER + {s, a, i, l, s}, // SAILS + {s, h, e, e, t}, // SHEET + {s, t, e, e, r}, // STEER + {h, e, e, l, 0}, // HEEL + {h, i, k, e, 0}, // HIKE + {k, e, e, l, 0}, // KEEL + {k, n, o, t, 0}, // KNOT + {l, i, n, e, 0}, // LINE + {a, f, t, 0, 0}, // AFT + {a, l, e, 0, 0}, // ALE + {e, e, l, 0, 0}, // EEL + {l, e, e, 0, 0}, // LEE + {t, i, e, 0, 0} + }; // TIE + + int num_overlapping = 12; + int[][] overlapping = { + {0, 2, 1, 0}, // s + {0, 4, 2, 0}, // s + {3, 1, 1, 2}, // i + {3, 2, 4, 0}, // k + {3, 3, 2, 2}, // e + {6, 0, 1, 3}, // l + {6, 1, 4, 1}, // e + {6, 2, 2, 3}, // e + {7, 0, 5, 1}, // l + {7, 2, 1, 4}, // s + {7, 3, 4, 2}, // e + {7, 4, 2, 4} + }; // r + + int N = 8; + + // + // variables + // + IntVar[][] A = new IntVar[num_words][word_len]; + IntVar[] A_flat = new IntVar[num_words * word_len]; + // for labeling on A and E + IntVar[] all = new IntVar[(num_words * word_len) + N]; + + for (int I = 0; I < num_words; I++) { + for (int J = 0; J < word_len; J++) { + A[I][J] = solver.makeIntVar(0, 26, "A[" + I + "," + J + "]"); + A_flat[I * word_len + J] = A[I][J]; + all[I * word_len + J] = A[I][J]; + } + } + + IntVar[] E = solver.makeIntVarArray(N, 0, num_words, "E"); + for (int I = 0; I < N; I++) { + all[num_words * word_len + I] = E[I]; + } + + // + // constraints + // + solver.addConstraint(solver.makeAllDifferent(E)); + + for (int I = 0; I < num_words; I++) { + for (int J = 0; J < word_len; J++) { + solver.addConstraint(solver.makeEquality(A[I][J], AA[I][J])); + } + } + + for (int I = 0; I < num_overlapping; I++) { + solver.addConstraint( + solver.makeEquality( + solver + .makeElement( + A_flat, + solver + .makeSum( + solver.makeProd(E[overlapping[I][0]], word_len).var(), + overlapping[I][1]) + .var()) + .var(), + solver + .makeElement( + A_flat, + solver + .makeSum( + solver.makeProd(E[overlapping[I][2]], word_len).var(), + overlapping[I][3]) + .var()) + .var())); + } + + // + // search + // + DecisionBuilder db = solver.makePhase(all, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + System.out.println("E:"); + for (int ee = 0; ee < N; ee++) { + int e_val = (int) E[ee].value(); + System.out.print(ee + ": (" + e_val + ") "); + for (int ii = 0; ii < word_len; ii++) { + System.out.print(alpha[(int) A[ee][ii].value()]); + } + System.out.println(); + } + + System.out.println(); + } + + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + + Crossword.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/DeBruijn.java b/libs/or-tools-src-ubuntu/examples/java/DeBruijn.java new file mode 100644 index 0000000000000000000000000000000000000000..55a45846625560713ad5862cb1f22cfe482ab5f1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/DeBruijn.java @@ -0,0 +1,210 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class DeBruijn { + + static { + System.loadLibrary("jniortools"); + } + + /** + * toNum(solver, a, num, base) + * + *

channelling between the array a and the number num + */ + private static void toNum(Solver solver, IntVar[] a, IntVar num, int base) { + int len = a.length; + + IntVar[] tmp = new IntVar[len]; + for (int i = 0; i < len; i++) { + tmp[i] = solver.makeProd(a[i], (int) Math.pow(base, (len - i - 1))).var(); + } + solver.addConstraint(solver.makeEquality(solver.makeSum(tmp).var(), num)); + } + + /** + * Implements "arbitrary" de Bruijn sequences. See + * http://www.hakank.org/google_or_tools/debruijn_binary.py + */ + private static void solve(int base, int n, int m) { + + Solver solver = new Solver("DeBruijn"); + + System.out.println("base: " + base + " n: " + n + " m: " + m); + + // Ensure that the number of each digit in bin_code is + // the same. Nice feature, but it can slow things down... + boolean check_same_gcc = false; // true; + + // + // variables + // + IntVar[] x = solver.makeIntVarArray(m, 0, (int) Math.pow(base, n) - 1, "x"); + + IntVar[][] binary = new IntVar[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + binary[i][j] = solver.makeIntVar(0, base - 1, "binary[" + i + "," + j + "]"); + } + } + + // this is the de Bruijn sequence + IntVar[] bin_code = solver.makeIntVarArray(m, 0, base - 1, "bin_code"); + + // occurences of each number in bin_code + IntVar[] gcc = solver.makeIntVarArray(base, 0, m, "gcc"); + + // for the branching + IntVar[] all = new IntVar[2 * m + base]; + for (int i = 0; i < m; i++) { + all[i] = x[i]; + all[m + i] = bin_code[i]; + } + for (int i = 0; i < base; i++) { + all[2 * m + i] = gcc[i]; + } + + // + // constraints + // + solver.addConstraint(solver.makeAllDifferent(x)); + + // converts x <-> binary + for (int i = 0; i < m; i++) { + IntVar[] t = new IntVar[n]; + for (int j = 0; j < n; j++) { + t[j] = binary[i][j]; + } + toNum(solver, t, x[i], base); + } + + // the de Bruijn condition: + // the first elements in binary[i] is the same as the last + // elements in binary[i-1] + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + solver.addConstraint(solver.makeEquality(binary[i - 1][j], binary[i][j - 1])); + } + } + + // ... and around the corner + for (int j = 1; j < n; j++) { + solver.addConstraint(solver.makeEquality(binary[m - 1][j], binary[0][j - 1])); + } + + // converts binary -> bin_code (de Bruijn sequence) + for (int i = 0; i < m; i++) { + solver.addConstraint(solver.makeEquality(bin_code[i], binary[i][0])); + } + + // extra: ensure that all the numbers in the de Bruijn sequence + // (bin_code) has the same occurrences (if check_same_gcc is True + // and mathematically possible) + solver.addConstraint(solver.makeDistribute(bin_code, gcc)); + if (check_same_gcc && m % base == 0) { + for (int i = 1; i < base; i++) { + solver.addConstraint(solver.makeEquality(gcc[i], gcc[i - 1])); + } + } + + // symmetry breaking: + // the minimum value of x should be first + solver.addConstraint(solver.makeEquality(x[0], solver.makeMin(x).var())); + + // + // search + // + DecisionBuilder db = + solver.makePhase(all, solver.CHOOSE_MIN_SIZE_LOWEST_MAX, solver.ASSIGN_MIN_VALUE); + + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + System.out.print("x: "); + for (int i = 0; i < m; i++) { + System.out.print(x[i].value() + " "); + } + + System.out.print("\nde Bruijn sequence:"); + for (int i = 0; i < m; i++) { + System.out.print(bin_code[i].value() + " "); + } + + System.out.print("\ngcc: "); + for (int i = 0; i < base; i++) { + System.out.print(gcc[i].value() + " "); + } + System.out.println("\n"); + + // for debugging etc: show the full binary table + /* + System.out.println("binary:"); + for(int i = 0; i < m; i++) { + for(int j = 0; j < n; j++) { + System.out.print(binary[i][j].value() + " "); + } + System.out.println(); + } + System.out.println(); + */ + + } + + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + + int base = 2; + int n = 3; + int m = 8; + + if (args.length > 0) { + base = Integer.parseInt(args[0]); + } + + if (args.length > 1) { + n = Integer.parseInt(args[1]); + m = (int) Math.pow(base, n); + } + + if (args.length > 2) { + int m_max = (int) Math.pow(base, n); + m = Integer.parseInt(args[2]); + if (m > m_max) { + System.out.println("m(" + m + ") is too large. Set m to " + m_max + "."); + m = m_max; + } + } + + DeBruijn.solve(base, n, m); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Diet.java b/libs/or-tools-src-ubuntu/examples/java/Diet.java new file mode 100644 index 0000000000000000000000000000000000000000..7cab09118725674f09a6dd6413981d102ce654ab --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Diet.java @@ -0,0 +1,95 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class Diet { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves the Diet problem. See http://www.hakank.org/google_or_tools/diet1.py */ + private static void solve() { + + Solver solver = new Solver("Diet"); + + int n = 4; + int[] price = {50, 20, 30, 80}; // in cents + + // requirements for each nutrition type + int[] limits = {500, 6, 10, 8}; + + // nutritions for each product + int[] calories = {400, 200, 150, 500}; + int[] chocolate = {3, 2, 0, 0}; + int[] sugar = {2, 2, 4, 4}; + int[] fat = {2, 4, 1, 5}; + + // + // Variables + // + IntVar[] x = solver.makeIntVarArray(n, 0, 100, "x"); + + IntVar cost = solver.makeScalProd(x, price).var(); + + // + // Constraints + // + solver.addConstraint(solver.makeScalProdGreaterOrEqual(x, calories, limits[0])); + + solver.addConstraint(solver.makeScalProdGreaterOrEqual(x, chocolate, limits[1])); + + solver.addConstraint(solver.makeScalProdGreaterOrEqual(x, sugar, limits[2])); + + solver.addConstraint(solver.makeScalProdGreaterOrEqual(x, fat, limits[3])); + + // + // Objective + // + OptimizeVar obj = solver.makeMinimize(cost, 1); + + // + // Search + // + DecisionBuilder db = solver.makePhase(x, solver.CHOOSE_PATH, solver.ASSIGN_MIN_VALUE); + solver.newSearch(db, obj); + while (solver.nextSolution()) { + System.out.println("cost: " + cost.value()); + System.out.print("x: "); + for (int i = 0; i < n; i++) { + System.out.print(x[i].value() + " "); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + Diet.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/DietMIP.java b/libs/or-tools-src-ubuntu/examples/java/DietMIP.java new file mode 100755 index 0000000000000000000000000000000000000000..843c980cbaaf02e0a5323e9f7d8d76d66d19ceb9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/DietMIP.java @@ -0,0 +1,93 @@ + +/* + * Copyright 2017 Darian Sastre darian.sastre@minimaxlabs.com + * 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 was created by Hakan Kjellerstrand (hakank@gmail.com) + */ +package com.google.ortools.examples; + +import com.google.ortools.linearsolver.MPConstraint; +import com.google.ortools.linearsolver.MPObjective; +import com.google.ortools.linearsolver.MPSolver; +import com.google.ortools.linearsolver.MPVariable; + +public class DietMIP { + static { + System.loadLibrary("jniortools"); + } + + private static MPSolver createSolver(String solverType) { + try { + return new MPSolver("MIPDiet", MPSolver.OptimizationProblemType.valueOf(solverType)); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + return null; + } + } + + private static void solve(String solverType) { + MPSolver solver = createSolver(solverType); + double infinity = MPSolver.infinity(); + + int n = 4; // variables number + int m = 4; // constraints number + + int[] price = { 50, 20, 30, 80 }; + + int[] limits = { 500, 6, 10, 8 }; + + int[] calories = { 400, 200, 150, 500 }; + int[] chocolate = { 3, 2, 0, 0 }; + int[] sugar = { 2, 2, 4, 4 }; + int[] fat = { 2, 4, 1, 5 }; + + int[][] values = { calories, chocolate, sugar, fat }; + + MPVariable[] x = solver.makeIntVarArray(n, 0, 100, "x"); + MPObjective objective = solver.objective(); + MPConstraint[] targets = new MPConstraint[4]; + + for (int i = 0; i < n; i++) { + objective.setCoefficient(x[i], price[i]); + + // constraints + targets[i] = solver.makeConstraint(limits[i], infinity); + for (int j = 0; j < m; j++) { + targets[i].setCoefficient(x[j], values[i][j]); + } + } + + final MPSolver.ResultStatus resultStatus = solver.solve(); + + /** printing */ + if (resultStatus != MPSolver.ResultStatus.OPTIMAL) { + System.err.println("The problem does not have an optimal solution!"); + return; + } else { + System.out.println("Optimal objective value = " + solver.objective().value()); + + System.out.print("Item quantities: "); + System.out.print((int) x[0].solutionValue() + " "); + System.out.print((int) x[1].solutionValue() + " "); + System.out.print((int) x[2].solutionValue() + " "); + System.out.print((int) x[3].solutionValue() + " "); + } + } + + public static void main(String[] args) throws Exception { + solve("CBC_MIXED_INTEGER_PROGRAMMING"); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/DivisibleBy9Through1.java b/libs/or-tools-src-ubuntu/examples/java/DivisibleBy9Through1.java new file mode 100644 index 0000000000000000000000000000000000000000..f8979a795eed309b3d1cd1f2b839933b0e8b1851 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/DivisibleBy9Through1.java @@ -0,0 +1,190 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class DivisibleBy9Through1 { + + static { + System.loadLibrary("jniortools"); + } + + /** + * A simple propagator for modulo constraint. + * + *

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

channelling between the array a and the number num + */ + private static void toNum(Solver solver, IntVar[] a, IntVar num, int base) { + int len = a.length; + + IntVar[] tmp = new IntVar[len]; + for (int i = 0; i < len; i++) { + tmp[i] = solver.makeProd(a[i], (int) Math.pow(base, (len - i - 1))).var(); + } + solver.addConstraint(solver.makeEquality(solver.makeSum(tmp).var(), num)); + } + + /** + * Solves the divisible by 9 through 1 problem. See + * http://www.hakank.org/google_or_tools/divisible_by_9_through_1.py + */ + private static void solve(int base) { + + Solver solver = new Solver("DivisibleBy9Through1"); + + // + // data + // + int m = (int) Math.pow(base, (base - 1)) - 1; + int n = base - 1; + + String[] digits_str = {"_", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; + + System.out.println("base: " + base); + + // + // variables + // + + // digits + IntVar[] x = solver.makeIntVarArray(n, 1, base - 1, "x"); + + // the numbers. t[0] contains the answe + IntVar[] t = solver.makeIntVarArray(n, 0, m, "t"); + + // + // constraints + // + solver.addConstraint(solver.makeAllDifferent(x)); + + // Ensure the divisibility of base .. 1 + IntVar zero = solver.makeIntConst(0); + for (int i = 0; i < n; i++) { + int mm = base - i - 1; + IntVar[] tt = new IntVar[mm]; + for (int j = 0; j < mm; j++) { + tt[j] = x[j]; + } + toNum(solver, tt, t[i], base); + IntVar mm_const = solver.makeIntConst(mm); + my_mod(solver, t[i], mm_const, zero); + } + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + System.out.print("x: "); + for (int i = 0; i < n; i++) { + System.out.print(x[i].value() + " "); + } + System.out.println("\nt: "); + for (int i = 0; i < n; i++) { + System.out.print(t[i].value() + " "); + } + System.out.println(); + + if (base != 10) { + System.out.print("Number base 10: " + t[0].value()); + System.out.print(" Base " + base + ": "); + for (int i = 0; i < n; i++) { + System.out.print(digits_str[(int) x[i].value() + 1]); + } + System.out.println("\n"); + } + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + + int base = 10; + + if (args.length > 0) { + int new_base = Integer.parseInt(args[0]); + if (new_base > 10) { + // Note: The next valid base after 10 is 14 and + // the number 559922224824157, which is too large in this model. + System.out.println("Sorry, max allowed base is 10. Setting base to 10."); + } else if (new_base < 2) { + System.out.println("Sorry, min allowed base is 2. Setting base to 2."); + base = 2; + } else { + base = new_base; + } + } + + DivisibleBy9Through1.solve(base); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/EarlinessTardinessCostSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/EarlinessTardinessCostSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..42c608cfacef1858d91876ca250048dc20a0c2e8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/EarlinessTardinessCostSampleSat.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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.DecisionStrategyProto; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.LinearExpr; +import com.google.ortools.sat.SatParameters; + +/** Encode the piecewise linear expression. */ +public class EarlinessTardinessCostSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + long earlinessDate = 5; + long earlinessCost = 8; + long latenessDate = 15; + long latenessCost = 12; + + // Create the CP-SAT model. + CpModel model = new CpModel(); + + // Declare our primary variable. + IntVar x = model.newIntVar(0, 20, "x"); + + // Create the expression variable and implement the piecewise linear function. + // + // \ / + // \______/ + // ed ld + // + long largeConstant = 1000; + IntVar expr = model.newIntVar(0, largeConstant, "expr"); + + // First segment: s1 == earlinessCost * (earlinessDate - x). + IntVar s1 = model.newIntVar(-largeConstant, largeConstant, "s1"); + model.addEquality(LinearExpr.scalProd(new IntVar[] {s1, x}, new long[] {1, earlinessCost}), + earlinessCost * earlinessDate); + + // Second segment. + IntVar s2 = model.newConstant(0); + + // Third segment: s3 == latenessCost * (x - latenessDate). + IntVar s3 = model.newIntVar(-largeConstant, largeConstant, "s3"); + model.addEquality(LinearExpr.scalProd(new IntVar[] {s3, x}, new long[] {1, -latenessCost}), + -latenessCost * latenessDate); + + // Link together expr and x through s1, s2, and s3. + model.addMaxEquality(expr, new IntVar[] {s1, s2, s3}); + + // Search for x values in increasing order. + model.addDecisionStrategy(new IntVar[] {x}, + DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST, + DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE); + + // Create the solver. + CpSolver solver = new CpSolver(); + + // Force the solver to follow the decision strategy exactly. + solver.getParameters().setSearchBranching(SatParameters.SearchBranching.FIXED_SEARCH); + + // Solve the problem with the printer callback. + solver.searchAllSolutions(model, new CpSolverSolutionCallback() { + public CpSolverSolutionCallback init(IntVar[] variables) { + variableArray = variables; + return this; + } + + @Override + public void onSolutionCallback() { + for (IntVar v : variableArray) { + System.out.printf("%s=%d ", v.getName(), value(v)); + } + System.out.println(); + } + + private IntVar[] variableArray; + }.init(new IntVar[] {x, expr})); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/FlowExample.java b/libs/or-tools-src-ubuntu/examples/java/FlowExample.java new file mode 100644 index 0000000000000000000000000000000000000000..5befc084acbce6becafa65f1800d089e9949b97b --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/FlowExample.java @@ -0,0 +1,89 @@ +// 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. +package com.google.ortools.examples; + +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-src-ubuntu/examples/java/GolombRuler.java b/libs/or-tools-src-ubuntu/examples/java/GolombRuler.java new file mode 100644 index 0000000000000000000000000000000000000000..5fc39e4b82556766406431251fba9af7fbdbd545 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/GolombRuler.java @@ -0,0 +1,88 @@ +/** + * Copyright (c) 1999-2011, Ecole des Mines de Nantes All rights reserved. Redistribution and use in + * source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: + * + *

* Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * Neither the name of the Ecole des Mines + * de Nantes nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + *

THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.SearchMonitor; +import com.google.ortools.constraintsolver.SolutionCollector; +import com.google.ortools.constraintsolver.Solver; + +/** + * Golomb ruler problem
+ * + * @author Charles Prud'homme + * @since 17/03/11 + */ +public class GolombRuler { + static { + System.loadLibrary("jniortools"); + } + + /** Golomb Ruler Problem. */ + private static void solve(int m) { + Solver solver = new Solver("GR " + m); + + IntVar[] ticks = solver.makeIntVarArray(m, 0, ((m < 31) ? (1 << (m + 1)) - 1 : 9999), "ticks"); + + solver.addConstraint(solver.makeEquality(ticks[0], 0)); + + for (int i = 0; i < ticks.length - 1; i++) { + solver.addConstraint(solver.makeLess(ticks[i], ticks[i + 1])); + } + + IntVar[] diff = new IntVar[(m * m - m) / 2]; + + for (int k = 0, i = 0; i < m - 1; i++) { + for (int j = i + 1; j < m; j++, k++) { + diff[k] = solver.makeDifference(ticks[j], ticks[i]).var(); + solver.addConstraint(solver.makeGreaterOrEqual(diff[k], (j - i) * (j - i + 1) / 2)); + } + } + + solver.addConstraint(solver.makeAllDifferent(diff)); + + // break symetries + if (m > 2) { + solver.addConstraint(solver.makeLess(diff[0], diff[diff.length - 1])); + } + + OptimizeVar opt = solver.makeMinimize(ticks[m - 1], 1); + DecisionBuilder db = + solver.makePhase(ticks, solver.CHOOSE_MIN_SIZE_LOWEST_MIN, solver.ASSIGN_MIN_VALUE); + SolutionCollector collector = solver.makeLastSolutionCollector(); + collector.add(ticks); + collector.addObjective(ticks[m - 1]); + SearchMonitor log = solver.makeSearchLog(10000, opt); + solver.solve(db, opt, log, collector); + System.out.println("Optimal solution = " + collector.objectiveValue(0)); + for (int i = 0; i < m; ++i) { + System.out.print("[" + collector.value(0, ticks[i]) + "] "); + } + System.out.println(); + } + + public static void main(String[] args) throws Exception { + GolombRuler.solve(8); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/IntervalSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/IntervalSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..e3c3ca4f47dc4aaf2c45121b1208b1741f0c498f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/IntervalSampleSat.java @@ -0,0 +1,37 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.IntervalVar; + +/** Code sample to demonstrates how to build an interval. */ +public class IntervalSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + CpModel model = new CpModel(); + int horizon = 100; + IntVar startVar = model.newIntVar(0, horizon, "start"); + IntVar endVar = model.newIntVar(0, horizon, "end"); + // Java code supports IntVar or integer constants in intervals. + int duration = 10; + IntervalVar interval = model.newIntervalVar(startVar, duration, endVar, "interval"); + + System.out.println(interval); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Issue173.java b/libs/or-tools-src-ubuntu/examples/java/Issue173.java new file mode 100644 index 0000000000000000000000000000000000000000..77e858a040c1c7f59a306ac0071f089188646775 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Issue173.java @@ -0,0 +1,39 @@ +package com.google.ortools.examples; + +import com.google.ortools.linearsolver.MPConstraint; +import com.google.ortools.linearsolver.MPObjective; +import com.google.ortools.linearsolver.MPSolver; +import com.google.ortools.linearsolver.MPVariable; + +public class Issue173 { + + static { + System.loadLibrary("jniortools"); + } + + public static void breakit() { + + for (int i = 0; i < 50000; i++) { + solveLP(); + } + } + + private static void solveLP() { + MPSolver solver = + new MPSolver("test", MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); + MPVariable x = solver.makeNumVar(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, "x"); + + final MPObjective objective = solver.objective(); + objective.setMaximization(); + objective.setCoefficient(x, 1); + + MPConstraint constraint = solver.makeConstraint(0, 5); + constraint.setCoefficient(x, 1); + + solver.solve(); + } + + public static void main(String[] args) throws Exception { + breakit(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Knapsack.java b/libs/or-tools-src-ubuntu/examples/java/Knapsack.java new file mode 100644 index 0000000000000000000000000000000000000000..abd36718983e8dd6dc84c1c3fce668edfede572b --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Knapsack.java @@ -0,0 +1,76 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.algorithms.KnapsackSolver; +import java.util.ArrayList; +// [END import] + +/** + * Sample showing how to model using the knapsack solver. + * + */ +public class Knapsack { + static { + System.loadLibrary("jniortools"); + } + private Knapsack() {} + + private static void solve() { + // [START solver] + KnapsackSolver solver = new KnapsackSolver( + KnapsackSolver.SolverType.KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, "test"); + // [END solver] + + // [START data] + final long[] 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}; + + final long[][] weights = {{7, 0, 30, 22, 80, 94, 11, 81, 70, 64, 59, 18, 0, 36, 3, 8, 15, 42, 9, + 0, 42, 47, 52, 32, 26, 48, 55, 6, 29, 84, 2, 4, 18, 56, 7, 29, 93, 44, 71, 3, 86, 66, 31, + 65, 0, 79, 20, 65, 52, 13}}; + + final long[] capacities = {850}; + // [END data] + + // [START solve] + solver.init(values, weights, capacities); + final long computedValue = solver.solve(); + // [END solve] + + // [START print_solution] + ArrayList packedItems = new ArrayList<>(); + ArrayList packedWeights = new ArrayList<>(); + int totalWeight = 0; + System.out.println("Total value = " + computedValue); + for (int i = 0; i < values.length; i++) { + if (solver.bestSolutionContains(i)) { + packedItems.add(i); + packedWeights.add(weights[0][i]); + totalWeight = (int) (totalWeight + weights[0][i]); + } + } + System.out.println("Total weight: " + totalWeight); + System.out.println("Packed items: " + packedItems); + System.out.println("Packed weights: " + packedWeights); + // [END print_solution] + } + + public static void main(String[] args) throws Exception { + Knapsack.solve(); + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/KnapsackMIP.java b/libs/or-tools-src-ubuntu/examples/java/KnapsackMIP.java new file mode 100755 index 0000000000000000000000000000000000000000..64f4cf31790ee6cc1bfdd7e67035ff145e353cb9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/KnapsackMIP.java @@ -0,0 +1,98 @@ +/* + * Copyright 2017 Darian Sastre darian.sastre@minimaxlabs.com + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ************************************************************************ + * + * Each knapsack perceives a different weight for each item. Item values are + * the same across knapsacks. Optimizing constrains the count of each item such + * that all knapsack capacities are respected, and their values are maximized. + * + * This model was created by Hakan Kjellerstrand (hakank@gmail.com) + */ +package com.google.ortools.examples; + +import com.google.ortools.linearsolver.*; + +public class KnapsackMIP { + static { + System.loadLibrary("jniortools"); + } + + private static MPSolver createSolver(String solverType) { + try { + return new MPSolver("MIPDiet", MPSolver.OptimizationProblemType.valueOf(solverType)); + } catch (java.lang.IllegalArgumentException e) { + System.err.println("Bad solver type: " + e); + return null; + } + } + + private static void solve(String solverType) { + MPSolver solver = createSolver(solverType); + + /** variables */ + int itemCount = 12; + int capacityCount = 7; + + int[] capacity = {18209, 7692, 1333, 924, 26638, 61188, 13360}; + int[] value = {96, 76, 56, 11, 86, 10, 66, 86, 83, 12, 9, 81}; + int[][] weights = { + {19, 1, 10, 1, 1, 14, 152, 11, 1, 1, 1, 1}, + {0, 4, 53, 0, 0, 80, 0, 4, 5, 0, 0, 0}, + {4, 660, 3, 0, 30, 0, 3, 0, 4, 90, 0, 0}, + {7, 0, 18, 6, 770, 330, 7, 0, 0, 6, 0, 0}, + {0, 20, 0, 4, 52, 3, 0, 0, 0, 5, 4, 0}, + {0, 0, 40, 70, 4, 63, 0, 0, 60, 0, 4, 0}, + {0, 32, 0, 0, 0, 5, 0, 3, 0, 660, 0, 9} + }; + + int maxCapacity = -1; + for (int c : capacity) { + if (c > maxCapacity) { + maxCapacity = c; + } + } + + MPVariable[] taken = solver.makeIntVarArray(itemCount, 0, maxCapacity); + + /** constraints */ + MPConstraint constraints[] = new MPConstraint[capacityCount]; + for (int i = 0; i < capacityCount; i++) { + constraints[i] = solver.makeConstraint(0, capacity[i]); + for (int j = 0; j < itemCount; j++) { + constraints[i].setCoefficient(taken[j], weights[i][j]); + } + } + + /** objective */ + MPObjective obj = solver.objective(); + obj.setMaximization(); + for (int i = 0; i < itemCount; i++) { + obj.setCoefficient(taken[i], value[i]); + } + + solver.solve(); + + /** printing */ + System.out.println("Max cost: " + obj.value()); + System.out.print("Item quantities: "); + for (MPVariable var : taken) { + System.out.print((int) var.solutionValue() + " "); + } + } + + public static void main(String[] args) { + solve("CBC_MIXED_INTEGER_PROGRAMMING"); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/LeastDiff.java b/libs/or-tools-src-ubuntu/examples/java/LeastDiff.java new file mode 100644 index 0000000000000000000000000000000000000000..cc04155b17c1f6224fe7fe47302bca9ed7860921 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/LeastDiff.java @@ -0,0 +1,95 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; +import java.io.*; +import java.text.*; +import java.util.*; + +public class LeastDiff { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves the Least Diff problem. See http://www.hakank.org/google_or_tools/least_diff.py */ + private static void solve() { + final int base = 10; + + Solver solver = new Solver("LeastDiff"); + + // + // Variables + // + IntVar a = solver.makeIntVar(0, base - 1, "a"); + IntVar b = solver.makeIntVar(0, base - 1, "b"); + IntVar c = solver.makeIntVar(0, base - 1, "c"); + IntVar d = solver.makeIntVar(0, base - 1, "d"); + IntVar e = solver.makeIntVar(0, base - 1, "e"); + + IntVar f = solver.makeIntVar(0, base - 1, "f"); + IntVar g = solver.makeIntVar(0, base - 1, "g"); + IntVar h = solver.makeIntVar(0, base - 1, "h"); + IntVar i = solver.makeIntVar(0, base - 1, "i"); + IntVar j = solver.makeIntVar(0, base - 1, "j"); + + IntVar[] all = {a, b, c, d, e, f, g, h, i, j}; + + // + // Constraints + // + int[] coeffs = {10000, 1000, 100, 10, 1}; + IntVar x = solver.makeScalProd(new IntVar[] {a, b, c, d, e}, coeffs).var(); + x.setName("x"); + IntVar y = solver.makeScalProd(new IntVar[] {f, g, h, i, j}, coeffs).var(); + y.setName("y"); + + // a > 0 + solver.addConstraint(solver.makeGreater(a, 0)); + // f > 0 + solver.addConstraint(solver.makeGreater(f, 0)); + + // diff = x - y + IntVar diff = solver.makeDifference(x, y).var(); + diff.setName("diff"); + + solver.addConstraint(solver.makeAllDifferent(all)); + + // + // Objective + // + OptimizeVar obj = solver.makeMinimize(diff, 1); + + // + // Search + // + DecisionBuilder db = solver.makePhase(all, solver.CHOOSE_PATH, solver.ASSIGN_MIN_VALUE); + solver.newSearch(db, obj); + while (solver.nextSolution()) { + System.out.println("" + x.value() + " - " + y.value() + " = " + diff.value()); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + LeastDiff.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/LinearAssignmentAPI.java b/libs/or-tools-src-ubuntu/examples/java/LinearAssignmentAPI.java new file mode 100644 index 0000000000000000000000000000000000000000..d7f6a834c8f2b666b17d76862c1e79c2d55083b7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/LinearAssignmentAPI.java @@ -0,0 +1,57 @@ +// 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. +package com.google.ortools.examples; + +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-src-ubuntu/examples/java/LiteralSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/LiteralSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..82ba74dc5d243b78d70b08aad7790b90b32ab565 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/LiteralSampleSat.java @@ -0,0 +1,32 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.Literal; + +/** Code sample to demonstrate Boolean variable and literals. */ +public class LiteralSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + CpModel model = new CpModel(); + IntVar x = model.newBoolVar("x"); + Literal notX = x.not(); + System.out.println(notX.getShortString()); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/MagicSquare.java b/libs/or-tools-src-ubuntu/examples/java/MagicSquare.java new file mode 100644 index 0000000000000000000000000000000000000000..c9f7c6b8f8c5a0c1ed5a7717eb8e380d1f62df99 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/MagicSquare.java @@ -0,0 +1,128 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class MagicSquare { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves the Magic Square problem. See http://www.hakank.org/google_or_tools/magic_square.py */ + private static void solve(int n, int num) { + + Solver solver = new Solver("MagicSquare"); + + System.out.println("n: " + n); + + // + // variables + // + IntVar[][] x = new IntVar[n][n]; + + // for the branching + IntVar[] x_flat = new IntVar[n * n]; + + // + // constraints + // + final long s = (n * (n * n + 1)) / 2; + System.out.println("s: " + s); + // IntVar s = solver.makeIntVar(0, n*n*n, "s"); + + IntVar[] diag1 = new IntVar[n]; + IntVar[] diag2 = new IntVar[n]; + for (int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + for (int j = 0; j < n; j++) { + x[i][j] = solver.makeIntVar(1, n * n, "x[" + i + "," + j + "]"); + x_flat[i * n + j] = x[i][j]; + row[j] = x[i][j]; + } + // sum row to s + solver.addConstraint(solver.makeSumEquality(row, s)); + + diag1[i] = x[i][i]; + diag2[i] = x[i][n - i - 1]; + } + // sum diagonals to s + solver.addConstraint(solver.makeSumEquality(diag1, s)); + solver.addConstraint(solver.makeSumEquality(diag2, s)); + + // sum columns to s + for (int j = 0; j < n; j++) { + IntVar[] col = new IntVar[n]; + for (int i = 0; i < n; i++) { + col[i] = x[i][j]; + } + solver.addConstraint(solver.makeSumEquality(col, s)); + } + + // all are different + solver.addConstraint(solver.makeAllDifferent(x_flat)); + + // symmetry breaking: upper left is 1 + // solver.addConstraint(solver.makeEquality(x[0][0], 1)); + + // + // Solve + // + DecisionBuilder db = + solver.makePhase(x_flat, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_CENTER_VALUE); + solver.newSearch(db); + int c = 0; + while (solver.nextSolution()) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + System.out.print(x[i][j].value() + " "); + } + System.out.println(); + } + System.out.println(); + c++; + if (num > 0 && c >= num) { + break; + } + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + int n = 4; + int num = 0; + + if (args.length > 0) { + n = Integer.parseInt(args[0]); + } + + if (args.length > 1) { + num = Integer.parseInt(args[1]); + } + + MagicSquare.solve(n, num); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Map.java b/libs/or-tools-src-ubuntu/examples/java/Map.java new file mode 100644 index 0000000000000000000000000000000000000000..920a72eb096416548fe3e3b179d19499ae74a26a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Map.java @@ -0,0 +1,95 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class Map { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves a simple map coloring problem. See http://www.hakank.org/google_or_tools/map.py */ + private static void solve() { + + Solver solver = new Solver("Map"); + + // + // data + // + int Belgium = 0; + int Denmark = 1; + int France = 2; + int Germany = 3; + int Netherlands = 4; + int Luxembourg = 5; + + int n = 6; + int max_num_colors = 4; + + // + // Variables + // + IntVar[] color = solver.makeIntVarArray(n, 1, max_num_colors, "x"); + + // + // Constraints + // + solver.addConstraint(solver.makeNonEquality(color[France], color[Belgium])); + solver.addConstraint(solver.makeNonEquality(color[France], color[Luxembourg])); + solver.addConstraint(solver.makeNonEquality(color[France], color[Germany])); + solver.addConstraint(solver.makeNonEquality(color[Luxembourg], color[Germany])); + solver.addConstraint(solver.makeNonEquality(color[Luxembourg], color[Belgium])); + solver.addConstraint(solver.makeNonEquality(color[Belgium], color[Netherlands])); + solver.addConstraint(solver.makeNonEquality(color[Belgium], color[Germany])); + solver.addConstraint(solver.makeNonEquality(color[Germany], color[Netherlands])); + solver.addConstraint(solver.makeNonEquality(color[Germany], color[Denmark])); + + // Symmetry breaking + solver.addConstraint(solver.makeEquality(color[Belgium], 1)); + + // + // Search + // + DecisionBuilder db = + solver.makePhase(color, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); + solver.newSearch(db); + + while (solver.nextSolution()) { + System.out.print("Colors: "); + for (int i = 0; i < n; i++) { + System.out.print(color[i].value() + " "); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + Map.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Map2.java b/libs/or-tools-src-ubuntu/examples/java/Map2.java new file mode 100644 index 0000000000000000000000000000000000000000..055a8669372eecf7b8b71d59c967f0eba023fd99 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Map2.java @@ -0,0 +1,104 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class Map2 { + + static { + System.loadLibrary("jniortools"); + } + + /** + * Solves a simple map coloring problem, take II. See http://www.hakank.org/google_or_tools/map.py + */ + private static void solve() { + + Solver solver = new Solver("Map2"); + + // + // data + // + int Belgium = 0; + int Denmark = 1; + int France = 2; + int Germany = 3; + int Netherlands = 4; + int Luxembourg = 5; + + int n = 6; + int max_num_colors = 4; + + int[][] neighbours = { + {France, Belgium}, + {France, Luxembourg}, + {France, Germany}, + {Luxembourg, Germany}, + {Luxembourg, Belgium}, + {Belgium, Netherlands}, + {Belgium, Germany}, + {Germany, Netherlands}, + {Germany, Denmark} + }; + + // + // Variables + // + IntVar[] color = solver.makeIntVarArray(n, 1, max_num_colors, "x"); + + // + // Constraints + // + for (int i = 0; i < neighbours.length; i++) { + solver.addConstraint( + solver.makeNonEquality(color[neighbours[i][0]], color[neighbours[i][1]])); + } + + // Symmetry breaking + solver.addConstraint(solver.makeEquality(color[Belgium], 1)); + + // + // Search + // + DecisionBuilder db = + solver.makePhase(color, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); + solver.newSearch(db); + + while (solver.nextSolution()) { + System.out.print("Colors: "); + for (int i = 0; i < n; i++) { + System.out.print(color[i].value() + " "); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + Map2.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Minesweeper.java b/libs/or-tools-src-ubuntu/examples/java/Minesweeper.java new file mode 100644 index 0000000000000000000000000000000000000000..2ca8e8c5e350f6e9bef96c5a7ad85d37234462cb --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Minesweeper.java @@ -0,0 +1,216 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class Minesweeper { + + static { + System.loadLibrary("jniortools"); + } + + static int X = -1; + + // + // Default problem. + // It has 4 solutions. + // + static int default_r = 8; + static int default_c = 8; + static int[][] default_game = { + {2, 3, X, 2, 2, X, 2, 1}, + {X, X, 4, X, X, 4, X, 2}, + {X, X, X, X, X, X, 4, X}, + {X, 5, X, 6, X, X, X, 2}, + {2, X, X, X, 5, 5, X, 2}, + {1, 3, 4, X, X, X, 4, X}, + {0, 1, X, 4, X, X, X, 3}, + {0, 1, 2, X, 2, 3, X, 2} + }; + + // for the actual problem + static int r; + static int c; + static int[][] game; + + /** Solves the Minesweeper problems. See http://www.hakank.org/google_or_tools/minesweeper.py */ + private static void solve() { + + Solver solver = new Solver("Minesweeper"); + + int[] S = {-1, 0, 1}; + + // + // data + // + System.out.println("Problem:"); + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if (game[i][j] > X) { + System.out.print(game[i][j] + " "); + } else { + System.out.print("X "); + } + } + System.out.println(); + } + System.out.println(); + + // + // Variables + // + IntVar[][] mines = new IntVar[r][c]; + IntVar[] mines_flat = new IntVar[r * c]; // for branching + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + mines[i][j] = solver.makeIntVar(0, 1, "mines[" + i + ", " + j + "]"); + mines_flat[i * c + j] = mines[i][j]; + } + } + + // + // Constraints + // + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if (game[i][j] >= 0) { + solver.addConstraint(solver.makeEquality(mines[i][j], 0)); + + // this cell is the sum of all its neighbours + ArrayList neighbours = new ArrayList(); + for (int a : S) { + for (int b : S) { + if (i + a >= 0 && j + b >= 0 && i + a < r && j + b < c) { + neighbours.add(mines[i + a][j + b]); + } + } + } + solver.addConstraint( + solver.makeSumEquality(neighbours.toArray(new IntVar[1]), game[i][j])); + } + + if (game[i][j] > X) { + // This cell cannot be a mine since it + // has some value assigned to it + solver.addConstraint(solver.makeEquality(mines[i][j], 0)); + } + } + } + + // + // Search + // + DecisionBuilder db = + solver.makePhase(mines_flat, solver.INT_VAR_SIMPLE, solver.ASSIGN_MIN_VALUE); + solver.newSearch(db); + + int sol = 0; + while (solver.nextSolution()) { + sol++; + System.out.println("Solution #" + sol + ":"); + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + System.out.print(mines[i][j].value() + " "); + } + System.out.println(); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + /** + * Reads a minesweeper file. File format: # a comment which is ignored % a comment which also is + * ignored number of rows number of columns < row number of neighbours lines... > + * + *

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

Example (from minesweeper0.txt) # Problem from Gecode/examples/minesweeper.cc problem 0 6 6 + * ..2.3. 2..... ..24.3 1.34.. .....3 .3.3.. + */ + private static void readFile(String file) { + + System.out.println("readFile(" + file + ")"); + int lineCount = 0; + + try { + + BufferedReader inr = new BufferedReader(new FileReader(file)); + String str; + while ((str = inr.readLine()) != null && str.length() > 0) { + + str = str.trim(); + + // ignore comments + if (str.startsWith("#") || str.startsWith("%")) { + continue; + } + + System.out.println(str); + if (lineCount == 0) { + r = Integer.parseInt(str); // number of rows + } else if (lineCount == 1) { + c = Integer.parseInt(str); // number of columns + game = new int[r][c]; + } else { + // the problem matrix + String row[] = str.split(""); + for (int j = 1; j <= c; j++) { + String s = row[j]; + if (s.equals(".")) { + game[lineCount - 2][j - 1] = -1; + } else { + game[lineCount - 2][j - 1] = Integer.parseInt(s); + } + } + } + + lineCount++; + } // end while + + inr.close(); + + } catch (IOException e) { + System.out.println(e); + } + } // end readFile + + public static void main(String[] args) throws Exception { + + String file = ""; + if (args.length > 0) { + file = args[0]; + Minesweeper.readFile(file); + } else { + game = default_game; + r = default_r; + c = default_c; + } + + Minesweeper.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/MultiThreadTest.java b/libs/or-tools-src-ubuntu/examples/java/MultiThreadTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d83efc5bc052b6b9763b97e2ffc270ca52210210 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/MultiThreadTest.java @@ -0,0 +1,122 @@ +package com.google.ortools.examples; + +import com.google.ortools.linearsolver.MPConstraint; +import com.google.ortools.linearsolver.MPObjective; +import com.google.ortools.linearsolver.MPSolver; +import com.google.ortools.linearsolver.MPSolver.OptimizationProblemType; +import com.google.ortools.linearsolver.MPSolver.ResultStatus; +import com.google.ortools.linearsolver.MPVariable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class MultiThreadTest { + + static { + System.loadLibrary("jniortools"); + } + + private static final boolean verboseOutput = false; // To enable Cbc logging + + public static void main(String[] args) throws Exception { + launchProtocol(10, 8, true); + System.out.println("Cbc multi thread test successful"); + return; + } + + public static void launchProtocol(int wholeLoopAttmpts, int threadPoolSize, boolean runInParallel) + throws Exception { + + for (int noAttmpt = 0; noAttmpt < wholeLoopAttmpts; noAttmpt++) { + + System.out.println(String.format("Attempt %d", noAttmpt)); + + int maxThreads = threadPoolSize; + + List threadList = new ArrayList(); + + for (int i = 0; i < maxThreads; i++) { + SolverThread thread = new SolverThread(); + threadList.add(thread); + } + + ExecutorService executor = Executors.newFixedThreadPool(maxThreads); + + if (runInParallel) { + System.out.println("Launching thread pool"); + executor.invokeAll(threadList); + for (SolverThread thread : threadList) { + System.out.println(thread.getStatusSolver().toString()); + } + } else { + + for (SolverThread thread : threadList) { + System.out.println("Launching single thread"); + executor.invokeAll(Arrays.asList(thread)); + System.out.println(thread.getStatusSolver().toString()); + } + } + + System.out.println("Attempt finalized!"); + executor.shutdown(); + } + + System.out.println("Now exiting multi thread execution"); + } + + private static MPSolver makeProblem() { + + MPSolver solver = + new MPSolver( + UUID.randomUUID().toString(), OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); + + double infinity = MPSolver.infinity(); + + // x1 and x2 are integer non-negative variables. + MPVariable x1 = solver.makeIntVar(0.0, infinity, "x1"); + MPVariable x2 = solver.makeIntVar(0.0, infinity, "x2"); + + // Minimize x1 + 2 * x2. + MPObjective objective = solver.objective(); + objective.setCoefficient(x1, 1); + objective.setCoefficient(x2, 2); + + // 2 * x2 + 3 * x1 >= 17. + MPConstraint ct = solver.makeConstraint(17, infinity); + ct.setCoefficient(x1, 3); + ct.setCoefficient(x2, 2); + + if (verboseOutput) { + solver.enableOutput(); + } + + return solver; + } + + private static final class SolverThread implements Callable { + + private MPSolver.ResultStatus statusSolver; + + public SolverThread() {} + + @Override + public ResultStatus call() throws Exception { + MPSolver solver = makeProblem(); + statusSolver = solver.solve(); + + // Check that the problem has an optimal solution. + if (MPSolver.ResultStatus.OPTIMAL.equals(statusSolver)) { + throw new RuntimeException("Non OPTIMAL status after solve."); + } + return statusSolver; + } + + public MPSolver.ResultStatus getStatusSolver() { + return statusSolver; + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/MultipleKnapsackSat.java b/libs/or-tools-src-ubuntu/examples/java/MultipleKnapsackSat.java new file mode 100644 index 0000000000000000000000000000000000000000..0502fac0c24bcfb88c16614ec1f94bdc606cd573 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/MultipleKnapsackSat.java @@ -0,0 +1,139 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.LinearExpr; +// [END import] + +/** Sample showing how to solve a multiple knapsack problem. */ +public class MultipleKnapsackSat { + static { + System.loadLibrary("jniortools"); + } + + // [START data] + static class DataModel { + int[] items = new int[] {48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36}; + int[] values = new int[] {10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25}; + int[] binCapacities = new int[] {100, 100, 100, 100, 100}; + int numItems = items.length; + int numBins = 5; + } + // [END data] + + // [START solution_printer] + static void printSolution( + DataModel data, CpSolver solver, IntVar[][] x, IntVar[] load, IntVar[] value) { + System.out.printf("Optimal objective value: %f%n", solver.objectiveValue()); + System.out.println(); + long packedWeight = 0; + long packedValue = 0; + for (int b = 0; b < data.numBins; ++b) { + System.out.println("Bin " + b); + for (int i = 0; i < data.numItems; ++i) { + if (solver.value(x[i][b]) > 0) { + System.out.println( + "Item " + i + " - Weight: " + data.items[i] + " Value: " + data.values[i]); + } + } + System.out.println("Packed bin weight: " + solver.value(load[b])); + packedWeight = packedWeight + solver.value(load[b]); + System.out.println("Packed bin value: " + solver.value(value[b]) + "\n"); + packedValue = packedValue + solver.value(value[b]); + } + System.out.println("Total packed weight: " + packedWeight); + System.out.println("Total packed value: " + packedValue); + } + + public static void main(String[] args) { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + int totalValue = 0; + for (int i = 0; i < data.numItems; ++i) { + totalValue = totalValue + data.values[i]; + } + + // [START model] + CpModel model = new CpModel(); + // [END model] + + // [START variables] + IntVar[][] x = new IntVar[data.numItems][data.numBins]; + for (int i = 0; i < data.numItems; ++i) { + for (int b = 0; b < data.numBins; ++b) { + x[i][b] = model.newIntVar(0, 1, "x_" + i + "_" + b); + } + } + // Main variables. + // Load and value variables. + IntVar[] load = new IntVar[data.numBins]; + IntVar[] value = new IntVar[data.numBins]; + for (int b = 0; b < data.numBins; ++b) { + load[b] = model.newIntVar(0, data.binCapacities[b], "load_" + b); + value[b] = model.newIntVar(0, totalValue, "value_" + b); + } + + // Links load and value with x. + int[] sizes = new int[data.numItems]; + for (int i = 0; i < data.numItems; ++i) { + sizes[i] = data.items[i]; + } + for (int b = 0; b < data.numBins; ++b) { + IntVar[] vars = new IntVar[data.numItems]; + for (int i = 0; i < data.numItems; ++i) { + vars[i] = x[i][b]; + } + model.addEquality(LinearExpr.scalProd(vars, data.items), load[b]); + model.addEquality(LinearExpr.scalProd(vars, data.values), value[b]); + } + // [END variables] + + // [START constraints] + // Each item can be in at most one bin. + // Place all items. + for (int i = 0; i < data.numItems; ++i) { + IntVar[] vars = new IntVar[data.numBins]; + for (int b = 0; b < data.numBins; ++b) { + vars[b] = x[i][b]; + } + model.addLessOrEqual(LinearExpr.sum(vars), 1); + } + // [END constraints] + // Maximize sum of load. + // [START objective] + model.maximize(LinearExpr.sum(value)); + // [END objective] + + // [START solve] + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.solve(model); + // [END solve] + + // [START print_solution] + System.out.println("Solve status: " + status); + if (status == CpSolverStatus.OPTIMAL) { + printSolution(data, solver, x, load, value); + } + // [END print_solution] + } + + private MultipleKnapsackSat() {} +} diff --git a/libs/or-tools-src-ubuntu/examples/java/NQueens.java b/libs/or-tools-src-ubuntu/examples/java/NQueens.java new file mode 100644 index 0000000000000000000000000000000000000000..2104afe537812aac431c0bb1cf499a080db2faa1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/NQueens.java @@ -0,0 +1,108 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class NQueens { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves the N Queens problem. See http://www.hakank.org/google_or_tools/nqueens2.py */ + private static void solve(int n, int num, int print) { + + Solver solver = new Solver("NQueens"); + + System.out.println("n: " + n); + + // + // variables + // + IntVar[] q = solver.makeIntVarArray(n, 0, n - 1, "q"); + + // + // constraints + // + solver.addConstraint(solver.makeAllDifferent(q)); + + IntVar b = solver.makeIntVar(1, 1, "b"); + IntVar[] q1 = new IntVar[n]; + IntVar[] q2 = new IntVar[n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + // // q[i]+i != q[j]+j + solver.addConstraint( + solver.makeNonEquality(solver.makeSum(q[i], i).var(), solver.makeSum(q[j], j).var())); + + // q[i]-i != q[j]-j + solver.addConstraint( + solver.makeNonEquality(solver.makeSum(q[i], -i).var(), solver.makeSum(q[j], -j).var())); + } + } + + // + // Solve + // + DecisionBuilder db = + solver.makePhase(q, solver.CHOOSE_MIN_SIZE_LOWEST_MAX, solver.ASSIGN_CENTER_VALUE); + solver.newSearch(db); + int c = 0; + while (solver.nextSolution()) { + if (print != 0) { + for (int i = 0; i < n; i++) { + System.out.print(q[i].value() + " "); + } + System.out.println(); + } + c++; + if (num > 0 && c >= num) { + break; + } + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + int n = 8; + int num = 0; + int print = 1; + + if (args.length > 0) { + n = Integer.parseInt(args[0]); + } + + if (args.length > 1) { + num = Integer.parseInt(args[1]); + } + + if (args.length > 2) { + print = Integer.parseInt(args[2]); + } + + NQueens.solve(n, num, print); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/NQueens2.java b/libs/or-tools-src-ubuntu/examples/java/NQueens2.java new file mode 100644 index 0000000000000000000000000000000000000000..b723432d476a1a4ce275f3d29f1276aea5fb1afd --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/NQueens2.java @@ -0,0 +1,102 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class NQueens2 { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves the N Queens problem. See http://www.hakank.org/google_or_tools/nqueens2.py */ + private static void solve(int n, int num, int print) { + + Solver solver = new Solver("NQueens"); + + System.out.println("n: " + n); + + // + // variables + // + IntVar[] q = solver.makeIntVarArray(n, 0, n - 1, "q"); + + // + // constraints + // + solver.addConstraint(solver.makeAllDifferent(q)); + + IntVar[] q1 = new IntVar[n]; + IntVar[] q2 = new IntVar[n]; + for (int i = 0; i < n; i++) { + q1[i] = solver.makeSum(q[i], i).var(); + q2[i] = solver.makeSum(q[i], -i).var(); + } + solver.addConstraint(solver.makeAllDifferent(q1)); + solver.addConstraint(solver.makeAllDifferent(q2)); + + // + // Solve + // + DecisionBuilder db = + solver.makePhase(q, solver.CHOOSE_MIN_SIZE_LOWEST_MAX, solver.ASSIGN_CENTER_VALUE); + solver.newSearch(db); + int c = 0; + while (solver.nextSolution()) { + if (print != 0) { + for (int i = 0; i < n; i++) { + System.out.print(q[i].value() + " "); + } + System.out.println(); + } + c++; + if (num > 0 && c >= num) { + break; + } + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + int n = 8; + int num = 0; + int print = 1; + + if (args.length > 0) { + n = Integer.parseInt(args[0]); + } + + if (args.length > 1) { + num = Integer.parseInt(args[1]); + } + + if (args.length > 2) { + print = Integer.parseInt(args[2]); + } + + NQueens2.solve(n, num, print); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/NoOverlapSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/NoOverlapSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..a3c25fcacd0edd7b3df2323431163f370294b6c1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/NoOverlapSampleSat.java @@ -0,0 +1,80 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.IntervalVar; + +/** + * We want to schedule 3 tasks on 3 weeks excluding weekends, making the final day as early as + * possible. + */ +public class NoOverlapSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + CpModel model = new CpModel(); + // Three weeks. + int horizon = 21; + + // Task 0, duration 2. + IntVar start0 = model.newIntVar(0, horizon, "start0"); + int duration0 = 2; + IntVar end0 = model.newIntVar(0, horizon, "end0"); + IntervalVar task0 = model.newIntervalVar(start0, duration0, end0, "task0"); + + // Task 1, duration 4. + IntVar start1 = model.newIntVar(0, horizon, "start1"); + int duration1 = 4; + IntVar end1 = model.newIntVar(0, horizon, "end1"); + IntervalVar task1 = model.newIntervalVar(start1, duration1, end1, "task1"); + + // Task 2, duration 3. + IntVar start2 = model.newIntVar(0, horizon, "start2"); + int duration2 = 3; + IntVar end2 = model.newIntVar(0, horizon, "end2"); + IntervalVar task2 = model.newIntervalVar(start2, duration2, end2, "task2"); + + // Weekends. + IntervalVar weekend0 = model.newFixedInterval(5, 2, "weekend0"); + IntervalVar weekend1 = model.newFixedInterval(12, 2, "weekend1"); + IntervalVar weekend2 = model.newFixedInterval(19, 2, "weekend2"); + + // No Overlap constraint. This constraint enforces that no two intervals can overlap. + // In this example, as we use 3 fixed intervals that span over weekends, this constraint makes + // sure that all tasks are executed on weekdays. + model.addNoOverlap(new IntervalVar[] {task0, task1, task2, weekend0, weekend1, weekend2}); + + // Makespan objective. + IntVar obj = model.newIntVar(0, horizon, "makespan"); + model.addMaxEquality(obj, new IntVar[] {end0, end1, end2}); + model.minimize(obj); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.solve(model); + + if (status == CpSolverStatus.OPTIMAL) { + System.out.println("Optimal Schedule Length: " + solver.objectiveValue()); + System.out.println("Task 0 starts at " + solver.value(start0)); + System.out.println("Task 1 starts at " + solver.value(start1)); + System.out.println("Task 2 starts at " + solver.value(start2)); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/OptionalIntervalSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/OptionalIntervalSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..1003ee9732965afbf1c4d3fa2cae8623859ef14d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/OptionalIntervalSampleSat.java @@ -0,0 +1,40 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.IntervalVar; +import com.google.ortools.sat.Literal; + +/** Code sample to demonstrates how to build an optional interval. */ +public class OptionalIntervalSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + CpModel model = new CpModel(); + int horizon = 100; + IntVar startVar = model.newIntVar(0, horizon, "start"); + IntVar endVar = model.newIntVar(0, horizon, "end"); + // Java code supports IntVar or integer constants in intervals. + int duration = 10; + Literal presence = model.newBoolVar("presence"); + IntervalVar interval = + model.newOptionalIntervalVar(startVar, duration, endVar, presence, "interval"); + + System.out.println(interval); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Partition.java b/libs/or-tools-src-ubuntu/examples/java/Partition.java new file mode 100644 index 0000000000000000000000000000000000000000..94fe6522f91cb648338d438fce19181191717312 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Partition.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 1999-2011, Ecole des Mines de Nantes All rights reserved. Redistribution and use in + * source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: + * + *

* Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * Neither the name of the Ecole des Mines + * de Nantes nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + *

THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; + +/** + * Partition n numbers into two groups, so that - the sum of the first group equals the sum of the + * second, - and the sum of the squares of the first group equals the sum of the squares of the + * second
+ * + * @author Charles Prud'homme + * @since 18/03/11 + */ +public class Partition { + + static { + System.loadLibrary("jniortools"); + } + + /** Partition Problem. */ + private static void solve(int m) { + Solver solver = new Solver("Partition " + m); + + IntVar[] x, y; + x = solver.makeIntVarArray(m, 1, 2 * m, "x"); + y = solver.makeIntVarArray(m, 1, 2 * m, "y"); + + // break symmetries + for (int i = 0; i < m - 1; i++) { + solver.addConstraint(solver.makeLess(x[i], x[i + 1])); + solver.addConstraint(solver.makeLess(y[i], y[i + 1])); + } + solver.addConstraint(solver.makeLess(x[0], y[0])); + + IntVar[] xy = new IntVar[2 * m]; + for (int i = m - 1; i >= 0; i--) { + xy[i] = x[i]; + xy[m + i] = y[i]; + } + + solver.addConstraint(solver.makeAllDifferent(xy)); + + int[] coeffs = new int[2 * m]; + for (int i = m - 1; i >= 0; i--) { + coeffs[i] = 1; + coeffs[m + i] = -1; + } + solver.addConstraint(solver.makeScalProdEquality(xy, coeffs, 0)); + + IntVar[] sxy, sx, sy; + sxy = new IntVar[2 * m]; + sx = new IntVar[m]; + sy = new IntVar[m]; + for (int i = m - 1; i >= 0; i--) { + sx[i] = solver.makeSquare(x[i]).var(); + sxy[i] = sx[i]; + sy[i] = solver.makeSquare(y[i]).var(); + sxy[m + i] = sy[i]; + } + solver.addConstraint(solver.makeScalProdEquality(sxy, coeffs, 0)); + + solver.addConstraint(solver.makeSumEquality(x, 2 * m * (2 * m + 1) / 4)); + solver.addConstraint(solver.makeSumEquality(y, 2 * m * (2 * m + 1) / 4)); + solver.addConstraint(solver.makeSumEquality(sx, 2 * m * (2 * m + 1) * (4 * m + 1) / 12)); + solver.addConstraint(solver.makeSumEquality(sy, 2 * m * (2 * m + 1) * (4 * m + 1) / 12)); + + DecisionBuilder db = solver.makeDefaultPhase(xy); + SolutionCollector collector = solver.makeFirstSolutionCollector(); + collector.add(xy); + SearchMonitor log = solver.makeSearchLog(10000); + solver.newSearch(db, log, collector); + solver.nextSolution(); + System.out.println("Solution solution"); + for (int i = 0; i < m; ++i) { + System.out.print("[" + collector.value(0, xy[i]) + "] "); + } + System.out.printf("\n"); + for (int i = 0; i < m; ++i) { + System.out.print("[" + collector.value(0, xy[m + i]) + "] "); + } + System.out.println(); + } + + public static void main(String[] args) throws Exception { + Partition.solve(32); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/QuasigroupCompletion.java b/libs/or-tools-src-ubuntu/examples/java/QuasigroupCompletion.java new file mode 100644 index 0000000000000000000000000000000000000000..e51602021825b55d0528b6ae8f2c419962ab60d6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/QuasigroupCompletion.java @@ -0,0 +1,216 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class QuasigroupCompletion { + + static { + System.loadLibrary("jniortools"); + } + + static int X = 0; + + /* + * default problem + * + * Example from Ruben Martins and Ines Lynce + * Breaking Local Symmetries in Quasigroup Completion Problems, page 3 + * The solution is unique: + * + * 1 3 2 5 4 + * 2 5 4 1 3 + * 4 1 3 2 5 + * 5 4 1 3 2 + * 3 2 5 4 1 + */ + static int default_n = 5; + static int[][] default_problem = { + {1, X, X, X, 4}, + {X, 5, X, X, X}, + {4, X, X, 2, X}, + {X, 4, X, X, X}, + {X, X, 5, X, 1} + }; + + // for the actual problem + static int n; + static int[][] problem; + + /** + * Solves the Quasigroup Completion problem. See + * http://www.hakank.org/google_or_tools/quasigroup_completion.py + */ + private static void solve() { + + Solver solver = new Solver("QuasigroupCompletion"); + + // + // data + // + System.out.println("Problem:"); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + System.out.print(problem[i][j] + " "); + } + System.out.println(); + } + System.out.println(); + + // + // Variables + // + IntVar[][] x = new IntVar[n][n]; + IntVar[] x_flat = new IntVar[n * n]; // for branching + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + x[i][j] = solver.makeIntVar(1, n, "x[" + i + "," + j + "]"); + x_flat[i * n + j] = x[i][j]; + } + } + + // + // Constraints + // + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (problem[i][j] > X) { + solver.addConstraint(solver.makeEquality(x[i][j], problem[i][j])); + } + } + } + + // + // rows and columns must be different + // + + // rows + for (int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + for (int j = 0; j < n; j++) { + row[j] = x[i][j]; + } + solver.addConstraint(solver.makeAllDifferent(row)); + } + + // columns + for (int j = 0; j < n; j++) { + IntVar[] col = new IntVar[n]; + for (int i = 0; i < n; i++) { + col[i] = x[i][j]; + } + solver.addConstraint(solver.makeAllDifferent(col)); + } + + // + // Search + // + DecisionBuilder db = solver.makePhase(x_flat, solver.INT_VAR_SIMPLE, solver.ASSIGN_MIN_VALUE); + solver.newSearch(db); + + int sol = 0; + while (solver.nextSolution()) { + sol++; + System.out.println("Solution #" + sol + ":"); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + System.out.print(x[i][j].value() + " "); + } + System.out.println(); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + /** + * Reads a Quasigroup completion file. File format: # a comment which is ignored % a comment which + * also is ignored number of rows (n) < row number of space separated entries > + * + *

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

Example 5 1 . . . 4 . 5 . . . 4 . . 2 . . 4 . . . . . 5 . 1 + */ + private static void readFile(String file) { + + System.out.println("readFile(" + file + ")"); + int lineCount = 0; + + try { + + BufferedReader inr = new BufferedReader(new FileReader(file)); + String str; + while ((str = inr.readLine()) != null && str.length() > 0) { + + str = str.trim(); + + // ignore comments + if (str.startsWith("#") || str.startsWith("%")) { + continue; + } + + System.out.println(str); + if (lineCount == 0) { + n = Integer.parseInt(str); // number of rows + problem = new int[n][n]; + } else { + // the problem matrix + String row[] = str.split(" "); + for (int i = 0; i < n; i++) { + String s = row[i]; + if (s.equals(".")) { + problem[lineCount - 1][i] = 0; + } else { + problem[lineCount - 1][i] = Integer.parseInt(s); + } + } + } + + lineCount++; + } // end while + + inr.close(); + + } catch (IOException e) { + System.out.println(e); + } + } // end readFile + + public static void main(String[] args) throws Exception { + + if (args.length > 0) { + String file = ""; + file = args[0]; + QuasigroupCompletion.readFile(file); + } else { + problem = default_problem; + n = default_n; + } + + QuasigroupCompletion.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/README.md b/libs/or-tools-src-ubuntu/examples/java/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4fff7a60a854fd1df79b9585ed65d120092b3c9d --- /dev/null +++ b/libs/or-tools-src-ubuntu/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-src-ubuntu/examples/java/RabbitsAndPheasantsSat.java b/libs/or-tools-src-ubuntu/examples/java/RabbitsAndPheasantsSat.java new file mode 100644 index 0000000000000000000000000000000000000000..b31a7ede7726d6c2c05ed597eac27a127ee00742 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/RabbitsAndPheasantsSat.java @@ -0,0 +1,50 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.LinearExpr; + +/** + * In a field of rabbits and pheasants, there are 20 heads and 56 legs. How many rabbits and + * pheasants are there? + */ +public class RabbitsAndPheasantsSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + // Creates the model. + CpModel model = new CpModel(); + // Creates the variables. + IntVar r = model.newIntVar(0, 100, "r"); + IntVar p = model.newIntVar(0, 100, "p"); + // 20 heads. + model.addEquality(LinearExpr.sum(new IntVar[] {r, p}), 20); + // 56 legs. + model.addEquality(LinearExpr.scalProd(new IntVar[] {r, p}, new long[] {4, 2}), 56); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.solve(model); + + if (status == CpSolverStatus.FEASIBLE) { + System.out.println(solver.value(r) + " rabbits, and " + solver.value(p) + " pheasants"); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/RabbitsPheasants.java b/libs/or-tools-src-ubuntu/examples/java/RabbitsPheasants.java new file mode 100644 index 0000000000000000000000000000000000000000..5a17b72325d3e6e7fee6b466651c40b30d0a43f2 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/RabbitsPheasants.java @@ -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. +package com.google.ortools.examples; + +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-src-ubuntu/examples/java/RandomTsp.java b/libs/or-tools-src-ubuntu/examples/java/RandomTsp.java new file mode 100644 index 0000000000000000000000000000000000000000..c47524a04d9c80299a99eb25a315b3b7ed43dc6a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/RandomTsp.java @@ -0,0 +1,142 @@ +// +// 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. +package com.google.ortools.examples; + +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-src-ubuntu/examples/java/RankingSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/RankingSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..5038e682c28172963217c92c58f8d6099f4f24fb --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/RankingSampleSat.java @@ -0,0 +1,181 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.IntervalVar; +import com.google.ortools.sat.LinearExpr; +import com.google.ortools.sat.Literal; +import java.util.ArrayList; +import java.util.List; + +/** Code sample to demonstrates how to rank intervals. */ +public class RankingSampleSat { + static { + System.loadLibrary("jniortools"); + } + + /** + * This code takes a list of interval variables in a noOverlap constraint, and a parallel list of + * integer variables and enforces the following constraint + *

    + *
  • rank[i] == -1 iff interval[i] is not active. + *
  • rank[i] == number of active intervals that precede interval[i]. + *
+ */ + static void rankTasks(CpModel model, IntVar[] starts, Literal[] presences, IntVar[] ranks) { + int numTasks = starts.length; + + // Creates precedence variables between pairs of intervals. + Literal[][] precedences = new Literal[numTasks][numTasks]; + for (int i = 0; i < numTasks; ++i) { + for (int j = 0; j < numTasks; ++j) { + if (i == j) { + precedences[i][i] = presences[i]; + } else { + IntVar prec = model.newBoolVar(String.format("%d before %d", i, j)); + precedences[i][j] = prec; + // Ensure that task i precedes task j if prec is true. + model.addLessOrEqualWithOffset(starts[i], starts[j], 1).onlyEnforceIf(prec); + } + } + } + + // Create optional intervals. + for (int i = 0; i < numTasks - 1; ++i) { + for (int j = i + 1; j < numTasks; ++j) { + List list = new ArrayList<>(); + list.add(precedences[i][j]); + list.add(precedences[j][i]); + list.add(presences[i].not()); + // Makes sure that if i is not performed, all precedences are false. + model.addImplication(presences[i].not(), precedences[i][j].not()); + model.addImplication(presences[i].not(), precedences[j][i].not()); + list.add(presences[j].not()); + // Makes sure that if j is not performed, all precedences are false. + model.addImplication(presences[j].not(), precedences[i][j].not()); + model.addImplication(presences[j].not(), precedences[j][i].not()); + // The following boolOr will enforce that for any two intervals: + // i precedes j or j precedes i or at least one interval is not + // performed. + model.addBoolOr(list.toArray(new Literal[0])); + // For efficiency, we add a redundant constraint declaring that only one of i precedes j and + // j precedes i are true. This will speed up the solve because the reason of this + // propagation is shorter that using interval bounds is true. + model.addImplication(precedences[i][j], precedences[j][i].not()); + model.addImplication(precedences[j][i], precedences[i][j].not()); + } + } + + // Links precedences and ranks. + for (int i = 0; i < numTasks; ++i) { + IntVar[] vars = new IntVar[numTasks + 1]; + int[] coefs = new int[numTasks + 1]; + for (int j = 0; j < numTasks; ++j) { + vars[j] = (IntVar) precedences[j][i]; + coefs[j] = 1; + } + vars[numTasks] = ranks[i]; + coefs[numTasks] = -1; + // ranks == sum(precedences) - 1; + model.addEquality(LinearExpr.scalProd(vars, coefs), 1); + } + } + + public static void main(String[] args) throws Exception { + CpModel model = new CpModel(); + int horizon = 100; + int numTasks = 4; + + IntVar[] starts = new IntVar[numTasks]; + IntVar[] ends = new IntVar[numTasks]; + IntervalVar[] intervals = new IntervalVar[numTasks]; + Literal[] presences = new Literal[numTasks]; + IntVar[] ranks = new IntVar[numTasks]; + + IntVar trueVar = model.newConstant(1); + + // Creates intervals, half of them are optional. + for (int t = 0; t < numTasks; ++t) { + starts[t] = model.newIntVar(0, horizon, "start_" + t); + int duration = t + 1; + ends[t] = model.newIntVar(0, horizon, "end_" + t); + if (t < numTasks / 2) { + intervals[t] = model.newIntervalVar(starts[t], duration, ends[t], "interval_" + t); + presences[t] = trueVar; + } else { + presences[t] = model.newBoolVar("presence_" + t); + intervals[t] = model.newOptionalIntervalVar( + starts[t], duration, ends[t], presences[t], "o_interval_" + t); + } + + // The rank will be -1 iff the task is not performed. + ranks[t] = model.newIntVar(-1, numTasks - 1, "rank_" + t); + } + + // Adds NoOverlap constraint. + model.addNoOverlap(intervals); + + // Adds ranking constraint. + rankTasks(model, starts, presences, ranks); + + // Adds a constraint on ranks (ranks[0] < ranks[1]). + model.addLessOrEqualWithOffset(ranks[0], ranks[1], 1); + + // Creates makespan variable. + IntVar makespan = model.newIntVar(0, horizon, "makespan"); + for (int t = 0; t < numTasks; ++t) { + model.addLessOrEqual(ends[t], makespan).onlyEnforceIf(presences[t]); + } + // The objective function is a mix of a fixed gain per task performed, and a fixed cost for each + // additional day of activity. + // The solver will balance both cost and gain and minimize makespan * per-day-penalty - number + // of tasks performed * per-task-gain. + // + // On this problem, as the fixed cost is less that the duration of the last interval, the solver + // will not perform the last interval. + IntVar[] objectiveVars = new IntVar[numTasks + 1]; + int[] objectiveCoefs = new int[numTasks + 1]; + for (int t = 0; t < numTasks; ++t) { + objectiveVars[t] = (IntVar) presences[t]; + objectiveCoefs[t] = -7; + } + objectiveVars[numTasks] = makespan; + objectiveCoefs[numTasks] = 2; + model.minimize(LinearExpr.scalProd(objectiveVars, objectiveCoefs)); + + // Creates a solver and solves the model. + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.solve(model); + + if (status == CpSolverStatus.OPTIMAL) { + System.out.println("Optimal cost: " + solver.objectiveValue()); + System.out.println("Makespan: " + solver.value(makespan)); + for (int t = 0; t < numTasks; ++t) { + if (solver.booleanValue(presences[t])) { + System.out.printf("Task %d starts at %d with rank %d%n", t, solver.value(starts[t]), + solver.value(ranks[t])); + } else { + System.out.printf( + "Task %d in not performed and ranked at %d%n", t, solver.value(ranks[t])); + } + } + } else { + System.out.println("Solver exited with nonoptimal status: " + status); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/ReifiedSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/ReifiedSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..7ecb532b80fcdbee245da2edc8f96b6cee0e8ddd --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/ReifiedSampleSat.java @@ -0,0 +1,53 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.Literal; + +/** + * Reification is the action of associating a Boolean variable to a constraint. This boolean + * enforces or prohibits the constraint according to the value the Boolean variable is fixed to. + * + *

Half-reification is defined as a simple implication: If the Boolean variable is true, then the + * constraint holds, instead of an complete equivalence. + * + *

The SAT solver offers half-reification. To implement full reification, two half-reified + * constraints must be used. + */ +public class ReifiedSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + CpModel model = new CpModel(); + + IntVar x = model.newBoolVar("x"); + IntVar y = model.newBoolVar("y"); + IntVar b = model.newBoolVar("b"); + + // Version 1: a half-reified boolean and. + model.addBoolAnd(new Literal[] {x, y.not()}).onlyEnforceIf(b); + + // Version 2: implications. + model.addImplication(b, x); + model.addImplication(b, y.not()); + + // Version 3: boolean or. + model.addBoolOr(new Literal[] {b.not(), x}); + model.addBoolOr(new Literal[] {b.not(), y.not()}); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SearchForAllSolutionsSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/SearchForAllSolutionsSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..e2edf042ba82c13f539f7052905b59aa9e7ecf36 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SearchForAllSolutionsSampleSat.java @@ -0,0 +1,82 @@ +// 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] +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.IntVar; + +/** Code sample that solves a model and displays all solutions. */ +public class SearchForAllSolutionsSampleSat { + static { + System.loadLibrary("jniortools"); + } + + // [START print_solution] + static class VarArraySolutionPrinter extends CpSolverSolutionCallback { + public VarArraySolutionPrinter(IntVar[] variables) { + variableArray = variables; + } + + @Override + public void onSolutionCallback() { + System.out.printf("Solution #%d: time = %.02f s%n", solutionCount, wallTime()); + for (IntVar v : variableArray) { + System.out.printf(" %s = %d%n", v.getName(), value(v)); + } + solutionCount++; + } + + public int getSolutionCount() { + return solutionCount; + } + + private int solutionCount; + private final IntVar[] variableArray; + } + // [END print_solution] + + public static void main(String[] args) throws Exception { + // Create the model. + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Create the variables. + // [START variables] + int numVals = 3; + + IntVar x = model.newIntVar(0, numVals - 1, "x"); + IntVar y = model.newIntVar(0, numVals - 1, "y"); + IntVar z = model.newIntVar(0, numVals - 1, "z"); + // [END variables] + + // Create the constraints. + // [START constraints] + model.addDifferent(x, y); + // [END constraints] + + // Create a solver and solve the model. + // [START solve] + CpSolver solver = new CpSolver(); + VarArraySolutionPrinter cb = new VarArraySolutionPrinter(new IntVar[] {x, y, z}); + solver.searchAllSolutions(model, cb); + // [END solve] + + System.out.println(cb.getSolutionCount() + " solutions found."); + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/SendMoreMoney.java b/libs/or-tools-src-ubuntu/examples/java/SendMoreMoney.java new file mode 100644 index 0000000000000000000000000000000000000000..f906fee5c414f50aa359ccdb0f127798b51baade --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SendMoreMoney.java @@ -0,0 +1,95 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class SendMoreMoney { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves the SEND+MORE=MONEY problem. */ + private static void solve() { + int base = 10; + + Solver solver = new Solver("SendMoreMoney"); + IntVar s = solver.makeIntVar(0, base - 1, "s"); + IntVar e = solver.makeIntVar(0, base - 1, "e"); + IntVar n = solver.makeIntVar(0, base - 1, "n"); + IntVar d = solver.makeIntVar(0, base - 1, "d"); + IntVar m = solver.makeIntVar(0, base - 1, "m"); + IntVar o = solver.makeIntVar(0, base - 1, "o"); + IntVar r = solver.makeIntVar(0, base - 1, "r"); + IntVar y = solver.makeIntVar(0, base - 1, "y"); + + IntVar[] x = {s, e, n, d, m, o, r, y}; + + IntVar[] eq = {s, e, n, d, m, o, r, e, m, o, n, e, y}; + int[] coeffs = { + 1000, + 100, + 10, + 1, // S E N D + + 1000, + 100, + 10, + 1, // M O R E + -10000, + -1000, + -100, + -10, + -1 // == M O N E Y + }; + solver.addConstraint(solver.makeScalProdEquality(eq, coeffs, 0)); + + // alternative: + solver.addConstraint( + solver.makeScalProdEquality( + new IntVar[] {s, e, n, d, m, o, r, e, m, o, n, e, y}, coeffs, 0)); + + // s > 0 + solver.addConstraint(solver.makeGreater(s, 0)); + // m > 0 + solver.addConstraint(solver.makeGreater(m, 0)); + + solver.addConstraint(solver.makeAllDifferent(x)); + + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db); + while (solver.nextSolution()) { + for (int i = 0; i < 8; i++) { + System.out.print(x[i].toString() + " "); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + SendMoreMoney.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SendMoreMoney2.java b/libs/or-tools-src-ubuntu/examples/java/SendMoreMoney2.java new file mode 100644 index 0000000000000000000000000000000000000000..80148e074b92bb2f48a2b5d848f78b70214104d4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SendMoreMoney2.java @@ -0,0 +1,202 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; +import java.io.*; +import java.text.*; +import java.util.*; + +public class SendMoreMoney2 { + + static Solver sol; + + static { + System.loadLibrary("jniortools"); + } + + // + // Some helper methods + // + static IntExpr p(IntExpr a, int b, IntExpr c) { + return sol.makeSum(sol.makeProd(a, b), c); + } + + static IntExpr p(IntVar a, int b) { + return sol.makeProd(a, b); + } + + // a slighly more intelligent scalar product + static IntExpr sp(IntVar[] a) { + int len = a.length; + int c = 1; + int[] t = new int[len]; + for (int i = len - 1; i >= 0; i--) { + t[i] = c; + c *= 10; + } + return sol.makeScalProd(a, t); + } + + /** Solves the SEND+MORE=MONEY problem with different approaches. */ + private static void solve(int alt) { + + sol = new Solver("SendMoreMoney"); + + int base = 10; + + // + // variables + // + IntVar s = sol.makeIntVar(0, base - 1, "s"); + IntVar e = sol.makeIntVar(0, base - 1, "e"); + IntVar n = sol.makeIntVar(0, base - 1, "n"); + IntVar d = sol.makeIntVar(0, base - 1, "d"); + IntVar m = sol.makeIntVar(0, base - 1, "m"); + IntVar o = sol.makeIntVar(0, base - 1, "o"); + IntVar r = sol.makeIntVar(0, base - 1, "r"); + IntVar y = sol.makeIntVar(0, base - 1, "y"); + + IntVar[] x = {s, e, n, d, m, o, r, y}; + + // + // Constraints + // + + /* + * + * Below are some alternatives encodings of the + * same idea: + * + * 1000*s + 100*e + 10*n + d + + * 1000*m + 100*o + 10*r + e == + * 10000*m + 1000*o + 100*n + 10*e + y + * + */ + + if (alt == 0) { + // + // First, a version approach which is just too noisy. + // + sol.addConstraint( + sol.makeEquality( + sol.makeSum( + sol.makeSum( + sol.makeProd(s, 1000), + sol.makeSum( + sol.makeProd(e, 100), + sol.makeSum(sol.makeProd(n, 10), sol.makeProd(d, 1)))), + sol.makeSum( + sol.makeProd(m, 1000), + sol.makeSum( + sol.makeProd(o, 100), + sol.makeSum(sol.makeProd(r, 10), sol.makeProd(e, 1))))) + .var(), + sol.makeSum( + sol.makeProd(m, 10000), + sol.makeSum( + sol.makeProd(o, 1000), + sol.makeSum( + sol.makeProd(n, 100), + sol.makeSum(sol.makeProd(e, 10), sol.makeProd(y, 1))))) + .var())); + + } else if (alt == 1) { + + // + // Alternative 1, using the helper methods + // + // p(IntExpr, int, IntExpr) and + // p(IntVar, int) + // + sol.addConstraint( + sol.makeEquality( + sol.makeSum( + p(s, 1000, p(e, 100, p(n, 10, p(d, 1)))), + p(m, 1000, p(o, 100, p(r, 10, p(e, 1))))) + .var(), + p(m, 10000, p(o, 1000, p(n, 100, p(e, 10, p(y, 1))))).var())); + + } else if (alt == 2) { + + // + // Alternative 2 + // + sol.addConstraint( + sol.makeEquality( + sol.makeSum( + sol.makeScalProd(new IntVar[] {s, e, n, d}, new int[] {1000, 100, 10, 1}), + sol.makeScalProd(new IntVar[] {m, o, r, e}, new int[] {1000, 100, 10, 1})) + .var(), + sol.makeScalProd(new IntVar[] {m, o, n, e, y}, new int[] {10000, 1000, 100, 10, 1}) + .var())); + + } else if (alt == 3) { + + // + // alternative 3: same approach as 2, with some helper methods + // + sol.addConstraint( + sol.makeEquality( + sol.makeSum(sp(new IntVar[] {s, e, n, d}), sp(new IntVar[] {m, o, r, e})).var(), + sp(new IntVar[] {m, o, n, e, y}).var())); + + } else if (alt == 4) { + + // + // Alternative 4, using explicit variables + // + IntExpr send = sol.makeScalProd(new IntVar[] {s, e, n, d}, new int[] {1000, 100, 10, 1}); + IntExpr more = sol.makeScalProd(new IntVar[] {m, o, r, e}, new int[] {1000, 100, 10, 1}); + IntExpr money = + sol.makeScalProd(new IntVar[] {m, o, n, e, y}, new int[] {10000, 1000, 100, 10, 1}); + sol.addConstraint(sol.makeEquality(sol.makeSum(send, more).var(), money.var())); + } + + // s > 0 + sol.addConstraint(sol.makeGreater(s, 0)); + // m > 0 + sol.addConstraint(sol.makeGreater(m, 0)); + + sol.addConstraint(sol.makeAllDifferent(x)); + + // + // Search + // + DecisionBuilder db = sol.makePhase(x, sol.INT_VAR_DEFAULT, sol.INT_VALUE_DEFAULT); + sol.newSearch(db); + while (sol.nextSolution()) { + for (int i = 0; i < 8; i++) { + System.out.print(x[i].toString() + " "); + } + System.out.println(); + } + sol.endSearch(); + + // + // Statistics + // + System.out.println(); + System.out.println("Solutions: " + sol.solutions()); + System.out.println("Failures: " + sol.failures()); + System.out.println("Branches: " + sol.branches()); + System.out.println("Wall time: " + sol.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + for (int i = 0; i < 5; i++) { + System.out.println("\nalternative #" + i); + SendMoreMoney2.solve(i); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SendMostMoney.java b/libs/or-tools-src-ubuntu/examples/java/SendMostMoney.java new file mode 100644 index 0000000000000000000000000000000000000000..cb49a1735495be09bdb7f4bb4041bc38b8d2d74d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SendMostMoney.java @@ -0,0 +1,136 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class SendMostMoney { + + static { + System.loadLibrary("jniortools"); + } + + /** + * Solves the SEND+MOST=MONEY problem, where we maximize MONEY. See + * http://www.hakank.org/google_or_tools/send_more_money.py + */ + private static long solve(long MONEY) { + + Solver solver = new Solver("SendMostMoney"); + + // + // data + // + final int base = 10; + + // + // variables + // + IntVar s = solver.makeIntVar(0, base - 1, "s"); + IntVar e = solver.makeIntVar(0, base - 1, "e"); + IntVar n = solver.makeIntVar(0, base - 1, "n"); + IntVar d = solver.makeIntVar(0, base - 1, "d"); + IntVar m = solver.makeIntVar(0, base - 1, "m"); + IntVar o = solver.makeIntVar(0, base - 1, "o"); + IntVar t = solver.makeIntVar(0, base - 1, "t"); + IntVar y = solver.makeIntVar(0, base - 1, "y"); + + IntVar[] x = {s, e, n, d, m, o, t, y}; + + IntVar[] eq = {s, e, n, d, m, o, s, t, m, o, n, e, y}; + int[] coeffs = { + 1000, + 100, + 10, + 1, // S E N D + + 1000, + 100, + 10, + 1, // M O S T + -10000, + -1000, + -100, + -10, + -1 // == M O N E Y + }; + solver.addConstraint(solver.makeScalProdEquality(eq, coeffs, 0)); + + IntVar money = + solver + .makeScalProd(new IntVar[] {m, o, n, e, y}, new int[] {10000, 1000, 100, 10, 1}) + .var(); + + // + // constraints + // + + // s > 0 + solver.addConstraint(solver.makeGreater(s, 0)); + // m > 0 + solver.addConstraint(solver.makeGreater(m, 0)); + + solver.addConstraint(solver.makeAllDifferent(x)); + + if (MONEY > 0) { + // Search for all solutions. + solver.addConstraint(solver.makeEquality(money, MONEY)); + } + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MAX_VALUE); + + if (MONEY == 0) { + // first round: get the optimal value + OptimizeVar obj = solver.makeMaximize(money, 1); + solver.newSearch(db, obj); + } else { + // search for all solutions + solver.newSearch(db); + } + + long money_ret = 0; + while (solver.nextSolution()) { + System.out.println("money: " + money.value()); + money_ret = money.value(); + for (int i = 0; i < x.length; i++) { + System.out.print(x[i].value() + " "); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + + return money_ret; + } + + public static void main(String[] args) throws Exception { + System.out.println("Get the max value of money:"); + long this_money = SendMostMoney.solve(0); + System.out.println("\nThen find all solutions with this value:"); + long tmp = SendMostMoney.solve(this_money); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Seseman.java b/libs/or-tools-src-ubuntu/examples/java/Seseman.java new file mode 100644 index 0000000000000000000000000000000000000000..ee597fd0ba06f6195740792e39f2671d92ea53f6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Seseman.java @@ -0,0 +1,125 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class Seseman { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves the Seseman convent problem. See http://www.hakank.org/google_or_tools/seseman.py */ + private static void solve(int n) { + + Solver solver = new Solver("Seseman"); + + // + // data + // + final int border_sum = n * n; + + // + // variables + // + IntVar[][] x = new IntVar[n][n]; + IntVar[] x_flat = new IntVar[n * n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + x[i][j] = solver.makeIntVar(0, n * n); + x_flat[i * n + j] = x[i][j]; + } + } + + IntVar total_sum = solver.makeSum(x_flat).var(); + + // + // constraints + // + + // zero in all middle cells + for (int i = 1; i < n - 1; i++) { + for (int j = 1; j < n - 1; j++) { + solver.addConstraint(solver.makeEquality(x[i][j], 0)); + } + } + + // all borders must be >= 1 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i == 0 || j == 0 || i == n - 1 || j == n - 1) { + solver.addConstraint(solver.makeGreaterOrEqual(x[i][j], 1)); + } + } + } + + // sum the four borders + IntVar[] border1 = new IntVar[n]; + IntVar[] border2 = new IntVar[n]; + IntVar[] border3 = new IntVar[n]; + IntVar[] border4 = new IntVar[n]; + for (int i = 0; i < n; i++) { + border1[i] = x[i][0]; + border2[i] = x[i][n - 1]; + border3[i] = x[0][i]; + border4[i] = x[n - 1][i]; + } + solver.addConstraint(solver.makeSumEquality(border1, border_sum)); + + solver.addConstraint(solver.makeSumEquality(border2, border_sum)); + + solver.addConstraint(solver.makeSumEquality(border3, border_sum)); + + solver.addConstraint(solver.makeSumEquality(border4, border_sum)); + + // + // search + // + + DecisionBuilder db = solver.makePhase(x_flat, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db); + + while (solver.nextSolution()) { + System.out.println("total_sum: " + total_sum.value()); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + System.out.print(x[i][j].value() + " "); + } + System.out.println(); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + int n = 3; + if (args.length > 0) { + n = Integer.parseInt(args[0]); + } + Seseman.solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SetCovering.java b/libs/or-tools-src-ubuntu/examples/java/SetCovering.java new file mode 100644 index 0000000000000000000000000000000000000000..3d6581bd8e2ce6179a8e9426b591670e96cfb064 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SetCovering.java @@ -0,0 +1,107 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class SetCovering { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves a set covering problem. See http://www.hakank.org/google_or_tools/set_covering.py */ + private static void solve() { + + Solver solver = new Solver("SetCovering"); + + // + // data + // + // Placing of firestations, from Winston 'Operations Research', + // page 486. + int min_distance = 15; + int num_cities = 6; + + int[][] distance = { + {0, 10, 20, 30, 30, 20}, + {10, 0, 25, 35, 20, 10}, + {20, 25, 0, 15, 30, 20}, + {30, 35, 15, 0, 15, 25}, + {30, 20, 30, 15, 0, 14}, + {20, 10, 20, 25, 14, 0} + }; + + // + // variables + // + IntVar[] x = solver.makeIntVarArray(num_cities, 0, 1, "x"); + IntVar z = solver.makeSum(x).var(); + + // + // constraints + // + + // ensure that all cities are covered + for (int i = 0; i < num_cities; i++) { + ArrayList b = new ArrayList(); + for (int j = 0; j < num_cities; j++) { + if (distance[i][j] <= min_distance) { + b.add(x[j]); + } + } + solver.addConstraint(solver.makeSumGreaterOrEqual(b.toArray(new IntVar[1]), 1)); + } + + // + // objective + // + OptimizeVar objective = solver.makeMinimize(z, 1); + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db, objective); + + // + // output + // + while (solver.nextSolution()) { + System.out.println("z: " + z.value()); + System.out.print("x: "); + for (int i = 0; i < num_cities; i++) { + System.out.print(x[i].value() + " "); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + SetCovering.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SetCovering2.java b/libs/or-tools-src-ubuntu/examples/java/SetCovering2.java new file mode 100644 index 0000000000000000000000000000000000000000..794ea61ed3afe1143312472eb7bdba07238220a3 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SetCovering2.java @@ -0,0 +1,118 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class SetCovering2 { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves a set covering problem. See http://www.hakank.org/google_or_tools/set_covering2.py */ + private static void solve() { + + Solver solver = new Solver("SetCovering2"); + + // + // data + // + + // Example 9.1-2 from + // Taha "Operations Research - An Introduction", + // page 354ff. + // Minimize the number of security telephones in street + // corners on a campus. + + int n = 8; // maximum number of corners + int num_streets = 11; // number of connected streets + + // corners of each street + // Note: 1-based (handled below) + int[][] corner = { + {1, 2}, + {2, 3}, + {4, 5}, + {7, 8}, + {6, 7}, + {2, 6}, + {1, 6}, + {4, 7}, + {2, 4}, + {5, 8}, + {3, 5} + }; + + // + // variables + // + IntVar[] x = solver.makeIntVarArray(n, 0, 1, "x"); + + // number of telephones, to be minimize + IntVar z = solver.makeSum(x).var(); + + // + // constraints + // + + // ensure that all cities are covered + for (int i = 0; i < num_streets; i++) { + IntVar[] b = new IntVar[2]; + b[0] = x[corner[i][0] - 1]; + b[1] = x[corner[i][1] - 1]; + solver.addConstraint(solver.makeSumGreaterOrEqual(b, 1)); + } + + // + // objective + // + OptimizeVar objective = solver.makeMinimize(z, 1); + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db, objective); + + // + // output + // + while (solver.nextSolution()) { + System.out.println("z: " + z.value()); + System.out.print("x: "); + for (int i = 0; i < n; i++) { + System.out.print(x[i].value() + " "); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + SetCovering2.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SetCovering3.java b/libs/or-tools-src-ubuntu/examples/java/SetCovering3.java new file mode 100644 index 0000000000000000000000000000000000000000..c288fef5c335833149117ae89fdf994c043cff77 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SetCovering3.java @@ -0,0 +1,125 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class SetCovering3 { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves a set covering problem. See http://www.hakank.org/google_or_tools/set_covering3.py */ + private static void solve() { + + Solver solver = new Solver("SetCovering3"); + + // + // data + // + + // Set covering problem from + // Katta G. Murty: 'Optimization Models for Decision Making', + // page 302f + // http://ioe.engin.umich.edu/people/fac/books/murty/opti_model/junior-7.pdf + int num_groups = 6; + int num_senators = 10; + + // which group does a senator belong to? + int[][] belongs = { + {1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, // 1 southern + {0, 0, 0, 0, 0, 1, 1, 1, 1, 1}, // 2 northern + {0, 1, 1, 0, 0, 0, 0, 1, 1, 1}, // 3 liberals + {1, 0, 0, 0, 1, 1, 1, 0, 0, 0}, // 4 conservative + {0, 0, 1, 1, 1, 1, 1, 0, 1, 0}, // 5 democrats + {1, 1, 0, 0, 0, 0, 0, 1, 0, 1} + }; // 6 republicans + + // + // variables + // + IntVar[] x = solver.makeIntVarArray(num_senators, 0, 1, "x"); + + // number of assigned senators, to be minimize + IntVar z = solver.makeSum(x).var(); + + // + // constraints + // + + // ensure that each group is covered by at least + // one senator + for (int i = 0; i < num_groups; i++) { + IntVar[] b = new IntVar[num_senators]; + for (int j = 0; j < num_senators; j++) { + b[j] = solver.makeProd(x[j], belongs[i][j]).var(); + } + solver.addConstraint(solver.makeSumGreaterOrEqual(b, 1)); + } + + // + // objective + // + OptimizeVar objective = solver.makeMinimize(z, 1); + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db, objective); + + // + // output + // + while (solver.nextSolution()) { + System.out.println("z: " + z.value()); + System.out.print("x: "); + for (int j = 0; j < num_senators; j++) { + System.out.print(x[j].value() + " "); + } + System.out.println(); + + // More details + for (int j = 0; j < num_senators; j++) { + if (x[j].value() == 1) { + System.out.print("Senator " + (1 + j) + " belongs to these groups: "); + for (int i = 0; i < num_groups; i++) { + if (belongs[i][j] == 1) { + System.out.print((1 + i) + " "); + } + } + System.out.println(); + } + } + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + SetCovering3.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SetCovering4.java b/libs/or-tools-src-ubuntu/examples/java/SetCovering4.java new file mode 100644 index 0000000000000000000000000000000000000000..05a0b1dff430653d9759612e37b1f390c5f957e0 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SetCovering4.java @@ -0,0 +1,129 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class SetCovering4 { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves a set covering problem. See http://www.hakank.org/google_or_tools/set_covering4.py */ + private static void solve(int set_partition) { + + Solver solver = new Solver("SetCovering4"); + + // + // data + // + + // Set partition and set covering problem from + // Example from the Swedish book + // Lundgren, Roennqvist, Vaebrand + // 'Optimeringslaera' (translation: 'Optimization theory'), + // page 408. + int num_alternatives = 10; + int num_objects = 8; + + // costs for the alternatives + int[] costs = {19, 16, 18, 13, 15, 19, 15, 17, 16, 15}; + + // the alternatives, and their objects + int[][] a = { + // 1 2 3 4 5 6 7 8 the objects + {1, 0, 0, 0, 0, 1, 0, 0}, // alternative 1 + {0, 1, 0, 0, 0, 1, 0, 1}, // alternative 2 + {1, 0, 0, 1, 0, 0, 1, 0}, // alternative 3 + {0, 1, 1, 0, 1, 0, 0, 0}, // alternative 4 + {0, 1, 0, 0, 1, 0, 0, 0}, // alternative 5 + {0, 1, 1, 0, 0, 0, 0, 0}, // alternative 6 + {0, 1, 1, 1, 0, 0, 0, 0}, // alternative 7 + {0, 0, 0, 1, 1, 0, 0, 1}, // alternative 8 + {0, 0, 1, 0, 0, 1, 0, 1}, // alternative 9 + {1, 0, 0, 0, 0, 1, 1, 0} + }; // alternative 10 + + // + // variables + // + IntVar[] x = solver.makeIntVarArray(num_alternatives, 0, 1, "x"); + + // number of assigned senators, to be minimize + IntVar z = solver.makeScalProd(x, costs).var(); + + // + // constraints + // + + for (int j = 0; j < num_objects; j++) { + IntVar[] b = new IntVar[num_alternatives]; + for (int i = 0; i < num_alternatives; i++) { + b[i] = solver.makeProd(x[i], a[i][j]).var(); + } + + if (set_partition == 1) { + solver.addConstraint(solver.makeSumGreaterOrEqual(b, 1)); + } else { + solver.addConstraint(solver.makeSumEquality(b, 1)); + } + } + + // + // objective + // + OptimizeVar objective = solver.makeMinimize(z, 1); + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db, objective); + + // + // output + // + while (solver.nextSolution()) { + System.out.println("z: " + z.value()); + System.out.print("Selected alternatives: "); + for (int i = 0; i < num_alternatives; i++) { + if (x[i].value() == 1) { + System.out.print((1 + i) + " "); + } + } + System.out.println("\n"); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + System.out.println("Set partition:"); + SetCovering4.solve(1); + System.out.println("\nSet covering:"); + SetCovering4.solve(0); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SetCoveringDeployment.java b/libs/or-tools-src-ubuntu/examples/java/SetCoveringDeployment.java new file mode 100644 index 0000000000000000000000000000000000000000..35e6f9ec610572d24a011fbea9ab0a1a065fdeaf --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SetCoveringDeployment.java @@ -0,0 +1,143 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.OptimizeVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class SetCoveringDeployment { + + static { + System.loadLibrary("jniortools"); + } + + /** + * Solves a set covering deployment problem. See + * http://www.hakank.org/google_or_tools/set_covering_deployment.py + */ + private static void solve() { + + Solver solver = new Solver("SetCoveringDeployment"); + + // + // data + // + + // From http://mathworld.wolfram.com/SetCoveringDeployment.html + String[] countries = { + "Alexandria", "Asia Minor", "Britain", "Byzantium", "Gaul", "Iberia", "Rome", "Tunis" + }; + + int n = countries.length; + + // the incidence matrix (neighbours) + int[][] mat = { + {0, 1, 0, 1, 0, 0, 1, 1}, + {1, 0, 0, 1, 0, 0, 0, 0}, + {0, 0, 0, 0, 1, 1, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 0}, + {0, 0, 1, 0, 0, 1, 1, 0}, + {0, 0, 1, 0, 1, 0, 1, 1}, + {1, 0, 0, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 1, 1, 0} + }; + + // + // variables + // + + // First army + IntVar[] x = solver.makeIntVarArray(n, 0, 1, "x"); + + // Second (reserve) army + IntVar[] y = solver.makeIntVarArray(n, 0, 1, "y"); + + // total number of armies + IntVar num_armies = solver.makeSum(solver.makeSum(x), solver.makeSum(y)).var(); + + // + // constraints + // + + // + // Constraint 1: There is always an army in a city + // (+ maybe a backup) + // Or rather: Is there a backup, there + // must be an an army + // + for (int i = 0; i < n; i++) { + solver.addConstraint(solver.makeGreaterOrEqual(x[i], y[i])); + } + + // + // Constraint 2: There should always be an backup + // army near every city + // + for (int i = 0; i < n; i++) { + ArrayList count_neighbours = new ArrayList(); + for (int j = 0; j < n; j++) { + if (mat[i][j] == 1) { + count_neighbours.add(y[j]); + } + } + solver.addConstraint( + solver.makeGreaterOrEqual( + solver.makeSum(x[i], solver.makeSum(count_neighbours.toArray(new IntVar[1])).var()), + 1)); + } + + // + // objective + // + OptimizeVar objective = solver.makeMinimize(num_armies, 1); + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db, objective); + + // + // output + // + while (solver.nextSolution()) { + System.out.println("num_armies: " + num_armies.value()); + for (int i = 0; i < n; i++) { + if (x[i].value() == 1) { + System.out.print("Army: " + countries[i] + " "); + } + if (y[i].value() == 1) { + System.out.println("Reserve army: " + countries[i]); + } + } + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + + SetCoveringDeployment.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SimpleCpProgram.java b/libs/or-tools-src-ubuntu/examples/java/SimpleCpProgram.java new file mode 100644 index 0000000000000000000000000000000000000000..f60abd8f9c4c4467868b36f2265eb99cfe6b3b25 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SimpleCpProgram.java @@ -0,0 +1,77 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.util.logging.Logger; +// [END import] + +/** Simple CP Program.*/ +public class SimpleCpProgram { + static { + System.loadLibrary("jniortools"); + } + private SimpleCpProgram() {} + + private static final Logger logger = Logger.getLogger(SimpleCpProgram.class.getName()); + + public static void main(String[] args) throws Exception { + // Instantiate the solver. + // [START solver] + Solver solver = new Solver("CpSimple"); + // [END solver] + + // Create the variables. + // [START variables] + final long numVals = 3; + final IntVar x = solver.makeIntVar(0, numVals - 1, "x"); + final IntVar y = solver.makeIntVar(0, numVals - 1, "y"); + final IntVar z = solver.makeIntVar(0, numVals - 1, "z"); + // [END variables] + + // Constraint 0: x != y.. + // [START constraints] + solver.addConstraint(solver.makeAllDifferent(new IntVar[] {x, y})); + logger.info("Number of constraints: " + solver.constraints()); + // [END constraints] + + // Solve the problem. + // [START solve] + final DecisionBuilder db = solver.makePhase( + new IntVar[] {x, y, z}, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); + // [END solve] + + // Print solution on console. + // [START print_solution] + int count = 0; + solver.newSearch(db); + while (solver.nextSolution()) { + ++count; + logger.info( + String.format("Solution: %d\n x=%d y=%d z=%d", count, x.value(), y.value(), z.value())); + } + solver.endSearch(); + logger.info("Number of solutions found: " + solver.solutions()); + // [END print_solution] + + // [START advanced] + logger.info(String.format("Advanced usage:\nProblem solved in %d ms\nMemory usage: %d bytes", + solver.wallTime(), Solver.memoryUsage())); + // [END advanced] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/SimpleRoutingProgram.java b/libs/or-tools-src-ubuntu/examples/java/SimpleRoutingProgram.java new file mode 100644 index 0000000000000000000000000000000000000000..5dc335f9940fd295eb831c559f3700fd0831c621 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SimpleRoutingProgram.java @@ -0,0 +1,104 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import static java.lang.Math.abs; + +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; +// [END import] + +/** Minimal Routing example to showcase calling the solver.*/ +public class SimpleRoutingProgram { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(SimpleRoutingProgram.class.getName()); + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final int numLocation = 5; + final int numVehicles = 1; + final int depot = 0; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager(numLocation, numVehicles, depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return abs(toNode - fromNode); + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + logger.info("Objective: " + solution.objectiveValue()); + // Inspect solution. + long index = routing.start(0); + logger.info("Route for Vehicle 0:"); + long routeDistance = 0; + String route = ""; + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, 0); + } + route += manager.indexToNode(index); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/SimpleRoutingTest.java b/libs/or-tools-src-ubuntu/examples/java/SimpleRoutingTest.java new file mode 100644 index 0000000000000000000000000000000000000000..97b73e8fe5d8b6edcc1e6081c21599ae71415fbf --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SimpleRoutingTest.java @@ -0,0 +1,117 @@ +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.main; +import java.util.ArrayList; +import java.util.function.LongBinaryOperator; + +public class SimpleRoutingTest { + + // Static Add Library + static { + System.loadLibrary("jniortools"); + } + + private ArrayList globalRes; + private long globalResCost; + private int[][] costMatrix; + + public ArrayList getGlobalRes() { + return globalRes; + } + + public void setGlobalRes(ArrayList globalRes) { + this.globalRes = globalRes; + } + + public long getGlobalResCost() { + return globalResCost; + } + + public void setGlobalResCost(int globalResCost) { + this.globalResCost = globalResCost; + } + + public int[][] getCostMatrix() { + return costMatrix; + } + + public void setCostMatrix(int[][] costMatrix) { + this.costMatrix = costMatrix; + } + + public SimpleRoutingTest(int[][] costMatrix) { + super(); + this.costMatrix = costMatrix; + globalRes = new ArrayList(); + } + + // Node Distance Evaluation + public static class NodeDistance implements LongBinaryOperator { + private int[][] costMatrix; + private RoutingIndexManager indexManager; + + public NodeDistance(RoutingIndexManager manager, int[][] costMatrix) { + this.costMatrix = costMatrix; + this.indexManager = manager; + } + + @Override + public long applyAsLong(long firstIndex, long secondIndex) { + final int firstNode = indexManager.indexToNode(firstIndex); + final int secondNode = indexManager.indexToNode(secondIndex); + return costMatrix[firstNode][secondNode]; + } + } + + // Solve Method + public void solve() { + RoutingIndexManager manager = new RoutingIndexManager(costMatrix.length, 1, 0); + RoutingModel routing = new RoutingModel(manager); + RoutingSearchParameters parameters = + RoutingSearchParameters.newBuilder() + .mergeFrom(main.defaultRoutingSearchParameters()) + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + NodeDistance distances = new NodeDistance(manager, costMatrix); + routing.setArcCostEvaluatorOfAllVehicles(routing.registerTransitCallback(distances)); + + Assignment solution = routing.solve(); + if (solution != null) { + int route_number = 0; + for (long node = routing.start(route_number); + !routing.isEnd(node); + node = solution.value(routing.nextVar(node))) { + globalRes.add((int) node); + } + } + globalResCost = solution.objectiveValue(); + System.out.println("cost = " + globalResCost); + } + + public static void main(String[] args) throws Exception { + int[][] values = new int[4][4]; + values[0][0] = 0; + values[0][1] = 5; + values[0][2] = 3; + values[0][3] = 6; + values[1][0] = 5; + values[1][1] = 0; + values[1][2] = 8; + values[1][3] = 1; + values[2][0] = 3; + values[2][1] = 8; + values[2][2] = 0; + values[2][3] = 4; + values[3][0] = 6; + values[3][1] = 1; + values[3][2] = 4; + values[3][3] = 0; + SimpleRoutingTest model = new SimpleRoutingTest(values); + model.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SimpleSatProgram.java b/libs/or-tools-src-ubuntu/examples/java/SimpleSatProgram.java new file mode 100644 index 0000000000000000000000000000000000000000..b4a3af39884796fca8d957a0bf873877df5cbf52 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SimpleSatProgram.java @@ -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. + +// [START program] +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; +import com.google.ortools.sat.IntVar; + +/** Minimal CP-SAT example to showcase calling the solver. */ +public class SimpleSatProgram { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + // Create the model. + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Create the variables. + // [START variables] + int numVals = 3; + + IntVar x = model.newIntVar(0, numVals - 1, "x"); + IntVar y = model.newIntVar(0, numVals - 1, "y"); + IntVar z = model.newIntVar(0, numVals - 1, "z"); + // [END variables] + + // Create the constraints. + // [START constraints] + model.addDifferent(x, y); + // [END constraints] + + // Create a solver and solve the model. + // [START solve] + CpSolver solver = new CpSolver(); + CpSolverStatus status = solver.solve(model); + // [END solve] + + if (status == CpSolverStatus.FEASIBLE) { + System.out.println("x = " + solver.value(x)); + System.out.println("y = " + solver.value(y)); + System.out.println("z = " + solver.value(z)); + } + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/SolutionHintingSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/SolutionHintingSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..81716a30b1df9011034017ce7ffe12642f322cc5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SolutionHintingSampleSat.java @@ -0,0 +1,85 @@ +// 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] +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.LinearExpr; + +/** Minimal CP-SAT example to showcase calling the solver. */ +public class SolutionHintingSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + // Create the model. + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Create the variables. + // [START variables] + int numVals = 3; + + IntVar x = model.newIntVar(0, numVals - 1, "x"); + IntVar y = model.newIntVar(0, numVals - 1, "y"); + IntVar z = model.newIntVar(0, numVals - 1, "z"); + // [END variables] + + // Create the constraints. + // [START constraints] + model.addDifferent(x, y); + // [END constraints] + + // [START objective] + model.maximize(LinearExpr.scalProd(new IntVar[] {x, y, z}, new int[] {1, 2, 3})); + // [END objective] + + // Solution hinting: x <- 1, y <- 2 + model.addHint(x, 1); + model.addHint(y, 2); + + // Create a solver and solve the model. + // [START solve] + CpSolver solver = new CpSolver(); + VarArraySolutionPrinterWithObjective cb = + new VarArraySolutionPrinterWithObjective(new IntVar[] {x, y, z}); + solver.solveWithSolutionCallback(model, cb); + // [END solve] + } + + static class VarArraySolutionPrinterWithObjective extends CpSolverSolutionCallback { + public VarArraySolutionPrinterWithObjective(IntVar[] variables) { + variableArray = variables; + } + + @Override + public void onSolutionCallback() { + System.out.printf("Solution #%d: time = %.02f s%n", solutionCount, wallTime()); + System.out.printf(" objective value = %f%n", objectiveValue()); + for (IntVar v : variableArray) { + System.out.printf(" %s = %d%n", v.getName(), value(v)); + } + solutionCount++; + } + + private int solutionCount; + private final IntVar[] variableArray; + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/SolveAndPrintIntermediateSolutionsSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/SolveAndPrintIntermediateSolutionsSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..7a49fbcb0b346bc9aab4165c0b1997e513c42f68 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SolveAndPrintIntermediateSolutionsSampleSat.java @@ -0,0 +1,90 @@ +// 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] +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.LinearExpr; + +/** Solves an optimization problem and displays all intermediate solutions. */ +public class SolveAndPrintIntermediateSolutionsSampleSat { + static { + System.loadLibrary("jniortools"); + } + + // [START print_solution] + static class VarArraySolutionPrinterWithObjective extends CpSolverSolutionCallback { + public VarArraySolutionPrinterWithObjective(IntVar[] variables) { + variableArray = variables; + } + + @Override + public void onSolutionCallback() { + System.out.printf("Solution #%d: time = %.02f s%n", solutionCount, wallTime()); + System.out.printf(" objective value = %f%n", objectiveValue()); + for (IntVar v : variableArray) { + System.out.printf(" %s = %d%n", v.getName(), value(v)); + } + solutionCount++; + } + + public int getSolutionCount() { + return solutionCount; + } + + private int solutionCount; + private final IntVar[] variableArray; + } + // [END print_solution] + + public static void main(String[] args) throws Exception { + // Create the model. + // [START model] + CpModel model = new CpModel(); + // [END model] + + // Create the variables. + // [START variables] + int numVals = 3; + + IntVar x = model.newIntVar(0, numVals - 1, "x"); + IntVar y = model.newIntVar(0, numVals - 1, "y"); + IntVar z = model.newIntVar(0, numVals - 1, "z"); + // [END variables] + + // Create the constraint. + // [START constraints] + model.addDifferent(x, y); + // [END constraints] + + // Maximize a linear combination of variables. + // [START objective] + model.maximize(LinearExpr.scalProd(new IntVar[] {x, y, z}, new int[] {1, 2, 3})); + // [END objective] + + // Create a solver and solve the model. + // [START solve] + CpSolver solver = new CpSolver(); + VarArraySolutionPrinterWithObjective cb = + new VarArraySolutionPrinterWithObjective(new IntVar[] {x, y, z}); + solver.solveWithSolutionCallback(model, cb); + // [END solve] + + System.out.println(cb.getSolutionCount() + " solutions found."); + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/SolveWithTimeLimitSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/SolveWithTimeLimitSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..b4157ad6c49b349758dd813352e24cc8051b71c7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SolveWithTimeLimitSampleSat.java @@ -0,0 +1,50 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverStatus; +import com.google.ortools.sat.IntVar; + +/** Solves a problem with a time limit. */ +public class SolveWithTimeLimitSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + // Create the model. + CpModel model = new CpModel(); + // Create the variables. + int numVals = 3; + + IntVar x = model.newIntVar(0, numVals - 1, "x"); + IntVar y = model.newIntVar(0, numVals - 1, "y"); + IntVar z = model.newIntVar(0, numVals - 1, "z"); + // Create the constraint. + model.addDifferent(x, y); + + // Create a solver and solve the model. + CpSolver solver = new CpSolver(); + solver.getParameters().setMaxTimeInSeconds(10.0); + CpSolverStatus status = solver.solve(model); + + if (status == CpSolverStatus.FEASIBLE) { + System.out.println("x = " + solver.value(x)); + System.out.println("y = " + solver.value(y)); + System.out.println("z = " + solver.value(z)); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/StableMarriage.java b/libs/or-tools-src-ubuntu/examples/java/StableMarriage.java new file mode 100644 index 0000000000000000000000000000000000000000..26bad0744e1d249e3ec68a39b6c347912d4e5ef1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/StableMarriage.java @@ -0,0 +1,241 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class StableMarriage { + + static { + System.loadLibrary("jniortools"); + } + + /** + * Solves some stable marriage problems. See + * http://www.hakank.org/google_or_tools/stable_marriage.py + */ + private static void solve(long[][][] ranks, String problem_name) { + + Solver solver = new Solver("StableMarriage"); + + // + // data + // + System.out.println("\n#####################"); + System.out.println("Problem: " + problem_name); + + long[][] rankWomen = ranks[0]; + long[][] rankMen = ranks[1]; + + int n = rankWomen.length; + + // + // variables + // + IntVar[] wife = solver.makeIntVarArray(n, 0, n - 1, "wife"); + IntVar[] husband = solver.makeIntVarArray(n, 0, n - 1, "husband"); + + // + // constraints + // (the comments are the Comet code) + // forall(m in Men) + // cp.post(husband[wife[m]] == m); + for (int m = 0; m < n; m++) { + solver.addConstraint(solver.makeEquality(solver.makeElement(husband, wife[m]), m)); + } + + // forall(w in Women) + // cp.post(wife[husband[w]] == w); + for (int w = 0; w < n; w++) { + solver.addConstraint(solver.makeEquality(solver.makeElement(wife, husband[w]), w)); + } + + // forall(m in Men, o in Women) + // cp.post(rankMen[m,o] < rankMen[m, wife[m]] => + // rankWomen[o,husband[o]] < rankWomen[o,m]); + for (int m = 0; m < n; m++) { + for (int o = 0; o < n; o++) { + IntVar b1 = + solver.makeIsGreaterCstVar( + solver.makeElement(rankMen[m], wife[m]).var(), rankMen[m][o]); + + IntVar b2 = + solver.makeIsLessCstVar( + solver.makeElement(rankWomen[o], husband[o]).var(), rankWomen[o][m]); + solver.addConstraint(solver.makeLessOrEqual(solver.makeDifference(b1, b2), 0)); + } + } + + // forall(w in Women, o in Men) + // cp.post(rankWomen[w,o] < rankWomen[w,husband[w]] => + // rankMen[o,wife[o]] < rankMen[o,w]); + for (int w = 0; w < n; w++) { + for (int o = 0; o < n; o++) { + IntVar b1 = + solver.makeIsGreaterCstVar( + solver.makeElement(rankWomen[w], husband[w]).var(), rankWomen[w][o]); + IntVar b2 = + solver.makeIsLessCstVar(solver.makeElement(rankMen[o], wife[o]).var(), rankMen[o][w]); + solver.addConstraint(solver.makeLessOrEqual(solver.makeDifference(b1, b2), 0)); + } + } + + // + // search + // + DecisionBuilder db = solver.makePhase(wife, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + System.out.print("wife : "); + for (int i = 0; i < n; i++) { + System.out.print(wife[i].value() + " "); + } + System.out.print("\nhusband: "); + for (int i = 0; i < n; i++) { + System.out.print(husband[i].value() + " "); + } + System.out.println("\n"); + } + + solver.endSearch(); + + // Statistics + // System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + + // + // From Pascal Van Hentenryck's OPL book + // + long[][][] van_hentenryck = { + // rankWomen + { + {1, 2, 4, 3, 5}, + {3, 5, 1, 2, 4}, + {5, 4, 2, 1, 3}, + {1, 3, 5, 4, 2}, + {4, 2, 3, 5, 1} + }, + + // rankMen + { + {5, 1, 2, 4, 3}, + {4, 1, 3, 2, 5}, + {5, 3, 2, 4, 1}, + {1, 5, 4, 3, 2}, + {4, 3, 2, 1, 5} + } + }; + + // + // Data from MathWorld + // http://mathworld.wolfram.com/StableMarriageProblem.html + // + long[][][] mathworld = { + // rankWomen + { + {3, 1, 5, 2, 8, 7, 6, 9, 4}, + {9, 4, 8, 1, 7, 6, 3, 2, 5}, + {3, 1, 8, 9, 5, 4, 2, 6, 7}, + {8, 7, 5, 3, 2, 6, 4, 9, 1}, + {6, 9, 2, 5, 1, 4, 7, 3, 8}, + {2, 4, 5, 1, 6, 8, 3, 9, 7}, + {9, 3, 8, 2, 7, 5, 4, 6, 1}, + {6, 3, 2, 1, 8, 4, 5, 9, 7}, + {8, 2, 6, 4, 9, 1, 3, 7, 5} + }, + + // rankMen + { + {7, 3, 8, 9, 6, 4, 2, 1, 5}, + {5, 4, 8, 3, 1, 2, 6, 7, 9}, + {4, 8, 3, 9, 7, 5, 6, 1, 2}, + {9, 7, 4, 2, 5, 8, 3, 1, 6}, + {2, 6, 4, 9, 8, 7, 5, 1, 3}, + {2, 7, 8, 6, 5, 3, 4, 1, 9}, + {1, 6, 2, 3, 8, 5, 4, 9, 7}, + {5, 6, 9, 1, 2, 8, 4, 3, 7}, + {6, 1, 4, 7, 5, 8, 3, 9, 2} + } + }; + + // + // Data from + // http://www.csee.wvu.edu/~ksmani/courses/fa01/random/lecnotes/lecture5.pdf + // + long[][][] problem3 = { + // rankWomen + { + {1, 2, 3, 4}, + {4, 3, 2, 1}, + {1, 2, 3, 4}, + {3, 4, 1, 2} + }, + + // rankMen" + { + {1, 2, 3, 4}, + {2, 1, 3, 4}, + {1, 4, 3, 2}, + {4, 3, 1, 2} + } + }; + + // + // Data from + // http://www.comp.rgu.ac.uk/staff/ha/ZCSP/additional_problems/stable_marriage/stable_marriage.pdf + // page 4 + // + long[][][] problem4 = { + // rankWomen + { + {1, 5, 4, 6, 2, 3}, + {4, 1, 5, 2, 6, 3}, + {6, 4, 2, 1, 5, 3}, + {1, 5, 2, 4, 3, 6}, + {4, 2, 1, 5, 6, 3}, + {2, 6, 3, 5, 1, 4} + }, + + // rankMen + { + {1, 4, 2, 5, 6, 3}, + {3, 4, 6, 1, 5, 2}, + {1, 6, 4, 2, 3, 5}, + {6, 5, 3, 4, 2, 1}, + {3, 1, 2, 4, 5, 6}, + {2, 3, 1, 6, 5, 4} + } + }; + + StableMarriage.solve(van_hentenryck, "Van Hentenryck"); + StableMarriage.solve(mathworld, "MathWorld"); + StableMarriage.solve(problem3, "Problem 3"); + StableMarriage.solve(problem4, "Problem 4"); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/StepFunctionSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/StepFunctionSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..87d5deb8221d31ab22797aa9d2a096d39ae5f6e0 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/StepFunctionSampleSat.java @@ -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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.DecisionStrategyProto; +import com.google.ortools.sat.IntVar; +import com.google.ortools.sat.Literal; +import com.google.ortools.sat.SatParameters; +import com.google.ortools.util.Domain; + +/** Link integer constraints together. */ +public class StepFunctionSampleSat { + static { + System.loadLibrary("jniortools"); + } + + public static void main(String[] args) throws Exception { + // Create the CP-SAT model. + CpModel model = new CpModel(); + + // Declare our primary variable. + IntVar x = model.newIntVar(0, 20, "x"); + + // Create the expression variable and implement the step function + // Note it is not defined for var == 2. + // + // - 3 + // -- -- --------- 2 + // 1 + // -- --- 0 + // 0 ================ 20 + // + IntVar expr = model.newIntVar(0, 3, "expr"); + + // expr == 0 on [5, 6] U [8, 10] + Literal b0 = model.newBoolVar("b0"); + model.addLinearExpressionInDomain(x, Domain.fromValues(new long[] {5, 6, 8, 9, 10})) + .onlyEnforceIf(b0); + model.addEquality(expr, 0).onlyEnforceIf(b0); + + // expr == 2 on [0, 1] U [3, 4] U [11, 20] + Literal b2 = model.newBoolVar("b2"); + model + .addLinearExpressionInDomain( + x, Domain.fromIntervals(new long[][] {{0, 1}, {3, 4}, {11, 20}})) + .onlyEnforceIf(b2); + model.addEquality(expr, 2).onlyEnforceIf(b2); + + // expr == 3 when x = 7 + Literal b3 = model.newBoolVar("b3"); + model.addEquality(x, 7).onlyEnforceIf(b3); + model.addEquality(expr, 3).onlyEnforceIf(b3); + + // At least one bi is true. (we could use a sum == 1). + model.addBoolOr(new Literal[] {b0, b2, b3}); + + // Search for x values in increasing order. + model.addDecisionStrategy(new IntVar[] {x}, + DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST, + DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE); + + // Create the solver. + CpSolver solver = new CpSolver(); + + // Force the solver to follow the decision strategy exactly. + solver.getParameters().setSearchBranching(SatParameters.SearchBranching.FIXED_SEARCH); + + // Solve the problem with the printer callback. + solver.searchAllSolutions(model, new CpSolverSolutionCallback() { + public CpSolverSolutionCallback init(IntVar[] variables) { + variableArray = variables; + return this; + } + + @Override + public void onSolutionCallback() { + for (IntVar v : variableArray) { + System.out.printf("%s=%d ", v.getName(), value(v)); + } + System.out.println(); + } + + private IntVar[] variableArray; + }.init(new IntVar[] {x, expr})); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/StopAfterNSolutionsSampleSat.java b/libs/or-tools-src-ubuntu/examples/java/StopAfterNSolutionsSampleSat.java new file mode 100644 index 0000000000000000000000000000000000000000..8c340e6b8210b89fc83a3011e1ef0f36c780659c --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/StopAfterNSolutionsSampleSat.java @@ -0,0 +1,76 @@ +// 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. + +package com.google.ortools.examples; + +import com.google.ortools.sat.CpModel; +import com.google.ortools.sat.CpSolver; +import com.google.ortools.sat.CpSolverSolutionCallback; +import com.google.ortools.sat.IntVar; + +/** Code sample that solves a model and displays a small number of solutions. */ +public class StopAfterNSolutionsSampleSat { + static { + System.loadLibrary("jniortools"); + } + + static class VarArraySolutionPrinterWithLimit extends CpSolverSolutionCallback { + public VarArraySolutionPrinterWithLimit(IntVar[] variables, int limit) { + variableArray = variables; + solutionLimit = limit; + } + + @Override + public void onSolutionCallback() { + System.out.printf("Solution #%d: time = %.02f s%n", solutionCount, wallTime()); + for (IntVar v : variableArray) { + System.out.printf(" %s = %d%n", v.getName(), value(v)); + } + solutionCount++; + if (solutionCount >= solutionLimit) { + System.out.printf("Stop search after %d solutions%n", solutionLimit); + stopSearch(); + } + } + + public int getSolutionCount() { + return solutionCount; + } + + private int solutionCount; + private final IntVar[] variableArray; + private final int solutionLimit; + } + + public static void main(String[] args) throws Exception { + // Create the model. + CpModel model = new CpModel(); + // Create the variables. + int numVals = 3; + + IntVar x = model.newIntVar(0, numVals - 1, "x"); + IntVar y = model.newIntVar(0, numVals - 1, "y"); + IntVar z = model.newIntVar(0, numVals - 1, "z"); + + // Create a solver and solve the model. + CpSolver solver = new CpSolver(); + VarArraySolutionPrinterWithLimit cb = + new VarArraySolutionPrinterWithLimit(new IntVar[] {x, y, z}, 5); + solver.searchAllSolutions(model, cb); + + System.out.println(cb.getSolutionCount() + " solutions found."); + if (cb.getSolutionCount() != 5) { + throw new RuntimeException("Did not stop the search correctly."); + } + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Strimko2.java b/libs/or-tools-src-ubuntu/examples/java/Strimko2.java new file mode 100644 index 0000000000000000000000000000000000000000..ad230ac02a0621820c7fe83743df0bbd98a79905 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Strimko2.java @@ -0,0 +1,146 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class Strimko2 { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves a Strimko problem. See http://www.hakank.org/google_or_tools/strimko2.py */ + private static void solve() { + + Solver solver = new Solver("Strimko2"); + + // + // data + // + int[][] streams = { + {1, 1, 2, 2, 2, 2, 2}, + {1, 1, 2, 3, 3, 3, 2}, + {1, 4, 1, 3, 3, 5, 5}, + {4, 4, 3, 1, 3, 5, 5}, + {4, 6, 6, 6, 7, 7, 5}, + {6, 4, 6, 4, 5, 5, 7}, + {6, 6, 4, 7, 7, 7, 7} + }; + + // Note: This is 1-based + int[][] placed = { + {2, 1, 1}, + {2, 3, 7}, + {2, 5, 6}, + {2, 7, 4}, + {3, 2, 7}, + {3, 6, 1}, + {4, 1, 4}, + {4, 7, 5}, + {5, 2, 2}, + {5, 6, 6} + }; + + int n = streams.length; + int num_placed = placed.length; + + // + // variables + // + + IntVar[][] x = new IntVar[n][n]; + IntVar[] x_flat = new IntVar[n * n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + x[i][j] = solver.makeIntVar(1, n, "x[" + i + "," + j + "]"); + x_flat[i * n + j] = x[i][j]; + } + } + + // + // constraints + // + + // all rows and columns must be unique, i.e. a Latin Square + for (int i = 0; i < n; i++) { + + IntVar[] row = new IntVar[n]; + IntVar[] col = new IntVar[n]; + for (int j = 0; j < n; j++) { + row[j] = x[i][j]; + col[j] = x[j][i]; + } + + solver.addConstraint(solver.makeAllDifferent(row)); + solver.addConstraint(solver.makeAllDifferent(col)); + } + + // streams + for (int s = 1; s <= n; s++) { + ArrayList tmp = new ArrayList(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (streams[i][j] == s) { + tmp.add(x[i][j]); + } + } + } + solver.addConstraint(solver.makeAllDifferent(tmp.toArray(new IntVar[1]))); + } + + // placed + for (int i = 0; i < num_placed; i++) { + // note: also adjust to 0-based + solver.addConstraint( + solver.makeEquality(x[placed[i][0] - 1][placed[i][1] - 1], placed[i][2])); + } + + // + // search + // + DecisionBuilder db = solver.makePhase(x_flat, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT); + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + System.out.print(x[i][j].value() + " "); + } + System.out.println(); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + + Strimko2.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Sudoku.java b/libs/or-tools-src-ubuntu/examples/java/Sudoku.java new file mode 100644 index 0000000000000000000000000000000000000000..45043b8f5d865e11c142d010fabf5f093967c6ad --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Sudoku.java @@ -0,0 +1,131 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class Sudoku { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves a Sudoku problem. */ + private static void solve() { + + Solver solver = new Solver("Sudoku"); + + int cell_size = 3; + int n = cell_size * cell_size; + + // 0 marks an unknown value + int[][] initial_grid = + new int[][] { + {0, 6, 0, 0, 5, 0, 0, 2, 0}, + {0, 0, 0, 3, 0, 0, 0, 9, 0}, + {7, 0, 0, 6, 0, 0, 0, 1, 0}, + {0, 0, 6, 0, 3, 0, 4, 0, 0}, + {0, 0, 4, 0, 7, 0, 1, 0, 0}, + {0, 0, 5, 0, 9, 0, 8, 0, 0}, + {0, 4, 0, 0, 0, 1, 0, 0, 6}, + {0, 3, 0, 0, 0, 8, 0, 0, 0}, + {0, 2, 0, 0, 4, 0, 0, 5, 0} + }; + + // + // variables + // + IntVar[][] grid = new IntVar[n][n]; + IntVar[] grid_flat = new IntVar[n * n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + grid[i][j] = solver.makeIntVar(1, 9, "grid[" + i + "," + j + "]"); + grid_flat[i * n + j] = grid[i][j]; + } + } + + // + // constraints + // + + // init and rows + for (int i = 0; i < n; i++) { + IntVar[] row = new IntVar[n]; + for (int j = 0; j < n; j++) { + if (initial_grid[i][j] > 0) { + solver.addConstraint(solver.makeEquality(grid[i][j], initial_grid[i][j])); + } + row[j] = grid[i][j]; + } + solver.addConstraint(solver.makeAllDifferent(row)); + } + + // columns + for (int j = 0; j < n; j++) { + IntVar[] col = new IntVar[n]; + for (int i = 0; i < n; i++) { + col[i] = grid[i][j]; + } + solver.addConstraint(solver.makeAllDifferent(col)); + } + + // cells + for (int i = 0; i < cell_size; i++) { + for (int j = 0; j < cell_size; j++) { + IntVar[] cell = new IntVar[n]; + for (int di = 0; di < cell_size; di++) { + for (int dj = 0; dj < cell_size; dj++) { + cell[di * cell_size + dj] = grid[i * cell_size + di][j * cell_size + dj]; + } + } + solver.addConstraint(solver.makeAllDifferent(cell)); + } + } + + // + // Search + // + DecisionBuilder db = + solver.makePhase(grid_flat, solver.INT_VAR_SIMPLE, solver.INT_VALUE_SIMPLE); + + solver.newSearch(db); + + while (solver.nextSolution()) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + System.out.print(grid[i][j].value() + " "); + } + System.out.println(); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + Sudoku.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/SurvoPuzzle.java b/libs/or-tools-src-ubuntu/examples/java/SurvoPuzzle.java new file mode 100644 index 0000000000000000000000000000000000000000..e4bf5cbc4fc57721eec05b7cf61edb45983746c4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/SurvoPuzzle.java @@ -0,0 +1,217 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class SurvoPuzzle { + + static { + System.loadLibrary("jniortools"); + } + + /* + * default problem + */ + static int default_r = 3; + static int default_c = 4; + static int[] default_rowsums = {30, 18, 30}; + static int[] default_colsums = {27, 16, 10, 25}; + static int[][] default_game = { + {0, 6, 0, 0}, + {8, 0, 0, 0}, + {0, 0, 3, 0} + }; + + // for the actual problem + static int r; + static int c; + static int[] rowsums; + static int[] colsums; + static int[][] game; + + /** Solves the Survo puzzle problem. See http://www.hakank.org/google_or_tools/survo_puzzle.py */ + private static void solve() { + + Solver solver = new Solver("Survopuzzle"); + + // + // data + // + System.out.println("Problem:"); + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + System.out.print(game[i][j] + " "); + } + System.out.println(); + } + System.out.println(); + + // + // Variables + // + IntVar[][] x = new IntVar[r][c]; + IntVar[] x_flat = new IntVar[r * c]; // for branching + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + x[i][j] = solver.makeIntVar(1, r * c, "x[" + i + "," + j + "]"); + x_flat[i * c + j] = x[i][j]; + } + } + + // + // Constraints + // + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if (game[i][j] > 0) { + solver.addConstraint(solver.makeEquality(x[i][j], game[i][j])); + } + } + } + + solver.addConstraint(solver.makeAllDifferent(x_flat)); + + // + // calculate rowsums and colsums + // + for (int i = 0; i < r; i++) { + IntVar[] row = new IntVar[c]; + for (int j = 0; j < c; j++) { + row[j] = x[i][j]; + } + solver.addConstraint(solver.makeEquality(solver.makeSum(row).var(), rowsums[i])); + } + + for (int j = 0; j < c; j++) { + IntVar[] col = new IntVar[r]; + for (int i = 0; i < r; i++) { + col[i] = x[i][j]; + } + solver.addConstraint(solver.makeEquality(solver.makeSum(col).var(), colsums[j])); + } + + // + // Search + // + DecisionBuilder db = solver.makePhase(x_flat, solver.INT_VAR_SIMPLE, solver.ASSIGN_MIN_VALUE); + solver.newSearch(db); + + int sol = 0; + while (solver.nextSolution()) { + sol++; + System.out.println("Solution #" + sol + ":"); + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + System.out.print(x[i][j].value() + " "); + } + System.out.println(); + } + System.out.println(); + } + + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + /** + * readFile() + * + *

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

Example: 3 4 30,18,30 27,16,10,25 0,6,0,0 8,0,0,0 0,0,3,0 + */ + private static void readFile(String file) { + + System.out.println("readFile(" + file + ")"); + + try { + + BufferedReader inr = new BufferedReader(new FileReader(file)); + + r = Integer.parseInt(inr.readLine()); + c = Integer.parseInt(inr.readLine()); + rowsums = new int[r]; + colsums = new int[c]; + System.out.println("r: " + r + " c: " + c); + + String[] rowsums_str = inr.readLine().split(",\\s*"); + String[] colsums_str = inr.readLine().split(",\\s*"); + System.out.println("rowsums:"); + for (int i = 0; i < r; i++) { + System.out.print(rowsums_str[i] + " "); + rowsums[i] = Integer.parseInt(rowsums_str[i]); + } + System.out.println("\ncolsums:"); + for (int j = 0; j < c; j++) { + System.out.print(colsums_str[j] + " "); + colsums[j] = Integer.parseInt(colsums_str[j]); + } + System.out.println(); + + // init the game matrix and read data from file + game = new int[r][c]; + String str; + int line_count = 0; + while ((str = inr.readLine()) != null && str.length() > 0) { + str = str.trim(); + + // ignore comments + // starting with either # or % + if (str.startsWith("#") || str.startsWith("%")) { + continue; + } + + String this_row[] = str.split(",\\s*"); + for (int j = 0; j < this_row.length; j++) { + game[line_count][j] = Integer.parseInt(this_row[j]); + } + + line_count++; + } // end while + + inr.close(); + + } catch (IOException e) { + System.out.println(e); + } + } // end readFile + + public static void main(String[] args) throws Exception { + + if (args.length > 0) { + String file = args[0]; + SurvoPuzzle.readFile(file); + } else { + r = default_r; + c = default_c; + game = default_game; + rowsums = default_rowsums; + colsums = default_colsums; + } + + SurvoPuzzle.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/ToNum.java b/libs/or-tools-src-ubuntu/examples/java/ToNum.java new file mode 100644 index 0000000000000000000000000000000000000000..30e546b7ed1c3c3e4f21d64030d2c3b9e74e72f2 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/ToNum.java @@ -0,0 +1,101 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class ToNum { + + static { + System.loadLibrary("jniortools"); + } + + /** + * toNum(solver, a, num, base) + * + *

channelling between the array a and the number num + */ + private static void toNum(Solver solver, IntVar[] a, IntVar num, int base) { + int len = a.length; + + IntVar[] tmp = new IntVar[len]; + for (int i = 0; i < len; i++) { + tmp[i] = solver.makeProd(a[i], (int) Math.pow(base, (len - i - 1))).var(); + } + solver.addConstraint(solver.makeEquality(solver.makeSum(tmp).var(), num)); + } + + /** + * Implements toNum: channeling between a number and an array. See + * http://www.hakank.org/google_or_tools/toNum.py + */ + private static void solve() { + + Solver solver = new Solver("ToNum"); + + int n = 5; + int base = 10; + + // + // variables + // + IntVar[] x = solver.makeIntVarArray(n, 0, base - 1, "x"); + IntVar num = solver.makeIntVar(0, (int) Math.pow(base, n) - 1, "num"); + + // + // constraints + // + solver.addConstraint(solver.makeAllDifferent(x)); + + toNum(solver, x, num, base); + + // extra constraint (just for fun): + // second digit should be 7 + // solver.addConstraint(solver.makeEquality(x[1], 7)); + + // + // search + // + DecisionBuilder db = solver.makePhase(x, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); + + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + System.out.print("num: " + num.value() + ": "); + for (int i = 0; i < n; i++) { + System.out.print(x[i].value() + " "); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + ToNum.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Tsp.java b/libs/or-tools-src-ubuntu/examples/java/Tsp.java new file mode 100644 index 0000000000000000000000000000000000000000..356c3b2462d4f311d40c1b491939eef4e135c7c4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Tsp.java @@ -0,0 +1,175 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import static java.lang.Math.abs; + +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.main; +import java.util.function.LongBinaryOperator; +import java.util.logging.Logger; +// [END import] + +/** Minimal TSP.*/ +public class Tsp { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(Tsp.class.getName()); + + // [START data_model] + static class DataModel { + public final int[][] locations = { + {4, 4}, + {2, 0}, + {8, 0}, + {0, 1}, + {1, 1}, + {5, 2}, + {7, 2}, + {3, 3}, + {6, 3}, + {5, 5}, + {8, 5}, + {1, 6}, + {2, 6}, + {3, 7}, + {6, 7}, + {0, 8}, + {7, 8}, + }; + public final int vehicleNumber = 1; + public final int depot = 0; + public DataModel() { + // Convert locations in meters using a city block dimension of 114m x 80m. + for (int[] element : locations) { + element[0] *= 114; + element[1] *= 80; + } + } + } + // [END data_model] + + // [START manhattan_distance] + /// @brief Manhattan distance implemented as a callback. + /// @details It uses an array of positions and computes + /// the Manhattan distance between the two positions of + /// two different indices. + static class ManhattanDistance implements LongBinaryOperator { + public ManhattanDistance(DataModel data, RoutingIndexManager manager) { + // precompute distance between location to have distance callback in O(1) + distanceMatrix = new long[data.locations.length][data.locations.length]; + indexManager = manager; + for (int fromNode = 0; fromNode < data.locations.length; ++fromNode) { + for (int toNode = 0; toNode < data.locations.length; ++toNode) { + if (fromNode == toNode) { + distanceMatrix[fromNode][toNode] = 0; + } else { + distanceMatrix[fromNode][toNode] = + (long) abs(data.locations[toNode][0] - data.locations[fromNode][0]) + + (long) abs(data.locations[toNode][1] - data.locations[fromNode][1]); + } + } + } + } + @Override + public long applyAsLong(long fromIndex, long toIndex) { + // Convert from routing variable Index to distance matrix NodeIndex. + int fromNode = indexManager.indexToNode(fromIndex); + int toNode = indexManager.indexToNode(toIndex); + return distanceMatrix[fromNode][toNode]; + } + private final long[][] distanceMatrix; + private final RoutingIndexManager indexManager; + } + // [END manhattan_distance] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Solution cost. + logger.info("Objective : " + solution.objectiveValue()); + // Inspect solution. + logger.info("Route for Vehicle 0:"); + long routeDistance = 0; + String route = ""; + long index = routing.start(0); + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, 0); + } + route += manager.indexToNode(routing.end(0)); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.locations.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback(new ManhattanDistance(data, manager)); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/TspCircuitBoard.java b/libs/or-tools-src-ubuntu/examples/java/TspCircuitBoard.java new file mode 100644 index 0000000000000000000000000000000000000000..98bed6e1986c2797e93a83700997a72260f63bf9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/TspCircuitBoard.java @@ -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. + +// [START program] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; + +// [END import] + +/** Minimal TSP. */ +public class TspCircuitBoard { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(TspCircuitBoard.class.getName()); + + // [START data_model] + static class DataModel { + public final int[][] locations = {{288, 149}, {288, 129}, {270, 133}, {256, 141}, {256, 157}, + {246, 157}, {236, 169}, {228, 169}, {228, 161}, {220, 169}, {212, 169}, {204, 169}, + {196, 169}, {188, 169}, {196, 161}, {188, 145}, {172, 145}, {164, 145}, {156, 145}, + {148, 145}, {140, 145}, {148, 169}, {164, 169}, {172, 169}, {156, 169}, {140, 169}, + {132, 169}, {124, 169}, {116, 161}, {104, 153}, {104, 161}, {104, 169}, {90, 165}, + {80, 157}, {64, 157}, {64, 165}, {56, 169}, {56, 161}, {56, 153}, {56, 145}, {56, 137}, + {56, 129}, {56, 121}, {40, 121}, {40, 129}, {40, 137}, {40, 145}, {40, 153}, {40, 161}, + {40, 169}, {32, 169}, {32, 161}, {32, 153}, {32, 145}, {32, 137}, {32, 129}, {32, 121}, + {32, 113}, {40, 113}, {56, 113}, {56, 105}, {48, 99}, {40, 99}, {32, 97}, {32, 89}, + {24, 89}, {16, 97}, {16, 109}, {8, 109}, {8, 97}, {8, 89}, {8, 81}, {8, 73}, {8, 65}, + {8, 57}, {16, 57}, {8, 49}, {8, 41}, {24, 45}, {32, 41}, {32, 49}, {32, 57}, {32, 65}, + {32, 73}, {32, 81}, {40, 83}, {40, 73}, {40, 63}, {40, 51}, {44, 43}, {44, 35}, {44, 27}, + {32, 25}, {24, 25}, {16, 25}, {16, 17}, {24, 17}, {32, 17}, {44, 11}, {56, 9}, {56, 17}, + {56, 25}, {56, 33}, {56, 41}, {64, 41}, {72, 41}, {72, 49}, {56, 49}, {48, 51}, {56, 57}, + {56, 65}, {48, 63}, {48, 73}, {56, 73}, {56, 81}, {48, 83}, {56, 89}, {56, 97}, {104, 97}, + {104, 105}, {104, 113}, {104, 121}, {104, 129}, {104, 137}, {104, 145}, {116, 145}, + {124, 145}, {132, 145}, {132, 137}, {140, 137}, {148, 137}, {156, 137}, {164, 137}, + {172, 125}, {172, 117}, {172, 109}, {172, 101}, {172, 93}, {172, 85}, {180, 85}, {180, 77}, + {180, 69}, {180, 61}, {180, 53}, {172, 53}, {172, 61}, {172, 69}, {172, 77}, {164, 81}, + {148, 85}, {124, 85}, {124, 93}, {124, 109}, {124, 125}, {124, 117}, {124, 101}, {104, 89}, + {104, 81}, {104, 73}, {104, 65}, {104, 49}, {104, 41}, {104, 33}, {104, 25}, {104, 17}, + {92, 9}, {80, 9}, {72, 9}, {64, 21}, {72, 25}, {80, 25}, {80, 25}, {80, 41}, {88, 49}, + {104, 57}, {124, 69}, {124, 77}, {132, 81}, {140, 65}, {132, 61}, {124, 61}, {124, 53}, + {124, 45}, {124, 37}, {124, 29}, {132, 21}, {124, 21}, {120, 9}, {128, 9}, {136, 9}, + {148, 9}, {162, 9}, {156, 25}, {172, 21}, {180, 21}, {180, 29}, {172, 29}, {172, 37}, + {172, 45}, {180, 45}, {180, 37}, {188, 41}, {196, 49}, {204, 57}, {212, 65}, {220, 73}, + {228, 69}, {228, 77}, {236, 77}, {236, 69}, {236, 61}, {228, 61}, {228, 53}, {236, 53}, + {236, 45}, {228, 45}, {228, 37}, {236, 37}, {236, 29}, {228, 29}, {228, 21}, {236, 21}, + {252, 21}, {260, 29}, {260, 37}, {260, 45}, {260, 53}, {260, 61}, {260, 69}, {260, 77}, + {276, 77}, {276, 69}, {276, 61}, {276, 53}, {284, 53}, {284, 61}, {284, 69}, {284, 77}, + {284, 85}, {284, 93}, {284, 101}, {288, 109}, {280, 109}, {276, 101}, {276, 93}, {276, 85}, + {268, 97}, {260, 109}, {252, 101}, {260, 93}, {260, 85}, {236, 85}, {228, 85}, {228, 93}, + {236, 93}, {236, 101}, {228, 101}, {228, 109}, {228, 117}, {228, 125}, {220, 125}, + {212, 117}, {204, 109}, {196, 101}, {188, 93}, {180, 93}, {180, 101}, {180, 109}, + {180, 117}, {180, 125}, {196, 145}, {204, 145}, {212, 145}, {220, 145}, {228, 145}, + {236, 145}, {246, 141}, {252, 125}, {260, 129}, {280, 133}}; + public final int vehicleNumber = 1; + public final int depot = 0; + } + // [END data_model] + + // [START euclidean_distance] + /// @brief Compute Euclidean distance matrix from locations array. + /// @details It uses an array of locations and computes + /// the Euclidean distance between any two locations. + private static long[][] computeEuclideanDistanceMatrix(int[][] locations) { + // Calculate distance matrix using Euclidean distance. + long[][] distanceMatrix = new long[locations.length][locations.length]; + for (int fromNode = 0; fromNode < locations.length; ++fromNode) { + for (int toNode = 0; toNode < locations.length; ++toNode) { + if (fromNode == toNode) { + distanceMatrix[fromNode][toNode] = 0; + } else { + distanceMatrix[fromNode][toNode] = + (long) Math.hypot(locations[toNode][0] - locations[fromNode][0], + locations[toNode][1] - locations[fromNode][1]); + } + } + } + return distanceMatrix; + } + // [END euclidean_distance] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Solution cost. + logger.info("Objective: " + solution.objectiveValue()); + // Inspect solution. + logger.info("Route:"); + long routeDistance = 0; + String route = ""; + long index = routing.start(0); + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routing.getArcCostForVehicle(previousIndex, index, 0); + } + route += manager.indexToNode(routing.end(0)); + logger.info(route); + logger.info("Route distance: " + routeDistance); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.locations.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final long[][] distanceMatrix = computeEuclideanDistanceMatrix(data.locations); + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/TspCities.java b/libs/or-tools-src-ubuntu/examples/java/TspCities.java new file mode 100644 index 0000000000000000000000000000000000000000..b3db984160fbc2f87ef0c6522b6bd0e25e899343 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/TspCities.java @@ -0,0 +1,133 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; + +// [END import] + +/** Minimal TSP using distance matrix. */ +public class TspCities { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(TspCities.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 2451, 713, 1018, 1631, 1374, 2408, 213, 2571, 875, 1420, 2145, 1972}, + {2451, 0, 1745, 1524, 831, 1240, 959, 2596, 403, 1589, 1374, 357, 579}, + {713, 1745, 0, 355, 920, 803, 1737, 851, 1858, 262, 940, 1453, 1260}, + {1018, 1524, 355, 0, 700, 862, 1395, 1123, 1584, 466, 1056, 1280, 987}, + {1631, 831, 920, 700, 0, 663, 1021, 1769, 949, 796, 879, 586, 371}, + {1374, 1240, 803, 862, 663, 0, 1681, 1551, 1765, 547, 225, 887, 999}, + {2408, 959, 1737, 1395, 1021, 1681, 0, 2493, 678, 1724, 1891, 1114, 701}, + {213, 2596, 851, 1123, 1769, 1551, 2493, 0, 2699, 1038, 1605, 2300, 2099}, + {2571, 403, 1858, 1584, 949, 1765, 678, 2699, 0, 1744, 1645, 653, 600}, + {875, 1589, 262, 466, 796, 547, 1724, 1038, 1744, 0, 679, 1272, 1162}, + {1420, 1374, 940, 1056, 879, 225, 1891, 1605, 1645, 679, 0, 1017, 1200}, + {2145, 357, 1453, 1280, 586, 887, 1114, 2300, 653, 1272, 1017, 0, 504}, + {1972, 579, 1260, 987, 371, 999, 701, 2099, 600, 1162, 1200, 504, 0}, + }; + public final int vehicleNumber = 1; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Solution cost. + logger.info("Objective: " + solution.objectiveValue() + "miles"); + // Inspect solution. + logger.info("Route:"); + long routeDistance = 0; + String route = ""; + long index = routing.start(0); + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, 0); + } + route += manager.indexToNode(routing.end(0)); + logger.info(route); + logger.info("Route distance: " + routeDistance + "miles"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/TspDistanceMatrix.java b/libs/or-tools-src-ubuntu/examples/java/TspDistanceMatrix.java new file mode 100644 index 0000000000000000000000000000000000000000..a36f02621d047d9b68cbbbaad0ec14fbdf7e7318 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/TspDistanceMatrix.java @@ -0,0 +1,136 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; +// [END import] + +/** Minimal TSP using distance matrix.*/ +public class TspDistanceMatrix { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(TspDistanceMatrix.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + public final int vehicleNumber = 1; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Solution cost. + logger.info("Objective : " + solution.objectiveValue()); + // Inspect solution. + logger.info("Route for Vehicle 0:"); + long routeDistance = 0; + String route = ""; + long index = routing.start(0); + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, 0); + } + route += manager.indexToNode(routing.end(0)); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/Vrp.java b/libs/or-tools-src-ubuntu/examples/java/Vrp.java new file mode 100644 index 0000000000000000000000000000000000000000..3524ca0a53c08f64a185e76b529b76061041a94f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Vrp.java @@ -0,0 +1,141 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; +// [END import] + +/** Minimal VRP.*/ +public class Vrp { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(Vrp.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + public final int vehicleNumber = 4; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Solution cost. + logger.info("Objective : " + solution.objectiveValue()); + // Inspect solution. + long totalDistance = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + long index = routing.start(i); + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + route += manager.indexToNode(routing.end(i)); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); + totalDistance += routeDistance; + } + logger.info("Total Distance of all routes: " + totalDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpCapacity.java b/libs/or-tools-src-ubuntu/examples/java/VrpCapacity.java new file mode 100644 index 0000000000000000000000000000000000000000..f14bf6dc5875de5ff4d6943aa4a4ab6aa3162eab --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpCapacity.java @@ -0,0 +1,162 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; +// [END import] + +/** Minimal VRP.*/ +public class VrpCapacity { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpCapacity.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + // [START demands_capacities] + public final long[] demands = {0, 1, 1, 2, 4, 2, 4, 8, 8, 1, 2, 1, 2, 4, 4, 8, 8}; + public final long[] vehicleCapacities = {15, 15, 15, 15}; + // [END demands_capacities] + public final int vehicleNumber = 4; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Inspect solution. + long totalDistance = 0; + long totalLoad = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + long routeLoad = 0; + String route = ""; + while (!routing.isEnd(index)) { + long nodeIndex = manager.indexToNode(index); + routeLoad += data.demands[(int) nodeIndex]; + route += nodeIndex + " Load(" + routeLoad + ") -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + route += manager.indexToNode(routing.end(i)); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); + totalDistance += routeDistance; + totalLoad += routeLoad; + } + logger.info("Total distance of all routes: " + totalDistance + "m"); + logger.info("Total load of all routes: " + totalLoad); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Capacity constraint. + // [START capacity_constraint] + final int demandCallbackIndex = routing.registerUnaryTransitCallback((long fromIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + return data.demands[fromNode]; + }); + routing.addDimensionWithVehicleCapacity(demandCallbackIndex, 0, // null capacity slack + data.vehicleCapacities, // vehicle maximum capacities + true, // start cumul to zero + "Capacity"); + // [END capacity_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpDropNodes.java b/libs/or-tools-src-ubuntu/examples/java/VrpDropNodes.java new file mode 100644 index 0000000000000000000000000000000000000000..cb254df07535a536e3c0fd6fc6a019ea474a486a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpDropNodes.java @@ -0,0 +1,178 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.RoutingIndexManager; +import com.google.ortools.constraintsolver.RoutingModel; +import com.google.ortools.constraintsolver.RoutingSearchParameters; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; +// [END import] + +/** Minimal VRP.*/ +public class VrpDropNodes { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpDropNodes.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + // [START demands_capacities] + public final long[] demands = {0, 1, 1, 3, 6, 3, 6, 8, 8, 1, 2, 1, 2, 6, 6, 8, 8}; + public final long[] vehicleCapacities = {15, 15, 15, 15}; + // [END demands_capacities] + public final int vehicleNumber = 4; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Display dropped nodes. + String droppedNodes = "Dropped nodes:"; + for (int node = 0; node < routing.size(); ++node) { + if (routing.isStart(node) || routing.isEnd(node)) { + continue; + } + if (solution.value(routing.nextVar(node)) == node) { + droppedNodes += " " + manager.indexToNode(node); + } + } + logger.info(droppedNodes); + // Display routes + long totalDistance = 0; + long totalLoad = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + long routeLoad = 0; + String route = ""; + while (!routing.isEnd(index)) { + long nodeIndex = manager.indexToNode(index); + routeLoad += data.demands[(int) nodeIndex]; + route += nodeIndex + " Load(" + routeLoad + ") -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + route += manager.indexToNode(routing.end(i)); + logger.info(route); + logger.info("Distance of the route: " + routeDistance + "m"); + totalDistance += routeDistance; + totalLoad += routeLoad; + } + logger.info("Total Distance of all routes: " + totalDistance + "m"); + logger.info("Total Load of all routes: " + totalLoad); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Capacity constraint. + // [START capacity_constraint] + final int demandCallbackIndex = routing.registerUnaryTransitCallback((long fromIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + return data.demands[fromNode]; + }); + routing.addDimensionWithVehicleCapacity(demandCallbackIndex, 0, // null capacity slack + data.vehicleCapacities, // vehicle maximum capacities + true, // start cumul to zero + "Capacity"); + // Allow to drop nodes. + long penalty = 1000; + for (int i = 1; i < data.distanceMatrix.length; ++i) { + routing.addDisjunction(new long[] {manager.nodeToIndex(i)}, penalty); + } + // [END capacity_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpGlobalSpan.java b/libs/or-tools-src-ubuntu/examples/java/VrpGlobalSpan.java new file mode 100644 index 0000000000000000000000000000000000000000..e78ead183b234ae09f4b6394ab6487f2bd56696f --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpGlobalSpan.java @@ -0,0 +1,148 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +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.logging.Logger; +// [END import] + +/** Minimal VRP.*/ +public class VrpGlobalSpan { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpGlobalSpan.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + public final int vehicleNumber = 4; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Inspect solution. + long maxRouteDistance = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + logger.info(route + manager.indexToNode(index)); + logger.info("Distance of the route: " + routeDistance + "m"); + maxRouteDistance = Math.max(routeDistance, maxRouteDistance); + } + logger.info("Maximum of the route distances: " + maxRouteDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.addDimension(transitCallbackIndex, 0, 3000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.getMutableDimension("Distance"); + distanceDimension.setGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpInitialRoutes.java b/libs/or-tools-src-ubuntu/examples/java/VrpInitialRoutes.java new file mode 100644 index 0000000000000000000000000000000000000000..369cec16c5127de7a1d750dc7a215a68aaa239c1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpInitialRoutes.java @@ -0,0 +1,159 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +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.logging.Logger; +// [END import] + +/** Minimal VRP. */ +public class VrpInitialRoutes { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpInitialRoutes.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + // [START initial_routes] + public final long[][] initialRoutes = { + {8, 16, 14, 13, 12, 11}, + {3, 4, 9, 10}, + {15, 1}, + {7, 5, 2, 6}, + }; + // [END initial_routes] + public final int vehicleNumber = 4; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Inspect solution. + long maxRouteDistance = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + logger.info(route + manager.indexToNode(index)); + logger.info("Distance of the route: " + routeDistance + "m"); + maxRouteDistance = Math.max(routeDistance, maxRouteDistance); + } + logger.info("Maximum of the route distances: " + maxRouteDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.addDimension(transitCallbackIndex, 0, 3000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.getMutableDimension("Distance"); + distanceDimension.setGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // [START print_initial_solution] + Assignment initialSolution = routing.readAssignmentFromRoutes(data.initialRoutes, true); + logger.info("Initial solution:"); + printSolution(data, routing, manager, initialSolution); + // [END print_initial_solution] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters().toBuilder().build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + logger.info("Solution after search:"); + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpPickupDelivery.java b/libs/or-tools-src-ubuntu/examples/java/VrpPickupDelivery.java new file mode 100644 index 0000000000000000000000000000000000000000..c85483aca33f38274d90bebaf4d20e9c86af20c5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpPickupDelivery.java @@ -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. + +// [START program] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +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.Solver; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; +// [END import] + +/** Minimal Pickup & Delivery Problem (PDP).*/ +public class VrpPickupDelivery { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpPickupDelivery.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + // [START pickups_deliveries] + public final int[][] pickupsDeliveries = { + {1, 6}, + {2, 10}, + {4, 3}, + {5, 9}, + {7, 8}, + {15, 11}, + {13, 12}, + {16, 14}, + }; + // [END pickups_deliveries] + public final int vehicleNumber = 4; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + long totalDistance = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + logger.info(route + manager.indexToNode(index)); + logger.info("Distance of the route: " + routeDistance + "m"); + totalDistance += routeDistance; + } + logger.info("Total Distance of all routes: " + totalDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.addDimension(transitCallbackIndex, // transit callback index + 0, // no slack + 3000, // vehicle maximum travel distance + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.getMutableDimension("Distance"); + distanceDimension.setGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Define Transportation Requests. + // [START pickup_delivery_constraint] + Solver solver = routing.solver(); + for (int[] request : data.pickupsDeliveries) { + long pickupIndex = manager.nodeToIndex(request[0]); + long deliveryIndex = manager.nodeToIndex(request[1]); + routing.addPickupAndDelivery(pickupIndex, deliveryIndex); + solver.addConstraint( + solver.makeEquality(routing.vehicleVar(pickupIndex), routing.vehicleVar(deliveryIndex))); + solver.addConstraint(solver.makeLessOrEqual( + distanceDimension.cumulVar(pickupIndex), distanceDimension.cumulVar(deliveryIndex))); + } + // [END pickup_delivery_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PARALLEL_CHEAPEST_INSERTION) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpPickupDeliveryFifo.java b/libs/or-tools-src-ubuntu/examples/java/VrpPickupDeliveryFifo.java new file mode 100644 index 0000000000000000000000000000000000000000..37f287ac982073981c6b2a93b595234285ef24c7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpPickupDeliveryFifo.java @@ -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. + +// [START program] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +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.Solver; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; +// [END import] + +/** Minimal Pickup & Delivery Problem (PDP).*/ +public class VrpPickupDeliveryFifo { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpPickupDeliveryFifo.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + // [START pickups_deliveries] + public final int[][] pickupsDeliveries = { + {1, 6}, + {2, 10}, + {4, 3}, + {5, 9}, + {7, 8}, + {15, 11}, + {13, 12}, + {16, 14}, + }; + // [END pickups_deliveries] + public final int vehicleNumber = 4; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + long totalDistance = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + logger.info(route + manager.indexToNode(index)); + logger.info("Distance of the route: " + routeDistance + "m"); + totalDistance += routeDistance; + } + logger.info("Total Distance of all routes: " + totalDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.addDimension(transitCallbackIndex, // transit callback index + 0, // no slack + 3000, // vehicle maximum travel distance + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.getMutableDimension("Distance"); + distanceDimension.setGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Define Transportation Requests. + // [START pickup_delivery_constraint] + Solver solver = routing.solver(); + for (int[] request : data.pickupsDeliveries) { + long pickupIndex = manager.nodeToIndex(request[0]); + long deliveryIndex = manager.nodeToIndex(request[1]); + routing.addPickupAndDelivery(pickupIndex, deliveryIndex); + solver.addConstraint( + solver.makeEquality(routing.vehicleVar(pickupIndex), routing.vehicleVar(deliveryIndex))); + solver.addConstraint(solver.makeLessOrEqual( + distanceDimension.cumulVar(pickupIndex), distanceDimension.cumulVar(deliveryIndex))); + } + routing.setPickupAndDeliveryPolicyOfAllVehicles(RoutingModel.PICKUP_AND_DELIVERY_FIFO); + // [END pickup_delivery_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PARALLEL_CHEAPEST_INSERTION) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpPickupDeliveryLifo.java b/libs/or-tools-src-ubuntu/examples/java/VrpPickupDeliveryLifo.java new file mode 100644 index 0000000000000000000000000000000000000000..d5b58954b6e29d8f0fe72436275dadbb5769148c --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpPickupDeliveryLifo.java @@ -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. + +// [START program] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +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.Solver; +import com.google.ortools.constraintsolver.main; +import java.util.logging.Logger; +// [END import] + +/** Minimal Pickup & Delivery Problem (PDP).*/ +public class VrpPickupDeliveryLifo { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpPickupDeliveryLifo.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + // [START pickups_deliveries] + public final int[][] pickupsDeliveries = { + {1, 6}, + {2, 10}, + {4, 3}, + {5, 9}, + {7, 8}, + {15, 11}, + {13, 12}, + {16, 14}, + }; + // [END pickups_deliveries] + public final int vehicleNumber = 4; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + long totalDistance = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + logger.info(route + manager.indexToNode(index)); + logger.info("Distance of the route: " + routeDistance + "m"); + totalDistance += routeDistance; + } + logger.info("Total Distance of all routes: " + totalDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.distanceMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.addDimension(transitCallbackIndex, // transit callback index + 0, // no slack + 3000, // vehicle maximum travel distance + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.getMutableDimension("Distance"); + distanceDimension.setGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Define Transportation Requests. + // [START pickup_delivery_constraint] + Solver solver = routing.solver(); + for (int[] request : data.pickupsDeliveries) { + long pickupIndex = manager.nodeToIndex(request[0]); + long deliveryIndex = manager.nodeToIndex(request[1]); + routing.addPickupAndDelivery(pickupIndex, deliveryIndex); + solver.addConstraint( + solver.makeEquality(routing.vehicleVar(pickupIndex), routing.vehicleVar(deliveryIndex))); + solver.addConstraint(solver.makeLessOrEqual( + distanceDimension.cumulVar(pickupIndex), distanceDimension.cumulVar(deliveryIndex))); + } + routing.setPickupAndDeliveryPolicyOfAllVehicles(RoutingModel.PICKUP_AND_DELIVERY_LIFO); + // [END pickup_delivery_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PARALLEL_CHEAPEST_INSERTION) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpResources.java b/libs/or-tools-src-ubuntu/examples/java/VrpResources.java new file mode 100644 index 0000000000000000000000000000000000000000..5826df316ce740a9a271db52d2af4f41c4c9388a --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpResources.java @@ -0,0 +1,217 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.IntervalVar; +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.Solver; +import com.google.ortools.constraintsolver.main; +import java.util.Arrays; +import java.util.logging.Logger; +// [END import] + +/** Minimal VRP with Resource Constraints.*/ +public class VrpResources { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpResources.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] timeMatrix = { + {0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7}, + {6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14}, + {9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9}, + {8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16}, + {7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14}, + {3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8}, + {6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5}, + {2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10}, + {3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6}, + {2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5}, + {6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4}, + {6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10}, + {4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8}, + {4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6}, + {5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2}, + {9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9}, + {7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0}, + }; + public final long[][] timeWindows = { + {0, 5}, // depot + {7, 12}, // 1 + {10, 15}, // 2 + {5, 14}, // 3 + {5, 13}, // 4 + {0, 5}, // 5 + {5, 10}, // 6 + {0, 10}, // 7 + {5, 10}, // 8 + {0, 5}, // 9 + {10, 16}, // 10 + {10, 15}, // 11 + {0, 5}, // 12 + {5, 10}, // 13 + {7, 12}, // 14 + {10, 15}, // 15 + {5, 15}, // 16 + }; + public final int vehicleNumber = 4; + // [START resources_data] + public final int vehicleLoadTime = 5; + public final int vehicleUnloadTime = 5; + public final int depotCapacity = 2; + // [END resources_data] + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + RoutingDimension timeDimension = routing.getMutableDimension("Time"); + long totalTime = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + String route = ""; + while (!routing.isEnd(index)) { + IntVar timeVar = timeDimension.cumulVar(index); + route += manager.indexToNode(index) + " Time(" + solution.min(timeVar) + "," + + solution.max(timeVar) + ") -> "; + index = solution.value(routing.nextVar(index)); + } + IntVar timeVar = timeDimension.cumulVar(index); + route += manager.indexToNode(index) + " Time(" + solution.min(timeVar) + "," + + solution.max(timeVar) + ")"; + logger.info(route); + logger.info("Time of the route: " + solution.min(timeVar) + "min"); + totalTime += solution.min(timeVar); + } + logger.info("Total time of all routes: " + totalTime + "min"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.timeMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.timeMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Time constraint. + // [START time_constraint] + routing.addDimension(transitCallbackIndex, // transit callback + 30, // allow waiting time + 30, // vehicle maximum capacities + false, // start cumul to zero + "Time"); + RoutingDimension timeDimension = routing.getMutableDimension("Time"); + // Add time window constraints for each location except depot. + for (int i = 1; i < data.timeWindows.length; ++i) { + long index = manager.nodeToIndex(i); + timeDimension.cumulVar(index).setRange(data.timeWindows[i][0], data.timeWindows[i][1]); + } + // Add time window constraints for each vehicle start node. + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + timeDimension.cumulVar(index).setRange(data.timeWindows[0][0], data.timeWindows[0][1]); + } + // [END time_constraint] + + // Add resource constraints at the depot. + // [START depot_load_time] + Solver solver = routing.solver(); + IntervalVar[] intervals = new IntervalVar[data.vehicleNumber * 2]; + for (int i = 0; i < data.vehicleNumber; ++i) { + // Add load duration at start of routes + intervals[2 * i] = solver.makeFixedDurationIntervalVar( + timeDimension.cumulVar(routing.start(i)), data.vehicleLoadTime, "depot_interval"); + // Add unload duration at end of routes. + intervals[2 * i + 1] = solver.makeFixedDurationIntervalVar( + timeDimension.cumulVar(routing.end(i)), data.vehicleUnloadTime, "depot_interval"); + } + // [END depot_load_time] + + // [START depot_capacity] + long[] depotUsage = new long[intervals.length]; + Arrays.fill(depotUsage, 1); + solver.addConstraint(solver.makeCumulative(intervals, depotUsage, data.depotCapacity, "depot")); + // [END depot_capacity] + + // Instantiate route start and end times to produce feasible times. + // [START depot_start_end_times] + for (int i = 0; i < data.vehicleNumber; ++i) { + routing.addVariableMinimizedByFinalizer(timeDimension.cumulVar(routing.start(i))); + routing.addVariableMinimizedByFinalizer(timeDimension.cumulVar(routing.end(i))); + } + // [END depot_start_end_times] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpStartsEnds.java b/libs/or-tools-src-ubuntu/examples/java/VrpStartsEnds.java new file mode 100644 index 0000000000000000000000000000000000000000..a7317cce42e6737c65fa662d0d4a02984ccd7add --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpStartsEnds.java @@ -0,0 +1,151 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +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.logging.Logger; +// [END import] + +/** Minimal VRP.*/ +public class VrpStartsEnds { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpStartsEnds.class.getName()); + + // [START data_model] + static class DataModel { + public final long[][] distanceMatrix = { + {0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662}, + {548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210}, + {776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754}, + {696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358}, + {582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244}, + {274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708}, + {502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480}, + {194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856}, + {308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514}, + {194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468}, + {536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354}, + {502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844}, + {388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730}, + {354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536}, + {468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194}, + {776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798}, + {662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0}, + }; + public final int vehicleNumber = 4; + // [START starts_ends] + public final int[] starts = {1, 2, 15, 16}; + public final int[] ends = {0, 0, 0, 0}; + // [END starts_ends] + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + // Inspect solution. + long maxRouteDistance = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + logger.info(route + manager.indexToNode(index)); + logger.info("Distance of the route: " + routeDistance + "m"); + maxRouteDistance = Math.max(routeDistance, maxRouteDistance); + } + logger.info("Maximum of the route distances: " + maxRouteDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = new RoutingIndexManager( + data.distanceMatrix.length, data.vehicleNumber, data.starts, data.ends); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.distanceMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.addDimension(transitCallbackIndex, 0, 2000, + true, // start cumul to zero + "Distance"); + RoutingDimension distanceDimension = routing.getMutableDimension("Distance"); + distanceDimension.setGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpTimeWindows.java b/libs/or-tools-src-ubuntu/examples/java/VrpTimeWindows.java new file mode 100644 index 0000000000000000000000000000000000000000..b8b3a57cb926bf268de59b59006805027fc94986 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpTimeWindows.java @@ -0,0 +1,190 @@ +// Copyright 2010-2018 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START program] +package com.google.ortools.examples; +// [START import] +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.logging.Logger; +// [END import] + +/** VRPTW. */ +public class VrpTimeWindows { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpTimeWindows.class.getName()); + // [START program_part1] + // [START data_model] + static class DataModel { + public final long[][] timeMatrix = { + {0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7}, + {6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14}, + {9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9}, + {8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16}, + {7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14}, + {3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8}, + {6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5}, + {2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10}, + {3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6}, + {2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5}, + {6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4}, + {6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10}, + {4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8}, + {4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6}, + {5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2}, + {9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9}, + {7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0}, + }; + public final long[][] timeWindows = { + {0, 5}, // depot + {7, 12}, // 1 + {10, 15}, // 2 + {16, 18}, // 3 + {10, 13}, // 4 + {0, 5}, // 5 + {5, 10}, // 6 + {0, 4}, // 7 + {5, 10}, // 8 + {0, 3}, // 9 + {10, 16}, // 10 + {10, 15}, // 11 + {0, 5}, // 12 + {5, 10}, // 13 + {7, 8}, // 14 + {10, 15}, // 15 + {11, 15}, // 16 + }; + public final int vehicleNumber = 4; + public final int depot = 0; + } + // [END data_model] + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution( + DataModel data, RoutingModel routing, RoutingIndexManager manager, Assignment solution) { + RoutingDimension timeDimension = routing.getMutableDimension("Time"); + long totalTime = 0; + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + String route = ""; + while (!routing.isEnd(index)) { + IntVar timeVar = timeDimension.cumulVar(index); + route += manager.indexToNode(index) + " Time(" + solution.min(timeVar) + "," + + solution.max(timeVar) + ") -> "; + index = solution.value(routing.nextVar(index)); + } + IntVar timeVar = timeDimension.cumulVar(index); + route += manager.indexToNode(index) + " Time(" + solution.min(timeVar) + "," + + solution.max(timeVar) + ")"; + logger.info(route); + logger.info("Time of the route: " + solution.min(timeVar) + "min"); + totalTime += solution.min(timeVar); + } + logger.info("Total time of all routes: " + totalTime + "min"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final DataModel data = new DataModel(); + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(data.timeMatrix.length, data.vehicleNumber, data.depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return data.timeMatrix[fromNode][toNode]; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Time constraint. + // [START time_constraint] + routing.addDimension(transitCallbackIndex, // transit callback + 30, // allow waiting time + 30, // vehicle maximum capacities + false, // start cumul to zero + "Time"); + RoutingDimension timeDimension = routing.getMutableDimension("Time"); + // Add time window constraints for each location except depot. + for (int i = 1; i < data.timeWindows.length; ++i) { + long index = manager.nodeToIndex(i); + timeDimension.cumulVar(index).setRange(data.timeWindows[i][0], data.timeWindows[i][1]); + } + // Add time window constraints for each vehicle start node. + for (int i = 0; i < data.vehicleNumber; ++i) { + long index = routing.start(i); + timeDimension.cumulVar(index).setRange(data.timeWindows[0][0], data.timeWindows[0][1]); + } + // [END time_constraint] + + // Instantiate route start and end times to produce feasible times. + // [START depot_start_end_times] + for (int i = 0; i < data.vehicleNumber; ++i) { + routing.addVariableMinimizedByFinalizer(timeDimension.cumulVar(routing.start(i))); + routing.addVariableMinimizedByFinalizer(timeDimension.cumulVar(routing.end(i))); + } + // [END depot_start_end_times] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(data, routing, manager, solution); + // [END print_solution] + } +} +// [END_program_part1] +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/VrpWithTimeLimit.java b/libs/or-tools-src-ubuntu/examples/java/VrpWithTimeLimit.java new file mode 100644 index 0000000000000000000000000000000000000000..a39dd65ff1863e21b2bb02f06287c69338c5ac72 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/VrpWithTimeLimit.java @@ -0,0 +1,131 @@ +// 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] +package com.google.ortools.examples; +// [START import] +import com.google.ortools.constraintsolver.Assignment; +import com.google.ortools.constraintsolver.FirstSolutionStrategy; +import com.google.ortools.constraintsolver.LocalSearchMetaheuristic; +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 com.google.protobuf.Duration; +import java.util.logging.Logger; +// [END import] + +/** Minimal VRP.*/ +public class VrpWithTimeLimit { + static { + System.loadLibrary("jniortools"); + } + + private static final Logger logger = Logger.getLogger(VrpWithTimeLimit.class.getName()); + + // [START solution_printer] + /// @brief Print the solution. + static void printSolution(RoutingIndexManager manager, RoutingModel routing, Assignment solution) { + // Inspect solution. + long maxRouteDistance = 0; + for (int i = 0; i < manager.getNumberOfVehicles(); ++i) { + long index = routing.start(i); + logger.info("Route for Vehicle " + i + ":"); + long routeDistance = 0; + String route = ""; + while (!routing.isEnd(index)) { + route += manager.indexToNode(index) + " -> "; + long previousIndex = index; + index = solution.value(routing.nextVar(index)); + routeDistance += routing.getArcCostForVehicle(previousIndex, index, i); + } + logger.info(route + manager.indexToNode(index)); + logger.info("Distance of the route: " + routeDistance + "m"); + maxRouteDistance = Math.max(routeDistance, maxRouteDistance); + } + logger.info("Maximum of the route distances: " + maxRouteDistance + "m"); + } + // [END solution_printer] + + public static void main(String[] args) throws Exception { + // Instantiate the data problem. + // [START data] + final int locationNumber = 20; + final int vehicleNumber = 5; + final int depot = 0; + // [END data] + + // Create Routing Index Manager + // [START index_manager] + RoutingIndexManager manager = + new RoutingIndexManager(locationNumber, vehicleNumber, depot); + // [END index_manager] + + // Create Routing Model. + // [START routing_model] + RoutingModel routing = new RoutingModel(manager); + // [END routing_model] + + // Create and register a transit callback. + // [START transit_callback] + final int transitCallbackIndex = + routing.registerTransitCallback((long fromIndex, long toIndex) -> { + // Convert from routing variable Index to user NodeIndex. + int fromNode = manager.indexToNode(fromIndex); + int toNode = manager.indexToNode(toIndex); + return 1; + }); + // [END transit_callback] + + // Define cost of each arc. + // [START arc_cost] + routing.setArcCostEvaluatorOfAllVehicles(transitCallbackIndex); + // [END arc_cost] + + // Add Distance constraint. + // [START distance_constraint] + routing.addDimension( + transitCallbackIndex, + /*slack=*/0, + /*horizon=*/3000, + /*start_cumul_to_zero=*/true, + "Distance"); + RoutingDimension distanceDimension = routing.getMutableDimension("Distance"); + distanceDimension.setGlobalSpanCostCoefficient(100); + // [END distance_constraint] + + // Setting first solution heuristic. + // [START parameters] + RoutingSearchParameters searchParameters = + main.defaultRoutingSearchParameters() + .toBuilder() + .setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC) + .setLocalSearchMetaheuristic(LocalSearchMetaheuristic.Value.GUIDED_LOCAL_SEARCH) + .setLogSearch(true) + .setTimeLimit(Duration.newBuilder().setSeconds(10).build()) + .build(); + // [END parameters] + + // Solve the problem. + // [START solve] + Assignment solution = routing.solveWithParameters(searchParameters); + // [END solve] + + // Print solution on console. + // [START print_solution] + printSolution(manager, routing, solution); + // [END print_solution] + } +} +// [END program] diff --git a/libs/or-tools-src-ubuntu/examples/java/WhoKilledAgatha.java b/libs/or-tools-src-ubuntu/examples/java/WhoKilledAgatha.java new file mode 100644 index 0000000000000000000000000000000000000000..6a73d8b814be10ec82cd54bbfd49b629c720ee92 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/WhoKilledAgatha.java @@ -0,0 +1,195 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class WhoKilledAgatha { + + static { + System.loadLibrary("jniortools"); + } + + /** + * Implements the Who killed Agatha problem. See + * http://www.hakank.org/google_or_tools/who_killed_agatha.py + */ + private static void solve() { + + Solver solver = new Solver("WhoKilledAgatha"); + + // + // data + // + final int n = 3; + final int agatha = 0; + final int butler = 1; + final int charles = 2; + + String[] names = {"Agatha", "Butler", "Charles"}; + + // + // variables + // + IntVar the_killer = solver.makeIntVar(0, 2, "the_killer"); + IntVar the_victim = solver.makeIntVar(0, 2, "the_victim"); + + IntVar[] all = new IntVar[2 * n * n]; // for branching + + IntVar[][] hates = new IntVar[n][n]; + IntVar[] hates_flat = new IntVar[n * n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + hates[i][j] = solver.makeIntVar(0, 1, "hates[" + i + "," + j + "]"); + hates_flat[i * n + j] = hates[i][j]; + all[i * n + j] = hates[i][j]; + } + } + + IntVar[][] richer = new IntVar[n][n]; + IntVar[] richer_flat = new IntVar[n * n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + richer[i][j] = solver.makeIntVar(0, 1, "richer[" + i + "," + j + "]"); + richer_flat[i * n + j] = richer[i][j]; + all[(n * n) + (i * n + j)] = richer[i][j]; + } + } + + // + // constraints + // + + // Agatha, the butler, and Charles live in Dreadsbury Mansion, and + // are the only ones to live there. + + // A killer always hates, and is no richer than his victim. + // hates[the_killer, the_victim] == 1 + // hates_flat[the_killer * n + the_victim] == 1 + solver.addConstraint( + solver.makeEquality( + solver + .makeElement( + hates_flat, + solver.makeSum(solver.makeProd(the_killer, n).var(), the_victim).var()) + .var(), + 1)); + + // richer[the_killer, the_victim] == 0 + solver.addConstraint( + solver.makeEquality( + solver + .makeElement( + richer_flat, + solver.makeSum(solver.makeProd(the_killer, n).var(), the_victim).var()) + .var(), + 0)); + + // define the concept of richer: + // no one is richer than him-/herself... + for (int i = 0; i < n; i++) { + solver.addConstraint(solver.makeEquality(richer[i][i], 0)); + } + + // (contd...) if i is richer than j then j is not richer than i + // if (i != j) => + // ((richer[i,j] = 1) <=> (richer[j,i] = 0)) + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i != j) { + IntVar bi = solver.makeIsEqualCstVar(richer[i][j], 1); + IntVar bj = solver.makeIsEqualCstVar(richer[j][i], 0); + solver.addConstraint(solver.makeEquality(bi, bj)); + } + } + } + + // Charles hates no one that Agatha hates. + // forall i in 0..2: + // (hates[agatha, i] = 1) => (hates[charles, i] = 0) + for (int i = 0; i < n; i++) { + IntVar b1a = solver.makeIsEqualCstVar(hates[agatha][i], 1); + IntVar b1b = solver.makeIsEqualCstVar(hates[charles][i], 0); + solver.addConstraint(solver.makeLessOrEqual(solver.makeDifference(b1a, b1b).var(), 0)); + } + + // Agatha hates everybody except the butler. + solver.addConstraint(solver.makeEquality(hates[agatha][charles], 1)); + solver.addConstraint(solver.makeEquality(hates[agatha][agatha], 1)); + solver.addConstraint(solver.makeEquality(hates[agatha][butler], 0)); + + // The butler hates everyone not richer than Aunt Agatha. + // forall i in 0..2: + // (richer[i, agatha] = 0) => (hates[butler, i] = 1) + for (int i = 0; i < n; i++) { + IntVar b2a = solver.makeIsEqualCstVar(richer[i][agatha], 0); + IntVar b2b = solver.makeIsEqualCstVar(hates[butler][i], 1); + solver.addConstraint(solver.makeLessOrEqual(solver.makeDifference(b2a, b2b).var(), 0)); + } + + // The butler hates everyone whom Agatha hates. + // forall i : 0..2: + // (hates[agatha, i] = 1) => (hates[butler, i] = 1) + for (int i = 0; i < n; i++) { + IntVar b3a = solver.makeIsEqualCstVar(hates[agatha][i], 1); + IntVar b3b = solver.makeIsEqualCstVar(hates[butler][i], 1); + solver.addConstraint(solver.makeLessOrEqual(solver.makeDifference(b3a, b3b).var(), 0)); + } + + // Noone hates everyone. + // forall i in 0..2: + // (sum j in 0..2: hates[i,j]) <= 2 + for (int i = 0; i < n; i++) { + IntVar[] tmp = new IntVar[n]; + for (int j = 0; j < n; j++) { + tmp[j] = hates[i][j]; + } + solver.addConstraint(solver.makeLessOrEqual(solver.makeSum(tmp).var(), 2)); + } + + // Who killed Agatha? + solver.addConstraint(solver.makeEquality(the_victim, agatha)); + + // + // search + // + DecisionBuilder db = + solver.makePhase(all, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); + + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + System.out.println("the_killer: " + names[(int) the_killer.value()]); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + WhoKilledAgatha.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/Xkcd.java b/libs/or-tools-src-ubuntu/examples/java/Xkcd.java new file mode 100644 index 0000000000000000000000000000000000000000..d631bd7959c13a000f5cf304178f4b231b36367d --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/Xkcd.java @@ -0,0 +1,75 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.*; +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class Xkcd { + + static { + System.loadLibrary("jniortools"); + } + + /** Solves the xkcd problem. See http://www.hakank.org/google_or_tools/xkcd.py */ + private static void solve() { + + Solver solver = new Solver("Xkcd"); + + int n = 6; + // for price and total: multiplied by 100 to be able to use integers + int[] price = {215, 275, 335, 355, 420, 580}; + int total = 1505; + + // + // Variables + // + IntVar[] x = solver.makeIntVarArray(n, 0, 10, "x"); + + // + // Constraints + // + solver.addConstraint(solver.makeEquality(solver.makeScalProd(x, price).var(), total)); + + // + // Search + // + DecisionBuilder db = solver.makePhase(x, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); + solver.newSearch(db); + + while (solver.nextSolution()) { + System.out.print("x: "); + for (int i = 0; i < n; i++) { + System.out.print(x[i].value() + " "); + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + Xkcd.solve(); + } +} diff --git a/libs/or-tools-src-ubuntu/examples/java/YoungTableaux.java b/libs/or-tools-src-ubuntu/examples/java/YoungTableaux.java new file mode 100644 index 0000000000000000000000000000000000000000..19544f7f9a3b29854308e4bd293553baa90776f9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/examples/java/YoungTableaux.java @@ -0,0 +1,144 @@ +// Copyright 2011 Hakan Kjellerstrand hakank@gmail.com +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.ortools.examples; + +import com.google.ortools.constraintsolver.DecisionBuilder; +import com.google.ortools.constraintsolver.IntVar; +import com.google.ortools.constraintsolver.Solver; +import java.io.*; +import java.text.*; +import java.util.*; + +public class YoungTableaux { + + static { + System.loadLibrary("jniortools"); + } + + /** + * Implements Young tableaux and partitions. See + * http://www.hakank.org/google_or_tools/young_tableuax.py + */ + private static void solve(int n) { + + Solver solver = new Solver("YoungTableaux"); + + System.out.println("n: " + n); + + // + // variables + // + IntVar[][] x = new IntVar[n][n]; + IntVar[] x_flat = new IntVar[n * n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + x[i][j] = solver.makeIntVar(1, n + 1, "x[" + i + "," + j + "]"); + x_flat[i * n + j] = x[i][j]; + } + } + + // partition structure + IntVar[] p = solver.makeIntVarArray(n, 0, n + 1, "p"); + + // + // constraints + // + + // 1..n is used exactly once + for (int i = 1; i <= n; i++) { + solver.addConstraint(solver.makeCount(x_flat, i, 1)); + } + + solver.addConstraint(solver.makeEquality(x[0][0], 1)); + + // row wise + for (int i = 0; i < n; i++) { + for (int j = 1; j < n; j++) { + solver.addConstraint(solver.makeGreaterOrEqual(x[i][j], x[i][j - 1])); + } + } + + // column wise + for (int j = 0; j < n; j++) { + for (int i = 1; i < n; i++) { + solver.addConstraint(solver.makeGreaterOrEqual(x[i][j], x[i - 1][j])); + } + } + + // calculate the structure (i.e. the partition) + for (int i = 0; i < n; i++) { + IntVar[] b = new IntVar[n]; + for (int j = 0; j < n; j++) { + b[j] = solver.makeIsLessOrEqualCstVar(x[i][j], n); + } + solver.addConstraint(solver.makeEquality(p[i], solver.makeSum(b).var())); + } + + solver.addConstraint(solver.makeEquality(solver.makeSum(p).var(), n)); + + for (int i = 1; i < n; i++) { + solver.addConstraint(solver.makeGreaterOrEqual(p[i - 1], p[i])); + } + + // + // search + // + DecisionBuilder db = + solver.makePhase(x_flat, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE); + + solver.newSearch(db); + + // + // output + // + while (solver.nextSolution()) { + System.out.print("p: "); + for (int i = 0; i < n; i++) { + System.out.print(p[i].value() + " "); + } + + System.out.println("\nx:"); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + long val = x[i][j].value(); + if (val <= n) { + System.out.print(val + " "); + } + } + if (p[i].value() > 0) { + System.out.println(); + } + } + System.out.println(); + } + solver.endSearch(); + + // Statistics + System.out.println(); + System.out.println("Solutions: " + solver.solutions()); + System.out.println("Failures: " + solver.failures()); + System.out.println("Branches: " + solver.branches()); + System.out.println("Wall time: " + solver.wallTime() + "ms"); + } + + public static void main(String[] args) throws Exception { + + int n = 5; + if (args.length > 0) { + n = Integer.parseInt(args[0]); + } + + YoungTableaux.solve(n); + } +} diff --git a/libs/or-tools-src-ubuntu/include/absl/algorithm/algorithm.h b/libs/or-tools-src-ubuntu/include/absl/algorithm/algorithm.h new file mode 100644 index 0000000000000000000000000000000000000000..e9b4733872788dc25ceaace921625209983c40a1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/algorithm/algorithm.h @@ -0,0 +1,159 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: algorithm.h +// ----------------------------------------------------------------------------- +// +// This header file contains Google extensions to the standard C++ +// header. + +#ifndef ABSL_ALGORITHM_ALGORITHM_H_ +#define ABSL_ALGORITHM_ALGORITHM_H_ + +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace algorithm_internal { + +// Performs comparisons with operator==, similar to C++14's `std::equal_to<>`. +struct EqualTo { + template + bool operator()(const T& a, const U& b) const { + return a == b; + } +}; + +template +bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, + InputIter2 last2, Pred pred, std::input_iterator_tag, + std::input_iterator_tag) { + while (true) { + if (first1 == last1) return first2 == last2; + if (first2 == last2) return false; + if (!pred(*first1, *first2)) return false; + ++first1; + ++first2; + } +} + +template +bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, + InputIter2 last2, Pred&& pred, std::random_access_iterator_tag, + std::random_access_iterator_tag) { + return (last1 - first1 == last2 - first2) && + std::equal(first1, last1, first2, std::forward(pred)); +} + +// When we are using our own internal predicate that just applies operator==, we +// forward to the non-predicate form of std::equal. This enables an optimization +// in libstdc++ that can result in std::memcmp being used for integer types. +template +bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, + InputIter2 last2, algorithm_internal::EqualTo /* unused */, + std::random_access_iterator_tag, + std::random_access_iterator_tag) { + return (last1 - first1 == last2 - first2) && + std::equal(first1, last1, first2); +} + +template +It RotateImpl(It first, It middle, It last, std::true_type) { + return std::rotate(first, middle, last); +} + +template +It RotateImpl(It first, It middle, It last, std::false_type) { + std::rotate(first, middle, last); + return std::next(first, std::distance(middle, last)); +} + +} // namespace algorithm_internal + +// equal() +// +// Compares the equality of two ranges specified by pairs of iterators, using +// the given predicate, returning true iff for each corresponding iterator i1 +// and i2 in the first and second range respectively, pred(*i1, *i2) == true +// +// This comparison takes at most min(`last1` - `first1`, `last2` - `first2`) +// invocations of the predicate. Additionally, if InputIter1 and InputIter2 are +// both random-access iterators, and `last1` - `first1` != `last2` - `first2`, +// then the predicate is never invoked and the function returns false. +// +// This is a C++11-compatible implementation of C++14 `std::equal`. See +// https://en.cppreference.com/w/cpp/algorithm/equal for more information. +template +bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, + InputIter2 last2, Pred&& pred) { + return algorithm_internal::EqualImpl( + first1, last1, first2, last2, std::forward(pred), + typename std::iterator_traits::iterator_category{}, + typename std::iterator_traits::iterator_category{}); +} + +// Overload of equal() that performs comparison of two ranges specified by pairs +// of iterators using operator==. +template +bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, + InputIter2 last2) { + return absl::equal(first1, last1, first2, last2, + algorithm_internal::EqualTo{}); +} + +// linear_search() +// +// Performs a linear search for `value` using the iterator `first` up to +// but not including `last`, returning true if [`first`, `last`) contains an +// element equal to `value`. +// +// A linear search is of O(n) complexity which is guaranteed to make at most +// n = (`last` - `first`) comparisons. A linear search over short containers +// may be faster than a binary search, even when the container is sorted. +template +bool linear_search(InputIterator first, InputIterator last, + const EqualityComparable& value) { + return std::find(first, last, value) != last; +} + +// rotate() +// +// Performs a left rotation on a range of elements (`first`, `last`) such that +// `middle` is now the first element. `rotate()` returns an iterator pointing to +// the first element before rotation. This function is exactly the same as +// `std::rotate`, but fixes a bug in gcc +// <= 4.9 where `std::rotate` returns `void` instead of an iterator. +// +// The complexity of this algorithm is the same as that of `std::rotate`, but if +// `ForwardIterator` is not a random-access iterator, then `absl::rotate` +// performs an additional pass over the range to construct the return value. +template +ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, + ForwardIterator last) { + return algorithm_internal::RotateImpl( + first, middle, last, + std::is_same()); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_ALGORITHM_ALGORITHM_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/algorithm/container.h b/libs/or-tools-src-ubuntu/include/absl/algorithm/container.h new file mode 100644 index 0000000000000000000000000000000000000000..d72532decf3810e9814a1bed838ff77dd3cb1d7e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/algorithm/container.h @@ -0,0 +1,1727 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: container.h +// ----------------------------------------------------------------------------- +// +// This header file provides Container-based versions of algorithmic functions +// within the C++ standard library. The following standard library sets of +// functions are covered within this file: +// +// * Algorithmic functions +// * Algorithmic functions +// * functions +// +// The standard library functions operate on iterator ranges; the functions +// within this API operate on containers, though many return iterator ranges. +// +// All functions within this API are named with a `c_` prefix. Calls such as +// `absl::c_xx(container, ...) are equivalent to std:: functions such as +// `std::xx(std::begin(cont), std::end(cont), ...)`. Functions that act on +// iterators but not conceptually on iterator ranges (e.g. `std::iter_swap`) +// have no equivalent here. +// +// For template parameter and variable naming, `C` indicates the container type +// to which the function is applied, `Pred` indicates the predicate object type +// to be used by the function and `T` indicates the applicable element type. + +#ifndef ABSL_ALGORITHM_CONTAINER_H_ +#define ABSL_ALGORITHM_CONTAINER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/algorithm.h" +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_algorithm_internal { + +// NOTE: it is important to defer to ADL lookup for building with C++ modules, +// especially for headers like which are not visible from this file +// but specialize std::begin and std::end. +using std::begin; +using std::end; + +// The type of the iterator given by begin(c) (possibly std::begin(c)). +// ContainerIter> gives vector::const_iterator, +// while ContainerIter> gives vector::iterator. +template +using ContainerIter = decltype(begin(std::declval())); + +// An MSVC bug involving template parameter substitution requires us to use +// decltype() here instead of just std::pair. +template +using ContainerIterPairType = + decltype(std::make_pair(ContainerIter(), ContainerIter())); + +template +using ContainerDifferenceType = + decltype(std::distance(std::declval>(), + std::declval>())); + +template +using ContainerPointerType = + typename std::iterator_traits>::pointer; + +// container_algorithm_internal::c_begin and +// container_algorithm_internal::c_end are abbreviations for proper ADL +// lookup of std::begin and std::end, i.e. +// using std::begin; +// using std::end; +// std::foo(begin(c), end(c); +// becomes +// std::foo(container_algorithm_internal::begin(c), +// container_algorithm_internal::end(c)); +// These are meant for internal use only. + +template +ContainerIter c_begin(C& c) { return begin(c); } + +template +ContainerIter c_end(C& c) { return end(c); } + +template +struct IsUnorderedContainer : std::false_type {}; + +template +struct IsUnorderedContainer< + std::unordered_map> : std::true_type {}; + +template +struct IsUnorderedContainer> + : std::true_type {}; + +// container_algorithm_internal::c_size. It is meant for internal use only. + +template +auto c_size(C& c) -> decltype(c.size()) { + return c.size(); +} + +template +constexpr std::size_t c_size(T (&)[N]) { + return N; +} + +} // namespace container_algorithm_internal + +// PUBLIC API + +//------------------------------------------------------------------------------ +// Abseil algorithm.h functions +//------------------------------------------------------------------------------ + +// c_linear_search() +// +// Container-based version of absl::linear_search() for performing a linear +// search within a container. +template +bool c_linear_search(const C& c, EqualityComparable&& value) { + return linear_search(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(value)); +} + +//------------------------------------------------------------------------------ +// algorithms +//------------------------------------------------------------------------------ + +// c_distance() +// +// Container-based version of the `std::distance()` function to +// return the number of elements within a container. +template +container_algorithm_internal::ContainerDifferenceType c_distance( + const C& c) { + return std::distance(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +//------------------------------------------------------------------------------ +// Non-modifying sequence operations +//------------------------------------------------------------------------------ + +// c_all_of() +// +// Container-based version of the `std::all_of()` function to +// test a condition on all elements within a container. +template +bool c_all_of(const C& c, Pred&& pred) { + return std::all_of(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_any_of() +// +// Container-based version of the `std::any_of()` function to +// test if any element in a container fulfills a condition. +template +bool c_any_of(const C& c, Pred&& pred) { + return std::any_of(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_none_of() +// +// Container-based version of the `std::none_of()` function to +// test if no elements in a container fulfil a condition. +template +bool c_none_of(const C& c, Pred&& pred) { + return std::none_of(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_for_each() +// +// Container-based version of the `std::for_each()` function to +// apply a function to a container's elements. +template +decay_t c_for_each(C&& c, Function&& f) { + return std::for_each(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(f)); +} + +// c_find() +// +// Container-based version of the `std::find()` function to find +// the first element containing the passed value within a container value. +template +container_algorithm_internal::ContainerIter c_find(C& c, T&& value) { + return std::find(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(value)); +} + +// c_find_if() +// +// Container-based version of the `std::find_if()` function to find +// the first element in a container matching the given condition. +template +container_algorithm_internal::ContainerIter c_find_if(C& c, Pred&& pred) { + return std::find_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_find_if_not() +// +// Container-based version of the `std::find_if_not()` function to +// find the first element in a container not matching the given condition. +template +container_algorithm_internal::ContainerIter c_find_if_not(C& c, + Pred&& pred) { + return std::find_if_not(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_find_end() +// +// Container-based version of the `std::find_end()` function to +// find the last subsequence within a container. +template +container_algorithm_internal::ContainerIter c_find_end( + Sequence1& sequence, Sequence2& subsequence) { + return std::find_end(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence)); +} + +// Overload of c_find_end() for using a predicate evaluation other than `==` as +// the function's test condition. +template +container_algorithm_internal::ContainerIter c_find_end( + Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) { + return std::find_end(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence), + std::forward(pred)); +} + +// c_find_first_of() +// +// Container-based version of the `std::find_first_of()` function to +// find the first element within the container that is also within the options +// container. +template +container_algorithm_internal::ContainerIter c_find_first_of(C1& container, + C2& options) { + return std::find_first_of(container_algorithm_internal::c_begin(container), + container_algorithm_internal::c_end(container), + container_algorithm_internal::c_begin(options), + container_algorithm_internal::c_end(options)); +} + +// Overload of c_find_first_of() for using a predicate evaluation other than +// `==` as the function's test condition. +template +container_algorithm_internal::ContainerIter c_find_first_of( + C1& container, C2& options, BinaryPredicate&& pred) { + return std::find_first_of(container_algorithm_internal::c_begin(container), + container_algorithm_internal::c_end(container), + container_algorithm_internal::c_begin(options), + container_algorithm_internal::c_end(options), + std::forward(pred)); +} + +// c_adjacent_find() +// +// Container-based version of the `std::adjacent_find()` function to +// find equal adjacent elements within a container. +template +container_algorithm_internal::ContainerIter c_adjacent_find( + Sequence& sequence) { + return std::adjacent_find(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_adjacent_find() for using a predicate evaluation other than +// `==` as the function's test condition. +template +container_algorithm_internal::ContainerIter c_adjacent_find( + Sequence& sequence, BinaryPredicate&& pred) { + return std::adjacent_find(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(pred)); +} + +// c_count() +// +// Container-based version of the `std::count()` function to count +// values that match within a container. +template +container_algorithm_internal::ContainerDifferenceType c_count( + const C& c, T&& value) { + return std::count(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(value)); +} + +// c_count_if() +// +// Container-based version of the `std::count_if()` function to +// count values matching a condition within a container. +template +container_algorithm_internal::ContainerDifferenceType c_count_if( + const C& c, Pred&& pred) { + return std::count_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_mismatch() +// +// Container-based version of the `std::mismatch()` function to +// return the first element where two ordered containers differ. +template +container_algorithm_internal::ContainerIterPairType +c_mismatch(C1& c1, C2& c2) { + return std::mismatch(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2)); +} + +// Overload of c_mismatch() for using a predicate evaluation other than `==` as +// the function's test condition. +template +container_algorithm_internal::ContainerIterPairType +c_mismatch(C1& c1, C2& c2, BinaryPredicate&& pred) { + return std::mismatch(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + std::forward(pred)); +} + +// c_equal() +// +// Container-based version of the `std::equal()` function to +// test whether two containers are equal. +// +// NOTE: the semantics of c_equal() are slightly different than those of +// equal(): while the latter iterates over the second container only up to the +// size of the first container, c_equal() also checks whether the container +// sizes are equal. This better matches expectations about c_equal() based on +// its signature. +// +// Example: +// vector v1 = <1, 2, 3>; +// vector v2 = <1, 2, 3, 4>; +// equal(std::begin(v1), std::end(v1), std::begin(v2)) returns true +// c_equal(v1, v2) returns false + +template +bool c_equal(const C1& c1, const C2& c2) { + return ((container_algorithm_internal::c_size(c1) == + container_algorithm_internal::c_size(c2)) && + std::equal(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2))); +} + +// Overload of c_equal() for using a predicate evaluation other than `==` as +// the function's test condition. +template +bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) { + return ((container_algorithm_internal::c_size(c1) == + container_algorithm_internal::c_size(c2)) && + std::equal(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + std::forward(pred))); +} + +// c_is_permutation() +// +// Container-based version of the `std::is_permutation()` function +// to test whether a container is a permutation of another. +template +bool c_is_permutation(const C1& c1, const C2& c2) { + using std::begin; + using std::end; + return c1.size() == c2.size() && + std::is_permutation(begin(c1), end(c1), begin(c2)); +} + +// Overload of c_is_permutation() for using a predicate evaluation other than +// `==` as the function's test condition. +template +bool c_is_permutation(const C1& c1, const C2& c2, BinaryPredicate&& pred) { + using std::begin; + using std::end; + return c1.size() == c2.size() && + std::is_permutation(begin(c1), end(c1), begin(c2), + std::forward(pred)); +} + +// c_search() +// +// Container-based version of the `std::search()` function to search +// a container for a subsequence. +template +container_algorithm_internal::ContainerIter c_search( + Sequence1& sequence, Sequence2& subsequence) { + return std::search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence)); +} + +// Overload of c_search() for using a predicate evaluation other than +// `==` as the function's test condition. +template +container_algorithm_internal::ContainerIter c_search( + Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) { + return std::search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(subsequence), + container_algorithm_internal::c_end(subsequence), + std::forward(pred)); +} + +// c_search_n() +// +// Container-based version of the `std::search_n()` function to +// search a container for the first sequence of N elements. +template +container_algorithm_internal::ContainerIter c_search_n( + Sequence& sequence, Size count, T&& value) { + return std::search_n(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), count, + std::forward(value)); +} + +// Overload of c_search_n() for using a predicate evaluation other than +// `==` as the function's test condition. +template +container_algorithm_internal::ContainerIter c_search_n( + Sequence& sequence, Size count, T&& value, BinaryPredicate&& pred) { + return std::search_n(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), count, + std::forward(value), + std::forward(pred)); +} + +//------------------------------------------------------------------------------ +// Modifying sequence operations +//------------------------------------------------------------------------------ + +// c_copy() +// +// Container-based version of the `std::copy()` function to copy a +// container's elements into an iterator. +template +OutputIterator c_copy(const InputSequence& input, OutputIterator output) { + return std::copy(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), output); +} + +// c_copy_n() +// +// Container-based version of the `std::copy_n()` function to copy a +// container's first N elements into an iterator. +template +OutputIterator c_copy_n(const C& input, Size n, OutputIterator output) { + return std::copy_n(container_algorithm_internal::c_begin(input), n, output); +} + +// c_copy_if() +// +// Container-based version of the `std::copy_if()` function to copy +// a container's elements satisfying some condition into an iterator. +template +OutputIterator c_copy_if(const InputSequence& input, OutputIterator output, + Pred&& pred) { + return std::copy_if(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), output, + std::forward(pred)); +} + +// c_copy_backward() +// +// Container-based version of the `std::copy_backward()` function to +// copy a container's elements in reverse order into an iterator. +template +BidirectionalIterator c_copy_backward(const C& src, + BidirectionalIterator dest) { + return std::copy_backward(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + +// c_move() +// +// Container-based version of the `std::move()` function to move +// a container's elements into an iterator. +template +OutputIterator c_move(C&& src, OutputIterator dest) { + return std::move(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + +// c_move_backward() +// +// Container-based version of the `std::move_backward()` function to +// move a container's elements into an iterator in reverse order. +template +BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { + return std::move_backward(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + +// c_swap_ranges() +// +// Container-based version of the `std::swap_ranges()` function to +// swap a container's elements with another container's elements. +template +container_algorithm_internal::ContainerIter c_swap_ranges(C1& c1, C2& c2) { + return std::swap_ranges(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2)); +} + +// c_transform() +// +// Container-based version of the `std::transform()` function to +// transform a container's elements using the unary operation, storing the +// result in an iterator pointing to the last transformed element in the output +// range. +template +OutputIterator c_transform(const InputSequence& input, OutputIterator output, + UnaryOp&& unary_op) { + return std::transform(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), output, + std::forward(unary_op)); +} + +// Overload of c_transform() for performing a transformation using a binary +// predicate. +template +OutputIterator c_transform(const InputSequence1& input1, + const InputSequence2& input2, OutputIterator output, + BinaryOp&& binary_op) { + return std::transform(container_algorithm_internal::c_begin(input1), + container_algorithm_internal::c_end(input1), + container_algorithm_internal::c_begin(input2), output, + std::forward(binary_op)); +} + +// c_replace() +// +// Container-based version of the `std::replace()` function to +// replace a container's elements of some value with a new value. The container +// is modified in place. +template +void c_replace(Sequence& sequence, const T& old_value, const T& new_value) { + std::replace(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), old_value, + new_value); +} + +// c_replace_if() +// +// Container-based version of the `std::replace_if()` function to +// replace a container's elements of some value with a new value based on some +// condition. The container is modified in place. +template +void c_replace_if(C& c, Pred&& pred, T&& new_value) { + std::replace_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred), std::forward(new_value)); +} + +// c_replace_copy() +// +// Container-based version of the `std::replace_copy()` function to +// replace a container's elements of some value with a new value and return the +// results within an iterator. +template +OutputIterator c_replace_copy(const C& c, OutputIterator result, T&& old_value, + T&& new_value) { + return std::replace_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(old_value), + std::forward(new_value)); +} + +// c_replace_copy_if() +// +// Container-based version of the `std::replace_copy_if()` function +// to replace a container's elements of some value with a new value based on +// some condition, and return the results within an iterator. +template +OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, + T&& new_value) { + return std::replace_copy_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(pred), + std::forward(new_value)); +} + +// c_fill() +// +// Container-based version of the `std::fill()` function to fill a +// container with some value. +template +void c_fill(C& c, T&& value) { + std::fill(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), std::forward(value)); +} + +// c_fill_n() +// +// Container-based version of the `std::fill_n()` function to fill +// the first N elements in a container with some value. +template +void c_fill_n(C& c, Size n, T&& value) { + std::fill_n(container_algorithm_internal::c_begin(c), n, + std::forward(value)); +} + +// c_generate() +// +// Container-based version of the `std::generate()` function to +// assign a container's elements to the values provided by the given generator. +template +void c_generate(C& c, Generator&& gen) { + std::generate(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(gen)); +} + +// c_generate_n() +// +// Container-based version of the `std::generate_n()` function to +// assign a container's first N elements to the values provided by the given +// generator. +template +container_algorithm_internal::ContainerIter c_generate_n(C& c, Size n, + Generator&& gen) { + return std::generate_n(container_algorithm_internal::c_begin(c), n, + std::forward(gen)); +} + +// Note: `c_xx()` container versions for `remove()`, `remove_if()`, +// and `unique()` are omitted, because it's not clear whether or not such +// functions should call erase on their supplied sequences afterwards. Either +// behavior would be surprising for a different set of users. + +// c_remove_copy() +// +// Container-based version of the `std::remove_copy()` function to +// copy a container's elements while removing any elements matching the given +// `value`. +template +OutputIterator c_remove_copy(const C& c, OutputIterator result, T&& value) { + return std::remove_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(value)); +} + +// c_remove_copy_if() +// +// Container-based version of the `std::remove_copy_if()` function +// to copy a container's elements while removing any elements matching the given +// condition. +template +OutputIterator c_remove_copy_if(const C& c, OutputIterator result, + Pred&& pred) { + return std::remove_copy_if(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(pred)); +} + +// c_unique_copy() +// +// Container-based version of the `std::unique_copy()` function to +// copy a container's elements while removing any elements containing duplicate +// values. +template +OutputIterator c_unique_copy(const C& c, OutputIterator result) { + return std::unique_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result); +} + +// Overload of c_unique_copy() for using a predicate evaluation other than +// `==` for comparing uniqueness of the element values. +template +OutputIterator c_unique_copy(const C& c, OutputIterator result, + BinaryPredicate&& pred) { + return std::unique_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, + std::forward(pred)); +} + +// c_reverse() +// +// Container-based version of the `std::reverse()` function to +// reverse a container's elements. +template +void c_reverse(Sequence& sequence) { + std::reverse(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// c_reverse_copy() +// +// Container-based version of the `std::reverse()` function to +// reverse a container's elements and write them to an iterator range. +template +OutputIterator c_reverse_copy(const C& sequence, OutputIterator result) { + return std::reverse_copy(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + result); +} + +// c_rotate() +// +// Container-based version of the `std::rotate()` function to +// shift a container's elements leftward such that the `middle` element becomes +// the first element in the container. +template > +Iterator c_rotate(C& sequence, Iterator middle) { + return absl::rotate(container_algorithm_internal::c_begin(sequence), middle, + container_algorithm_internal::c_end(sequence)); +} + +// c_rotate_copy() +// +// Container-based version of the `std::rotate_copy()` function to +// shift a container's elements leftward such that the `middle` element becomes +// the first element in a new iterator range. +template +OutputIterator c_rotate_copy( + const C& sequence, + container_algorithm_internal::ContainerIter middle, + OutputIterator result) { + return std::rotate_copy(container_algorithm_internal::c_begin(sequence), + middle, container_algorithm_internal::c_end(sequence), + result); +} + +// c_shuffle() +// +// Container-based version of the `std::shuffle()` function to +// randomly shuffle elements within the container using a `gen()` uniform random +// number generator. +template +void c_shuffle(RandomAccessContainer& c, UniformRandomBitGenerator&& gen) { + std::shuffle(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(gen)); +} + +//------------------------------------------------------------------------------ +// Partition functions +//------------------------------------------------------------------------------ + +// c_is_partitioned() +// +// Container-based version of the `std::is_partitioned()` function +// to test whether all elements in the container for which `pred` returns `true` +// precede those for which `pred` is `false`. +template +bool c_is_partitioned(const C& c, Pred&& pred) { + return std::is_partitioned(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_partition() +// +// Container-based version of the `std::partition()` function +// to rearrange all elements in a container in such a way that all elements for +// which `pred` returns `true` precede all those for which it returns `false`, +// returning an iterator to the first element of the second group. +template +container_algorithm_internal::ContainerIter c_partition(C& c, Pred&& pred) { + return std::partition(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_stable_partition() +// +// Container-based version of the `std::stable_partition()` function +// to rearrange all elements in a container in such a way that all elements for +// which `pred` returns `true` precede all those for which it returns `false`, +// preserving the relative ordering between the two groups. The function returns +// an iterator to the first element of the second group. +template +container_algorithm_internal::ContainerIter c_stable_partition(C& c, + Pred&& pred) { + return std::stable_partition(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +// c_partition_copy() +// +// Container-based version of the `std::partition_copy()` function +// to partition a container's elements and return them into two iterators: one +// for which `pred` returns `true`, and one for which `pred` returns `false.` + +template +std::pair c_partition_copy( + const C& c, OutputIterator1 out_true, OutputIterator2 out_false, + Pred&& pred) { + return std::partition_copy(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), out_true, + out_false, std::forward(pred)); +} + +// c_partition_point() +// +// Container-based version of the `std::partition_point()` function +// to return the first element of an already partitioned container for which +// the given `pred` is not `true`. +template +container_algorithm_internal::ContainerIter c_partition_point(C& c, + Pred&& pred) { + return std::partition_point(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(pred)); +} + +//------------------------------------------------------------------------------ +// Sorting functions +//------------------------------------------------------------------------------ + +// c_sort() +// +// Container-based version of the `std::sort()` function +// to sort elements in ascending order of their values. +template +void c_sort(C& c) { + std::sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_sort() for performing a `comp` comparison other than the +// default `operator<`. +template +void c_sort(C& c, Compare&& comp) { + std::sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_stable_sort() +// +// Container-based version of the `std::stable_sort()` function +// to sort elements in ascending order of their values, preserving the order +// of equivalents. +template +void c_stable_sort(C& c) { + std::stable_sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_stable_sort() for performing a `comp` comparison other than the +// default `operator<`. +template +void c_stable_sort(C& c, Compare&& comp) { + std::stable_sort(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_is_sorted() +// +// Container-based version of the `std::is_sorted()` function +// to evaluate whether the given container is sorted in ascending order. +template +bool c_is_sorted(const C& c) { + return std::is_sorted(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// c_is_sorted() overload for performing a `comp` comparison other than the +// default `operator<`. +template +bool c_is_sorted(const C& c, Compare&& comp) { + return std::is_sorted(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_partial_sort() +// +// Container-based version of the `std::partial_sort()` function +// to rearrange elements within a container such that elements before `middle` +// are sorted in ascending order. +template +void c_partial_sort( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter middle) { + std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_partial_sort() for performing a `comp` comparison other than +// the default `operator<`. +template +void c_partial_sort( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter middle, + Compare&& comp) { + std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_partial_sort_copy() +// +// Container-based version of the `std::partial_sort_copy()` +// function to sort elements within a container such that elements before +// `middle` are sorted in ascending order, and return the result within an +// iterator. +template +container_algorithm_internal::ContainerIter +c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { + return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(result), + container_algorithm_internal::c_end(result)); +} + +// Overload of c_partial_sort_copy() for performing a `comp` comparison other +// than the default `operator<`. +template +container_algorithm_internal::ContainerIter +c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, + Compare&& comp) { + return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + container_algorithm_internal::c_begin(result), + container_algorithm_internal::c_end(result), + std::forward(comp)); +} + +// c_is_sorted_until() +// +// Container-based version of the `std::is_sorted_until()` function +// to return the first element within a container that is not sorted in +// ascending order as an iterator. +template +container_algorithm_internal::ContainerIter c_is_sorted_until(C& c) { + return std::is_sorted_until(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_is_sorted_until() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIter c_is_sorted_until( + C& c, Compare&& comp) { + return std::is_sorted_until(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_nth_element() +// +// Container-based version of the `std::nth_element()` function +// to rearrange the elements within a container such that the `nth` element +// would be in that position in an ordered sequence; other elements may be in +// any order, except that all preceding `nth` will be less than that element, +// and all following `nth` will be greater than that element. +template +void c_nth_element( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter nth) { + std::nth_element(container_algorithm_internal::c_begin(sequence), nth, + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_nth_element() for performing a `comp` comparison other than +// the default `operator<`. +template +void c_nth_element( + RandomAccessContainer& sequence, + container_algorithm_internal::ContainerIter nth, + Compare&& comp) { + std::nth_element(container_algorithm_internal::c_begin(sequence), nth, + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Binary Search +//------------------------------------------------------------------------------ + +// c_lower_bound() +// +// Container-based version of the `std::lower_bound()` function +// to return an iterator pointing to the first element in a sorted container +// which does not compare less than `value`. +template +container_algorithm_internal::ContainerIter c_lower_bound( + Sequence& sequence, T&& value) { + return std::lower_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value)); +} + +// Overload of c_lower_bound() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIter c_lower_bound( + Sequence& sequence, T&& value, Compare&& comp) { + return std::lower_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value), std::forward(comp)); +} + +// c_upper_bound() +// +// Container-based version of the `std::upper_bound()` function +// to return an iterator pointing to the first element in a sorted container +// which is greater than `value`. +template +container_algorithm_internal::ContainerIter c_upper_bound( + Sequence& sequence, T&& value) { + return std::upper_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value)); +} + +// Overload of c_upper_bound() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIter c_upper_bound( + Sequence& sequence, T&& value, Compare&& comp) { + return std::upper_bound(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value), std::forward(comp)); +} + +// c_equal_range() +// +// Container-based version of the `std::equal_range()` function +// to return an iterator pair pointing to the first and last elements in a +// sorted container which compare equal to `value`. +template +container_algorithm_internal::ContainerIterPairType +c_equal_range(Sequence& sequence, T&& value) { + return std::equal_range(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value)); +} + +// Overload of c_equal_range() for performing a `comp` comparison other than +// the default `operator<`. +template +container_algorithm_internal::ContainerIterPairType +c_equal_range(Sequence& sequence, T&& value, Compare&& comp) { + return std::equal_range(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value), std::forward(comp)); +} + +// c_binary_search() +// +// Container-based version of the `std::binary_search()` function +// to test if any element in the sorted container contains a value equivalent to +// 'value'. +template +bool c_binary_search(Sequence&& sequence, T&& value) { + return std::binary_search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value)); +} + +// Overload of c_binary_search() for performing a `comp` comparison other than +// the default `operator<`. +template +bool c_binary_search(Sequence&& sequence, T&& value, Compare&& comp) { + return std::binary_search(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Merge functions +//------------------------------------------------------------------------------ + +// c_merge() +// +// Container-based version of the `std::merge()` function +// to merge two sorted containers into a single sorted iterator. +template +OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) { + return std::merge(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), result); +} + +// Overload of c_merge() for performing a `comp` comparison other than +// the default `operator<`. +template +OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result, + Compare&& comp) { + return std::merge(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), result, + std::forward(comp)); +} + +// c_inplace_merge() +// +// Container-based version of the `std::inplace_merge()` function +// to merge a supplied iterator `middle` into a container. +template +void c_inplace_merge(C& c, + container_algorithm_internal::ContainerIter middle) { + std::inplace_merge(container_algorithm_internal::c_begin(c), middle, + container_algorithm_internal::c_end(c)); +} + +// Overload of c_inplace_merge() for performing a merge using a `comp` other +// than `operator<`. +template +void c_inplace_merge(C& c, + container_algorithm_internal::ContainerIter middle, + Compare&& comp) { + std::inplace_merge(container_algorithm_internal::c_begin(c), middle, + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_includes() +// +// Container-based version of the `std::includes()` function +// to test whether a sorted container `c1` entirely contains another sorted +// container `c2`. +template +bool c_includes(const C1& c1, const C2& c2) { + return std::includes(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2)); +} + +// Overload of c_includes() for performing a merge using a `comp` other than +// `operator<`. +template +bool c_includes(const C1& c1, const C2& c2, Compare&& comp) { + return std::includes(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), + std::forward(comp)); +} + +// c_set_union() +// +// Container-based version of the `std::set_union()` function +// to return an iterator containing the union of two containers; duplicate +// values are not copied into the output. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { + return std::set_union(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_union() for performing a merge using a `comp` other than +// `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, + Compare&& comp) { + return std::set_union(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +// c_set_intersection() +// +// Container-based version of the `std::set_intersection()` function +// to return an iterator containing the intersection of two containers. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_intersection(const C1& c1, const C2& c2, + OutputIterator output) { + return std::set_intersection(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_intersection() for performing a merge using a `comp` other +// than `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_intersection(const C1& c1, const C2& c2, + OutputIterator output, Compare&& comp) { + return std::set_intersection(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +// c_set_difference() +// +// Container-based version of the `std::set_difference()` function +// to return an iterator containing elements present in the first container but +// not in the second. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_difference(const C1& c1, const C2& c2, + OutputIterator output) { + return std::set_difference(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_difference() for performing a merge using a `comp` other +// than `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_difference(const C1& c1, const C2& c2, + OutputIterator output, Compare&& comp) { + return std::set_difference(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +// c_set_symmetric_difference() +// +// Container-based version of the `std::set_symmetric_difference()` +// function to return an iterator containing elements present in either one +// container or the other, but not both. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, + OutputIterator output) { + return std::set_symmetric_difference( + container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output); +} + +// Overload of c_set_symmetric_difference() for performing a merge using a +// `comp` other than `operator<`. +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> +OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, + OutputIterator output, + Compare&& comp) { + return std::set_symmetric_difference( + container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), output, + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Heap functions +//------------------------------------------------------------------------------ + +// c_push_heap() +// +// Container-based version of the `std::push_heap()` function +// to push a value onto a container heap. +template +void c_push_heap(RandomAccessContainer& sequence) { + std::push_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_push_heap() for performing a push operation on a heap using a +// `comp` other than `operator<`. +template +void c_push_heap(RandomAccessContainer& sequence, Compare&& comp) { + std::push_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_pop_heap() +// +// Container-based version of the `std::pop_heap()` function +// to pop a value from a heap container. +template +void c_pop_heap(RandomAccessContainer& sequence) { + std::pop_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_pop_heap() for performing a pop operation on a heap using a +// `comp` other than `operator<`. +template +void c_pop_heap(RandomAccessContainer& sequence, Compare&& comp) { + std::pop_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_make_heap() +// +// Container-based version of the `std::make_heap()` function +// to make a container a heap. +template +void c_make_heap(RandomAccessContainer& sequence) { + std::make_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_make_heap() for performing heap comparisons using a +// `comp` other than `operator<` +template +void c_make_heap(RandomAccessContainer& sequence, Compare&& comp) { + std::make_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_sort_heap() +// +// Container-based version of the `std::sort_heap()` function +// to sort a heap into ascending order (after which it is no longer a heap). +template +void c_sort_heap(RandomAccessContainer& sequence) { + std::sort_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_sort_heap() for performing heap comparisons using a +// `comp` other than `operator<` +template +void c_sort_heap(RandomAccessContainer& sequence, Compare&& comp) { + std::sort_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_is_heap() +// +// Container-based version of the `std::is_heap()` function +// to check whether the given container is a heap. +template +bool c_is_heap(const RandomAccessContainer& sequence) { + return std::is_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_is_heap() for performing heap comparisons using a +// `comp` other than `operator<` +template +bool c_is_heap(const RandomAccessContainer& sequence, Compare&& comp) { + return std::is_heap(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_is_heap_until() +// +// Container-based version of the `std::is_heap_until()` function +// to find the first element in a given container which is not in heap order. +template +container_algorithm_internal::ContainerIter +c_is_heap_until(RandomAccessContainer& sequence) { + return std::is_heap_until(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_is_heap_until() for performing heap comparisons using a +// `comp` other than `operator<` +template +container_algorithm_internal::ContainerIter +c_is_heap_until(RandomAccessContainer& sequence, Compare&& comp) { + return std::is_heap_until(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Min/max +//------------------------------------------------------------------------------ + +// c_min_element() +// +// Container-based version of the `std::min_element()` function +// to return an iterator pointing to the element with the smallest value, using +// `operator<` to make the comparisons. +template +container_algorithm_internal::ContainerIter c_min_element( + Sequence& sequence) { + return std::min_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_min_element() for performing a `comp` comparison other than +// `operator<`. +template +container_algorithm_internal::ContainerIter c_min_element( + Sequence& sequence, Compare&& comp) { + return std::min_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_max_element() +// +// Container-based version of the `std::max_element()` function +// to return an iterator pointing to the element with the largest value, using +// `operator<` to make the comparisons. +template +container_algorithm_internal::ContainerIter c_max_element( + Sequence& sequence) { + return std::max_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence)); +} + +// Overload of c_max_element() for performing a `comp` comparison other than +// `operator<`. +template +container_algorithm_internal::ContainerIter c_max_element( + Sequence& sequence, Compare&& comp) { + return std::max_element(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(comp)); +} + +// c_minmax_element() +// +// Container-based version of the `std::minmax_element()` function +// to return a pair of iterators pointing to the elements containing the +// smallest and largest values, respectively, using `operator<` to make the +// comparisons. +template +container_algorithm_internal::ContainerIterPairType +c_minmax_element(C& c) { + return std::minmax_element(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_minmax_element() for performing `comp` comparisons other than +// `operator<`. +template +container_algorithm_internal::ContainerIterPairType +c_minmax_element(C& c, Compare&& comp) { + return std::minmax_element(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// Lexicographical Comparisons +//------------------------------------------------------------------------------ + +// c_lexicographical_compare() +// +// Container-based version of the `std::lexicographical_compare()` +// function to lexicographically compare (e.g. sort words alphabetically) two +// container sequences. The comparison is performed using `operator<`. Note +// that capital letters ("A-Z") have ASCII values less than lowercase letters +// ("a-z"). +template +bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) { + return std::lexicographical_compare( + container_algorithm_internal::c_begin(sequence1), + container_algorithm_internal::c_end(sequence1), + container_algorithm_internal::c_begin(sequence2), + container_algorithm_internal::c_end(sequence2)); +} + +// Overload of c_lexicographical_compare() for performing a lexicographical +// comparison using a `comp` operator instead of `operator<`. +template +bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2, + Compare&& comp) { + return std::lexicographical_compare( + container_algorithm_internal::c_begin(sequence1), + container_algorithm_internal::c_end(sequence1), + container_algorithm_internal::c_begin(sequence2), + container_algorithm_internal::c_end(sequence2), + std::forward(comp)); +} + +// c_next_permutation() +// +// Container-based version of the `std::next_permutation()` function +// to rearrange a container's elements into the next lexicographically greater +// permutation. +template +bool c_next_permutation(C& c) { + return std::next_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_next_permutation() for performing a lexicographical +// comparison using a `comp` operator instead of `operator<`. +template +bool c_next_permutation(C& c, Compare&& comp) { + return std::next_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +// c_prev_permutation() +// +// Container-based version of the `std::prev_permutation()` function +// to rearrange a container's elements into the next lexicographically lesser +// permutation. +template +bool c_prev_permutation(C& c) { + return std::prev_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c)); +} + +// Overload of c_prev_permutation() for performing a lexicographical +// comparison using a `comp` operator instead of `operator<`. +template +bool c_prev_permutation(C& c, Compare&& comp) { + return std::prev_permutation(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(comp)); +} + +//------------------------------------------------------------------------------ +// algorithms +//------------------------------------------------------------------------------ + +// c_iota() +// +// Container-based version of the `std::iota()` function +// to compute successive values of `value`, as if incremented with `++value` +// after each element is written. and write them to the container. +template +void c_iota(Sequence& sequence, T&& value) { + std::iota(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(value)); +} +// c_accumulate() +// +// Container-based version of the `std::accumulate()` function +// to accumulate the element values of a container to `init` and return that +// accumulation by value. +// +// Note: Due to a language technicality this function has return type +// absl::decay_t. As a user of this function you can casually read +// this as "returns T by value" and assume it does the right thing. +template +decay_t c_accumulate(const Sequence& sequence, T&& init) { + return std::accumulate(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(init)); +} + +// Overload of c_accumulate() for using a binary operations other than +// addition for computing the accumulation. +template +decay_t c_accumulate(const Sequence& sequence, T&& init, + BinaryOp&& binary_op) { + return std::accumulate(container_algorithm_internal::c_begin(sequence), + container_algorithm_internal::c_end(sequence), + std::forward(init), + std::forward(binary_op)); +} + +// c_inner_product() +// +// Container-based version of the `std::inner_product()` function +// to compute the cumulative inner product of container element pairs. +// +// Note: Due to a language technicality this function has return type +// absl::decay_t. As a user of this function you can casually read +// this as "returns T by value" and assume it does the right thing. +template +decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, + T&& sum) { + return std::inner_product(container_algorithm_internal::c_begin(factors1), + container_algorithm_internal::c_end(factors1), + container_algorithm_internal::c_begin(factors2), + std::forward(sum)); +} + +// Overload of c_inner_product() for using binary operations other than +// `operator+` (for computing the accumulation) and `operator*` (for computing +// the product between the two container's element pair). +template +decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, + T&& sum, BinaryOp1&& op1, BinaryOp2&& op2) { + return std::inner_product(container_algorithm_internal::c_begin(factors1), + container_algorithm_internal::c_end(factors1), + container_algorithm_internal::c_begin(factors2), + std::forward(sum), std::forward(op1), + std::forward(op2)); +} + +// c_adjacent_difference() +// +// Container-based version of the `std::adjacent_difference()` +// function to compute the difference between each element and the one preceding +// it and write it to an iterator. +template +OutputIt c_adjacent_difference(const InputSequence& input, + OutputIt output_first) { + return std::adjacent_difference(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first); +} + +// Overload of c_adjacent_difference() for using a binary operation other than +// subtraction to compute the adjacent difference. +template +OutputIt c_adjacent_difference(const InputSequence& input, + OutputIt output_first, BinaryOp&& op) { + return std::adjacent_difference(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first, std::forward(op)); +} + +// c_partial_sum() +// +// Container-based version of the `std::partial_sum()` function +// to compute the partial sum of the elements in a sequence and write them +// to an iterator. The partial sum is the sum of all element values so far in +// the sequence. +template +OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first) { + return std::partial_sum(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first); +} + +// Overload of c_partial_sum() for using a binary operation other than addition +// to compute the "partial sum". +template +OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first, + BinaryOp&& op) { + return std::partial_sum(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input), + output_first, std::forward(op)); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_ALGORITHM_CONTAINER_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/attributes.h b/libs/or-tools-src-ubuntu/include/absl/base/attributes.h new file mode 100644 index 0000000000000000000000000000000000000000..ff138629d1f1ceb2d6fcd6fee328539673522841 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/attributes.h @@ -0,0 +1,621 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 file defines macros for declaring attributes for functions, +// types, and variables. +// +// These macros are used within Abseil and allow the compiler to optimize, where +// applicable, certain function calls. +// +// This file is used for both C and C++! +// +// Most macros here are exposing GCC or Clang features, and are stubbed out for +// other compilers. +// +// GCC attributes documentation: +// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html +// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Variable-Attributes.html +// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Type-Attributes.html +// +// Most attributes in this file are already supported by GCC 4.7. However, some +// of them are not supported in older version of Clang. Thus, we check +// `__has_attribute()` first. If the check fails, we check if we are on GCC and +// assume the attribute exists on GCC (which is verified on GCC 4.7). +// +// ----------------------------------------------------------------------------- +// Sanitizer Attributes +// ----------------------------------------------------------------------------- +// +// Sanitizer-related attributes are not "defined" in this file (and indeed +// are not defined as such in any file). To utilize the following +// sanitizer-related attributes within your builds, define the following macros +// within your build using a `-D` flag, along with the given value for +// `-fsanitize`: +// +// * `ADDRESS_SANITIZER` + `-fsanitize=address` (Clang, GCC 4.8) +// * `MEMORY_SANITIZER` + `-fsanitize=memory` (Clang-only) +// * `THREAD_SANITIZER + `-fsanitize=thread` (Clang, GCC 4.8+) +// * `UNDEFINED_BEHAVIOR_SANITIZER` + `-fsanitize=undefined` (Clang, GCC 4.9+) +// * `CONTROL_FLOW_INTEGRITY` + -fsanitize=cfi (Clang-only) +// +// Example: +// +// // Enable branches in the Abseil code that are tagged for ASan: +// $ bazel build --copt=-DADDRESS_SANITIZER --copt=-fsanitize=address +// --linkopt=-fsanitize=address *target* +// +// Since these macro names are only supported by GCC and Clang, we only check +// for `__GNUC__` (GCC or Clang) and the above macros. +#ifndef ABSL_BASE_ATTRIBUTES_H_ +#define ABSL_BASE_ATTRIBUTES_H_ + +// ABSL_HAVE_ATTRIBUTE +// +// A function-like feature checking macro that is a wrapper around +// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a +// nonzero constant integer if the attribute is supported or 0 if not. +// +// It evaluates to zero if `__has_attribute` is not defined by the compiler. +// +// GCC: https://gcc.gnu.org/gcc-5/changes.html +// Clang: https://clang.llvm.org/docs/LanguageExtensions.html +#ifdef __has_attribute +#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) +#else +#define ABSL_HAVE_ATTRIBUTE(x) 0 +#endif + +// ABSL_HAVE_CPP_ATTRIBUTE +// +// A function-like feature checking macro that accepts C++11 style attributes. +// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6 +// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't +// find `__has_cpp_attribute`, will evaluate to 0. +#if defined(__cplusplus) && defined(__has_cpp_attribute) +// NOTE: requiring __cplusplus above should not be necessary, but +// works around https://bugs.llvm.org/show_bug.cgi?id=23435. +#define ABSL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +#define ABSL_HAVE_CPP_ATTRIBUTE(x) 0 +#endif + +// ----------------------------------------------------------------------------- +// Function Attributes +// ----------------------------------------------------------------------------- +// +// GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html +// Clang: https://clang.llvm.org/docs/AttributeReference.html + +// ABSL_PRINTF_ATTRIBUTE +// ABSL_SCANF_ATTRIBUTE +// +// Tells the compiler to perform `printf` format string checking if the +// compiler supports it; see the 'format' attribute in +// . +// +// Note: As the GCC manual states, "[s]ince non-static C++ methods +// have an implicit 'this' argument, the arguments of such methods +// should be counted from two, not one." +#if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((__format__(__printf__, string_index, first_to_check))) +#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((__format__(__scanf__, string_index, first_to_check))) +#else +#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) +#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) +#endif + +// ABSL_ATTRIBUTE_ALWAYS_INLINE +// ABSL_ATTRIBUTE_NOINLINE +// +// Forces functions to either inline or not inline. Introduced in gcc 3.1. +#if ABSL_HAVE_ATTRIBUTE(always_inline) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) +#define ABSL_HAVE_ATTRIBUTE_ALWAYS_INLINE 1 +#else +#define ABSL_ATTRIBUTE_ALWAYS_INLINE +#endif + +#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_NOINLINE __attribute__((noinline)) +#define ABSL_HAVE_ATTRIBUTE_NOINLINE 1 +#else +#define ABSL_ATTRIBUTE_NOINLINE +#endif + +// ABSL_ATTRIBUTE_NO_TAIL_CALL +// +// Prevents the compiler from optimizing away stack frames for functions which +// end in a call to another function. +#if ABSL_HAVE_ATTRIBUTE(disable_tail_calls) +#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 +#define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls)) +#elif defined(__GNUC__) && !defined(__clang__) +#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 +#define ABSL_ATTRIBUTE_NO_TAIL_CALL \ + __attribute__((optimize("no-optimize-sibling-calls"))) +#else +#define ABSL_ATTRIBUTE_NO_TAIL_CALL +#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 0 +#endif + +// ABSL_ATTRIBUTE_WEAK +// +// Tags a function as weak for the purposes of compilation and linking. +// Weak attributes currently do not work properly in LLVM's Windows backend, +// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 +// for further information. +// The MinGW compiler doesn't complain about the weak attribute until the link +// step, presumably because Windows doesn't use ELF binaries. +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + !(defined(__llvm__) && defined(_WIN32)) && !defined(__MINGW32__) +#undef ABSL_ATTRIBUTE_WEAK +#define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) +#define ABSL_HAVE_ATTRIBUTE_WEAK 1 +#else +#define ABSL_ATTRIBUTE_WEAK +#define ABSL_HAVE_ATTRIBUTE_WEAK 0 +#endif + +// ABSL_ATTRIBUTE_NONNULL +// +// Tells the compiler either (a) that a particular function parameter +// should be a non-null pointer, or (b) that all pointer arguments should +// be non-null. +// +// Note: As the GCC manual states, "[s]ince non-static C++ methods +// have an implicit 'this' argument, the arguments of such methods +// should be counted from two, not one." +// +// Args are indexed starting at 1. +// +// For non-static class member functions, the implicit `this` argument +// is arg 1, and the first explicit argument is arg 2. For static class member +// functions, there is no implicit `this`, and the first explicit argument is +// arg 1. +// +// Example: +// +// /* arg_a cannot be null, but arg_b can */ +// void Function(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(1); +// +// class C { +// /* arg_a cannot be null, but arg_b can */ +// void Method(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(2); +// +// /* arg_a cannot be null, but arg_b can */ +// static void StaticMethod(void* arg_a, void* arg_b) +// ABSL_ATTRIBUTE_NONNULL(1); +// }; +// +// If no arguments are provided, then all pointer arguments should be non-null. +// +// /* No pointer arguments may be null. */ +// void Function(void* arg_a, void* arg_b, int arg_c) ABSL_ATTRIBUTE_NONNULL(); +// +// NOTE: The GCC nonnull attribute actually accepts a list of arguments, but +// ABSL_ATTRIBUTE_NONNULL does not. +#if ABSL_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index))) +#else +#define ABSL_ATTRIBUTE_NONNULL(...) +#endif + +// ABSL_ATTRIBUTE_NORETURN +// +// Tells the compiler that a given function never returns. +#if ABSL_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define ABSL_ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define ABSL_ATTRIBUTE_NORETURN +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS +// +// Tells the AddressSanitizer (or other memory testing tools) to ignore a given +// function. Useful for cases when a function reads random locations on stack, +// calls _exit from a cloned subprocess, deliberately accesses buffer +// out of bounds or does other scary things with memory. +// NOTE: GCC supports AddressSanitizer(asan) since 4.8. +// https://gcc.gnu.org/gcc-4.8/changes.html +#if defined(__GNUC__) +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY +// +// Tells the MemorySanitizer to relax the handling of a given function. All +// "Use of uninitialized value" warnings from such functions will be suppressed, +// and all values loaded from memory will be considered fully initialized. +// This attribute is similar to the ADDRESS_SANITIZER attribute above, but deals +// with initialized-ness rather than addressability issues. +// NOTE: MemorySanitizer(msan) is supported by Clang but not GCC. +#if defined(__clang__) +#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_THREAD +// +// Tells the ThreadSanitizer to not instrument a given function. +// NOTE: GCC supports ThreadSanitizer(tsan) since 4.8. +// https://gcc.gnu.org/gcc-4.8/changes.html +#if defined(__GNUC__) +#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED +// +// Tells the UndefinedSanitizer to ignore a given function. Useful for cases +// where certain behavior (eg. division by zero) is being used intentionally. +// NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9. +// https://gcc.gnu.org/gcc-4.9/changes.html +#if defined(__GNUC__) && \ + (defined(UNDEFINED_BEHAVIOR_SANITIZER) || defined(ADDRESS_SANITIZER)) +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ + __attribute__((no_sanitize("undefined"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_CFI +// +// Tells the ControlFlowIntegrity sanitizer to not instrument a given function. +// See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details. +#if defined(__GNUC__) && defined(CONTROL_FLOW_INTEGRITY) +#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI +#endif + +// ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK +// +// Tells the SafeStack to not instrument a given function. +// See https://clang.llvm.org/docs/SafeStack.html for details. +#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER) +#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \ + __attribute__((no_sanitize("safe-stack"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK +#endif + +// ABSL_ATTRIBUTE_RETURNS_NONNULL +// +// Tells the compiler that a particular function never returns a null pointer. +#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) || \ + (defined(__GNUC__) && \ + (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \ + !defined(__clang__)) +#define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) +#else +#define ABSL_ATTRIBUTE_RETURNS_NONNULL +#endif + +// ABSL_HAVE_ATTRIBUTE_SECTION +// +// Indicates whether labeled sections are supported. Weak symbol support is +// a prerequisite. Labeled sections are not supported on Darwin/iOS. +#ifdef ABSL_HAVE_ATTRIBUTE_SECTION +#error ABSL_HAVE_ATTRIBUTE_SECTION cannot be directly set +#elif (ABSL_HAVE_ATTRIBUTE(section) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + !defined(__APPLE__) && ABSL_HAVE_ATTRIBUTE_WEAK +#define ABSL_HAVE_ATTRIBUTE_SECTION 1 + +// ABSL_ATTRIBUTE_SECTION +// +// Tells the compiler/linker to put a given function into a section and define +// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. +// This functionality is supported by GNU linker. Any function annotated with +// `ABSL_ATTRIBUTE_SECTION` must not be inlined, or it will be placed into +// whatever section its caller is placed into. +// +#ifndef ABSL_ATTRIBUTE_SECTION +#define ABSL_ATTRIBUTE_SECTION(name) \ + __attribute__((section(#name))) __attribute__((noinline)) +#endif + + +// ABSL_ATTRIBUTE_SECTION_VARIABLE +// +// Tells the compiler/linker to put a given variable into a section and define +// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. +// This functionality is supported by GNU linker. +#ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name))) +#endif + +// ABSL_DECLARE_ATTRIBUTE_SECTION_VARS +// +// A weak section declaration to be used as a global declaration +// for ABSL_ATTRIBUTE_SECTION_START|STOP(name) to compile and link +// even without functions with ABSL_ATTRIBUTE_SECTION(name). +// ABSL_DEFINE_ATTRIBUTE_SECTION should be in the exactly one file; it's +// a no-op on ELF but not on Mach-O. +// +#ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS +#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ + extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ + extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK +#endif +#ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS +#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name) +#endif + +// ABSL_ATTRIBUTE_SECTION_START +// +// Returns `void*` pointers to start/end of a section of code with +// functions having ABSL_ATTRIBUTE_SECTION(name). +// Returns 0 if no such functions exist. +// One must ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) for this to compile and +// link. +// +#define ABSL_ATTRIBUTE_SECTION_START(name) \ + (reinterpret_cast(__start_##name)) +#define ABSL_ATTRIBUTE_SECTION_STOP(name) \ + (reinterpret_cast(__stop_##name)) + +#else // !ABSL_HAVE_ATTRIBUTE_SECTION + +#define ABSL_HAVE_ATTRIBUTE_SECTION 0 + +// provide dummy definitions +#define ABSL_ATTRIBUTE_SECTION(name) +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) +#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) +#define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast(0)) +#define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast(0)) + +#endif // ABSL_ATTRIBUTE_SECTION + +// ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +// +// Support for aligning the stack on 32-bit x86. +#if ABSL_HAVE_ATTRIBUTE(force_align_arg_pointer) || \ + (defined(__GNUC__) && !defined(__clang__)) +#if defined(__i386__) +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC \ + __attribute__((force_align_arg_pointer)) +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) +#elif defined(__x86_64__) +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (1) +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +#else // !__i386__ && !__x86_64 +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +#endif // __i386__ +#else +#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC +#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) +#endif + +// ABSL_MUST_USE_RESULT +// +// Tells the compiler to warn about unused results. +// +// When annotating a function, it must appear as the first part of the +// declaration or definition. The compiler will warn if the return value from +// such a function is unused: +// +// ABSL_MUST_USE_RESULT Sprocket* AllocateSprocket(); +// AllocateSprocket(); // Triggers a warning. +// +// When annotating a class, it is equivalent to annotating every function which +// returns an instance. +// +// class ABSL_MUST_USE_RESULT Sprocket {}; +// Sprocket(); // Triggers a warning. +// +// Sprocket MakeSprocket(); +// MakeSprocket(); // Triggers a warning. +// +// Note that references and pointers are not instances: +// +// Sprocket* SprocketPointer(); +// SprocketPointer(); // Does *not* trigger a warning. +// +// ABSL_MUST_USE_RESULT allows using cast-to-void to suppress the unused result +// warning. For that, warn_unused_result is used only for clang but not for gcc. +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 +// +// Note: past advice was to place the macro after the argument list. +#if ABSL_HAVE_ATTRIBUTE(nodiscard) +#define ABSL_MUST_USE_RESULT [[nodiscard]] +#elif defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) +#define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +#define ABSL_MUST_USE_RESULT +#endif + +// ABSL_ATTRIBUTE_HOT, ABSL_ATTRIBUTE_COLD +// +// Tells GCC that a function is hot or cold. GCC can use this information to +// improve static analysis, i.e. a conditional branch to a cold function +// is likely to be not-taken. +// This annotation is used for function declarations. +// +// Example: +// +// int foo() ABSL_ATTRIBUTE_HOT; +#if ABSL_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_HOT __attribute__((hot)) +#else +#define ABSL_ATTRIBUTE_HOT +#endif + +#if ABSL_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_COLD __attribute__((cold)) +#else +#define ABSL_ATTRIBUTE_COLD +#endif + +// ABSL_XRAY_ALWAYS_INSTRUMENT, ABSL_XRAY_NEVER_INSTRUMENT, ABSL_XRAY_LOG_ARGS +// +// We define the ABSL_XRAY_ALWAYS_INSTRUMENT and ABSL_XRAY_NEVER_INSTRUMENT +// macro used as an attribute to mark functions that must always or never be +// instrumented by XRay. Currently, this is only supported in Clang/LLVM. +// +// For reference on the LLVM XRay instrumentation, see +// http://llvm.org/docs/XRay.html. +// +// A function with the XRAY_ALWAYS_INSTRUMENT macro attribute in its declaration +// will always get the XRay instrumentation sleds. These sleds may introduce +// some binary size and runtime overhead and must be used sparingly. +// +// These attributes only take effect when the following conditions are met: +// +// * The file/target is built in at least C++11 mode, with a Clang compiler +// that supports XRay attributes. +// * The file/target is built with the -fxray-instrument flag set for the +// Clang/LLVM compiler. +// * The function is defined in the translation unit (the compiler honors the +// attribute in either the definition or the declaration, and must match). +// +// There are cases when, even when building with XRay instrumentation, users +// might want to control specifically which functions are instrumented for a +// particular build using special-case lists provided to the compiler. These +// special case lists are provided to Clang via the +// -fxray-always-instrument=... and -fxray-never-instrument=... flags. The +// attributes in source take precedence over these special-case lists. +// +// To disable the XRay attributes at build-time, users may define +// ABSL_NO_XRAY_ATTRIBUTES. Do NOT define ABSL_NO_XRAY_ATTRIBUTES on specific +// packages/targets, as this may lead to conflicting definitions of functions at +// link-time. +// +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && \ + !defined(ABSL_NO_XRAY_ATTRIBUTES) +#define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]] +#define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]] +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args) +#define ABSL_XRAY_LOG_ARGS(N) \ + [[clang::xray_always_instrument, clang::xray_log_args(N)]] +#else +#define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]] +#endif +#else +#define ABSL_XRAY_ALWAYS_INSTRUMENT +#define ABSL_XRAY_NEVER_INSTRUMENT +#define ABSL_XRAY_LOG_ARGS(N) +#endif + +// ABSL_ATTRIBUTE_REINITIALIZES +// +// Indicates that a member function reinitializes the entire object to a known +// state, independent of the previous state of the object. +// +// The clang-tidy check bugprone-use-after-move allows member functions marked +// with this attribute to be called on objects that have been moved from; +// without the attribute, this would result in a use-after-move warning. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::reinitializes) +#define ABSL_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]] +#else +#define ABSL_ATTRIBUTE_REINITIALIZES +#endif + +// ----------------------------------------------------------------------------- +// Variable Attributes +// ----------------------------------------------------------------------------- + +// ABSL_ATTRIBUTE_UNUSED +// +// Prevents the compiler from complaining about variables that appear unused. +#if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__)) +#undef ABSL_ATTRIBUTE_UNUSED +#define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__)) +#else +#define ABSL_ATTRIBUTE_UNUSED +#endif + +// ABSL_ATTRIBUTE_INITIAL_EXEC +// +// Tells the compiler to use "initial-exec" mode for a thread-local variable. +// See http://people.redhat.com/drepper/tls.pdf for the gory details. +#if ABSL_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec"))) +#else +#define ABSL_ATTRIBUTE_INITIAL_EXEC +#endif + +// ABSL_ATTRIBUTE_PACKED +// +// Instructs the compiler not to use natural alignment for a tagged data +// structure, but instead to reduce its alignment to 1. This attribute can +// either be applied to members of a structure or to a structure in its +// entirety. Applying this attribute (judiciously) to a structure in its +// entirety to optimize the memory footprint of very commonly-used structs is +// fine. Do not apply this attribute to a structure in its entirety if the +// purpose is to control the offsets of the members in the structure. Instead, +// apply this attribute only to structure members that need it. +// +// When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the +// natural alignment of structure members not annotated is preserved. Aligned +// member accesses are faster than non-aligned member accesses even if the +// targeted microprocessor supports non-aligned accesses. +#if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__)) +#else +#define ABSL_ATTRIBUTE_PACKED +#endif + +// ABSL_ATTRIBUTE_FUNC_ALIGN +// +// Tells the compiler to align the function start at least to certain +// alignment boundary +#if ABSL_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) __attribute__((aligned(bytes))) +#else +#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) +#endif + +// ABSL_CONST_INIT +// +// A variable declaration annotated with the `ABSL_CONST_INIT` attribute will +// not compile (on supported platforms) unless the variable has a constant +// initializer. This is useful for variables with static and thread storage +// duration, because it guarantees that they will not suffer from the so-called +// "static init order fiasco". Prefer to put this attribute on the most visible +// declaration of the variable, if there's more than one, because code that +// accesses the variable can then use the attribute for optimization. +// +// Example: +// +// class MyClass { +// public: +// ABSL_CONST_INIT static MyType my_var; +// }; +// +// MyType MyClass::my_var = MakeMyType(...); +// +// Note that this attribute is redundant if the variable is declared constexpr. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#define ABSL_CONST_INIT [[clang::require_constant_initialization]] +#else +#define ABSL_CONST_INIT +#endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) + +#endif // ABSL_BASE_ATTRIBUTES_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/call_once.h b/libs/or-tools-src-ubuntu/include/absl/base/call_once.h new file mode 100644 index 0000000000000000000000000000000000000000..bc5ec937041334a51a50d7f46a937ebcd8d6cf8c --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/call_once.h @@ -0,0 +1,226 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: call_once.h +// ----------------------------------------------------------------------------- +// +// This header file provides an Abseil version of `std::call_once` for invoking +// a given function at most once, across all threads. This Abseil version is +// faster than the C++11 version and incorporates the C++17 argument-passing +// fix, so that (for example) non-const references may be passed to the invoked +// function. + +#ifndef ABSL_BASE_CALL_ONCE_H_ +#define ABSL_BASE_CALL_ONCE_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/internal/invoke.h" +#include "absl/base/internal/low_level_scheduling.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/scheduling_mode.h" +#include "absl/base/internal/spinlock_wait.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +class once_flag; + +namespace base_internal { +std::atomic* ControlWord(absl::once_flag* flag); +} // namespace base_internal + +// call_once() +// +// For all invocations using a given `once_flag`, invokes a given `fn` exactly +// once across all threads. The first call to `call_once()` with a particular +// `once_flag` argument (that does not throw an exception) will run the +// specified function with the provided `args`; other calls with the same +// `once_flag` argument will not run the function, but will wait +// for the provided function to finish running (if it is still running). +// +// This mechanism provides a safe, simple, and fast mechanism for one-time +// initialization in a multi-threaded process. +// +// Example: +// +// class MyInitClass { +// public: +// ... +// mutable absl::once_flag once_; +// +// MyInitClass* init() const { +// absl::call_once(once_, &MyInitClass::Init, this); +// return ptr_; +// } +// +template +void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args); + +// once_flag +// +// Objects of this type are used to distinguish calls to `call_once()` and +// ensure the provided function is only invoked once across all threads. This +// type is not copyable or movable. However, it has a `constexpr` +// constructor, and is safe to use as a namespace-scoped global variable. +class once_flag { + public: + constexpr once_flag() : control_(0) {} + once_flag(const once_flag&) = delete; + once_flag& operator=(const once_flag&) = delete; + + private: + friend std::atomic* base_internal::ControlWord(once_flag* flag); + std::atomic control_; +}; + +//------------------------------------------------------------------------------ +// End of public interfaces. +// Implementation details follow. +//------------------------------------------------------------------------------ + +namespace base_internal { + +// Like call_once, but uses KERNEL_ONLY scheduling. Intended to be used to +// initialize entities used by the scheduler implementation. +template +void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args); + +// Disables scheduling while on stack when scheduling mode is non-cooperative. +// No effect for cooperative scheduling modes. +class SchedulingHelper { + public: + explicit SchedulingHelper(base_internal::SchedulingMode mode) : mode_(mode) { + if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) { + guard_result_ = base_internal::SchedulingGuard::DisableRescheduling(); + } + } + + ~SchedulingHelper() { + if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) { + base_internal::SchedulingGuard::EnableRescheduling(guard_result_); + } + } + + private: + base_internal::SchedulingMode mode_; + bool guard_result_; +}; + +// Bit patterns for call_once state machine values. Internal implementation +// detail, not for use by clients. +// +// The bit patterns are arbitrarily chosen from unlikely values, to aid in +// debugging. However, kOnceInit must be 0, so that a zero-initialized +// once_flag will be valid for immediate use. +enum { + kOnceInit = 0, + kOnceRunning = 0x65C2937B, + kOnceWaiter = 0x05A308D2, + // A very small constant is chosen for kOnceDone so that it fit in a single + // compare with immediate instruction for most common ISAs. This is verified + // for x86, POWER and ARM. + kOnceDone = 221, // Random Number +}; + +template +ABSL_ATTRIBUTE_NOINLINE +void CallOnceImpl(std::atomic* control, + base_internal::SchedulingMode scheduling_mode, Callable&& fn, + Args&&... args) { +#ifndef NDEBUG + { + uint32_t old_control = control->load(std::memory_order_relaxed); + if (old_control != kOnceInit && + old_control != kOnceRunning && + old_control != kOnceWaiter && + old_control != kOnceDone) { + ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx", + static_cast(old_control)); // NOLINT + } + } +#endif // NDEBUG + static const base_internal::SpinLockWaitTransition trans[] = { + {kOnceInit, kOnceRunning, true}, + {kOnceRunning, kOnceWaiter, false}, + {kOnceDone, kOnceDone, true}}; + + // Must do this before potentially modifying control word's state. + base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode); + // Short circuit the simplest case to avoid procedure call overhead. + // The base_internal::SpinLockWait() call returns either kOnceInit or + // kOnceDone. If it returns kOnceDone, it must have loaded the control word + // with std::memory_order_acquire and seen a value of kOnceDone. + uint32_t old_control = kOnceInit; + if (control->compare_exchange_strong(old_control, kOnceRunning, + std::memory_order_relaxed) || + base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans, + scheduling_mode) == kOnceInit) { + base_internal::Invoke(std::forward(fn), + std::forward(args)...); + // The call to SpinLockWake below is an optimization, because the waiter + // in SpinLockWait is waiting with a short timeout. The atomic load/store + // sequence is slightly faster than an atomic exchange: + // old_control = control->exchange(base_internal::kOnceDone, + // std::memory_order_release); + // We opt for a slightly faster case when there are no waiters, in spite + // of longer tail latency when there are waiters. + old_control = control->load(std::memory_order_relaxed); + control->store(base_internal::kOnceDone, std::memory_order_release); + if (old_control == base_internal::kOnceWaiter) { + base_internal::SpinLockWake(control, true); + } + } // else *control is already kOnceDone +} + +inline std::atomic* ControlWord(once_flag* flag) { + return &flag->control_; +} + +template +void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args) { + std::atomic* once = base_internal::ControlWord(flag); + uint32_t s = once->load(std::memory_order_acquire); + if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { + base_internal::CallOnceImpl(once, base_internal::SCHEDULE_KERNEL_ONLY, + std::forward(fn), + std::forward(args)...); + } +} + +} // namespace base_internal + +template +void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { + std::atomic* once = base_internal::ControlWord(&flag); + uint32_t s = once->load(std::memory_order_acquire); + if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) { + base_internal::CallOnceImpl( + once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL, + std::forward(fn), std::forward(args)...); + } +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_CALL_ONCE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/casts.h b/libs/or-tools-src-ubuntu/include/absl/base/casts.h new file mode 100644 index 0000000000000000000000000000000000000000..322cc1d243f6f5bfac116ff65e4ad9024a81f930 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/casts.h @@ -0,0 +1,184 @@ +// +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: casts.h +// ----------------------------------------------------------------------------- +// +// This header file defines casting templates to fit use cases not covered by +// the standard casts provided in the C++ standard. As with all cast operations, +// use these with caution and only if alternatives do not exist. + +#ifndef ABSL_BASE_CASTS_H_ +#define ABSL_BASE_CASTS_H_ + +#include +#include +#include +#include + +#include "absl/base/internal/identity.h" +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace internal_casts { + +template +struct is_bitcastable + : std::integral_constant< + bool, + sizeof(Dest) == sizeof(Source) && + type_traits_internal::is_trivially_copyable::value && + type_traits_internal::is_trivially_copyable::value && + std::is_default_constructible::value> {}; + +} // namespace internal_casts + +// implicit_cast() +// +// Performs an implicit conversion between types following the language +// rules for implicit conversion; if an implicit conversion is otherwise +// allowed by the language in the given context, this function performs such an +// implicit conversion. +// +// Example: +// +// // If the context allows implicit conversion: +// From from; +// To to = from; +// +// // Such code can be replaced by: +// implicit_cast(from); +// +// An `implicit_cast()` may also be used to annotate numeric type conversions +// that, although safe, may produce compiler warnings (such as `long` to `int`). +// Additionally, an `implicit_cast()` is also useful within return statements to +// indicate a specific implicit conversion is being undertaken. +// +// Example: +// +// return implicit_cast(size_in_bytes) / capacity_; +// +// Annotating code with `implicit_cast()` allows you to explicitly select +// particular overloads and template instantiations, while providing a safer +// cast than `reinterpret_cast()` or `static_cast()`. +// +// Additionally, an `implicit_cast()` can be used to allow upcasting within a +// type hierarchy where incorrect use of `static_cast()` could accidentally +// allow downcasting. +// +// Finally, an `implicit_cast()` can be used to perform implicit conversions +// from unrelated types that otherwise couldn't be implicitly cast directly; +// C++ will normally only implicitly cast "one step" in such conversions. +// +// That is, if C is a type which can be implicitly converted to B, with B being +// a type that can be implicitly converted to A, an `implicit_cast()` can be +// used to convert C to B (which the compiler can then implicitly convert to A +// using language rules). +// +// Example: +// +// // Assume an object C is convertible to B, which is implicitly convertible +// // to A +// A a = implicit_cast(C); +// +// Such implicit cast chaining may be useful within template logic. +template +constexpr To implicit_cast(typename absl::internal::identity_t to) { + return to; +} + +// bit_cast() +// +// Performs a bitwise cast on a type without changing the underlying bit +// representation of that type's value. The two types must be of the same size +// and both types must be trivially copyable. As with most casts, use with +// caution. A `bit_cast()` might be needed when you need to temporarily treat a +// type as some other type, such as in the following cases: +// +// * Serialization (casting temporarily to `char *` for those purposes is +// always allowed by the C++ standard) +// * Managing the individual bits of a type within mathematical operations +// that are not normally accessible through that type +// * Casting non-pointer types to pointer types (casting the other way is +// allowed by `reinterpret_cast()` but round-trips cannot occur the other +// way). +// +// Example: +// +// float f = 3.14159265358979; +// int i = bit_cast(f); +// // i = 0x40490fdb +// +// Casting non-pointer types to pointer types and then dereferencing them +// traditionally produces undefined behavior. +// +// Example: +// +// // WRONG +// float f = 3.14159265358979; // WRONG +// int i = * reinterpret_cast(&f); // WRONG +// +// The address-casting method produces undefined behavior according to the ISO +// C++ specification section [basic.lval]. Roughly, this section says: if an +// object in memory has one type, and a program accesses it with a different +// type, the result is undefined behavior for most values of "different type". +// +// Such casting results in type punning: holding an object in memory of one type +// and reading its bits back using a different type. A `bit_cast()` avoids this +// issue by implementing its casts using `memcpy()`, which avoids introducing +// this undefined behavior. +// +// NOTE: The requirements here are more strict than the bit_cast of standard +// proposal p0476 due to the need for workarounds and lack of intrinsics. +// Specifically, this implementation also requires `Dest` to be +// default-constructible. +template < + typename Dest, typename Source, + typename std::enable_if::value, + int>::type = 0> +inline Dest bit_cast(const Source& source) { + Dest dest; + memcpy(static_cast(std::addressof(dest)), + static_cast(std::addressof(source)), sizeof(dest)); + return dest; +} + +// NOTE: This overload is only picked if the requirements of bit_cast are not +// met. It is therefore UB, but is provided temporarily as previous versions of +// this function template were unchecked. Do not use this in new code. +template < + typename Dest, typename Source, + typename std::enable_if< + !internal_casts::is_bitcastable::value, int>::type = 0> +ABSL_DEPRECATED( + "absl::bit_cast type requirements were violated. Update the types being " + "used such that they are the same size and are both TriviallyCopyable.") +inline Dest bit_cast(const Source& source) { + static_assert(sizeof(Dest) == sizeof(Source), + "Source and destination types should have equal sizes."); + + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_CASTS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/config.h b/libs/or-tools-src-ubuntu/include/absl/base/config.h new file mode 100644 index 0000000000000000000000000000000000000000..ee99f94629a2f8388d8b6e6e665a31dd021a2837 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/config.h @@ -0,0 +1,671 @@ +// +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: config.h +// ----------------------------------------------------------------------------- +// +// This header file defines a set of macros for checking the presence of +// important compiler and platform features. Such macros can be used to +// produce portable code by parameterizing compilation based on the presence or +// lack of a given feature. +// +// We define a "feature" as some interface we wish to program to: for example, +// a library function or system call. A value of `1` indicates support for +// that feature; any other value indicates the feature support is undefined. +// +// Example: +// +// Suppose a programmer wants to write a program that uses the 'mmap()' system +// call. The Abseil macro for that feature (`ABSL_HAVE_MMAP`) allows you to +// selectively include the `mmap.h` header and bracket code using that feature +// in the macro: +// +// #include "absl/base/config.h" +// +// #ifdef ABSL_HAVE_MMAP +// #include "sys/mman.h" +// #endif //ABSL_HAVE_MMAP +// +// ... +// #ifdef ABSL_HAVE_MMAP +// void *ptr = mmap(...); +// ... +// #endif // ABSL_HAVE_MMAP + +#ifndef ABSL_BASE_CONFIG_H_ +#define ABSL_BASE_CONFIG_H_ + +// Included for the __GLIBC__ macro (or similar macros on other systems). +#include + +#ifdef __cplusplus +// Included for __GLIBCXX__, _LIBCPP_VERSION +#include +#endif // __cplusplus + +#if defined(__APPLE__) +// Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED, +// __IPHONE_8_0. +#include +#include +#endif + +#include "absl/base/options.h" +#include "absl/base/policy_checks.h" + +// Helper macro to convert a CPP variable to a string literal. +#define ABSL_INTERNAL_DO_TOKEN_STR(x) #x +#define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x) + +// ----------------------------------------------------------------------------- +// Abseil namespace annotations +// ----------------------------------------------------------------------------- + +// ABSL_NAMESPACE_BEGIN/ABSL_NAMESPACE_END +// +// An annotation placed at the beginning/end of each `namespace absl` scope. +// This is used to inject an inline namespace. +// +// The proper way to write Abseil code in the `absl` namespace is: +// +// namespace absl { +// ABSL_NAMESPACE_BEGIN +// +// void Foo(); // absl::Foo(). +// +// ABSL_NAMESPACE_END +// } // namespace absl +// +// Users of Abseil should not use these macros, because users of Abseil should +// not write `namespace absl {` in their own code for any reason. (Abseil does +// not support forward declarations of its own types, nor does it support +// user-provided specialization of Abseil templates. Code that violates these +// rules may be broken without warning.) +#if !defined(ABSL_OPTION_USE_INLINE_NAMESPACE) || \ + !defined(ABSL_OPTION_INLINE_NAMESPACE_NAME) +#error options.h is misconfigured. +#endif + +// Check that ABSL_OPTION_INLINE_NAMESPACE_NAME is neither "head" nor "" +#if defined(__cplusplus) && ABSL_OPTION_USE_INLINE_NAMESPACE == 1 + +#define ABSL_INTERNAL_INLINE_NAMESPACE_STR \ + ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) + +static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != '\0', + "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must " + "not be empty."); +static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[1] != 'e' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[2] != 'a' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[3] != 'd' || + ABSL_INTERNAL_INLINE_NAMESPACE_STR[4] != '\0', + "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must " + "be changed to a new, unique identifier name."); + +#endif + +#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 +#define ABSL_NAMESPACE_BEGIN +#define ABSL_NAMESPACE_END +#elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1 +#define ABSL_NAMESPACE_BEGIN \ + inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME { +#define ABSL_NAMESPACE_END } +#else +#error options.h is misconfigured. +#endif + +// ----------------------------------------------------------------------------- +// Compiler Feature Checks +// ----------------------------------------------------------------------------- + +// ABSL_HAVE_BUILTIN() +// +// Checks whether the compiler supports a Clang Feature Checking Macro, and if +// so, checks whether it supports the provided builtin function "x" where x +// is one of the functions noted in +// https://clang.llvm.org/docs/LanguageExtensions.html +// +// Note: Use this macro to avoid an extra level of #ifdef __has_builtin check. +// http://releases.llvm.org/3.3/tools/clang/docs/LanguageExtensions.html +#ifdef __has_builtin +#define ABSL_HAVE_BUILTIN(x) __has_builtin(x) +#else +#define ABSL_HAVE_BUILTIN(x) 0 +#endif + +#if defined(__is_identifier) +#define ABSL_INTERNAL_HAS_KEYWORD(x) !(__is_identifier(x)) +#else +#define ABSL_INTERNAL_HAS_KEYWORD(x) 0 +#endif + +// ABSL_HAVE_TLS is defined to 1 when __thread should be supported. +// We assume __thread is supported on Linux when compiled with Clang or compiled +// against libstdc++ with _GLIBCXX_HAVE_TLS defined. +#ifdef ABSL_HAVE_TLS +#error ABSL_HAVE_TLS cannot be directly set +#elif defined(__linux__) && (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) +#define ABSL_HAVE_TLS 1 +#endif + +// ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE +// +// Checks whether `std::is_trivially_destructible` is supported. +// +// Notes: All supported compilers using libc++ support this feature, as does +// gcc >= 4.8.1 using libstdc++, and Visual Studio. +#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE +#error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set +#elif defined(_LIBCPP_VERSION) || \ + (!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \ + defined(_MSC_VER) +#define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1 +#endif + +// ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE +// +// Checks whether `std::is_trivially_default_constructible` and +// `std::is_trivially_copy_constructible` are supported. + +// ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE +// +// Checks whether `std::is_trivially_copy_assignable` is supported. + +// Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with +// either libc++ or libstdc++, and Visual Studio (but not NVCC). +#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) +#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set +#elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) +#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set +#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ + (!defined(__clang__) && defined(__GNUC__) && \ + (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \ + (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ + (defined(_MSC_VER) && !defined(__NVCC__)) +#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 +#define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 +#endif + +// ABSL_HAVE_SOURCE_LOCATION_CURRENT +// +// Indicates whether `absl::SourceLocation::current()` will return useful +// information in some contexts. +#ifndef ABSL_HAVE_SOURCE_LOCATION_CURRENT +#if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \ + ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE) +#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 +#endif +#endif + +// ABSL_HAVE_THREAD_LOCAL +// +// Checks whether C++11's `thread_local` storage duration specifier is +// supported. +#ifdef ABSL_HAVE_THREAD_LOCAL +#error ABSL_HAVE_THREAD_LOCAL cannot be directly set +#elif defined(__APPLE__) +// Notes: +// * Xcode's clang did not support `thread_local` until version 8, and +// even then not for all iOS < 9.0. +// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator +// targeting iOS 9.x. +// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time +// making __has_feature unreliable there. +// +// Otherwise, `__has_feature` is only supported by Clang so it has be inside +// `defined(__APPLE__)` check. +#if __has_feature(cxx_thread_local) && \ + !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) +#define ABSL_HAVE_THREAD_LOCAL 1 +#endif +#else // !defined(__APPLE__) +#define ABSL_HAVE_THREAD_LOCAL 1 +#endif + +// There are platforms for which TLS should not be used even though the compiler +// makes it seem like it's supported (Android NDK < r12b for example). +// This is primarily because of linker problems and toolchain misconfiguration: +// Abseil does not intend to support this indefinitely. Currently, the newest +// toolchain that we intend to support that requires this behavior is the +// r11 NDK - allowing for a 5 year support window on that means this option +// is likely to be removed around June of 2021. +// TLS isn't supported until NDK r12b per +// https://developer.android.com/ndk/downloads/revision_history.html +// Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in +// . For NDK < r16, users should define these macros, +// e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11. +#if defined(__ANDROID__) && defined(__clang__) +#if __has_include() +#include +#endif // __has_include() +#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \ + defined(__NDK_MINOR__) && \ + ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1))) +#undef ABSL_HAVE_TLS +#undef ABSL_HAVE_THREAD_LOCAL +#endif +#endif // defined(__ANDROID__) && defined(__clang__) + +// Emscripten doesn't yet support `thread_local` or `__thread`. +// https://github.com/emscripten-core/emscripten/issues/3502 +#if defined(__EMSCRIPTEN__) +#undef ABSL_HAVE_TLS +#undef ABSL_HAVE_THREAD_LOCAL +#endif // defined(__EMSCRIPTEN__) + +// ABSL_HAVE_INTRINSIC_INT128 +// +// Checks whether the __int128 compiler extension for a 128-bit integral type is +// supported. +// +// Note: __SIZEOF_INT128__ is defined by Clang and GCC when __int128 is +// supported, but we avoid using it in certain cases: +// * On Clang: +// * Building using Clang for Windows, where the Clang runtime library has +// 128-bit support only on LP64 architectures, but Windows is LLP64. +// * On Nvidia's nvcc: +// * nvcc also defines __GNUC__ and __SIZEOF_INT128__, but not all versions +// actually support __int128. +#ifdef ABSL_HAVE_INTRINSIC_INT128 +#error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set +#elif defined(__SIZEOF_INT128__) +#if (defined(__clang__) && !defined(_WIN32)) || \ + (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \ + (defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__)) +#define ABSL_HAVE_INTRINSIC_INT128 1 +#elif defined(__CUDACC__) +// __CUDACC_VER__ is a full version number before CUDA 9, and is defined to a +// string explaining that it has been removed starting with CUDA 9. We use +// nested #ifs because there is no short-circuiting in the preprocessor. +// NOTE: `__CUDACC__` could be undefined while `__CUDACC_VER__` is defined. +#if __CUDACC_VER__ >= 70000 +#define ABSL_HAVE_INTRINSIC_INT128 1 +#endif // __CUDACC_VER__ >= 70000 +#endif // defined(__CUDACC__) +#endif // ABSL_HAVE_INTRINSIC_INT128 + +// ABSL_HAVE_EXCEPTIONS +// +// Checks whether the compiler both supports and enables exceptions. Many +// compilers support a "no exceptions" mode that disables exceptions. +// +// Generally, when ABSL_HAVE_EXCEPTIONS is not defined: +// +// * Code using `throw` and `try` may not compile. +// * The `noexcept` specifier will still compile and behave as normal. +// * The `noexcept` operator may still return `false`. +// +// For further details, consult the compiler's documentation. +#ifdef ABSL_HAVE_EXCEPTIONS +#error ABSL_HAVE_EXCEPTIONS cannot be directly set. + +#elif defined(__clang__) + +#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) +// Clang >= 3.6 +#if __has_feature(cxx_exceptions) +#define ABSL_HAVE_EXCEPTIONS 1 +#endif // __has_feature(cxx_exceptions) +#else +// Clang < 3.6 +// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro +#if defined(__EXCEPTIONS) && __has_feature(cxx_exceptions) +#define ABSL_HAVE_EXCEPTIONS 1 +#endif // defined(__EXCEPTIONS) && __has_feature(cxx_exceptions) +#endif // __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) + +// Handle remaining special cases and default to exceptions being supported. +#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ + !(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \ + !(defined(_MSC_VER) && !defined(_CPPUNWIND)) +#define ABSL_HAVE_EXCEPTIONS 1 +#endif + +// ----------------------------------------------------------------------------- +// Platform Feature Checks +// ----------------------------------------------------------------------------- + +// Currently supported operating systems and associated preprocessor +// symbols: +// +// Linux and Linux-derived __linux__ +// Android __ANDROID__ (implies __linux__) +// Linux (non-Android) __linux__ && !__ANDROID__ +// Darwin (macOS and iOS) __APPLE__ +// Akaros (http://akaros.org) __ros__ +// Windows _WIN32 +// NaCL __native_client__ +// AsmJS __asmjs__ +// WebAssembly __wasm__ +// Fuchsia __Fuchsia__ +// +// Note that since Android defines both __ANDROID__ and __linux__, one +// may probe for either Linux or Android by simply testing for __linux__. + +// ABSL_HAVE_MMAP +// +// Checks whether the platform has an mmap(2) implementation as defined in +// POSIX.1-2001. +#ifdef ABSL_HAVE_MMAP +#error ABSL_HAVE_MMAP cannot be directly set +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \ + defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \ + defined(__ASYLO__) +#define ABSL_HAVE_MMAP 1 +#endif + +// ABSL_HAVE_PTHREAD_GETSCHEDPARAM +// +// Checks whether the platform implements the pthread_(get|set)schedparam(3) +// functions as defined in POSIX.1-2001. +#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM +#error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__ros__) +#define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 +#endif + +// ABSL_HAVE_SCHED_YIELD +// +// Checks whether the platform implements sched_yield(2) as defined in +// POSIX.1-2001. +#ifdef ABSL_HAVE_SCHED_YIELD +#error ABSL_HAVE_SCHED_YIELD cannot be directly set +#elif defined(__linux__) || defined(__ros__) || defined(__native_client__) +#define ABSL_HAVE_SCHED_YIELD 1 +#endif + +// ABSL_HAVE_SEMAPHORE_H +// +// Checks whether the platform supports the header and sem_init(3) +// family of functions as standardized in POSIX.1-2001. +// +// Note: While Apple provides for both iOS and macOS, it is +// explicitly deprecated and will cause build failures if enabled for those +// platforms. We side-step the issue by not defining it here for Apple +// platforms. +#ifdef ABSL_HAVE_SEMAPHORE_H +#error ABSL_HAVE_SEMAPHORE_H cannot be directly set +#elif defined(__linux__) || defined(__ros__) +#define ABSL_HAVE_SEMAPHORE_H 1 +#endif + +// ABSL_HAVE_ALARM +// +// Checks whether the platform supports the header and alarm(2) +// function as standardized in POSIX.1-2001. +#ifdef ABSL_HAVE_ALARM +#error ABSL_HAVE_ALARM cannot be directly set +#elif defined(__GOOGLE_GRTE_VERSION__) +// feature tests for Google's GRTE +#define ABSL_HAVE_ALARM 1 +#elif defined(__GLIBC__) +// feature test for glibc +#define ABSL_HAVE_ALARM 1 +#elif defined(_MSC_VER) +// feature tests for Microsoft's library +#elif defined(__MINGW32__) +// mingw32 doesn't provide alarm(2): +// https://osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.2-trunk/mingwrt/include/unistd.h +// mingw-w64 provides a no-op implementation: +// https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-crt/misc/alarm.c +#elif defined(__EMSCRIPTEN__) +// emscripten doesn't support signals +#elif defined(__Fuchsia__) +// Signals don't exist on fuchsia. +#elif defined(__native_client__) +#else +// other standard libraries +#define ABSL_HAVE_ALARM 1 +#endif + +// ABSL_IS_LITTLE_ENDIAN +// ABSL_IS_BIG_ENDIAN +// +// Checks the endianness of the platform. +// +// Notes: uses the built in endian macros provided by GCC (since 4.6) and +// Clang (since 3.2); see +// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html. +// Otherwise, if _WIN32, assume little endian. Otherwise, bail with an error. +#if defined(ABSL_IS_BIG_ENDIAN) +#error "ABSL_IS_BIG_ENDIAN cannot be directly set." +#endif +#if defined(ABSL_IS_LITTLE_ENDIAN) +#error "ABSL_IS_LITTLE_ENDIAN cannot be directly set." +#endif + +#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define ABSL_IS_LITTLE_ENDIAN 1 +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define ABSL_IS_BIG_ENDIAN 1 +#elif defined(_WIN32) +#define ABSL_IS_LITTLE_ENDIAN 1 +#else +#error "absl endian detection needs to be set up for your compiler" +#endif + +// macOS 10.13 and iOS 10.11 don't let you use , , or +// even though the headers exist and are publicly noted to work. See +// https://github.com/abseil/abseil-cpp/issues/207 and +// https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes +// libc++ spells out the availability requirements in the file +// llvm-project/libcxx/include/__config via the #define +// _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS. +#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ + ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \ + (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 50000)) +#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1 +#else +#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0 +#endif + +// ABSL_HAVE_STD_ANY +// +// Checks whether C++17 std::any is available by checking whether exists. +#ifdef ABSL_HAVE_STD_ANY +#error "ABSL_HAVE_STD_ANY cannot be directly set." +#endif + +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L && \ + !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE +#define ABSL_HAVE_STD_ANY 1 +#endif +#endif + +// ABSL_HAVE_STD_OPTIONAL +// +// Checks whether C++17 std::optional is available. +#ifdef ABSL_HAVE_STD_OPTIONAL +#error "ABSL_HAVE_STD_OPTIONAL cannot be directly set." +#endif + +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L && \ + !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE +#define ABSL_HAVE_STD_OPTIONAL 1 +#endif +#endif + +// ABSL_HAVE_STD_VARIANT +// +// Checks whether C++17 std::variant is available. +#ifdef ABSL_HAVE_STD_VARIANT +#error "ABSL_HAVE_STD_VARIANT cannot be directly set." +#endif + +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L && \ + !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE +#define ABSL_HAVE_STD_VARIANT 1 +#endif +#endif + +// ABSL_HAVE_STD_STRING_VIEW +// +// Checks whether C++17 std::string_view is available. +#ifdef ABSL_HAVE_STD_STRING_VIEW +#error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set." +#endif + +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#define ABSL_HAVE_STD_STRING_VIEW 1 +#endif +#endif + +// For MSVC, `__has_include` is supported in VS 2017 15.3, which is later than +// the support for , , , . So we use +// _MSC_VER to check whether we have VS 2017 RTM (when , , +// , is implemented) or higher. Also, `__cplusplus` is +// not correctly set by MSVC, so we use `_MSVC_LANG` to check the language +// version. +// TODO(zhangxy): fix tests before enabling aliasing for `std::any`. +#if defined(_MSC_VER) && _MSC_VER >= 1910 && \ + ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402) +// #define ABSL_HAVE_STD_ANY 1 +#define ABSL_HAVE_STD_OPTIONAL 1 +#define ABSL_HAVE_STD_VARIANT 1 +#define ABSL_HAVE_STD_STRING_VIEW 1 +#endif + +// ABSL_USES_STD_ANY +// +// Indicates whether absl::any is an alias for std::any. +#if !defined(ABSL_OPTION_USE_STD_ANY) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_ANY == 0 || \ + (ABSL_OPTION_USE_STD_ANY == 2 && !defined(ABSL_HAVE_STD_ANY)) +#undef ABSL_USES_STD_ANY +#elif ABSL_OPTION_USE_STD_ANY == 1 || \ + (ABSL_OPTION_USE_STD_ANY == 2 && defined(ABSL_HAVE_STD_ANY)) +#define ABSL_USES_STD_ANY 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_OPTIONAL +// +// Indicates whether absl::optional is an alias for std::optional. +#if !defined(ABSL_OPTION_USE_STD_OPTIONAL) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_OPTIONAL == 0 || \ + (ABSL_OPTION_USE_STD_OPTIONAL == 2 && !defined(ABSL_HAVE_STD_OPTIONAL)) +#undef ABSL_USES_STD_OPTIONAL +#elif ABSL_OPTION_USE_STD_OPTIONAL == 1 || \ + (ABSL_OPTION_USE_STD_OPTIONAL == 2 && defined(ABSL_HAVE_STD_OPTIONAL)) +#define ABSL_USES_STD_OPTIONAL 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_VARIANT +// +// Indicates whether absl::variant is an alias for std::variant. +#if !defined(ABSL_OPTION_USE_STD_VARIANT) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_VARIANT == 0 || \ + (ABSL_OPTION_USE_STD_VARIANT == 2 && !defined(ABSL_HAVE_STD_VARIANT)) +#undef ABSL_USES_STD_VARIANT +#elif ABSL_OPTION_USE_STD_VARIANT == 1 || \ + (ABSL_OPTION_USE_STD_VARIANT == 2 && defined(ABSL_HAVE_STD_VARIANT)) +#define ABSL_USES_STD_VARIANT 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_STRING_VIEW +// +// Indicates whether absl::string_view is an alias for std::string_view. +#if !defined(ABSL_OPTION_USE_STD_STRING_VIEW) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 || \ + (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ + !defined(ABSL_HAVE_STD_STRING_VIEW)) +#undef ABSL_USES_STD_STRING_VIEW +#elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \ + (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ + defined(ABSL_HAVE_STD_STRING_VIEW)) +#define ABSL_USES_STD_STRING_VIEW 1 +#else +#error options.h is misconfigured. +#endif + +// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION +// SEH exception from emplace for variant when constructing the +// struct can throw. This defeats some of variant_test and +// variant_exception_safety_test. +#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG) +#define ABSL_INTERNAL_MSVC_2017_DBG_MODE +#endif + +// ABSL_INTERNAL_MANGLED_NS +// ABSL_INTERNAL_MANGLED_BACKREFERENCE +// +// Internal macros for building up mangled names in our internal fork of CCTZ. +// This implementation detail is only needed and provided for the MSVC build. +// +// These macros both expand to string literals. ABSL_INTERNAL_MANGLED_NS is +// the mangled spelling of the `absl` namespace, and +// ABSL_INTERNAL_MANGLED_BACKREFERENCE is a back-reference integer representing +// the proper count to skip past the CCTZ fork namespace names. (This number +// is one larger when there is an inline namespace name to skip.) +#if defined(_MSC_VER) +#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 +#define ABSL_INTERNAL_MANGLED_NS "absl" +#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "5" +#else +#define ABSL_INTERNAL_MANGLED_NS \ + ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) "@absl" +#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "6" +#endif +#endif + +#undef ABSL_INTERNAL_HAS_KEYWORD + +// ABSL_DLL +// +// When building Abseil as a DLL, this macro expands to `__declspec(dllexport)` +// so we can annotate symbols appropriately as being exported. When used in +// headers consuming a DLL, this macro expands to `__declspec(dllimport)` so +// that consumers know the symbol is defined inside the DLL. In all other cases, +// the macro expands to nothing. +#if defined(_MSC_VER) +#if defined(ABSL_BUILD_DLL) +#define ABSL_DLL __declspec(dllexport) +#elif defined(ABSL_CONSUME_DLL) +#define ABSL_DLL __declspec(dllimport) +#else +#define ABSL_DLL +#endif +#else +#define ABSL_DLL +#endif // defined(_MSC_VER) + +#endif // ABSL_BASE_CONFIG_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/const_init.h b/libs/or-tools-src-ubuntu/include/absl/base/const_init.h new file mode 100644 index 0000000000000000000000000000000000000000..16520b61d95b6ee0cb60e66ae7c7be4aeb40aa3d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/const_init.h @@ -0,0 +1,76 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// kConstInit +// ----------------------------------------------------------------------------- +// +// A constructor tag used to mark an object as safe for use as a global +// variable, avoiding the usual lifetime issues that can affect globals. + +#ifndef ABSL_BASE_CONST_INIT_H_ +#define ABSL_BASE_CONST_INIT_H_ + +#include "absl/base/config.h" + +// In general, objects with static storage duration (such as global variables) +// can trigger tricky object lifetime situations. Attempting to access them +// from the constructors or destructors of other global objects can result in +// undefined behavior, unless their constructors and destructors are designed +// with this issue in mind. +// +// The normal way to deal with this issue in C++11 is to use constant +// initialization and trivial destructors. +// +// Constant initialization is guaranteed to occur before any other code +// executes. Constructors that are declared 'constexpr' are eligible for +// constant initialization. You can annotate a variable declaration with the +// ABSL_CONST_INIT macro to express this intent. For compilers that support +// it, this annotation will cause a compilation error for declarations that +// aren't subject to constant initialization (perhaps because a runtime value +// was passed as a constructor argument). +// +// On program shutdown, lifetime issues can be avoided on global objects by +// ensuring that they contain trivial destructors. A class has a trivial +// destructor unless it has a user-defined destructor, a virtual method or base +// class, or a data member or base class with a non-trivial destructor of its +// own. Objects with static storage duration and a trivial destructor are not +// cleaned up on program shutdown, and are thus safe to access from other code +// running during shutdown. +// +// For a few core Abseil classes, we make a best effort to allow for safe global +// instances, even though these classes have non-trivial destructors. These +// objects can be created with the absl::kConstInit tag. For example: +// ABSL_CONST_INIT absl::Mutex global_mutex(absl::kConstInit); +// +// The line above declares a global variable of type absl::Mutex which can be +// accessed at any point during startup or shutdown. global_mutex's destructor +// will still run, but will not invalidate the object. Note that C++ specifies +// that accessing an object after its destructor has run results in undefined +// behavior, but this pattern works on the toolchains we support. +// +// The absl::kConstInit tag should only be used to define objects with static +// or thread_local storage duration. + +namespace absl { +ABSL_NAMESPACE_BEGIN + +enum ConstInitType { + kConstInit, +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_CONST_INIT_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/dynamic_annotations.h b/libs/or-tools-src-ubuntu/include/absl/base/dynamic_annotations.h new file mode 100644 index 0000000000000000000000000000000000000000..65a54b447f8061a247e99e9a8b9fb94d26b9faa0 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/dynamic_annotations.h @@ -0,0 +1,389 @@ +/* + * Copyright 2017 The Abseil Authors. + * + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* This file defines dynamic annotations for use with dynamic analysis + tool such as valgrind, PIN, etc. + + Dynamic annotation is a source code annotation that affects + the generated code (that is, the annotation is not a comment). + Each such annotation is attached to a particular + instruction and/or to a particular object (address) in the program. + + The annotations that should be used by users are macros in all upper-case + (e.g., ANNOTATE_THREAD_NAME). + + Actual implementation of these macros may differ depending on the + dynamic analysis tool being used. + + This file supports the following configurations: + - Dynamic Annotations enabled (with static thread-safety warnings disabled). + In this case, macros expand to functions implemented by Thread Sanitizer, + when building with TSan. When not provided an external implementation, + dynamic_annotations.cc provides no-op implementations. + + - Static Clang thread-safety warnings enabled. + When building with a Clang compiler that supports thread-safety warnings, + a subset of annotations can be statically-checked at compile-time. We + expand these macros to static-inline functions that can be analyzed for + thread-safety, but afterwards elided when building the final binary. + + - All annotations are disabled. + If neither Dynamic Annotations nor Clang thread-safety warnings are + enabled, then all annotation-macros expand to empty. */ + +#ifndef ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ +#define ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ + +#ifndef DYNAMIC_ANNOTATIONS_ENABLED +# define DYNAMIC_ANNOTATIONS_ENABLED 0 +#endif + +#if DYNAMIC_ANNOTATIONS_ENABLED != 0 + + /* ------------------------------------------------------------- + Annotations that suppress errors. It is usually better to express the + program's synchronization using the other annotations, but these can + be used when all else fails. */ + + /* Report that we may have a benign race at "pointer", with size + "sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the + point where "pointer" has been allocated, preferably close to the point + where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. */ + #define ANNOTATE_BENIGN_RACE(pointer, description) \ + AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, \ + sizeof(*(pointer)), description) + + /* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to + the memory range [address, address+size). */ + #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ + AnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) + + /* Enable (enable!=0) or disable (enable==0) race detection for all threads. + This annotation could be useful if you want to skip expensive race analysis + during some period of program execution, e.g. during initialization. */ + #define ANNOTATE_ENABLE_RACE_DETECTION(enable) \ + AnnotateEnableRaceDetection(__FILE__, __LINE__, enable) + + /* ------------------------------------------------------------- + Annotations useful for debugging. */ + + /* Report the current thread name to a race detector. */ + #define ANNOTATE_THREAD_NAME(name) \ + AnnotateThreadName(__FILE__, __LINE__, name) + + /* ------------------------------------------------------------- + Annotations useful when implementing locks. They are not + normally needed by modules that merely use locks. + The "lock" argument is a pointer to the lock object. */ + + /* Report that a lock has been created at address "lock". */ + #define ANNOTATE_RWLOCK_CREATE(lock) \ + AnnotateRWLockCreate(__FILE__, __LINE__, lock) + + /* Report that a linker initialized lock has been created at address "lock". + */ +#ifdef THREAD_SANITIZER + #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ + AnnotateRWLockCreateStatic(__FILE__, __LINE__, lock) +#else + #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) ANNOTATE_RWLOCK_CREATE(lock) +#endif + + /* Report that the lock at address "lock" is about to be destroyed. */ + #define ANNOTATE_RWLOCK_DESTROY(lock) \ + AnnotateRWLockDestroy(__FILE__, __LINE__, lock) + + /* Report that the lock at address "lock" has been acquired. + is_w=1 for writer lock, is_w=0 for reader lock. */ + #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ + AnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w) + + /* Report that the lock at address "lock" is about to be released. */ + #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ + AnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w) + +#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */ + + #define ANNOTATE_RWLOCK_CREATE(lock) /* empty */ + #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) /* empty */ + #define ANNOTATE_RWLOCK_DESTROY(lock) /* empty */ + #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) /* empty */ + #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) /* empty */ + #define ANNOTATE_BENIGN_RACE(address, description) /* empty */ + #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) /* empty */ + #define ANNOTATE_THREAD_NAME(name) /* empty */ + #define ANNOTATE_ENABLE_RACE_DETECTION(enable) /* empty */ + +#endif /* DYNAMIC_ANNOTATIONS_ENABLED */ + +/* These annotations are also made available to LLVM's Memory Sanitizer */ +#if DYNAMIC_ANNOTATIONS_ENABLED == 1 || defined(MEMORY_SANITIZER) + #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ + AnnotateMemoryIsInitialized(__FILE__, __LINE__, address, size) + + #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ + AnnotateMemoryIsUninitialized(__FILE__, __LINE__, address, size) +#else + #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */ + #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) /* empty */ +#endif /* DYNAMIC_ANNOTATIONS_ENABLED || MEMORY_SANITIZER */ + +/* TODO(delesley) -- Replace __CLANG_SUPPORT_DYN_ANNOTATION__ with the + appropriate feature ID. */ +#if defined(__clang__) && (!defined(SWIG)) \ + && defined(__CLANG_SUPPORT_DYN_ANNOTATION__) + + #if DYNAMIC_ANNOTATIONS_ENABLED == 0 + #define ANNOTALYSIS_ENABLED + #endif + + /* When running in opt-mode, GCC will issue a warning, if these attributes are + compiled. Only include them when compiling using Clang. */ + #define ATTRIBUTE_IGNORE_READS_BEGIN \ + __attribute((exclusive_lock_function("*"))) + #define ATTRIBUTE_IGNORE_READS_END \ + __attribute((unlock_function("*"))) +#else + #define ATTRIBUTE_IGNORE_READS_BEGIN /* empty */ + #define ATTRIBUTE_IGNORE_READS_END /* empty */ +#endif /* defined(__clang__) && ... */ + +#if (DYNAMIC_ANNOTATIONS_ENABLED != 0) || defined(ANNOTALYSIS_ENABLED) + #define ANNOTATIONS_ENABLED +#endif + +#if (DYNAMIC_ANNOTATIONS_ENABLED != 0) + + /* Request the analysis tool to ignore all reads in the current thread + until ANNOTATE_IGNORE_READS_END is called. + Useful to ignore intentional racey reads, while still checking + other reads and all writes. + See also ANNOTATE_UNPROTECTED_READ. */ + #define ANNOTATE_IGNORE_READS_BEGIN() \ + AnnotateIgnoreReadsBegin(__FILE__, __LINE__) + + /* Stop ignoring reads. */ + #define ANNOTATE_IGNORE_READS_END() \ + AnnotateIgnoreReadsEnd(__FILE__, __LINE__) + + /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. */ + #define ANNOTATE_IGNORE_WRITES_BEGIN() \ + AnnotateIgnoreWritesBegin(__FILE__, __LINE__) + + /* Stop ignoring writes. */ + #define ANNOTATE_IGNORE_WRITES_END() \ + AnnotateIgnoreWritesEnd(__FILE__, __LINE__) + +/* Clang provides limited support for static thread-safety analysis + through a feature called Annotalysis. We configure macro-definitions + according to whether Annotalysis support is available. */ +#elif defined(ANNOTALYSIS_ENABLED) + + #define ANNOTATE_IGNORE_READS_BEGIN() \ + StaticAnnotateIgnoreReadsBegin(__FILE__, __LINE__) + + #define ANNOTATE_IGNORE_READS_END() \ + StaticAnnotateIgnoreReadsEnd(__FILE__, __LINE__) + + #define ANNOTATE_IGNORE_WRITES_BEGIN() \ + StaticAnnotateIgnoreWritesBegin(__FILE__, __LINE__) + + #define ANNOTATE_IGNORE_WRITES_END() \ + StaticAnnotateIgnoreWritesEnd(__FILE__, __LINE__) + +#else + #define ANNOTATE_IGNORE_READS_BEGIN() /* empty */ + #define ANNOTATE_IGNORE_READS_END() /* empty */ + #define ANNOTATE_IGNORE_WRITES_BEGIN() /* empty */ + #define ANNOTATE_IGNORE_WRITES_END() /* empty */ +#endif + +/* Implement the ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more + primitive annotations defined above. */ +#if defined(ANNOTATIONS_ENABLED) + + /* Start ignoring all memory accesses (both reads and writes). */ + #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ + do { \ + ANNOTATE_IGNORE_READS_BEGIN(); \ + ANNOTATE_IGNORE_WRITES_BEGIN(); \ + }while (0) + + /* Stop ignoring both reads and writes. */ + #define ANNOTATE_IGNORE_READS_AND_WRITES_END() \ + do { \ + ANNOTATE_IGNORE_WRITES_END(); \ + ANNOTATE_IGNORE_READS_END(); \ + }while (0) + +#else + #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() /* empty */ + #define ANNOTATE_IGNORE_READS_AND_WRITES_END() /* empty */ +#endif + +/* Use the macros above rather than using these functions directly. */ +#include +#ifdef __cplusplus +extern "C" { +#endif +void AnnotateRWLockCreate(const char *file, int line, + const volatile void *lock); +void AnnotateRWLockCreateStatic(const char *file, int line, + const volatile void *lock); +void AnnotateRWLockDestroy(const char *file, int line, + const volatile void *lock); +void AnnotateRWLockAcquired(const char *file, int line, + const volatile void *lock, long is_w); /* NOLINT */ +void AnnotateRWLockReleased(const char *file, int line, + const volatile void *lock, long is_w); /* NOLINT */ +void AnnotateBenignRace(const char *file, int line, + const volatile void *address, + const char *description); +void AnnotateBenignRaceSized(const char *file, int line, + const volatile void *address, + size_t size, + const char *description); +void AnnotateThreadName(const char *file, int line, + const char *name); +void AnnotateEnableRaceDetection(const char *file, int line, int enable); +void AnnotateMemoryIsInitialized(const char *file, int line, + const volatile void *mem, size_t size); +void AnnotateMemoryIsUninitialized(const char *file, int line, + const volatile void *mem, size_t size); + +/* Annotations expand to these functions, when Dynamic Annotations are enabled. + These functions are either implemented as no-op calls, if no Sanitizer is + attached, or provided with externally-linked implementations by a library + like ThreadSanitizer. */ +void AnnotateIgnoreReadsBegin(const char *file, int line) + ATTRIBUTE_IGNORE_READS_BEGIN; +void AnnotateIgnoreReadsEnd(const char *file, int line) + ATTRIBUTE_IGNORE_READS_END; +void AnnotateIgnoreWritesBegin(const char *file, int line); +void AnnotateIgnoreWritesEnd(const char *file, int line); + +#if defined(ANNOTALYSIS_ENABLED) +/* When Annotalysis is enabled without Dynamic Annotations, the use of + static-inline functions allows the annotations to be read at compile-time, + while still letting the compiler elide the functions from the final build. + + TODO(delesley) -- The exclusive lock here ignores writes as well, but + allows IGNORE_READS_AND_WRITES to work properly. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +static inline void StaticAnnotateIgnoreReadsBegin(const char *file, int line) + ATTRIBUTE_IGNORE_READS_BEGIN { (void)file; (void)line; } +static inline void StaticAnnotateIgnoreReadsEnd(const char *file, int line) + ATTRIBUTE_IGNORE_READS_END { (void)file; (void)line; } +static inline void StaticAnnotateIgnoreWritesBegin( + const char *file, int line) { (void)file; (void)line; } +static inline void StaticAnnotateIgnoreWritesEnd( + const char *file, int line) { (void)file; (void)line; } +#pragma GCC diagnostic pop +#endif + +/* Return non-zero value if running under valgrind. + + If "valgrind.h" is included into dynamic_annotations.cc, + the regular valgrind mechanism will be used. + See http://valgrind.org/docs/manual/manual-core-adv.html about + RUNNING_ON_VALGRIND and other valgrind "client requests". + The file "valgrind.h" may be obtained by doing + svn co svn://svn.valgrind.org/valgrind/trunk/include + + If for some reason you can't use "valgrind.h" or want to fake valgrind, + there are two ways to make this function return non-zero: + - Use environment variable: export RUNNING_ON_VALGRIND=1 + - Make your tool intercept the function RunningOnValgrind() and + change its return value. + */ +int RunningOnValgrind(void); + +/* ValgrindSlowdown returns: + * 1.0, if (RunningOnValgrind() == 0) + * 50.0, if (RunningOnValgrind() != 0 && getenv("VALGRIND_SLOWDOWN") == NULL) + * atof(getenv("VALGRIND_SLOWDOWN")) otherwise + This function can be used to scale timeout values: + EXAMPLE: + for (;;) { + DoExpensiveBackgroundTask(); + SleepForSeconds(5 * ValgrindSlowdown()); + } + */ +double ValgrindSlowdown(void); + +#ifdef __cplusplus +} +#endif + +/* ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. + + Instead of doing + ANNOTATE_IGNORE_READS_BEGIN(); + ... = x; + ANNOTATE_IGNORE_READS_END(); + one can use + ... = ANNOTATE_UNPROTECTED_READ(x); */ +#if defined(__cplusplus) && defined(ANNOTATIONS_ENABLED) +template +inline T ANNOTATE_UNPROTECTED_READ(const volatile T &x) { /* NOLINT */ + ANNOTATE_IGNORE_READS_BEGIN(); + T res = x; + ANNOTATE_IGNORE_READS_END(); + return res; + } +#else + #define ANNOTATE_UNPROTECTED_READ(x) (x) +#endif + +#if DYNAMIC_ANNOTATIONS_ENABLED != 0 && defined(__cplusplus) + /* Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable. */ + #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ + namespace { \ + class static_var ## _annotator { \ + public: \ + static_var ## _annotator() { \ + ANNOTATE_BENIGN_RACE_SIZED(&static_var, \ + sizeof(static_var), \ + # static_var ": " description); \ + } \ + }; \ + static static_var ## _annotator the ## static_var ## _annotator;\ + } // namespace +#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */ + #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) /* empty */ +#endif /* DYNAMIC_ANNOTATIONS_ENABLED */ + +#ifdef ADDRESS_SANITIZER +/* Describe the current state of a contiguous container such as e.g. + * std::vector or std::string. For more details see + * sanitizer/common_interface_defs.h, which is provided by the compiler. */ +#include +#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \ + __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid) +#define ADDRESS_SANITIZER_REDZONE(name) \ + struct { char x[8] __attribute__ ((aligned (8))); } name +#else +#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) +#define ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") +#endif // ADDRESS_SANITIZER + +/* Undefine the macros intended only in this file. */ +#undef ANNOTALYSIS_ENABLED +#undef ANNOTATIONS_ENABLED +#undef ATTRIBUTE_IGNORE_READS_BEGIN +#undef ATTRIBUTE_IGNORE_READS_END + +#endif /* ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ */ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/atomic_hook.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/atomic_hook.h new file mode 100644 index 0000000000000000000000000000000000000000..ae21cd7fe50af011c94d73a0e848badd77d43ed5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/atomic_hook.h @@ -0,0 +1,200 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ +#define ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +#if defined(_MSC_VER) && !defined(__clang__) +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 0 +#else +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 1 +#endif + +#if defined(_MSC_VER) +#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0 +#else +#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1 +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +template +class AtomicHook; + +// To workaround AtomicHook not being constant-initializable on some platforms, +// prefer to annotate instances with `ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES` +// instead of `ABSL_CONST_INIT`. +#if ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT +#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_CONST_INIT +#else +#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +#endif + +// `AtomicHook` is a helper class, templatized on a raw function pointer type, +// for implementing Abseil customization hooks. It is a callable object that +// dispatches to the registered hook. Objects of type `AtomicHook` must have +// static or thread storage duration. +// +// A default constructed object performs a no-op (and returns a default +// constructed object) if no hook has been registered. +// +// Hooks can be pre-registered via constant initialization, for example: +// +// ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static AtomicHook +// my_hook(DefaultAction); +// +// and then changed at runtime via a call to `Store()`. +// +// Reads and writes guarantee memory_order_acquire/memory_order_release +// semantics. +template +class AtomicHook { + public: + using FnPtr = ReturnType (*)(Args...); + + // Constructs an object that by default performs a no-op (and + // returns a default constructed object) when no hook as been registered. + constexpr AtomicHook() : AtomicHook(DummyFunction) {} + + // Constructs an object that by default dispatches to/returns the + // pre-registered default_fn when no hook has been registered at runtime. +#if ABSL_HAVE_WORKING_ATOMIC_POINTER && ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT + explicit constexpr AtomicHook(FnPtr default_fn) + : hook_(default_fn), default_fn_(default_fn) {} +#elif ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT + explicit constexpr AtomicHook(FnPtr default_fn) + : hook_(kUninitialized), default_fn_(default_fn) {} +#else + // As of January 2020, on all known versions of MSVC this constructor runs in + // the global constructor sequence. If `Store()` is called by a dynamic + // initializer, we want to preserve the value, even if this constructor runs + // after the call to `Store()`. If not, `hook_` will be + // zero-initialized by the linker and we have no need to set it. + // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html + explicit constexpr AtomicHook(FnPtr default_fn) + : /* hook_(deliberately omitted), */ default_fn_(default_fn) { + static_assert(kUninitialized == 0, "here we rely on zero-initialization"); + } +#endif + + // Stores the provided function pointer as the value for this hook. + // + // This is intended to be called once. Multiple calls are legal only if the + // same function pointer is provided for each call. The store is implemented + // as a memory_order_release operation, and read accesses are implemented as + // memory_order_acquire. + void Store(FnPtr fn) { + bool success = DoStore(fn); + static_cast(success); + assert(success); + } + + // Invokes the registered callback. If no callback has yet been registered, a + // default-constructed object of the appropriate type is returned instead. + template + ReturnType operator()(CallArgs&&... args) const { + return DoLoad()(std::forward(args)...); + } + + // Returns the registered callback, or nullptr if none has been registered. + // Useful if client code needs to conditionalize behavior based on whether a + // callback was registered. + // + // Note that atomic_hook.Load()() and atomic_hook() have different semantics: + // operator()() will perform a no-op if no callback was registered, while + // Load()() will dereference a null function pointer. Prefer operator()() to + // Load()() unless you must conditionalize behavior on whether a hook was + // registered. + FnPtr Load() const { + FnPtr ptr = DoLoad(); + return (ptr == DummyFunction) ? nullptr : ptr; + } + + private: + static ReturnType DummyFunction(Args...) { + return ReturnType(); + } + + // Current versions of MSVC (as of September 2017) have a broken + // implementation of std::atomic: Its constructor attempts to do the + // equivalent of a reinterpret_cast in a constexpr context, which is not + // allowed. + // + // This causes an issue when building with LLVM under Windows. To avoid this, + // we use a less-efficient, intptr_t-based implementation on Windows. +#if ABSL_HAVE_WORKING_ATOMIC_POINTER + // Return the stored value, or DummyFunction if no value has been stored. + FnPtr DoLoad() const { return hook_.load(std::memory_order_acquire); } + + // Store the given value. Returns false if a different value was already + // stored to this object. + bool DoStore(FnPtr fn) { + assert(fn); + FnPtr expected = default_fn_; + const bool store_succeeded = hook_.compare_exchange_strong( + expected, fn, std::memory_order_acq_rel, std::memory_order_acquire); + const bool same_value_already_stored = (expected == fn); + return store_succeeded || same_value_already_stored; + } + + std::atomic hook_; +#else // !ABSL_HAVE_WORKING_ATOMIC_POINTER + // Use a sentinel value unlikely to be the address of an actual function. + static constexpr intptr_t kUninitialized = 0; + + static_assert(sizeof(intptr_t) >= sizeof(FnPtr), + "intptr_t can't contain a function pointer"); + + FnPtr DoLoad() const { + const intptr_t value = hook_.load(std::memory_order_acquire); + if (value == kUninitialized) { + return default_fn_; + } + return reinterpret_cast(value); + } + + bool DoStore(FnPtr fn) { + assert(fn); + const auto value = reinterpret_cast(fn); + intptr_t expected = kUninitialized; + const bool store_succeeded = hook_.compare_exchange_strong( + expected, value, std::memory_order_acq_rel, std::memory_order_acquire); + const bool same_value_already_stored = (expected == value); + return store_succeeded || same_value_already_stored; + } + + std::atomic hook_; +#endif + + const FnPtr default_fn_; +}; + +#undef ABSL_HAVE_WORKING_ATOMIC_POINTER +#undef ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/atomic_hook_test_helper.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/atomic_hook_test_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..3e72b4977d2f406f4ce0b38325f4763d426b8c8f --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/atomic_hook_test_helper.h @@ -0,0 +1,34 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ +#define ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ + +#include "absl/base/internal/atomic_hook.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace atomic_hook_internal { + +using VoidF = void (*)(); +extern absl::base_internal::AtomicHook func; +extern int default_func_calls; +void DefaultFunc(); +void RegisterFunc(VoidF func); + +} // namespace atomic_hook_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/bits.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/bits.h new file mode 100644 index 0000000000000000000000000000000000000000..8b03453c184f592beea9d9d1d49cac086521aa8b --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/bits.h @@ -0,0 +1,218 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_BITS_H_ +#define ABSL_BASE_INTERNAL_BITS_H_ + +// This file contains bitwise ops which are implementation details of various +// absl libraries. + +#include + +#include "absl/base/config.h" + +// Clang on Windows has __builtin_clzll; otherwise we need to use the +// windows intrinsic functions. +#if defined(_MSC_VER) +#include +#if defined(_M_X64) +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_BitScanForward64) +#endif +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) +#endif + +#include "absl/base/attributes.h" + +#if defined(_MSC_VER) +// We can achieve something similar to attribute((always_inline)) with MSVC by +// using the __forceinline keyword, however this is not perfect. MSVC is +// much less aggressive about inlining, and even with the __forceinline keyword. +#define ABSL_BASE_INTERNAL_FORCEINLINE __forceinline +#else +// Use default attribute inline. +#define ABSL_BASE_INTERNAL_FORCEINLINE inline ABSL_ATTRIBUTE_ALWAYS_INLINE +#endif + + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { + int zeroes = 60; + if (n >> 32) { + zeroes -= 32; + n >>= 32; + } + if (n >> 16) { + zeroes -= 16; + n >>= 16; + } + if (n >> 8) { + zeroes -= 8; + n >>= 8; + } + if (n >> 4) { + zeroes -= 4; + n >>= 4; + } + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) { +#if defined(_MSC_VER) && defined(_M_X64) + // MSVC does not have __buitin_clzll. Use _BitScanReverse64. + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse64(&result, n)) { + return 63 - result; + } + return 64; +#elif defined(_MSC_VER) + // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse + unsigned long result = 0; // NOLINT(runtime/int) + if ((n >> 32) && _BitScanReverse(&result, n >> 32)) { + return 31 - result; + } + if (_BitScanReverse(&result, n)) { + return 63 - result; + } + return 64; +#elif defined(__GNUC__) + // Use __builtin_clzll, which uses the following instructions: + // x86: bsr + // ARM64: clz + // PPC: cntlzd + static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) + "__builtin_clzll does not take 64-bit arg"); + + // Handle 0 as a special case because __builtin_clzll(0) is undefined. + if (n == 0) { + return 64; + } + return __builtin_clzll(n); +#else + return CountLeadingZeros64Slow(n); +#endif +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) { + int zeroes = 28; + if (n >> 16) { + zeroes -= 16; + n >>= 16; + } + if (n >> 8) { + zeroes -= 8; + n >>= 8; + } + if (n >> 4) { + zeroes -= 4; + n >>= 4; + } + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) { +#if defined(_MSC_VER) + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse(&result, n)) { + return 31 - result; + } + return 32; +#elif defined(__GNUC__) + // Use __builtin_clz, which uses the following instructions: + // x86: bsr + // ARM64: clz + // PPC: cntlzd + static_assert(sizeof(int) == sizeof(n), + "__builtin_clz does not take 32-bit arg"); + + // Handle 0 as a special case because __builtin_clz(0) is undefined. + if (n == 0) { + return 32; + } + return __builtin_clz(n); +#else + return CountLeadingZeros32Slow(n); +#endif +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) { + int c = 63; + n &= ~n + 1; + if (n & 0x00000000FFFFFFFF) c -= 32; + if (n & 0x0000FFFF0000FFFF) c -= 16; + if (n & 0x00FF00FF00FF00FF) c -= 8; + if (n & 0x0F0F0F0F0F0F0F0F) c -= 4; + if (n & 0x3333333333333333) c -= 2; + if (n & 0x5555555555555555) c -= 1; + return c; +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) { +#if defined(_MSC_VER) && defined(_M_X64) + unsigned long result = 0; // NOLINT(runtime/int) + _BitScanForward64(&result, n); + return result; +#elif defined(_MSC_VER) + unsigned long result = 0; // NOLINT(runtime/int) + if (static_cast(n) == 0) { + _BitScanForward(&result, n >> 32); + return result + 32; + } + _BitScanForward(&result, n); + return result; +#elif defined(__GNUC__) + static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) + "__builtin_ctzll does not take 64-bit arg"); + return __builtin_ctzll(n); +#else + return CountTrailingZerosNonZero64Slow(n); +#endif +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) { + int c = 31; + n &= ~n + 1; + if (n & 0x0000FFFF) c -= 16; + if (n & 0x00FF00FF) c -= 8; + if (n & 0x0F0F0F0F) c -= 4; + if (n & 0x33333333) c -= 2; + if (n & 0x55555555) c -= 1; + return c; +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { +#if defined(_MSC_VER) + unsigned long result = 0; // NOLINT(runtime/int) + _BitScanForward(&result, n); + return result; +#elif defined(__GNUC__) + static_assert(sizeof(int) == sizeof(n), + "__builtin_ctz does not take 32-bit arg"); + return __builtin_ctz(n); +#else + return CountTrailingZerosNonZero32Slow(n); +#endif +} + +#undef ABSL_BASE_INTERNAL_FORCEINLINE + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_BITS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/cycleclock.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/cycleclock.h new file mode 100644 index 0000000000000000000000000000000000000000..a18b58444560f839d7669f6204930371631aa121 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/cycleclock.h @@ -0,0 +1,94 @@ +// +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// + +// ----------------------------------------------------------------------------- +// File: cycleclock.h +// ----------------------------------------------------------------------------- +// +// This header file defines a `CycleClock`, which yields the value and frequency +// of a cycle counter that increments at a rate that is approximately constant. +// +// NOTE: +// +// The cycle counter frequency is not necessarily related to the core clock +// frequency and should not be treated as such. That is, `CycleClock` cycles are +// not necessarily "CPU cycles" and code should not rely on that behavior, even +// if experimentally observed. +// +// An arbitrary offset may have been added to the counter at power on. +// +// On some platforms, the rate and offset of the counter may differ +// slightly when read from different CPUs of a multiprocessor. Usually, +// we try to ensure that the operating system adjusts values periodically +// so that values agree approximately. If you need stronger guarantees, +// consider using alternate interfaces. +// +// The CPU is not required to maintain the ordering of a cycle counter read +// with respect to surrounding instructions. + +#ifndef ABSL_BASE_INTERNAL_CYCLECLOCK_H_ +#define ABSL_BASE_INTERNAL_CYCLECLOCK_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// ----------------------------------------------------------------------------- +// CycleClock +// ----------------------------------------------------------------------------- +class CycleClock { + public: + // CycleClock::Now() + // + // Returns the value of a cycle counter that counts at a rate that is + // approximately constant. + static int64_t Now(); + + // CycleClock::Frequency() + // + // Returns the amount by which `CycleClock::Now()` increases per second. Note + // that this value may not necessarily match the core CPU clock frequency. + static double Frequency(); + + private: + CycleClock() = delete; // no instances + CycleClock(const CycleClock&) = delete; + CycleClock& operator=(const CycleClock&) = delete; +}; + +using CycleClockSourceFunc = int64_t (*)(); + +class CycleClockSource { + private: + // CycleClockSource::Register() + // + // Register a function that provides an alternate source for the unscaled CPU + // cycle count value. The source function must be async signal safe, must not + // call CycleClock::Now(), and must have a frequency that matches that of the + // unscaled clock used by CycleClock. A nullptr value resets CycleClock to use + // the default source. + static void Register(CycleClockSourceFunc source); +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_CYCLECLOCK_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/direct_mmap.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/direct_mmap.h new file mode 100644 index 0000000000000000000000000000000000000000..5618867ba0c604abebb8d5005c9bb08f7e99960f --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/direct_mmap.h @@ -0,0 +1,161 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Functions for directly invoking mmap() via syscall, avoiding the case where +// mmap() has been locally overridden. + +#ifndef ABSL_BASE_INTERNAL_DIRECT_MMAP_H_ +#define ABSL_BASE_INTERNAL_DIRECT_MMAP_H_ + +#include "absl/base/config.h" + +#if ABSL_HAVE_MMAP + +#include + +#ifdef __linux__ + +#include +#ifdef __BIONIC__ +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef __mips__ +// Include definitions of the ABI currently in use. +#ifdef __BIONIC__ +// Android doesn't have sgidefs.h, but does have asm/sgidefs.h, which has the +// definitions we need. +#include +#else +#include +#endif // __BIONIC__ +#endif // __mips__ + +// SYS_mmap and SYS_munmap are not defined in Android. +#ifdef __BIONIC__ +extern "C" void* __mmap2(void*, size_t, int, int, int, size_t); +#if defined(__NR_mmap) && !defined(SYS_mmap) +#define SYS_mmap __NR_mmap +#endif +#ifndef SYS_munmap +#define SYS_munmap __NR_munmap +#endif +#endif // __BIONIC__ + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Platform specific logic extracted from +// https://chromium.googlesource.com/linux-syscall-support/+/master/linux_syscall_support.h +inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, + off64_t offset) noexcept { +#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + (defined(__PPC__) && !defined(__PPC64__)) || \ + (defined(__s390__) && !defined(__s390x__)) + // On these architectures, implement mmap with mmap2. + static int pagesize = 0; + if (pagesize == 0) { +#if defined(__wasm__) || defined(__asmjs__) + pagesize = getpagesize(); +#else + pagesize = sysconf(_SC_PAGESIZE); +#endif + } + if (offset < 0 || offset % pagesize != 0) { + errno = EINVAL; + return MAP_FAILED; + } +#ifdef __BIONIC__ + // SYS_mmap2 has problems on Android API level <= 16. + // Workaround by invoking __mmap2() instead. + return __mmap2(start, length, prot, flags, fd, offset / pagesize); +#else + return reinterpret_cast( + syscall(SYS_mmap2, start, length, prot, flags, fd, + static_cast(offset / pagesize))); +#endif +#elif defined(__s390x__) + // On s390x, mmap() arguments are passed in memory. + unsigned long buf[6] = {reinterpret_cast(start), // NOLINT + static_cast(length), // NOLINT + static_cast(prot), // NOLINT + static_cast(flags), // NOLINT + static_cast(fd), // NOLINT + static_cast(offset)}; // NOLINT + return reinterpret_cast(syscall(SYS_mmap, buf)); +#elif defined(__x86_64__) +// The x32 ABI has 32 bit longs, but the syscall interface is 64 bit. +// We need to explicitly cast to an unsigned 64 bit type to avoid implicit +// sign extension. We can't cast pointers directly because those are +// 32 bits, and gcc will dump ugly warnings about casting from a pointer +// to an integer of a different size. We also need to make sure __off64_t +// isn't truncated to 32-bits under x32. +#define MMAP_SYSCALL_ARG(x) ((uint64_t)(uintptr_t)(x)) + return reinterpret_cast( + syscall(SYS_mmap, MMAP_SYSCALL_ARG(start), MMAP_SYSCALL_ARG(length), + MMAP_SYSCALL_ARG(prot), MMAP_SYSCALL_ARG(flags), + MMAP_SYSCALL_ARG(fd), static_cast(offset))); +#undef MMAP_SYSCALL_ARG +#else // Remaining 64-bit aritectures. + static_assert(sizeof(unsigned long) == 8, "Platform is not 64-bit"); + return reinterpret_cast( + syscall(SYS_mmap, start, length, prot, flags, fd, offset)); +#endif +} + +inline int DirectMunmap(void* start, size_t length) { + return static_cast(syscall(SYS_munmap, start, length)); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#else // !__linux__ + +// For non-linux platforms where we have mmap, just dispatch directly to the +// actual mmap()/munmap() methods. + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return mmap(start, length, prot, flags, fd, offset); +} + +inline int DirectMunmap(void* start, size_t length) { + return munmap(start, length); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // __linux__ + +#endif // ABSL_HAVE_MMAP + +#endif // ABSL_BASE_INTERNAL_DIRECT_MMAP_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/endian.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/endian.h new file mode 100644 index 0000000000000000000000000000000000000000..9677530e8de320bf3f8d00e9bd0e1567996a2377 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/endian.h @@ -0,0 +1,266 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_ENDIAN_H_ +#define ABSL_BASE_INTERNAL_ENDIAN_H_ + +// The following guarantees declaration of the byte swap functions +#ifdef _MSC_VER +#include // NOLINT(build/include) +#elif defined(__FreeBSD__) +#include +#elif defined(__GLIBC__) +#include // IWYU pragma: export +#endif + +#include +#include "absl/base/config.h" +#include "absl/base/internal/unaligned_access.h" +#include "absl/base/port.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Use compiler byte-swapping intrinsics if they are available. 32-bit +// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0. +// The 16-bit version is available in Clang and GCC only as of GCC 4.8.0. +// For simplicity, we enable them all only for GCC 4.8.0 or later. +#if defined(__clang__) || \ + (defined(__GNUC__) && \ + ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5)) +inline uint64_t gbswap_64(uint64_t host_int) { + return __builtin_bswap64(host_int); +} +inline uint32_t gbswap_32(uint32_t host_int) { + return __builtin_bswap32(host_int); +} +inline uint16_t gbswap_16(uint16_t host_int) { + return __builtin_bswap16(host_int); +} + +#elif defined(_MSC_VER) +inline uint64_t gbswap_64(uint64_t host_int) { + return _byteswap_uint64(host_int); +} +inline uint32_t gbswap_32(uint32_t host_int) { + return _byteswap_ulong(host_int); +} +inline uint16_t gbswap_16(uint16_t host_int) { + return _byteswap_ushort(host_int); +} + +#else +inline uint64_t gbswap_64(uint64_t host_int) { +#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__) + // Adapted from /usr/include/byteswap.h. Not available on Mac. + if (__builtin_constant_p(host_int)) { + return __bswap_constant_64(host_int); + } else { + uint64_t result; + __asm__("bswap %0" : "=r"(result) : "0"(host_int)); + return result; + } +#elif defined(__GLIBC__) + return bswap_64(host_int); +#else + return (((host_int & uint64_t{0xFF}) << 56) | + ((host_int & uint64_t{0xFF00}) << 40) | + ((host_int & uint64_t{0xFF0000}) << 24) | + ((host_int & uint64_t{0xFF000000}) << 8) | + ((host_int & uint64_t{0xFF00000000}) >> 8) | + ((host_int & uint64_t{0xFF0000000000}) >> 24) | + ((host_int & uint64_t{0xFF000000000000}) >> 40) | + ((host_int & uint64_t{0xFF00000000000000}) >> 56)); +#endif // bswap_64 +} + +inline uint32_t gbswap_32(uint32_t host_int) { +#if defined(__GLIBC__) + return bswap_32(host_int); +#else + return (((host_int & uint32_t{0xFF}) << 24) | + ((host_int & uint32_t{0xFF00}) << 8) | + ((host_int & uint32_t{0xFF0000}) >> 8) | + ((host_int & uint32_t{0xFF000000}) >> 24)); +#endif +} + +inline uint16_t gbswap_16(uint16_t host_int) { +#if defined(__GLIBC__) + return bswap_16(host_int); +#else + return (((host_int & uint16_t{0xFF}) << 8) | + ((host_int & uint16_t{0xFF00}) >> 8)); +#endif +} + +#endif // intrinsics available + +#ifdef ABSL_IS_LITTLE_ENDIAN + +// Definitions for ntohl etc. that don't require us to include +// netinet/in.h. We wrap gbswap_32 and gbswap_16 in functions rather +// than just #defining them because in debug mode, gcc doesn't +// correctly handle the (rather involved) definitions of bswap_32. +// gcc guarantees that inline functions are as fast as macros, so +// this isn't a performance hit. +inline uint16_t ghtons(uint16_t x) { return gbswap_16(x); } +inline uint32_t ghtonl(uint32_t x) { return gbswap_32(x); } +inline uint64_t ghtonll(uint64_t x) { return gbswap_64(x); } + +#elif defined ABSL_IS_BIG_ENDIAN + +// These definitions are simpler on big-endian machines +// These are functions instead of macros to avoid self-assignment warnings +// on calls such as "i = ghtnol(i);". This also provides type checking. +inline uint16_t ghtons(uint16_t x) { return x; } +inline uint32_t ghtonl(uint32_t x) { return x; } +inline uint64_t ghtonll(uint64_t x) { return x; } + +#else +#error \ + "Unsupported byte order: Either ABSL_IS_BIG_ENDIAN or " \ + "ABSL_IS_LITTLE_ENDIAN must be defined" +#endif // byte order + +inline uint16_t gntohs(uint16_t x) { return ghtons(x); } +inline uint32_t gntohl(uint32_t x) { return ghtonl(x); } +inline uint64_t gntohll(uint64_t x) { return ghtonll(x); } + +// Utilities to convert numbers between the current hosts's native byte +// order and little-endian byte order +// +// Load/Store methods are alignment safe +namespace little_endian { +// Conversion functions. +#ifdef ABSL_IS_LITTLE_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return x; } +inline uint16_t ToHost16(uint16_t x) { return x; } + +inline uint32_t FromHost32(uint32_t x) { return x; } +inline uint32_t ToHost32(uint32_t x) { return x; } + +inline uint64_t FromHost64(uint64_t x) { return x; } +inline uint64_t ToHost64(uint64_t x) { return x; } + +inline constexpr bool IsLittleEndian() { return true; } + +#elif defined ABSL_IS_BIG_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); } +inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); } + +inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); } +inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); } + +inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); } +inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); } + +inline constexpr bool IsLittleEndian() { return false; } + +#endif /* ENDIAN */ + +// Functions to do unaligned loads and stores in little-endian order. +inline uint16_t Load16(const void *p) { + return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); +} + +inline void Store16(void *p, uint16_t v) { + ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); +} + +inline uint32_t Load32(const void *p) { + return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p)); +} + +inline void Store32(void *p, uint32_t v) { + ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); +} + +inline uint64_t Load64(const void *p) { + return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p)); +} + +inline void Store64(void *p, uint64_t v) { + ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); +} + +} // namespace little_endian + +// Utilities to convert numbers between the current hosts's native byte +// order and big-endian byte order (same as network byte order) +// +// Load/Store methods are alignment safe +namespace big_endian { +#ifdef ABSL_IS_LITTLE_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); } +inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); } + +inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); } +inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); } + +inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); } +inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); } + +inline constexpr bool IsLittleEndian() { return true; } + +#elif defined ABSL_IS_BIG_ENDIAN + +inline uint16_t FromHost16(uint16_t x) { return x; } +inline uint16_t ToHost16(uint16_t x) { return x; } + +inline uint32_t FromHost32(uint32_t x) { return x; } +inline uint32_t ToHost32(uint32_t x) { return x; } + +inline uint64_t FromHost64(uint64_t x) { return x; } +inline uint64_t ToHost64(uint64_t x) { return x; } + +inline constexpr bool IsLittleEndian() { return false; } + +#endif /* ENDIAN */ + +// Functions to do unaligned loads and stores in big-endian order. +inline uint16_t Load16(const void *p) { + return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); +} + +inline void Store16(void *p, uint16_t v) { + ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); +} + +inline uint32_t Load32(const void *p) { + return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p)); +} + +inline void Store32(void *p, uint32_t v) { + ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); +} + +inline uint64_t Load64(const void *p) { + return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p)); +} + +inline void Store64(void *p, uint64_t v) { + ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); +} + +} // namespace big_endian + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_ENDIAN_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/errno_saver.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/errno_saver.h new file mode 100644 index 0000000000000000000000000000000000000000..251de510fc939d42fd10a9d2d148554f7998a17e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/errno_saver.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ +#define ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// `ErrnoSaver` captures the value of `errno` upon construction and restores it +// upon deletion. It is used in low-level code and must be super fast. Do not +// add instrumentation, even in debug modes. +class ErrnoSaver { + public: + ErrnoSaver() : saved_errno_(errno) {} + ~ErrnoSaver() { errno = saved_errno_; } + int operator()() const { return saved_errno_; } + + private: + const int saved_errno_; +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/exception_safety_testing.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/exception_safety_testing.h new file mode 100644 index 0000000000000000000000000000000000000000..6ba89d05dfca5dd9ba0028334358356716d82120 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/exception_safety_testing.h @@ -0,0 +1,1101 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 for testing exception-safety + +#ifndef ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ +#define ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ + +#include "absl/base/config.h" + +#ifdef ABSL_HAVE_EXCEPTIONS + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/internal/pretty_function.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" +#include "absl/strings/substitute.h" +#include "absl/utility/utility.h" + +namespace testing { + +enum class TypeSpec; +enum class AllocSpec; + +constexpr TypeSpec operator|(TypeSpec a, TypeSpec b) { + using T = absl::underlying_type_t; + return static_cast(static_cast(a) | static_cast(b)); +} + +constexpr TypeSpec operator&(TypeSpec a, TypeSpec b) { + using T = absl::underlying_type_t; + return static_cast(static_cast(a) & static_cast(b)); +} + +constexpr AllocSpec operator|(AllocSpec a, AllocSpec b) { + using T = absl::underlying_type_t; + return static_cast(static_cast(a) | static_cast(b)); +} + +constexpr AllocSpec operator&(AllocSpec a, AllocSpec b) { + using T = absl::underlying_type_t; + return static_cast(static_cast(a) & static_cast(b)); +} + +namespace exceptions_internal { + +std::string GetSpecString(TypeSpec); +std::string GetSpecString(AllocSpec); + +struct NoThrowTag {}; +struct StrongGuaranteeTagType {}; + +// A simple exception class. We throw this so that test code can catch +// exceptions specifically thrown by ThrowingValue. +class TestException { + public: + explicit TestException(absl::string_view msg) : msg_(msg) {} + virtual ~TestException() {} + virtual const char* what() const noexcept { return msg_.c_str(); } + + private: + std::string msg_; +}; + +// TestBadAllocException exists because allocation functions must throw an +// exception which can be caught by a handler of std::bad_alloc. We use a child +// class of std::bad_alloc so we can customise the error message, and also +// derive from TestException so we don't accidentally end up catching an actual +// bad_alloc exception in TestExceptionSafety. +class TestBadAllocException : public std::bad_alloc, public TestException { + public: + explicit TestBadAllocException(absl::string_view msg) : TestException(msg) {} + using TestException::what; +}; + +extern int countdown; + +// Allows the countdown variable to be set manually (defaulting to the initial +// value of 0) +inline void SetCountdown(int i = 0) { countdown = i; } +// Sets the countdown to the terminal value -1 +inline void UnsetCountdown() { SetCountdown(-1); } + +void MaybeThrow(absl::string_view msg, bool throw_bad_alloc = false); + +testing::AssertionResult FailureMessage(const TestException& e, + int countdown) noexcept; + +struct TrackedAddress { + bool is_alive; + std::string description; +}; + +// Inspects the constructions and destructions of anything inheriting from +// TrackedObject. This allows us to safely "leak" TrackedObjects, as +// ConstructorTracker will destroy everything left over in its destructor. +class ConstructorTracker { + public: + explicit ConstructorTracker(int count) : countdown_(count) { + assert(current_tracker_instance_ == nullptr); + current_tracker_instance_ = this; + } + + ~ConstructorTracker() { + assert(current_tracker_instance_ == this); + current_tracker_instance_ = nullptr; + + for (auto& it : address_map_) { + void* address = it.first; + TrackedAddress& tracked_address = it.second; + if (tracked_address.is_alive) { + ADD_FAILURE() << ErrorMessage(address, tracked_address.description, + countdown_, "Object was not destroyed."); + } + } + } + + static void ObjectConstructed(void* address, std::string description) { + if (!CurrentlyTracking()) return; + + TrackedAddress& tracked_address = + current_tracker_instance_->address_map_[address]; + if (tracked_address.is_alive) { + ADD_FAILURE() << ErrorMessage( + address, tracked_address.description, + current_tracker_instance_->countdown_, + "Object was re-constructed. Current object was constructed by " + + description); + } + tracked_address = {true, std::move(description)}; + } + + static void ObjectDestructed(void* address) { + if (!CurrentlyTracking()) return; + + auto it = current_tracker_instance_->address_map_.find(address); + // Not tracked. Ignore. + if (it == current_tracker_instance_->address_map_.end()) return; + + TrackedAddress& tracked_address = it->second; + if (!tracked_address.is_alive) { + ADD_FAILURE() << ErrorMessage(address, tracked_address.description, + current_tracker_instance_->countdown_, + "Object was re-destroyed."); + } + tracked_address.is_alive = false; + } + + private: + static bool CurrentlyTracking() { + return current_tracker_instance_ != nullptr; + } + + static std::string ErrorMessage(void* address, + const std::string& address_description, + int countdown, + const std::string& error_description) { + return absl::Substitute( + "With coundtown at $0:\n" + " $1\n" + " Object originally constructed by $2\n" + " Object address: $3\n", + countdown, error_description, address_description, address); + } + + std::unordered_map address_map_; + int countdown_; + + static ConstructorTracker* current_tracker_instance_; +}; + +class TrackedObject { + public: + TrackedObject(const TrackedObject&) = delete; + TrackedObject(TrackedObject&&) = delete; + + protected: + explicit TrackedObject(std::string description) { + ConstructorTracker::ObjectConstructed(this, std::move(description)); + } + + ~TrackedObject() noexcept { ConstructorTracker::ObjectDestructed(this); } +}; +} // namespace exceptions_internal + +extern exceptions_internal::NoThrowTag nothrow_ctor; + +extern exceptions_internal::StrongGuaranteeTagType strong_guarantee; + +// A test class which is convertible to bool. The conversion can be +// instrumented to throw at a controlled time. +class ThrowingBool { + public: + ThrowingBool(bool b) noexcept : b_(b) {} // NOLINT(runtime/explicit) + operator bool() const { // NOLINT + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return b_; + } + + private: + bool b_; +}; + +/* + * Configuration enum for the ThrowingValue type that defines behavior for the + * lifetime of the instance. Use testing::nothrow_ctor to prevent the integer + * constructor from throwing. + * + * kEverythingThrows: Every operation can throw an exception + * kNoThrowCopy: Copy construction and copy assignment will not throw + * kNoThrowMove: Move construction and move assignment will not throw + * kNoThrowNew: Overloaded operators new and new[] will not throw + */ +enum class TypeSpec { + kEverythingThrows = 0, + kNoThrowCopy = 1, + kNoThrowMove = 1 << 1, + kNoThrowNew = 1 << 2, +}; + +/* + * A testing class instrumented to throw an exception at a controlled time. + * + * ThrowingValue implements a slightly relaxed version of the Regular concept -- + * that is it's a value type with the expected semantics. It also implements + * arithmetic operations. It doesn't implement member and pointer operators + * like operator-> or operator[]. + * + * ThrowingValue can be instrumented to have certain operations be noexcept by + * using compile-time bitfield template arguments. That is, to make an + * ThrowingValue which has noexcept move construction/assignment and noexcept + * copy construction/assignment, use the following: + * ThrowingValue my_thrwr{val}; + */ +template +class ThrowingValue : private exceptions_internal::TrackedObject { + static constexpr bool IsSpecified(TypeSpec spec) { + return static_cast(Spec & spec); + } + + static constexpr int kDefaultValue = 0; + static constexpr int kBadValue = 938550620; + + public: + ThrowingValue() : TrackedObject(GetInstanceString(kDefaultValue)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ = kDefaultValue; + } + + ThrowingValue(const ThrowingValue& other) noexcept( + IsSpecified(TypeSpec::kNoThrowCopy)) + : TrackedObject(GetInstanceString(other.dummy_)) { + if (!IsSpecified(TypeSpec::kNoThrowCopy)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + } + dummy_ = other.dummy_; + } + + ThrowingValue(ThrowingValue&& other) noexcept( + IsSpecified(TypeSpec::kNoThrowMove)) + : TrackedObject(GetInstanceString(other.dummy_)) { + if (!IsSpecified(TypeSpec::kNoThrowMove)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + } + dummy_ = other.dummy_; + } + + explicit ThrowingValue(int i) : TrackedObject(GetInstanceString(i)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ = i; + } + + ThrowingValue(int i, exceptions_internal::NoThrowTag) noexcept + : TrackedObject(GetInstanceString(i)), dummy_(i) {} + + // absl expects nothrow destructors + ~ThrowingValue() noexcept = default; + + ThrowingValue& operator=(const ThrowingValue& other) noexcept( + IsSpecified(TypeSpec::kNoThrowCopy)) { + dummy_ = kBadValue; + if (!IsSpecified(TypeSpec::kNoThrowCopy)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + } + dummy_ = other.dummy_; + return *this; + } + + ThrowingValue& operator=(ThrowingValue&& other) noexcept( + IsSpecified(TypeSpec::kNoThrowMove)) { + dummy_ = kBadValue; + if (!IsSpecified(TypeSpec::kNoThrowMove)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + } + dummy_ = other.dummy_; + return *this; + } + + // Arithmetic Operators + ThrowingValue operator+(const ThrowingValue& other) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_ + other.dummy_, nothrow_ctor); + } + + ThrowingValue operator+() const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_, nothrow_ctor); + } + + ThrowingValue operator-(const ThrowingValue& other) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_ - other.dummy_, nothrow_ctor); + } + + ThrowingValue operator-() const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(-dummy_, nothrow_ctor); + } + + ThrowingValue& operator++() { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + ++dummy_; + return *this; + } + + ThrowingValue operator++(int) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + auto out = ThrowingValue(dummy_, nothrow_ctor); + ++dummy_; + return out; + } + + ThrowingValue& operator--() { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + --dummy_; + return *this; + } + + ThrowingValue operator--(int) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + auto out = ThrowingValue(dummy_, nothrow_ctor); + --dummy_; + return out; + } + + ThrowingValue operator*(const ThrowingValue& other) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_ * other.dummy_, nothrow_ctor); + } + + ThrowingValue operator/(const ThrowingValue& other) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_ / other.dummy_, nothrow_ctor); + } + + ThrowingValue operator%(const ThrowingValue& other) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_ % other.dummy_, nothrow_ctor); + } + + ThrowingValue operator<<(int shift) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_ << shift, nothrow_ctor); + } + + ThrowingValue operator>>(int shift) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_ >> shift, nothrow_ctor); + } + + // Comparison Operators + // NOTE: We use `ThrowingBool` instead of `bool` because most STL + // types/containers requires T to be convertible to bool. + friend ThrowingBool operator==(const ThrowingValue& a, + const ThrowingValue& b) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return a.dummy_ == b.dummy_; + } + friend ThrowingBool operator!=(const ThrowingValue& a, + const ThrowingValue& b) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return a.dummy_ != b.dummy_; + } + friend ThrowingBool operator<(const ThrowingValue& a, + const ThrowingValue& b) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return a.dummy_ < b.dummy_; + } + friend ThrowingBool operator<=(const ThrowingValue& a, + const ThrowingValue& b) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return a.dummy_ <= b.dummy_; + } + friend ThrowingBool operator>(const ThrowingValue& a, + const ThrowingValue& b) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return a.dummy_ > b.dummy_; + } + friend ThrowingBool operator>=(const ThrowingValue& a, + const ThrowingValue& b) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return a.dummy_ >= b.dummy_; + } + + // Logical Operators + ThrowingBool operator!() const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return !dummy_; + } + + ThrowingBool operator&&(const ThrowingValue& other) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return dummy_ && other.dummy_; + } + + ThrowingBool operator||(const ThrowingValue& other) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return dummy_ || other.dummy_; + } + + // Bitwise Logical Operators + ThrowingValue operator~() const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(~dummy_, nothrow_ctor); + } + + ThrowingValue operator&(const ThrowingValue& other) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_ & other.dummy_, nothrow_ctor); + } + + ThrowingValue operator|(const ThrowingValue& other) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_ | other.dummy_, nothrow_ctor); + } + + ThrowingValue operator^(const ThrowingValue& other) const { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return ThrowingValue(dummy_ ^ other.dummy_, nothrow_ctor); + } + + // Compound Assignment operators + ThrowingValue& operator+=(const ThrowingValue& other) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ += other.dummy_; + return *this; + } + + ThrowingValue& operator-=(const ThrowingValue& other) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ -= other.dummy_; + return *this; + } + + ThrowingValue& operator*=(const ThrowingValue& other) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ *= other.dummy_; + return *this; + } + + ThrowingValue& operator/=(const ThrowingValue& other) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ /= other.dummy_; + return *this; + } + + ThrowingValue& operator%=(const ThrowingValue& other) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ %= other.dummy_; + return *this; + } + + ThrowingValue& operator&=(const ThrowingValue& other) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ &= other.dummy_; + return *this; + } + + ThrowingValue& operator|=(const ThrowingValue& other) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ |= other.dummy_; + return *this; + } + + ThrowingValue& operator^=(const ThrowingValue& other) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ ^= other.dummy_; + return *this; + } + + ThrowingValue& operator<<=(int shift) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ <<= shift; + return *this; + } + + ThrowingValue& operator>>=(int shift) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ >>= shift; + return *this; + } + + // Pointer operators + void operator&() const = delete; // NOLINT(runtime/operator) + + // Stream operators + friend std::ostream& operator<<(std::ostream& os, const ThrowingValue& tv) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return os << GetInstanceString(tv.dummy_); + } + + friend std::istream& operator>>(std::istream& is, const ThrowingValue&) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + return is; + } + + // Memory management operators + // Args.. allows us to overload regular and placement new in one shot + template + static void* operator new(size_t s, Args&&... args) noexcept( + IsSpecified(TypeSpec::kNoThrowNew)) { + if (!IsSpecified(TypeSpec::kNoThrowNew)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); + } + return ::operator new(s, std::forward(args)...); + } + + template + static void* operator new[](size_t s, Args&&... args) noexcept( + IsSpecified(TypeSpec::kNoThrowNew)) { + if (!IsSpecified(TypeSpec::kNoThrowNew)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); + } + return ::operator new[](s, std::forward(args)...); + } + + // Abseil doesn't support throwing overloaded operator delete. These are + // provided so a throwing operator-new can clean up after itself. + // + // We provide both regular and templated operator delete because if only the + // templated version is provided as we did with operator new, the compiler has + // no way of knowing which overload of operator delete to call. See + // https://en.cppreference.com/w/cpp/memory/new/operator_delete and + // https://en.cppreference.com/w/cpp/language/delete for the gory details. + void operator delete(void* p) noexcept { ::operator delete(p); } + + template + void operator delete(void* p, Args&&... args) noexcept { + ::operator delete(p, std::forward(args)...); + } + + void operator delete[](void* p) noexcept { return ::operator delete[](p); } + + template + void operator delete[](void* p, Args&&... args) noexcept { + return ::operator delete[](p, std::forward(args)...); + } + + // Non-standard access to the actual contained value. No need for this to + // throw. + int& Get() noexcept { return dummy_; } + const int& Get() const noexcept { return dummy_; } + + private: + static std::string GetInstanceString(int dummy) { + return absl::StrCat("ThrowingValue<", + exceptions_internal::GetSpecString(Spec), ">(", dummy, + ")"); + } + + int dummy_; +}; +// While not having to do with exceptions, explicitly delete comma operator, to +// make sure we don't use it on user-supplied types. +template +void operator,(const ThrowingValue&, T&&) = delete; +template +void operator,(T&&, const ThrowingValue&) = delete; + +/* + * Configuration enum for the ThrowingAllocator type that defines behavior for + * the lifetime of the instance. + * + * kEverythingThrows: Calls to the member functions may throw + * kNoThrowAllocate: Calls to the member functions will not throw + */ +enum class AllocSpec { + kEverythingThrows = 0, + kNoThrowAllocate = 1, +}; + +/* + * An allocator type which is instrumented to throw at a controlled time, or not + * to throw, using AllocSpec. The supported settings are the default of every + * function which is allowed to throw in a conforming allocator possibly + * throwing, or nothing throws, in line with the ABSL_ALLOCATOR_THROWS + * configuration macro. + */ +template +class ThrowingAllocator : private exceptions_internal::TrackedObject { + static constexpr bool IsSpecified(AllocSpec spec) { + return static_cast(Spec & spec); + } + + public: + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using void_pointer = void*; + using const_void_pointer = const void*; + using value_type = T; + using size_type = size_t; + using difference_type = ptrdiff_t; + + using is_nothrow = + std::integral_constant; + using propagate_on_container_copy_assignment = std::true_type; + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; + using is_always_equal = std::false_type; + + ThrowingAllocator() : TrackedObject(GetInstanceString(next_id_)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + dummy_ = std::make_shared(next_id_++); + } + + template + ThrowingAllocator(const ThrowingAllocator& other) noexcept // NOLINT + : TrackedObject(GetInstanceString(*other.State())), + dummy_(other.State()) {} + + // According to C++11 standard [17.6.3.5], Table 28, the move/copy ctors of + // allocator shall not exit via an exception, thus they are marked noexcept. + ThrowingAllocator(const ThrowingAllocator& other) noexcept + : TrackedObject(GetInstanceString(*other.State())), + dummy_(other.State()) {} + + template + ThrowingAllocator(ThrowingAllocator&& other) noexcept // NOLINT + : TrackedObject(GetInstanceString(*other.State())), + dummy_(std::move(other.State())) {} + + ThrowingAllocator(ThrowingAllocator&& other) noexcept + : TrackedObject(GetInstanceString(*other.State())), + dummy_(std::move(other.State())) {} + + ~ThrowingAllocator() noexcept = default; + + ThrowingAllocator& operator=(const ThrowingAllocator& other) noexcept { + dummy_ = other.State(); + return *this; + } + + template + ThrowingAllocator& operator=( + const ThrowingAllocator& other) noexcept { + dummy_ = other.State(); + return *this; + } + + template + ThrowingAllocator& operator=(ThrowingAllocator&& other) noexcept { + dummy_ = std::move(other.State()); + return *this; + } + + template + struct rebind { + using other = ThrowingAllocator; + }; + + pointer allocate(size_type n) noexcept( + IsSpecified(AllocSpec::kNoThrowAllocate)) { + ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); + return static_cast(::operator new(n * sizeof(T))); + } + + pointer allocate(size_type n, const_void_pointer) noexcept( + IsSpecified(AllocSpec::kNoThrowAllocate)) { + return allocate(n); + } + + void deallocate(pointer ptr, size_type) noexcept { + ReadState(); + ::operator delete(static_cast(ptr)); + } + + template + void construct(U* ptr, Args&&... args) noexcept( + IsSpecified(AllocSpec::kNoThrowAllocate)) { + ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); + ::new (static_cast(ptr)) U(std::forward(args)...); + } + + template + void destroy(U* p) noexcept { + ReadState(); + p->~U(); + } + + size_type max_size() const noexcept { + return (std::numeric_limits::max)() / sizeof(value_type); + } + + ThrowingAllocator select_on_container_copy_construction() noexcept( + IsSpecified(AllocSpec::kNoThrowAllocate)) { + auto& out = *this; + ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); + return out; + } + + template + bool operator==(const ThrowingAllocator& other) const noexcept { + return dummy_ == other.dummy_; + } + + template + bool operator!=(const ThrowingAllocator& other) const noexcept { + return dummy_ != other.dummy_; + } + + template + friend class ThrowingAllocator; + + private: + static std::string GetInstanceString(int dummy) { + return absl::StrCat("ThrowingAllocator<", + exceptions_internal::GetSpecString(Spec), ">(", dummy, + ")"); + } + + const std::shared_ptr& State() const { return dummy_; } + std::shared_ptr& State() { return dummy_; } + + void ReadState() { + // we know that this will never be true, but the compiler doesn't, so this + // should safely force a read of the value. + if (*dummy_ < 0) std::abort(); + } + + void ReadStateAndMaybeThrow(absl::string_view msg) const { + if (!IsSpecified(AllocSpec::kNoThrowAllocate)) { + exceptions_internal::MaybeThrow( + absl::Substitute("Allocator id $0 threw from $1", *dummy_, msg)); + } + } + + static int next_id_; + std::shared_ptr dummy_; +}; + +template +int ThrowingAllocator::next_id_ = 0; + +// Tests for resource leaks by attempting to construct a T using args repeatedly +// until successful, using the countdown method. Side effects can then be +// tested for resource leaks. +template +void TestThrowingCtor(Args&&... args) { + struct Cleanup { + ~Cleanup() { exceptions_internal::UnsetCountdown(); } + } c; + for (int count = 0;; ++count) { + exceptions_internal::ConstructorTracker ct(count); + exceptions_internal::SetCountdown(count); + try { + T temp(std::forward(args)...); + static_cast(temp); + break; + } catch (const exceptions_internal::TestException&) { + } + } +} + +// Tests the nothrow guarantee of the provided nullary operation. If the an +// exception is thrown, the result will be AssertionFailure(). Otherwise, it +// will be AssertionSuccess(). +template +testing::AssertionResult TestNothrowOp(const Operation& operation) { + struct Cleanup { + Cleanup() { exceptions_internal::SetCountdown(); } + ~Cleanup() { exceptions_internal::UnsetCountdown(); } + } c; + try { + operation(); + return testing::AssertionSuccess(); + } catch (const exceptions_internal::TestException&) { + return testing::AssertionFailure() + << "TestException thrown during call to operation() when nothrow " + "guarantee was expected."; + } catch (...) { + return testing::AssertionFailure() + << "Unknown exception thrown during call to operation() when " + "nothrow guarantee was expected."; + } +} + +namespace exceptions_internal { + +// Dummy struct for ExceptionSafetyTestBuilder<> partial state. +struct UninitializedT {}; + +template +class DefaultFactory { + public: + explicit DefaultFactory(const T& t) : t_(t) {} + std::unique_ptr operator()() const { return absl::make_unique(t_); } + + private: + T t_; +}; + +template +using EnableIfTestable = typename absl::enable_if_t< + LazyContractsCount != 0 && + !std::is_same::value && + !std::is_same::value>; + +template +class ExceptionSafetyTestBuilder; + +} // namespace exceptions_internal + +/* + * Constructs an empty ExceptionSafetyTestBuilder. All + * ExceptionSafetyTestBuilder objects are immutable and all With[thing] mutation + * methods return new instances of ExceptionSafetyTestBuilder. + * + * In order to test a T for exception safety, a factory for that T, a testable + * operation, and at least one contract callback returning an assertion + * result must be applied using the respective methods. + */ +exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester(); + +namespace exceptions_internal { +template +struct IsUniquePtr : std::false_type {}; + +template +struct IsUniquePtr> : std::true_type {}; + +template +struct FactoryPtrTypeHelper { + using type = decltype(std::declval()()); + + static_assert(IsUniquePtr::value, "Factories must return a unique_ptr"); +}; + +template +using FactoryPtrType = typename FactoryPtrTypeHelper::type; + +template +using FactoryElementType = typename FactoryPtrType::element_type; + +template +class ExceptionSafetyTest { + using Factory = std::function()>; + using Operation = std::function; + using Contract = std::function; + + public: + template + explicit ExceptionSafetyTest(const Factory& f, const Operation& op, + const Contracts&... contracts) + : factory_(f), operation_(op), contracts_{WrapContract(contracts)...} {} + + AssertionResult Test() const { + for (int count = 0;; ++count) { + exceptions_internal::ConstructorTracker ct(count); + + for (const auto& contract : contracts_) { + auto t_ptr = factory_(); + try { + SetCountdown(count); + operation_(t_ptr.get()); + // Unset for the case that the operation throws no exceptions, which + // would leave the countdown set and break the *next* exception safety + // test after this one. + UnsetCountdown(); + return AssertionSuccess(); + } catch (const exceptions_internal::TestException& e) { + if (!contract(t_ptr.get())) { + return AssertionFailure() << e.what() << " failed contract check"; + } + } + } + } + } + + private: + template + Contract WrapContract(const ContractFn& contract) { + return [contract](T* t_ptr) { return AssertionResult(contract(t_ptr)); }; + } + + Contract WrapContract(StrongGuaranteeTagType) { + return [this](T* t_ptr) { return AssertionResult(*factory_() == *t_ptr); }; + } + + Factory factory_; + Operation operation_; + std::vector contracts_; +}; + +/* + * Builds a tester object that tests if performing a operation on a T follows + * exception safety guarantees. Verification is done via contract assertion + * callbacks applied to T instances post-throw. + * + * Template parameters for ExceptionSafetyTestBuilder: + * + * - Factory: The factory object (passed in via tester.WithFactory(...) or + * tester.WithInitialValue(...)) must be invocable with the signature + * `std::unique_ptr operator()() const` where T is the type being tested. + * It is used for reliably creating identical T instances to test on. + * + * - Operation: The operation object (passsed in via tester.WithOperation(...) + * or tester.Test(...)) must be invocable with the signature + * `void operator()(T*) const` where T is the type being tested. It is used + * for performing steps on a T instance that may throw and that need to be + * checked for exception safety. Each call to the operation will receive a + * fresh T instance so it's free to modify and destroy the T instances as it + * pleases. + * + * - Contracts...: The contract assertion callback objects (passed in via + * tester.WithContracts(...)) must be invocable with the signature + * `testing::AssertionResult operator()(T*) const` where T is the type being + * tested. Contract assertion callbacks are provided T instances post-throw. + * They must return testing::AssertionSuccess when the type contracts of the + * provided T instance hold. If the type contracts of the T instance do not + * hold, they must return testing::AssertionFailure. Execution order of + * Contracts... is unspecified. They will each individually get a fresh T + * instance so they are free to modify and destroy the T instances as they + * please. + */ +template +class ExceptionSafetyTestBuilder { + public: + /* + * Returns a new ExceptionSafetyTestBuilder with an included T factory based + * on the provided T instance. The existing factory will not be included in + * the newly created tester instance. The created factory returns a new T + * instance by copy-constructing the provided const T& t. + * + * Preconditions for tester.WithInitialValue(const T& t): + * + * - The const T& t object must be copy-constructible where T is the type + * being tested. For non-copy-constructible objects, use the method + * tester.WithFactory(...). + */ + template + ExceptionSafetyTestBuilder, Operation, Contracts...> + WithInitialValue(const T& t) const { + return WithFactory(DefaultFactory(t)); + } + + /* + * Returns a new ExceptionSafetyTestBuilder with the provided T factory + * included. The existing factory will not be included in the newly-created + * tester instance. This method is intended for use with types lacking a copy + * constructor. Types that can be copy-constructed should instead use the + * method tester.WithInitialValue(...). + */ + template + ExceptionSafetyTestBuilder, Operation, Contracts...> + WithFactory(const NewFactory& new_factory) const { + return {new_factory, operation_, contracts_}; + } + + /* + * Returns a new ExceptionSafetyTestBuilder with the provided testable + * operation included. The existing operation will not be included in the + * newly created tester. + */ + template + ExceptionSafetyTestBuilder, Contracts...> + WithOperation(const NewOperation& new_operation) const { + return {factory_, new_operation, contracts_}; + } + + /* + * Returns a new ExceptionSafetyTestBuilder with the provided MoreContracts... + * combined with the Contracts... that were already included in the instance + * on which the method was called. Contracts... cannot be removed or replaced + * once added to an ExceptionSafetyTestBuilder instance. A fresh object must + * be created in order to get an empty Contracts... list. + * + * In addition to passing in custom contract assertion callbacks, this method + * accepts `testing::strong_guarantee` as an argument which checks T instances + * post-throw against freshly created T instances via operator== to verify + * that any state changes made during the execution of the operation were + * properly rolled back. + */ + template + ExceptionSafetyTestBuilder...> + WithContracts(const MoreContracts&... more_contracts) const { + return { + factory_, operation_, + std::tuple_cat(contracts_, std::tuple...>( + more_contracts...))}; + } + + /* + * Returns a testing::AssertionResult that is the reduced result of the + * exception safety algorithm. The algorithm short circuits and returns + * AssertionFailure after the first contract callback returns an + * AssertionFailure. Otherwise, if all contract callbacks return an + * AssertionSuccess, the reduced result is AssertionSuccess. + * + * The passed-in testable operation will not be saved in a new tester instance + * nor will it modify/replace the existing tester instance. This is useful + * when each operation being tested is unique and does not need to be reused. + * + * Preconditions for tester.Test(const NewOperation& new_operation): + * + * - May only be called after at least one contract assertion callback and a + * factory or initial value have been provided. + */ + template < + typename NewOperation, + typename = EnableIfTestable> + testing::AssertionResult Test(const NewOperation& new_operation) const { + return TestImpl(new_operation, absl::index_sequence_for()); + } + + /* + * Returns a testing::AssertionResult that is the reduced result of the + * exception safety algorithm. The algorithm short circuits and returns + * AssertionFailure after the first contract callback returns an + * AssertionFailure. Otherwise, if all contract callbacks return an + * AssertionSuccess, the reduced result is AssertionSuccess. + * + * Preconditions for tester.Test(): + * + * - May only be called after at least one contract assertion callback, a + * factory or initial value and a testable operation have been provided. + */ + template < + typename LazyOperation = Operation, + typename = EnableIfTestable> + testing::AssertionResult Test() const { + return Test(operation_); + } + + private: + template + friend class ExceptionSafetyTestBuilder; + + friend ExceptionSafetyTestBuilder<> testing::MakeExceptionSafetyTester(); + + ExceptionSafetyTestBuilder() {} + + ExceptionSafetyTestBuilder(const Factory& f, const Operation& o, + const std::tuple& i) + : factory_(f), operation_(o), contracts_(i) {} + + template + testing::AssertionResult TestImpl(SelectedOperation selected_operation, + absl::index_sequence) const { + return ExceptionSafetyTest>( + factory_, selected_operation, std::get(contracts_)...) + .Test(); + } + + Factory factory_; + Operation operation_; + std::tuple contracts_; +}; + +} // namespace exceptions_internal + +} // namespace testing + +#endif // ABSL_HAVE_EXCEPTIONS + +#endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/exception_testing.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/exception_testing.h new file mode 100644 index 0000000000000000000000000000000000000000..01b546557117f60d48607901413aeeb9b2f1ea18 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/exception_testing.h @@ -0,0 +1,42 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. + +// Testing utilities for ABSL types which throw exceptions. + +#ifndef ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_ +#define ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_ + +#include "gtest/gtest.h" +#include "absl/base/config.h" + +// ABSL_BASE_INTERNAL_EXPECT_FAIL tests either for a specified thrown exception +// if exceptions are enabled, or for death with a specified text in the error +// message +#ifdef ABSL_HAVE_EXCEPTIONS + +#define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \ + EXPECT_THROW(expr, exception_t) + +#elif defined(__ANDROID__) +// Android asserts do not log anywhere that gtest can currently inspect. +// So we expect exit, but cannot match the message. +#define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \ + EXPECT_DEATH(expr, ".*") +#else +#define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \ + EXPECT_DEATH_IF_SUPPORTED(expr, text) + +#endif + +#endif // ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/exponential_biased.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/exponential_biased.h new file mode 100644 index 0000000000000000000000000000000000000000..94f79a3378db3d17dbf5a820a71db9af8fb60ce5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/exponential_biased.h @@ -0,0 +1,130 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ +#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ + +#include + +#include "absl/base/config.h" +#include "absl/base/macros.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// ExponentialBiased provides a small and fast random number generator for a +// rounded exponential distribution. This generator manages very little state, +// and imposes no synchronization overhead. This makes it useful in specialized +// scenarios requiring minimum overhead, such as stride based periodic sampling. +// +// ExponentialBiased provides two closely related functions, GetSkipCount() and +// GetStride(), both returning a rounded integer defining a number of events +// required before some event with a given mean probability occurs. +// +// The distribution is useful to generate a random wait time or some periodic +// event with a given mean probability. For example, if an action is supposed to +// happen on average once every 'N' events, then we can get a random 'stride' +// counting down how long before the event to happen. For example, if we'd want +// to sample one in every 1000 'Frobber' calls, our code could look like this: +// +// Frobber::Frobber() { +// stride_ = exponential_biased_.GetStride(1000); +// } +// +// void Frobber::Frob(int arg) { +// if (--stride == 0) { +// SampleFrob(arg); +// stride_ = exponential_biased_.GetStride(1000); +// } +// ... +// } +// +// The rounding of the return value creates a bias, especially for smaller means +// where the distribution of the fraction is not evenly distributed. We correct +// this bias by tracking the fraction we rounded up or down on each iteration, +// effectively tracking the distance between the cumulative value, and the +// rounded cumulative value. For example, given a mean of 2: +// +// raw = 1.63076, cumulative = 1.63076, rounded = 2, bias = -0.36923 +// raw = 0.14624, cumulative = 1.77701, rounded = 2, bias = 0.14624 +// raw = 4.93194, cumulative = 6.70895, rounded = 7, bias = -0.06805 +// raw = 0.24206, cumulative = 6.95101, rounded = 7, bias = 0.24206 +// etc... +// +// Adjusting with rounding bias is relatively trivial: +// +// double value = bias_ + exponential_distribution(mean)(); +// double rounded_value = std::round(value); +// bias_ = value - rounded_value; +// return rounded_value; +// +// This class is thread-compatible. +class ExponentialBiased { + public: + // The number of bits set by NextRandom. + static constexpr int kPrngNumBits = 48; + + // `GetSkipCount()` returns the number of events to skip before some chosen + // event happens. For example, randomly tossing a coin, we will on average + // throw heads once before we get tails. We can simulate random coin tosses + // using GetSkipCount() as: + // + // ExponentialBiased eb; + // for (...) { + // int number_of_heads_before_tail = eb.GetSkipCount(1); + // for (int flips = 0; flips < number_of_heads_before_tail; ++flips) { + // printf("head..."); + // } + // printf("tail\n"); + // } + // + int64_t GetSkipCount(int64_t mean); + + // GetStride() returns the number of events required for a specific event to + // happen. See the class comments for a usage example. `GetStride()` is + // equivalent to `GetSkipCount(mean - 1) + 1`. When to use `GetStride()` or + // `GetSkipCount()` depends mostly on what best fits the use case. + int64_t GetStride(int64_t mean); + + // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1] + // + // This is public to enable testing. + static uint64_t NextRandom(uint64_t rnd); + + private: + void Initialize(); + + uint64_t rng_{0}; + double bias_{0}; + bool initialized_{false}; +}; + +// Returns the next prng value. +// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48 +// This is the lrand64 generator. +inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) { + const uint64_t prng_mult = uint64_t{0x5DEECE66D}; + const uint64_t prng_add = 0xB; + const uint64_t prng_mod_power = 48; + const uint64_t prng_mod_mask = + ~((~static_cast(0)) << prng_mod_power); + return (prng_mult * rnd + prng_add) & prng_mod_mask; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/hide_ptr.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/hide_ptr.h new file mode 100644 index 0000000000000000000000000000000000000000..1dba80909a9b512b07543f80fe73433069b31e99 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/hide_ptr.h @@ -0,0 +1,51 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_HIDE_PTR_H_ +#define ABSL_BASE_INTERNAL_HIDE_PTR_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Arbitrary value with high bits set. Xor'ing with it is unlikely +// to map one valid pointer to another valid pointer. +constexpr uintptr_t HideMask() { + return (uintptr_t{0xF03A5F7BU} << (sizeof(uintptr_t) - 4) * 8) | 0xF03A5F7BU; +} + +// Hide a pointer from the leak checker. For internal use only. +// Differs from absl::IgnoreLeak(ptr) in that absl::IgnoreLeak(ptr) causes ptr +// and all objects reachable from ptr to be ignored by the leak checker. +template +inline uintptr_t HidePtr(T* ptr) { + return reinterpret_cast(ptr) ^ HideMask(); +} + +// Return a pointer that has been hidden from the leak checker. +// For internal use only. +template +inline T* UnhidePtr(uintptr_t hidden) { + return reinterpret_cast(hidden ^ HideMask()); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_HIDE_PTR_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/identity.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/identity.h new file mode 100644 index 0000000000000000000000000000000000000000..a3154ed7bc592ebce69d733c7ef0aae3b5403e53 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/identity.h @@ -0,0 +1,37 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_IDENTITY_H_ +#define ABSL_BASE_INTERNAL_IDENTITY_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace internal { + +template +struct identity { + typedef T type; +}; + +template +using identity_t = typename identity::type; + +} // namespace internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_IDENTITY_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/inline_variable.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/inline_variable.h new file mode 100644 index 0000000000000000000000000000000000000000..130d8c2476dd2033f0abde02761b41e88b825124 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/inline_variable.h @@ -0,0 +1,107 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ +#define ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ + +#include + +#include "absl/base/internal/identity.h" + +// File: +// This file define a macro that allows the creation of or emulation of C++17 +// inline variables based on whether or not the feature is supported. + +//////////////////////////////////////////////////////////////////////////////// +// Macro: ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) +// +// Description: +// Expands to the equivalent of an inline constexpr instance of the specified +// `type` and `name`, initialized to the value `init`. If the compiler being +// used is detected as supporting actual inline variables as a language +// feature, then the macro expands to an actual inline variable definition. +// +// Requires: +// `type` is a type that is usable in an extern variable declaration. +// +// Requires: `name` is a valid identifier +// +// Requires: +// `init` is an expression that can be used in the following definition: +// constexpr type name = init; +// +// Usage: +// +// // Equivalent to: `inline constexpr size_t variant_npos = -1;` +// ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -1); +// +// Differences in implementation: +// For a direct, language-level inline variable, decltype(name) will be the +// type that was specified along with const qualification, whereas for +// emulated inline variables, decltype(name) may be different (in practice +// it will likely be a reference type). +//////////////////////////////////////////////////////////////////////////////// + +#ifdef __cpp_inline_variables + +// Clang's -Wmissing-variable-declarations option erroneously warned that +// inline constexpr objects need to be pre-declared. This has now been fixed, +// but we will need to support this workaround for people building with older +// versions of clang. +// +// Bug: https://bugs.llvm.org/show_bug.cgi?id=35862 +// +// Note: +// identity_t is used here so that the const and name are in the +// appropriate place for pointer types, reference types, function pointer +// types, etc.. +#if defined(__clang__) +#define ABSL_INTERNAL_EXTERN_DECL(type, name) \ + extern const ::absl::internal::identity_t name; +#else // Otherwise, just define the macro to do nothing. +#define ABSL_INTERNAL_EXTERN_DECL(type, name) +#endif // defined(__clang__) + +// See above comment at top of file for details. +#define ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) \ + ABSL_INTERNAL_EXTERN_DECL(type, name) \ + inline constexpr ::absl::internal::identity_t name = init + +#else + +// See above comment at top of file for details. +// +// Note: +// identity_t is used here so that the const and name are in the +// appropriate place for pointer types, reference types, function pointer +// types, etc.. +#define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init) \ + template \ + struct AbslInternalInlineVariableHolder##name { \ + static constexpr ::absl::internal::identity_t kInstance = init; \ + }; \ + \ + template \ + constexpr ::absl::internal::identity_t \ + AbslInternalInlineVariableHolder##name::kInstance; \ + \ + static constexpr const ::absl::internal::identity_t& \ + name = /* NOLINT */ \ + AbslInternalInlineVariableHolder##name<>::kInstance; \ + static_assert(sizeof(void (*)(decltype(name))) != 0, \ + "Silence unused variable warnings.") + +#endif // __cpp_inline_variables + +#endif // ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/inline_variable_testing.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/inline_variable_testing.h new file mode 100644 index 0000000000000000000000000000000000000000..3856b9f80f20007bc20bb41743b711930c24b355 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/inline_variable_testing.h @@ -0,0 +1,46 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INLINE_VARIABLE_TESTING_H_ +#define ABSL_BASE_INLINE_VARIABLE_TESTING_H_ + +#include "absl/base/internal/inline_variable.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace inline_variable_testing_internal { + +struct Foo { + int value = 5; +}; + +ABSL_INTERNAL_INLINE_CONSTEXPR(Foo, inline_variable_foo, {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(Foo, other_inline_variable_foo, {}); + +ABSL_INTERNAL_INLINE_CONSTEXPR(int, inline_variable_int, 5); +ABSL_INTERNAL_INLINE_CONSTEXPR(int, other_inline_variable_int, 5); + +ABSL_INTERNAL_INLINE_CONSTEXPR(void(*)(), inline_variable_fun_ptr, nullptr); + +const Foo& get_foo_a(); +const Foo& get_foo_b(); + +const int& get_int_a(); +const int& get_int_b(); + +} // namespace inline_variable_testing_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INLINE_VARIABLE_TESTING_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/invoke.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/invoke.h new file mode 100644 index 0000000000000000000000000000000000000000..c4eceebd7cdaaf42b1ea2dcb96d9081615f71ef1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/invoke.h @@ -0,0 +1,187 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// absl::base_internal::Invoke(f, args...) is an implementation of +// INVOKE(f, args...) from section [func.require] of the C++ standard. +// +// [func.require] +// Define INVOKE (f, t1, t2, ..., tN) as follows: +// 1. (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T +// and t1 is an object of type T or a reference to an object of type T or a +// reference to an object of a type derived from T; +// 2. ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a +// class T and t1 is not one of the types described in the previous item; +// 3. t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is +// an object of type T or a reference to an object of type T or a reference +// to an object of a type derived from T; +// 4. (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 +// is not one of the types described in the previous item; +// 5. f(t1, t2, ..., tN) in all other cases. +// +// The implementation is SFINAE-friendly: substitution failure within Invoke() +// isn't an error. + +#ifndef ABSL_BASE_INTERNAL_INVOKE_H_ +#define ABSL_BASE_INTERNAL_INVOKE_H_ + +#include +#include +#include + +#include "absl/meta/type_traits.h" + +// The following code is internal implementation detail. See the comment at the +// top of this file for the API documentation. + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// The five classes below each implement one of the clauses from the definition +// of INVOKE. The inner class template Accept checks whether the +// clause is applicable; static function template Invoke(f, args...) does the +// invocation. +// +// By separating the clause selection logic from invocation we make sure that +// Invoke() does exactly what the standard says. + +template +struct StrippedAccept { + template + struct Accept : Derived::template AcceptImpl::type>::type...> {}; +}; + +// (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T +// and t1 is an object of type T or a reference to an object of type T or a +// reference to an object of a type derived from T. +struct MemFunAndRef : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + absl::is_function::value> { + }; + + template + static decltype((std::declval().* + std::declval())(std::declval()...)) + Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) { + return (std::forward(obj).* + std::forward(mem_fun))(std::forward(args)...); + } +}; + +// ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a +// class T and t1 is not one of the types described in the previous item. +struct MemFunAndPtr : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + absl::is_function::value> { + }; + + template + static decltype(((*std::declval()).* + std::declval())(std::declval()...)) + Invoke(MemFun&& mem_fun, Ptr&& ptr, Args&&... args) { + return ((*std::forward(ptr)).* + std::forward(mem_fun))(std::forward(args)...); + } +}; + +// t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is +// an object of type T or a reference to an object of type T or a reference +// to an object of a type derived from T. +struct DataMemAndRef : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + !absl::is_function::value> {}; + + template + static decltype(std::declval().*std::declval()) Invoke( + DataMem&& data_mem, Ref&& ref) { + return std::forward(ref).*std::forward(data_mem); + } +}; + +// (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 +// is not one of the types described in the previous item. +struct DataMemAndPtr : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value && + !absl::is_function::value> {}; + + template + static decltype((*std::declval()).*std::declval()) Invoke( + DataMem&& data_mem, Ptr&& ptr) { + return (*std::forward(ptr)).*std::forward(data_mem); + } +}; + +// f(t1, t2, ..., tN) in all other cases. +struct Callable { + // Callable doesn't have Accept because it's the last clause that gets picked + // when none of the previous clauses are applicable. + template + static decltype(std::declval()(std::declval()...)) Invoke( + F&& f, Args&&... args) { + return std::forward(f)(std::forward(args)...); + } +}; + +// Resolves to the first matching clause. +template +struct Invoker { + typedef typename std::conditional< + MemFunAndRef::Accept::value, MemFunAndRef, + typename std::conditional< + MemFunAndPtr::Accept::value, MemFunAndPtr, + typename std::conditional< + DataMemAndRef::Accept::value, DataMemAndRef, + typename std::conditional::value, + DataMemAndPtr, Callable>::type>::type>:: + type>::type type; +}; + +// The result type of Invoke. +template +using InvokeT = decltype(Invoker::type::Invoke( + std::declval(), std::declval()...)); + +// Invoke(f, args...) is an implementation of INVOKE(f, args...) from section +// [func.require] of the C++ standard. +template +InvokeT Invoke(F&& f, Args&&... args) { + return Invoker::type::Invoke(std::forward(f), + std::forward(args)...); +} +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_INVOKE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/low_level_alloc.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/low_level_alloc.h new file mode 100644 index 0000000000000000000000000000000000000000..db91951c825f2666a90c00baabad349f7e3f168e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/low_level_alloc.h @@ -0,0 +1,126 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ +#define ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ + +// A simple thread-safe memory allocator that does not depend on +// mutexes or thread-specific data. It is intended to be used +// sparingly, and only when malloc() would introduce an unwanted +// dependency, such as inside the heap-checker, or the Mutex +// implementation. + +// IWYU pragma: private, include "base/low_level_alloc.h" + +#include + +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +// LowLevelAlloc requires that the platform support low-level +// allocation of virtual memory. Platforms lacking this cannot use +// LowLevelAlloc. +#ifdef ABSL_LOW_LEVEL_ALLOC_MISSING +#error ABSL_LOW_LEVEL_ALLOC_MISSING cannot be directly set +#elif !defined(ABSL_HAVE_MMAP) && !defined(_WIN32) +#define ABSL_LOW_LEVEL_ALLOC_MISSING 1 +#endif + +// Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows or +// asm.js / WebAssembly. +// See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html +// for more information. +#ifdef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING +#error ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING cannot be directly set +#elif defined(_WIN32) || defined(__asmjs__) || defined(__wasm__) +#define ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING 1 +#endif + +#include + +#include "absl/base/port.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +class LowLevelAlloc { + public: + struct Arena; // an arena from which memory may be allocated + + // Returns a pointer to a block of at least "request" bytes + // that have been newly allocated from the specific arena. + // for Alloc() call the DefaultArena() is used. + // Returns 0 if passed request==0. + // Does not return 0 under other circumstances; it crashes if memory + // is not available. + static void *Alloc(size_t request) ABSL_ATTRIBUTE_SECTION(malloc_hook); + static void *AllocWithArena(size_t request, Arena *arena) + ABSL_ATTRIBUTE_SECTION(malloc_hook); + + // Deallocates a region of memory that was previously allocated with + // Alloc(). Does nothing if passed 0. "s" must be either 0, + // or must have been returned from a call to Alloc() and not yet passed to + // Free() since that call to Alloc(). The space is returned to the arena + // from which it was allocated. + static void Free(void *s) ABSL_ATTRIBUTE_SECTION(malloc_hook); + + // ABSL_ATTRIBUTE_SECTION(malloc_hook) for Alloc* and Free + // are to put all callers of MallocHook::Invoke* in this module + // into special section, + // so that MallocHook::GetCallerStackTrace can function accurately. + + // Create a new arena. + // The root metadata for the new arena is allocated in the + // meta_data_arena; the DefaultArena() can be passed for meta_data_arena. + // These values may be ored into flags: + enum { + // Report calls to Alloc() and Free() via the MallocHook interface. + // Set in the DefaultArena. + kCallMallocHook = 0x0001, + +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING + // Make calls to Alloc(), Free() be async-signal-safe. Not set in + // DefaultArena(). Not supported on all platforms. + kAsyncSignalSafe = 0x0002, +#endif + }; + // Construct a new arena. The allocation of the underlying metadata honors + // the provided flags. For example, the call NewArena(kAsyncSignalSafe) + // is itself async-signal-safe, as well as generatating an arena that provides + // async-signal-safe Alloc/Free. + static Arena *NewArena(int32_t flags); + + // Destroys an arena allocated by NewArena and returns true, + // provided no allocated blocks remain in the arena. + // If allocated blocks remain in the arena, does nothing and + // returns false. + // It is illegal to attempt to destroy the DefaultArena(). + static bool DeleteArena(Arena *arena); + + // The default arena that always exists. + static Arena *DefaultArena(); + + private: + LowLevelAlloc(); // no instances +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/low_level_scheduling.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/low_level_scheduling.h new file mode 100644 index 0000000000000000000000000000000000000000..961cc981b864aab613bcb573c4c36aff70b31efe --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/low_level_scheduling.h @@ -0,0 +1,107 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Core interfaces and definitions used by by low-level interfaces such as +// SpinLock. + +#ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ +#define ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ + +#include "absl/base/internal/scheduling_mode.h" +#include "absl/base/macros.h" + +// The following two declarations exist so SchedulingGuard may friend them with +// the appropriate language linkage. These callbacks allow libc internals, such +// as function level statics, to schedule cooperatively when locking. +extern "C" bool __google_disable_rescheduling(void); +extern "C" void __google_enable_rescheduling(bool disable_result); + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +class SchedulingHelper; // To allow use of SchedulingGuard. +class SpinLock; // To allow use of SchedulingGuard. + +// SchedulingGuard +// Provides guard semantics that may be used to disable cooperative rescheduling +// of the calling thread within specific program blocks. This is used to +// protect resources (e.g. low-level SpinLocks or Domain code) that cooperative +// scheduling depends on. +// +// Domain implementations capable of rescheduling in reaction to involuntary +// kernel thread actions (e.g blocking due to a pagefault or syscall) must +// guarantee that an annotated thread is not allowed to (cooperatively) +// reschedule until the annotated region is complete. +// +// It is an error to attempt to use a cooperatively scheduled resource (e.g. +// Mutex) within a rescheduling-disabled region. +// +// All methods are async-signal safe. +class SchedulingGuard { + public: + // Returns true iff the calling thread may be cooperatively rescheduled. + static bool ReschedulingIsAllowed(); + + private: + // Disable cooperative rescheduling of the calling thread. It may still + // initiate scheduling operations (e.g. wake-ups), however, it may not itself + // reschedule. Nestable. The returned result is opaque, clients should not + // attempt to interpret it. + // REQUIRES: Result must be passed to a pairing EnableScheduling(). + static bool DisableRescheduling(); + + // Marks the end of a rescheduling disabled region, previously started by + // DisableRescheduling(). + // REQUIRES: Pairs with innermost call (and result) of DisableRescheduling(). + static void EnableRescheduling(bool disable_result); + + // A scoped helper for {Disable, Enable}Rescheduling(). + // REQUIRES: destructor must run in same thread as constructor. + struct ScopedDisable { + ScopedDisable() { disabled = SchedulingGuard::DisableRescheduling(); } + ~ScopedDisable() { SchedulingGuard::EnableRescheduling(disabled); } + + bool disabled; + }; + + // Access to SchedulingGuard is explicitly white-listed. + friend class SchedulingHelper; + friend class SpinLock; + + SchedulingGuard(const SchedulingGuard&) = delete; + SchedulingGuard& operator=(const SchedulingGuard&) = delete; +}; + +//------------------------------------------------------------------------------ +// End of public interfaces. +//------------------------------------------------------------------------------ + +inline bool SchedulingGuard::ReschedulingIsAllowed() { + return false; +} + +inline bool SchedulingGuard::DisableRescheduling() { + return false; +} + +inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) { + return; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/per_thread_tls.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/per_thread_tls.h new file mode 100644 index 0000000000000000000000000000000000000000..cf5e97a0470eb57c15862e0c24195ebbf783ac12 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/per_thread_tls.h @@ -0,0 +1,52 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ +#define ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ + +// This header defines two macros: +// +// If the platform supports thread-local storage: +// +// * ABSL_PER_THREAD_TLS_KEYWORD is the C keyword needed to declare a +// thread-local variable +// * ABSL_PER_THREAD_TLS is 1 +// +// Otherwise: +// +// * ABSL_PER_THREAD_TLS_KEYWORD is empty +// * ABSL_PER_THREAD_TLS is 0 +// +// Microsoft C supports thread-local storage. +// GCC supports it if the appropriate version of glibc is available, +// which the programmer can indicate by defining ABSL_HAVE_TLS + +#include "absl/base/port.h" // For ABSL_HAVE_TLS + +#if defined(ABSL_PER_THREAD_TLS) +#error ABSL_PER_THREAD_TLS cannot be directly set +#elif defined(ABSL_PER_THREAD_TLS_KEYWORD) +#error ABSL_PER_THREAD_TLS_KEYWORD cannot be directly set +#elif defined(ABSL_HAVE_TLS) +#define ABSL_PER_THREAD_TLS_KEYWORD __thread +#define ABSL_PER_THREAD_TLS 1 +#elif defined(_MSC_VER) +#define ABSL_PER_THREAD_TLS_KEYWORD __declspec(thread) +#define ABSL_PER_THREAD_TLS 1 +#else +#define ABSL_PER_THREAD_TLS_KEYWORD +#define ABSL_PER_THREAD_TLS 0 +#endif + +#endif // ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/periodic_sampler.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/periodic_sampler.h new file mode 100644 index 0000000000000000000000000000000000000000..f8a86796b117976a957ab2279ff4d2b231034257 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/periodic_sampler.h @@ -0,0 +1,211 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ +#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ + +#include + +#include + +#include "absl/base/internal/exponential_biased.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// PeriodicSamplerBase provides the basic period sampler implementation. +// +// This is the base class for the templated PeriodicSampler class, which holds +// a global std::atomic value identified by a user defined tag, such that +// each specific PeriodSampler implementation holds its own global period. +// +// PeriodicSamplerBase is thread-compatible except where stated otherwise. +class PeriodicSamplerBase { + public: + // PeriodicSamplerBase is trivial / copyable / movable / destructible. + PeriodicSamplerBase() = default; + PeriodicSamplerBase(PeriodicSamplerBase&&) = default; + PeriodicSamplerBase(const PeriodicSamplerBase&) = default; + + // Returns true roughly once every `period` calls. This is established by a + // randomly picked `stride` that is counted down on each call to `Sample`. + // This stride is picked such that the probability of `Sample()` returning + // true is 1 in `period`. + inline bool Sample() noexcept; + + // The below methods are intended for optimized use cases where the + // size of the inlined fast path code is highly important. Applications + // should use the `Sample()` method unless they have proof that their + // specific use case requires the optimizations offered by these methods. + // + // An example of such a use case is SwissTable sampling. All sampling checks + // are in inlined SwissTable methods, and the number of call sites is huge. + // In this case, the inlined code size added to each translation unit calling + // SwissTable methods is non-trivial. + // + // The `SubtleMaybeSample()` function spuriously returns true even if the + // function should not be sampled, applications MUST match each call to + // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call, + // and use the result of the latter as the sampling decision. + // In other words: the code should logically be equivalent to: + // + // if (SubtleMaybeSample() && SubtleConfirmSample()) { + // // Sample this call + // } + // + // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can + // be placed out of line, for example, the typical use case looks as follows: + // + // // --- frobber.h ----------- + // void FrobberSampled(); + // + // inline void FrobberImpl() { + // // ... + // } + // + // inline void Frobber() { + // if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) { + // FrobberSampled(); + // } else { + // FrobberImpl(); + // } + // } + // + // // --- frobber.cc ----------- + // void FrobberSampled() { + // if (!sampler.SubtleConfirmSample())) { + // // Spurious false positive + // FrobberImpl(); + // return; + // } + // + // // Sampled execution + // // ... + // } + inline bool SubtleMaybeSample() noexcept; + bool SubtleConfirmSample() noexcept; + + protected: + // We explicitly don't use a virtual destructor as this class is never + // virtually destroyed, and it keeps the class trivial, which avoids TLS + // prologue and epilogue code for our TLS instances. + ~PeriodicSamplerBase() = default; + + // Returns the next stride for our sampler. + // This function is virtual for testing purposes only. + virtual int64_t GetExponentialBiased(int period) noexcept; + + private: + // Returns the current period of this sampler. Thread-safe. + virtual int period() const noexcept = 0; + + // Keep and decrement stride_ as an unsigned integer, but compare the value + // to zero casted as a signed int. clang and msvc do not create optimum code + // if we use signed for the combined decrement and sign comparison. + // + // Below 3 alternative options, all compiles generate the best code + // using the unsigned increment <---> signed int comparison option. + // + // Option 1: + // int64_t stride_; + // if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/R5MzzA + // GCC ppc (OK) : https://gcc.godbolt.org/z/z7NZAt + // Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd + // ICC x64 (OK) : https://gcc.godbolt.org/z/rE6s8W + // MSVC x64 (OK) : https://gcc.godbolt.org/z/ARMXqS + // + // Option 2: + // int64_t stride_ = 0; + // if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/jSQxYK + // GCC ppc (OK) : https://gcc.godbolt.org/z/VJdYaA + // Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX + // ICC x64 (OK) : https://gcc.godbolt.org/z/4snaFd + // MSVC x64 (BAD): https://gcc.godbolt.org/z/BgnEKE + // + // Option 3: + // uint64_t stride_; + // if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/bFbfPy + // GCC ppc (OK) : https://gcc.godbolt.org/z/S9KkUE + // Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4 + // ICC x64 (OK) : https://gcc.godbolt.org/z/ptTNfD + // MSVC x64 (OK) : https://gcc.godbolt.org/z/76j4-5 + uint64_t stride_ = 0; + ExponentialBiased rng_; +}; + +inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept { + // See comments on `stride_` for the unsigned increment / signed compare. + if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { + return false; + } + return true; +} + +inline bool PeriodicSamplerBase::Sample() noexcept { + return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample() + : false; +} + +// PeriodicSampler is a concreted periodic sampler implementation. +// The user provided Tag identifies the implementation, and is required to +// isolate the global state of this instance from other instances. +// +// Typical use case: +// +// struct HashTablezTag {}; +// thread_local PeriodicSampler sampler; +// +// void HashTableSamplingLogic(...) { +// if (sampler.Sample()) { +// HashTableSlowSamplePath(...); +// } +// } +// +template +class PeriodicSampler final : public PeriodicSamplerBase { + public: + ~PeriodicSampler() = default; + + int period() const noexcept final { + return period_.load(std::memory_order_relaxed); + } + + // Sets the global period for this sampler. Thread-safe. + // Setting a period of 0 disables the sampler, i.e., every call to Sample() + // will return false. Setting a period of 1 puts the sampler in 'always on' + // mode, i.e., every call to Sample() returns true. + static void SetGlobalPeriod(int period) { + period_.store(period, std::memory_order_relaxed); + } + + private: + static std::atomic period_; +}; + +template +std::atomic PeriodicSampler::period_(default_period); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/pretty_function.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/pretty_function.h new file mode 100644 index 0000000000000000000000000000000000000000..35d51676dc2e7e2996af900224b04b99ee75475a --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/pretty_function.h @@ -0,0 +1,33 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_ +#define ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_ + +// ABSL_PRETTY_FUNCTION +// +// In C++11, __func__ gives the undecorated name of the current function. That +// is, "main", not "int main()". Various compilers give extra macros to get the +// decorated function name, including return type and arguments, to +// differentiate between overload sets. ABSL_PRETTY_FUNCTION is a portable +// version of these macros which forwards to the correct macro on each compiler. +#if defined(_MSC_VER) +#define ABSL_PRETTY_FUNCTION __FUNCSIG__ +#elif defined(__GNUC__) +#define ABSL_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#error "Unsupported compiler" +#endif + +#endif // ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/raw_logging.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/raw_logging.h new file mode 100644 index 0000000000000000000000000000000000000000..418d6c856febceaca942d180c85010ec9acf42f8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/raw_logging.h @@ -0,0 +1,183 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Thread-safe logging routines that do not allocate any memory or +// acquire any locks, and can therefore be used by low-level memory +// allocation, synchronization, and signal-handling code. + +#ifndef ABSL_BASE_INTERNAL_RAW_LOGGING_H_ +#define ABSL_BASE_INTERNAL_RAW_LOGGING_H_ + +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/atomic_hook.h" +#include "absl/base/log_severity.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" + +// This is similar to LOG(severity) << format..., but +// * it is to be used ONLY by low-level modules that can't use normal LOG() +// * it is designed to be a low-level logger that does not allocate any +// memory and does not need any locks, hence: +// * it logs straight and ONLY to STDERR w/o buffering +// * it uses an explicit printf-format and arguments list +// * it will silently chop off really long message strings +// Usage example: +// ABSL_RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); +// This will print an almost standard log line like this to stderr only: +// E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file + +#define ABSL_RAW_LOG(severity, ...) \ + do { \ + constexpr const char* absl_raw_logging_internal_basename = \ + ::absl::raw_logging_internal::Basename(__FILE__, \ + sizeof(__FILE__) - 1); \ + ::absl::raw_logging_internal::RawLog(ABSL_RAW_LOGGING_INTERNAL_##severity, \ + absl_raw_logging_internal_basename, \ + __LINE__, __VA_ARGS__); \ + } while (0) + +// Similar to CHECK(condition) << message, but for low-level modules: +// we use only ABSL_RAW_LOG that does not allocate memory. +// We do not want to provide args list here to encourage this usage: +// if (!cond) ABSL_RAW_LOG(FATAL, "foo ...", hard_to_compute_args); +// so that the args are not computed when not needed. +#define ABSL_RAW_CHECK(condition, message) \ + do { \ + if (ABSL_PREDICT_FALSE(!(condition))) { \ + ABSL_RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \ + } \ + } while (0) + +// ABSL_INTERNAL_LOG and ABSL_INTERNAL_CHECK work like the RAW variants above, +// except that if the richer log library is linked into the binary, we dispatch +// to that instead. This is potentially useful for internal logging and +// assertions, where we are using RAW_LOG neither for its async-signal-safety +// nor for its non-allocating nature, but rather because raw logging has very +// few other dependencies. +// +// The API is a subset of the above: each macro only takes two arguments. Use +// StrCat if you need to build a richer message. +#define ABSL_INTERNAL_LOG(severity, message) \ + do { \ + ::absl::raw_logging_internal::internal_log_function( \ + ABSL_RAW_LOGGING_INTERNAL_##severity, __FILE__, __LINE__, message); \ + } while (0) + +#define ABSL_INTERNAL_CHECK(condition, message) \ + do { \ + if (ABSL_PREDICT_FALSE(!(condition))) { \ + std::string death_message = "Check " #condition " failed: "; \ + death_message += std::string(message); \ + ABSL_INTERNAL_LOG(FATAL, death_message); \ + } \ + } while (0) + +#define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo +#define ABSL_RAW_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning +#define ABSL_RAW_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError +#define ABSL_RAW_LOGGING_INTERNAL_FATAL ::absl::LogSeverity::kFatal +#define ABSL_RAW_LOGGING_INTERNAL_LEVEL(severity) \ + ::absl::NormalizeLogSeverity(severity) + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace raw_logging_internal { + +// Helper function to implement ABSL_RAW_LOG +// Logs format... at "severity" level, reporting it +// as called from file:line. +// This does not allocate memory or acquire locks. +void RawLog(absl::LogSeverity severity, const char* file, int line, + const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); + +// Writes the provided buffer directly to stderr, in a safe, low-level manner. +// +// In POSIX this means calling write(), which is async-signal safe and does +// not malloc. If the platform supports the SYS_write syscall, we invoke that +// directly to side-step any libc interception. +void SafeWriteToStderr(const char *s, size_t len); + +// compile-time function to get the "base" filename, that is, the part of +// a filename after the last "/" or "\" path separator. The search starts at +// the end of the string; the second parameter is the length of the string. +constexpr const char* Basename(const char* fname, int offset) { + return offset == 0 || fname[offset - 1] == '/' || fname[offset - 1] == '\\' + ? fname + offset + : Basename(fname, offset - 1); +} + +// For testing only. +// Returns true if raw logging is fully supported. When it is not +// fully supported, no messages will be emitted, but a log at FATAL +// severity will cause an abort. +// +// TODO(gfalcon): Come up with a better name for this method. +bool RawLoggingFullySupported(); + +// Function type for a raw_logging customization hook for suppressing messages +// by severity, and for writing custom prefixes on non-suppressed messages. +// +// The installed hook is called for every raw log invocation. The message will +// be logged to stderr only if the hook returns true. FATAL errors will cause +// the process to abort, even if writing to stderr is suppressed. The hook is +// also provided with an output buffer, where it can write a custom log message +// prefix. +// +// The raw_logging system does not allocate memory or grab locks. User-provided +// hooks must avoid these operations, and must not throw exceptions. +// +// 'severity' is the severity level of the message being written. +// 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro +// was located. +// 'buffer' and 'buf_size' are pointers to the buffer and buffer size. If the +// hook writes a prefix, it must increment *buffer and decrement *buf_size +// accordingly. +using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file, + int line, char** buffer, int* buf_size); + +// Function type for a raw_logging customization hook called to abort a process +// when a FATAL message is logged. If the provided AbortHook() returns, the +// logging system will call abort(). +// +// 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro +// was located. +// The NUL-terminated logged message lives in the buffer between 'buf_start' +// and 'buf_end'. 'prefix_end' points to the first non-prefix character of the +// buffer (as written by the LogPrefixHook.) +using AbortHook = void (*)(const char* file, int line, const char* buf_start, + const char* prefix_end, const char* buf_end); + +// Internal logging function for ABSL_INTERNAL_LOG to dispatch to. +// +// TODO(gfalcon): When string_view no longer depends on base, change this +// interface to take its message as a string_view instead. +using InternalLogFunction = void (*)(absl::LogSeverity severity, + const char* file, int line, + const std::string& message); + +ABSL_DLL ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES extern base_internal::AtomicHook< + InternalLogFunction> + internal_log_function; + +void RegisterInternalLogFunction(InternalLogFunction func); + +} // namespace raw_logging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_RAW_LOGGING_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/scheduling_mode.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/scheduling_mode.h new file mode 100644 index 0000000000000000000000000000000000000000..8be5ab6dd3c00bd61ce847892e8434e467478fe8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/scheduling_mode.h @@ -0,0 +1,58 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Core interfaces and definitions used by by low-level interfaces such as +// SpinLock. + +#ifndef ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ +#define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Used to describe how a thread may be scheduled. Typically associated with +// the declaration of a resource supporting synchronized access. +// +// SCHEDULE_COOPERATIVE_AND_KERNEL: +// Specifies that when waiting, a cooperative thread (e.g. a Fiber) may +// reschedule (using base::scheduling semantics); allowing other cooperative +// threads to proceed. +// +// SCHEDULE_KERNEL_ONLY: (Also described as "non-cooperative") +// Specifies that no cooperative scheduling semantics may be used, even if the +// current thread is itself cooperatively scheduled. This means that +// cooperative threads will NOT allow other cooperative threads to execute in +// their place while waiting for a resource of this type. Host operating system +// semantics (e.g. a futex) may still be used. +// +// When optional, clients should strongly prefer SCHEDULE_COOPERATIVE_AND_KERNEL +// by default. SCHEDULE_KERNEL_ONLY should only be used for resources on which +// base::scheduling (e.g. the implementation of a Scheduler) may depend. +// +// NOTE: Cooperative resources may not be nested below non-cooperative ones. +// This means that it is invalid to to acquire a SCHEDULE_COOPERATIVE_AND_KERNEL +// resource if a SCHEDULE_KERNEL_ONLY resource is already held. +enum SchedulingMode { + SCHEDULE_KERNEL_ONLY = 0, // Allow scheduling only the host OS. + SCHEDULE_COOPERATIVE_AND_KERNEL, // Also allow cooperative scheduling. +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/scoped_set_env.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/scoped_set_env.h new file mode 100644 index 0000000000000000000000000000000000000000..19ec7b5d8a04a05ff62d31d8730a34f662829757 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/scoped_set_env.h @@ -0,0 +1,45 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ +#define ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +class ScopedSetEnv { + public: + ScopedSetEnv(const char* var_name, const char* new_value); + ~ScopedSetEnv(); + + private: + std::string var_name_; + std::string old_value_; + + // True if the environment variable was initially not set. + bool was_unset_; +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock.h new file mode 100644 index 0000000000000000000000000000000000000000..24e2e9a6f8203848b3591c58d7d01cdaa61e631f --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock.h @@ -0,0 +1,243 @@ +// +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// + +// Most users requiring mutual exclusion should use Mutex. +// SpinLock is provided for use in three situations: +// - for use in code that Mutex itself depends on +// - to get a faster fast-path release under low contention (without an +// atomic read-modify-write) In return, SpinLock has worse behaviour under +// contention, which is why Mutex is preferred in most situations. +// - for async signal safety (see below) + +// SpinLock is async signal safe. If a spinlock is used within a signal +// handler, all code that acquires the lock must ensure that the signal cannot +// arrive while they are holding the lock. Typically, this is done by blocking +// the signal. + +#ifndef ABSL_BASE_INTERNAL_SPINLOCK_H_ +#define ABSL_BASE_INTERNAL_SPINLOCK_H_ + +#include +#include + +#include + +#include "absl/base/attributes.h" +#include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/low_level_scheduling.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/scheduling_mode.h" +#include "absl/base/internal/tsan_mutex_interface.h" +#include "absl/base/macros.h" +#include "absl/base/port.h" +#include "absl/base/thread_annotations.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +class ABSL_LOCKABLE SpinLock { + public: + SpinLock() : lockword_(kSpinLockCooperative) { + ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); + } + + // Special constructor for use with static SpinLock objects. E.g., + // + // static SpinLock lock(base_internal::kLinkerInitialized); + // + // When initialized using this constructor, we depend on the fact + // that the linker has already initialized the memory appropriately. The lock + // is initialized in non-cooperative mode. + // + // A SpinLock constructed like this can be freely used from global + // initializers without worrying about the order in which global + // initializers run. + explicit SpinLock(base_internal::LinkerInitialized) { + // Does nothing; lockword_ is already initialized + ABSL_TSAN_MUTEX_CREATE(this, 0); + } + + // Constructors that allow non-cooperative spinlocks to be created for use + // inside thread schedulers. Normal clients should not use these. + explicit SpinLock(base_internal::SchedulingMode mode); + SpinLock(base_internal::LinkerInitialized, + base_internal::SchedulingMode mode); + + ~SpinLock() { ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static); } + + // Acquire this SpinLock. + inline void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { + ABSL_TSAN_MUTEX_PRE_LOCK(this, 0); + if (!TryLockImpl()) { + SlowLock(); + } + ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0); + } + + // Try to acquire this SpinLock without blocking and return true if the + // acquisition was successful. If the lock was not acquired, false is + // returned. If this SpinLock is free at the time of the call, TryLock + // will return true with high probability. + inline bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) { + ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock); + bool res = TryLockImpl(); + ABSL_TSAN_MUTEX_POST_LOCK( + this, __tsan_mutex_try_lock | (res ? 0 : __tsan_mutex_try_lock_failed), + 0); + return res; + } + + // Release this SpinLock, which must be held by the calling thread. + inline void Unlock() ABSL_UNLOCK_FUNCTION() { + ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0); + uint32_t lock_value = lockword_.load(std::memory_order_relaxed); + lock_value = lockword_.exchange(lock_value & kSpinLockCooperative, + std::memory_order_release); + + if ((lock_value & kSpinLockDisabledScheduling) != 0) { + base_internal::SchedulingGuard::EnableRescheduling(true); + } + if ((lock_value & kWaitTimeMask) != 0) { + // Collect contentionz profile info, and speed the wakeup of any waiter. + // The wait_cycles value indicates how long this thread spent waiting + // for the lock. + SlowUnlock(lock_value); + } + ABSL_TSAN_MUTEX_POST_UNLOCK(this, 0); + } + + // Determine if the lock is held. When the lock is held by the invoking + // thread, true will always be returned. Intended to be used as + // CHECK(lock.IsHeld()). + inline bool IsHeld() const { + return (lockword_.load(std::memory_order_relaxed) & kSpinLockHeld) != 0; + } + + protected: + // These should not be exported except for testing. + + // Store number of cycles between wait_start_time and wait_end_time in a + // lock value. + static uint32_t EncodeWaitCycles(int64_t wait_start_time, + int64_t wait_end_time); + + // Extract number of wait cycles in a lock value. + static uint64_t DecodeWaitCycles(uint32_t lock_value); + + // Provide access to protected method above. Use for testing only. + friend struct SpinLockTest; + + private: + // lockword_ is used to store the following: + // + // bit[0] encodes whether a lock is being held. + // bit[1] encodes whether a lock uses cooperative scheduling. + // bit[2] encodes whether a lock disables scheduling. + // bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int. + enum { kSpinLockHeld = 1 }; + enum { kSpinLockCooperative = 2 }; + enum { kSpinLockDisabledScheduling = 4 }; + enum { kSpinLockSleeper = 8 }; + enum { kWaitTimeMask = // Includes kSpinLockSleeper. + ~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling) }; + + // Returns true if the provided scheduling mode is cooperative. + static constexpr bool IsCooperative( + base_internal::SchedulingMode scheduling_mode) { + return scheduling_mode == base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL; + } + + uint32_t TryLockInternal(uint32_t lock_value, uint32_t wait_cycles); + void InitLinkerInitializedAndCooperative(); + void SlowLock() ABSL_ATTRIBUTE_COLD; + void SlowUnlock(uint32_t lock_value) ABSL_ATTRIBUTE_COLD; + uint32_t SpinLoop(); + + inline bool TryLockImpl() { + uint32_t lock_value = lockword_.load(std::memory_order_relaxed); + return (TryLockInternal(lock_value, 0) & kSpinLockHeld) == 0; + } + + std::atomic lockword_; + + SpinLock(const SpinLock&) = delete; + SpinLock& operator=(const SpinLock&) = delete; +}; + +// Corresponding locker object that arranges to acquire a spinlock for +// the duration of a C++ scope. +class ABSL_SCOPED_LOCKABLE SpinLockHolder { + public: + inline explicit SpinLockHolder(SpinLock* l) ABSL_EXCLUSIVE_LOCK_FUNCTION(l) + : lock_(l) { + l->Lock(); + } + inline ~SpinLockHolder() ABSL_UNLOCK_FUNCTION() { lock_->Unlock(); } + + SpinLockHolder(const SpinLockHolder&) = delete; + SpinLockHolder& operator=(const SpinLockHolder&) = delete; + + private: + SpinLock* lock_; +}; + +// Register a hook for profiling support. +// +// The function pointer registered here will be called whenever a spinlock is +// contended. The callback is given an opaque handle to the contended spinlock +// and the number of wait cycles. This is thread-safe, but only a single +// profiler can be registered. It is an error to call this function multiple +// times with different arguments. +void RegisterSpinLockProfiler(void (*fn)(const void* lock, + int64_t wait_cycles)); + +//------------------------------------------------------------------------------ +// Public interface ends here. +//------------------------------------------------------------------------------ + +// If (result & kSpinLockHeld) == 0, then *this was successfully locked. +// Otherwise, returns last observed value for lockword_. +inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value, + uint32_t wait_cycles) { + if ((lock_value & kSpinLockHeld) != 0) { + return lock_value; + } + + uint32_t sched_disabled_bit = 0; + if ((lock_value & kSpinLockCooperative) == 0) { + // For non-cooperative locks we must make sure we mark ourselves as + // non-reschedulable before we attempt to CompareAndSwap. + if (base_internal::SchedulingGuard::DisableRescheduling()) { + sched_disabled_bit = kSpinLockDisabledScheduling; + } + } + + if (!lockword_.compare_exchange_strong( + lock_value, + kSpinLockHeld | lock_value | wait_cycles | sched_disabled_bit, + std::memory_order_acquire, std::memory_order_relaxed)) { + base_internal::SchedulingGuard::EnableRescheduling(sched_disabled_bit != 0); + } + + return lock_value; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_SPINLOCK_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_akaros.inc b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_akaros.inc new file mode 100644 index 0000000000000000000000000000000000000000..bc468940fc5dee4c25786c32f783bd4680f04b21 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_akaros.inc @@ -0,0 +1,35 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is an Akaros-specific part of spinlock_wait.cc + +#include + +#include "absl/base/internal/scheduling_mode.h" + +extern "C" { + +ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( + std::atomic* /* lock_word */, uint32_t /* value */, + int /* loop */, absl::base_internal::SchedulingMode /* mode */) { + // In Akaros, one must take care not to call anything that could cause a + // malloc(), a blocking system call, or a uthread_yield() while holding a + // spinlock. Our callers assume will not call into libraries or other + // arbitrary code. +} + +ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake( + std::atomic* /* lock_word */, bool /* all */) {} + +} // extern "C" diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_linux.inc b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_linux.inc new file mode 100644 index 0000000000000000000000000000000000000000..323edd62f4642b657e539aff00d99a6f2f2720b7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_linux.inc @@ -0,0 +1,66 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a Linux-specific part of spinlock_wait.cc + +#include +#include +#include + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/internal/errno_saver.h" + +// The SpinLock lockword is `std::atomic`. Here we assert that +// `std::atomic` is bitwise equivalent of the `int` expected +// by SYS_futex. We also assume that reads/writes done to the lockword +// by SYS_futex have rational semantics with regard to the +// std::atomic<> API. C++ provides no guarantees of these assumptions, +// but they are believed to hold in practice. +static_assert(sizeof(std::atomic) == sizeof(int), + "SpinLock lockword has the wrong size for a futex"); + +// Some Android headers are missing these definitions even though they +// support these futex operations. +#ifdef __BIONIC__ +#ifndef SYS_futex +#define SYS_futex __NR_futex +#endif +#ifndef FUTEX_PRIVATE_FLAG +#define FUTEX_PRIVATE_FLAG 128 +#endif +#endif + +extern "C" { + +ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( + std::atomic *w, uint32_t value, int loop, + absl::base_internal::SchedulingMode) { + absl::base_internal::ErrnoSaver errno_saver; + struct timespec tm; + tm.tv_sec = 0; + tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop); + syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm); +} + +ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic *w, + bool all) { + syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0); +} + +} // extern "C" diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_posix.inc b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_posix.inc new file mode 100644 index 0000000000000000000000000000000000000000..fcd21b151b6e2fa626e97a468662b9995d3620c9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_posix.inc @@ -0,0 +1,46 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a Posix-specific part of spinlock_wait.cc + +#include + +#include +#include + +#include "absl/base/internal/errno_saver.h" +#include "absl/base/internal/scheduling_mode.h" +#include "absl/base/port.h" + +extern "C" { + +ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( + std::atomic* /* lock_word */, uint32_t /* value */, int loop, + absl::base_internal::SchedulingMode /* mode */) { + absl::base_internal::ErrnoSaver errno_saver; + if (loop == 0) { + } else if (loop == 1) { + sched_yield(); + } else { + struct timespec tm; + tm.tv_sec = 0; + tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop); + nanosleep(&tm, nullptr); + } +} + +ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake( + std::atomic* /* lock_word */, bool /* all */) {} + +} // extern "C" diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_wait.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_wait.h new file mode 100644 index 0000000000000000000000000000000000000000..169bc749fbc55c9cce5e1129d857f3eec4c1427d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_wait.h @@ -0,0 +1,93 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ +#define ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ + +// Operations to make atomic transitions on a word, and to allow +// waiting for those transitions to become possible. + +#include +#include + +#include "absl/base/internal/scheduling_mode.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// SpinLockWait() waits until it can perform one of several transitions from +// "from" to "to". It returns when it performs a transition where done==true. +struct SpinLockWaitTransition { + uint32_t from; + uint32_t to; + bool done; +}; + +// Wait until *w can transition from trans[i].from to trans[i].to for some i +// satisfying 0<=i *w, int n, + const SpinLockWaitTransition trans[], + SchedulingMode scheduling_mode); + +// If possible, wake some thread that has called SpinLockDelay(w, ...). If +// "all" is true, wake all such threads. This call is a hint, and on some +// systems it may be a no-op; threads calling SpinLockDelay() will always wake +// eventually even if SpinLockWake() is never called. +void SpinLockWake(std::atomic *w, bool all); + +// Wait for an appropriate spin delay on iteration "loop" of a +// spin loop on location *w, whose previously observed value was "value". +// SpinLockDelay() may do nothing, may yield the CPU, may sleep a clock tick, +// or may wait for a delay that can be truncated by a call to SpinLockWake(w). +// In all cases, it must return in bounded time even if SpinLockWake() is not +// called. +void SpinLockDelay(std::atomic *w, uint32_t value, int loop, + base_internal::SchedulingMode scheduling_mode); + +// Helper used by AbslInternalSpinLockDelay. +// Returns a suggested delay in nanoseconds for iteration number "loop". +int SpinLockSuggestedDelayNS(int loop); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +// In some build configurations we pass --detect-odr-violations to the +// gold linker. This causes it to flag weak symbol overrides as ODR +// violations. Because ODR only applies to C++ and not C, +// --detect-odr-violations ignores symbols not mangled with C++ names. +// By changing our extension points to be extern "C", we dodge this +// check. +extern "C" { +void AbslInternalSpinLockWake(std::atomic *w, bool all); +void AbslInternalSpinLockDelay( + std::atomic *w, uint32_t value, int loop, + absl::base_internal::SchedulingMode scheduling_mode); +} + +inline void absl::base_internal::SpinLockWake(std::atomic *w, + bool all) { + AbslInternalSpinLockWake(w, all); +} + +inline void absl::base_internal::SpinLockDelay( + std::atomic *w, uint32_t value, int loop, + absl::base_internal::SchedulingMode scheduling_mode) { + AbslInternalSpinLockDelay(w, value, loop, scheduling_mode); +} + +#endif // ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_win32.inc b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_win32.inc new file mode 100644 index 0000000000000000000000000000000000000000..78654b5b5966c945e004556d312593cacc674e0f --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/spinlock_win32.inc @@ -0,0 +1,37 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a Win32-specific part of spinlock_wait.cc + +#include +#include +#include "absl/base/internal/scheduling_mode.h" + +extern "C" { + +void AbslInternalSpinLockDelay(std::atomic* /* lock_word */, + uint32_t /* value */, int loop, + absl::base_internal::SchedulingMode /* mode */) { + if (loop == 0) { + } else if (loop == 1) { + Sleep(0); + } else { + Sleep(absl::base_internal::SpinLockSuggestedDelayNS(loop) / 1000000); + } +} + +void AbslInternalSpinLockWake(std::atomic* /* lock_word */, + bool /* all */) {} + +} // extern "C" diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/sysinfo.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/sysinfo.h new file mode 100644 index 0000000000000000000000000000000000000000..7246d5dd95c3f48d89e9a0137fd0df708390c59e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/sysinfo.h @@ -0,0 +1,66 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file includes routines to find out characteristics +// of the machine a program is running on. It is undoubtedly +// system-dependent. + +// Functions listed here that accept a pid_t as an argument act on the +// current process if the pid_t argument is 0 +// All functions here are thread-hostile due to file caching unless +// commented otherwise. + +#ifndef ABSL_BASE_INTERNAL_SYSINFO_H_ +#define ABSL_BASE_INTERNAL_SYSINFO_H_ + +#ifndef _WIN32 +#include +#endif + +#include + +#include "absl/base/port.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Nominal core processor cycles per second of each processor. This is _not_ +// necessarily the frequency of the CycleClock counter (see cycleclock.h) +// Thread-safe. +double NominalCPUFrequency(); + +// Number of logical processors (hyperthreads) in system. Thread-safe. +int NumCPUs(); + +// Return the thread id of the current thread, as told by the system. +// No two currently-live threads implemented by the OS shall have the same ID. +// Thread ids of exited threads may be reused. Multiple user-level threads +// may have the same thread ID if multiplexed on the same OS thread. +// +// On Linux, you may send a signal to the resulting ID with kill(). However, +// it is recommended for portability that you use pthread_kill() instead. +#ifdef _WIN32 +// On Windows, process id and thread id are of the same type according to the +// return types of GetProcessId() and GetThreadId() are both DWORD, an unsigned +// 32-bit type. +using pid_t = uint32_t; +#endif +pid_t GetTID(); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_SYSINFO_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/thread_annotations.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/thread_annotations.h new file mode 100644 index 0000000000000000000000000000000000000000..4dab6a9c150a5401053731fe40e9052fd05ed3f8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/thread_annotations.h @@ -0,0 +1,271 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: thread_annotations.h +// ----------------------------------------------------------------------------- +// +// WARNING: This is a backwards compatible header and it will be removed after +// the migration to prefixed thread annotations is finished; please include +// "absl/base/thread_annotations.h". +// +// This header file contains macro definitions for thread safety annotations +// that allow developers to document the locking policies of multi-threaded +// code. The annotations can also help program analysis tools to identify +// potential thread safety issues. +// +// These annotations are implemented using compiler attributes. Using the macros +// defined here instead of raw attributes allow for portability and future +// compatibility. +// +// When referring to mutexes in the arguments of the attributes, you should +// use variable names or more complex expressions (e.g. my_object->mutex_) +// that evaluate to a concrete mutex object whenever possible. If the mutex +// you want to refer to is not in scope, you may use a member pointer +// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object. + +#ifndef ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ +#define ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ + +#if defined(__clang__) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +// GUARDED_BY() +// +// Documents if a shared field or global variable needs to be protected by a +// mutex. GUARDED_BY() allows the user to specify a particular mutex that +// should be held when accessing the annotated variable. +// +// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to +// local variables, a local variable and its associated mutex can often be +// combined into a small class or struct, thereby allowing the annotation. +// +// Example: +// +// class Foo { +// Mutex mu_; +// int p1_ GUARDED_BY(mu_); +// ... +// }; +#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +// PT_GUARDED_BY() +// +// Documents if the memory location pointed to by a pointer should be guarded +// by a mutex when dereferencing the pointer. +// +// Example: +// class Foo { +// Mutex mu_; +// int *p1_ PT_GUARDED_BY(mu_); +// ... +// }; +// +// Note that a pointer variable to a shared memory location could itself be a +// shared variable. +// +// Example: +// +// // `q_`, guarded by `mu1_`, points to a shared memory location that is +// // guarded by `mu2_`: +// int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_); +#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +// ACQUIRED_AFTER() / ACQUIRED_BEFORE() +// +// Documents the acquisition order between locks that can be held +// simultaneously by a thread. For any two locks that need to be annotated +// to establish an acquisition order, only one of them needs the annotation. +// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER +// and ACQUIRED_BEFORE.) +// +// As with GUARDED_BY, this is only applicable to mutexes that are shared +// fields or global variables. +// +// Example: +// +// Mutex m1_; +// Mutex m2_ ACQUIRED_AFTER(m1_); +#define ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +#define ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED() +// +// Documents a function that expects a mutex to be held prior to entry. +// The mutex is expected to be held both on entry to, and exit from, the +// function. +// +// An exclusive lock allows read-write access to the guarded data member(s), and +// only one thread can acquire a lock exclusively at any one time. A shared lock +// allows read-only access, and any number of threads can acquire a shared lock +// concurrently. +// +// Generally, non-const methods should be annotated with +// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with +// SHARED_LOCKS_REQUIRED. +// +// Example: +// +// Mutex mu1, mu2; +// int a GUARDED_BY(mu1); +// int b GUARDED_BY(mu2); +// +// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } +// void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } +#define EXCLUSIVE_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) + +#define SHARED_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) + +// LOCKS_EXCLUDED() +// +// Documents the locks acquired in the body of the function. These locks +// cannot be held when calling this function (as Abseil's `Mutex` locks are +// non-reentrant). +#define LOCKS_EXCLUDED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +// LOCK_RETURNED() +// +// Documents a function that returns a mutex without acquiring it. For example, +// a public getter method that returns a pointer to a private mutex should +// be annotated with LOCK_RETURNED. +#define LOCK_RETURNED(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +// LOCKABLE +// +// Documents if a class/type is a lockable type (such as the `Mutex` class). +#define LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(lockable) + +// SCOPED_LOCKABLE +// +// Documents if a class does RAII locking (such as the `MutexLock` class). +// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is +// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no +// arguments; the analysis will assume that the destructor unlocks whatever the +// constructor locked. +#define SCOPED_LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +// EXCLUSIVE_LOCK_FUNCTION() +// +// Documents functions that acquire a lock in the body of a function, and do +// not release it. +#define EXCLUSIVE_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) + +// SHARED_LOCK_FUNCTION() +// +// Documents functions that acquire a shared (reader) lock in the body of a +// function, and do not release it. +#define SHARED_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) + +// UNLOCK_FUNCTION() +// +// Documents functions that expect a lock to be held on entry to the function, +// and release it in the body of the function. +#define UNLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) + +// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION() +// +// Documents functions that try to acquire a lock, and return success or failure +// (or a non-boolean value that can be interpreted as a boolean). +// The first argument should be `true` for functions that return `true` on +// success, or `false` for functions that return `false` on success. The second +// argument specifies the mutex that is locked on success. If unspecified, this +// mutex is assumed to be `this`. +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) + +#define SHARED_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) + +// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK() +// +// Documents functions that dynamically check to see if a lock is held, and fail +// if it is not held. +#define ASSERT_EXCLUSIVE_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) + +#define ASSERT_SHARED_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) + +// NO_THREAD_SAFETY_ANALYSIS +// +// Turns off thread safety checking within the body of a particular function. +// This annotation is used to mark functions that are known to be correct, but +// the locking behavior is more complicated than the analyzer can handle. +#define NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +//------------------------------------------------------------------------------ +// Tool-Supplied Annotations +//------------------------------------------------------------------------------ + +// TS_UNCHECKED should be placed around lock expressions that are not valid +// C++ syntax, but which are present for documentation purposes. These +// annotations will be ignored by the analysis. +#define TS_UNCHECKED(x) "" + +// TS_FIXME is used to mark lock expressions that are not valid C++ syntax. +// It is used by automated tools to mark and disable invalid expressions. +// The annotation should either be fixed, or changed to TS_UNCHECKED. +#define TS_FIXME(x) "" + +// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of +// a particular function. However, this attribute is used to mark functions +// that are incorrect and need to be fixed. It is used by automated tools to +// avoid breaking the build when the analysis is updated. +// Code owners are expected to eventually fix the routine. +#define NO_THREAD_SAFETY_ANALYSIS_FIXME NO_THREAD_SAFETY_ANALYSIS + +// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY +// annotation that needs to be fixed, because it is producing thread safety +// warning. It disables the GUARDED_BY. +#define GUARDED_BY_FIXME(x) + +// Disables warnings for a single read operation. This can be used to avoid +// warnings when it is known that the read is not actually involved in a race, +// but the compiler cannot confirm that. +#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x) + + +namespace thread_safety_analysis { + +// Takes a reference to a guarded data member, and returns an unguarded +// reference. +template +inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +template +inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +} // namespace thread_safety_analysis + +#endif // ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/thread_identity.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/thread_identity.h new file mode 100644 index 0000000000000000000000000000000000000000..ceb109b41c6a19fb0b3e915398ea3e9880732062 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/thread_identity.h @@ -0,0 +1,259 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Each active thread has an ThreadIdentity that may represent the thread in +// various level interfaces. ThreadIdentity objects are never deallocated. +// When a thread terminates, its ThreadIdentity object may be reused for a +// thread created later. + +#ifndef ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ +#define ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ + +#ifndef _WIN32 +#include +// Defines __GOOGLE_GRTE_VERSION__ (via glibc-specific features.h) when +// supported. +#include +#endif + +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/per_thread_tls.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +struct SynchLocksHeld; +struct SynchWaitParams; + +namespace base_internal { + +class SpinLock; +struct ThreadIdentity; + +// Used by the implementation of absl::Mutex and absl::CondVar. +struct PerThreadSynch { + // The internal representation of absl::Mutex and absl::CondVar rely + // on the alignment of PerThreadSynch. Both store the address of the + // PerThreadSynch in the high-order bits of their internal state, + // which means the low kLowZeroBits of the address of PerThreadSynch + // must be zero. + static constexpr int kLowZeroBits = 8; + static constexpr int kAlignment = 1 << kLowZeroBits; + + // Returns the associated ThreadIdentity. + // This can be implemented as a cast because we guarantee + // PerThreadSynch is the first element of ThreadIdentity. + ThreadIdentity* thread_identity() { + return reinterpret_cast(this); + } + + PerThreadSynch *next; // Circular waiter queue; initialized to 0. + PerThreadSynch *skip; // If non-zero, all entries in Mutex queue + // up to and including "skip" have same + // condition as this, and will be woken later + bool may_skip; // if false while on mutex queue, a mutex unlocker + // is using this PerThreadSynch as a terminator. Its + // skip field must not be filled in because the loop + // might then skip over the terminator. + + // The wait parameters of the current wait. waitp is null if the + // thread is not waiting. Transitions from null to non-null must + // occur before the enqueue commit point (state = kQueued in + // Enqueue() and CondVarEnqueue()). Transitions from non-null to + // null must occur after the wait is finished (state = kAvailable in + // Mutex::Block() and CondVar::WaitCommon()). This field may be + // changed only by the thread that describes this PerThreadSynch. A + // special case is Fer(), which calls Enqueue() on another thread, + // but with an identical SynchWaitParams pointer, thus leaving the + // pointer unchanged. + SynchWaitParams *waitp; + + bool suppress_fatal_errors; // If true, try to proceed even in the face of + // broken invariants. This is used within fatal + // signal handlers to improve the chances of + // debug logging information being output + // successfully. + + intptr_t readers; // Number of readers in mutex. + int priority; // Priority of thread (updated every so often). + + // When priority will next be read (cycles). + int64_t next_priority_read_cycles; + + // State values: + // kAvailable: This PerThreadSynch is available. + // kQueued: This PerThreadSynch is unavailable, it's currently queued on a + // Mutex or CondVar waistlist. + // + // Transitions from kQueued to kAvailable require a release + // barrier. This is needed as a waiter may use "state" to + // independently observe that it's no longer queued. + // + // Transitions from kAvailable to kQueued require no barrier, they + // are externally ordered by the Mutex. + enum State { + kAvailable, + kQueued + }; + std::atomic state; + + bool maybe_unlocking; // Valid at head of Mutex waiter queue; + // true if UnlockSlow could be searching + // for a waiter to wake. Used for an optimization + // in Enqueue(). true is always a valid value. + // Can be reset to false when the unlocker or any + // writer releases the lock, or a reader fully releases + // the lock. It may not be set to false by a reader + // that decrements the count to non-zero. + // protected by mutex spinlock + + bool wake; // This thread is to be woken from a Mutex. + + // If "x" is on a waiter list for a mutex, "x->cond_waiter" is true iff the + // waiter is waiting on the mutex as part of a CV Wait or Mutex Await. + // + // The value of "x->cond_waiter" is meaningless if "x" is not on a + // Mutex waiter list. + bool cond_waiter; + + // Locks held; used during deadlock detection. + // Allocated in Synch_GetAllLocks() and freed in ReclaimThreadIdentity(). + SynchLocksHeld *all_locks; +}; + +struct ThreadIdentity { + // Must be the first member. The Mutex implementation requires that + // the PerThreadSynch object associated with each thread is + // PerThreadSynch::kAlignment aligned. We provide this alignment on + // ThreadIdentity itself. + PerThreadSynch per_thread_synch; + + // Private: Reserved for absl::synchronization_internal::Waiter. + struct WaiterState { + char data[128]; + } waiter_state; + + // Used by PerThreadSem::{Get,Set}ThreadBlockedCounter(). + std::atomic* blocked_count_ptr; + + // The following variables are mostly read/written just by the + // thread itself. The only exception is that these are read by + // a ticker thread as a hint. + std::atomic ticker; // Tick counter, incremented once per second. + std::atomic wait_start; // Ticker value when thread started waiting. + std::atomic is_idle; // Has thread become idle yet? + + ThreadIdentity* next; +}; + +// Returns the ThreadIdentity object representing the calling thread; guaranteed +// to be unique for its lifetime. The returned object will remain valid for the +// program's lifetime; although it may be re-assigned to a subsequent thread. +// If one does not exist, return nullptr instead. +// +// Does not malloc(*), and is async-signal safe. +// [*] Technically pthread_setspecific() does malloc on first use; however this +// is handled internally within tcmalloc's initialization already. +// +// New ThreadIdentity objects can be constructed and associated with a thread +// by calling GetOrCreateCurrentThreadIdentity() in per-thread-sem.h. +ThreadIdentity* CurrentThreadIdentityIfPresent(); + +using ThreadIdentityReclaimerFunction = void (*)(void*); + +// Sets the current thread identity to the given value. 'reclaimer' is a +// pointer to the global function for cleaning up instances on thread +// destruction. +void SetCurrentThreadIdentity(ThreadIdentity* identity, + ThreadIdentityReclaimerFunction reclaimer); + +// Removes the currently associated ThreadIdentity from the running thread. +// This must be called from inside the ThreadIdentityReclaimerFunction, and only +// from that function. +void ClearCurrentThreadIdentity(); + +// May be chosen at compile time via: -DABSL_FORCE_THREAD_IDENTITY_MODE= +#ifdef ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC +#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be direcly set +#else +#define ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 0 +#endif + +#ifdef ABSL_THREAD_IDENTITY_MODE_USE_TLS +#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be direcly set +#else +#define ABSL_THREAD_IDENTITY_MODE_USE_TLS 1 +#endif + +#ifdef ABSL_THREAD_IDENTITY_MODE_USE_CPP11 +#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be direcly set +#else +#define ABSL_THREAD_IDENTITY_MODE_USE_CPP11 2 +#endif + +#ifdef ABSL_THREAD_IDENTITY_MODE +#error ABSL_THREAD_IDENTITY_MODE cannot be direcly set +#elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE) +#define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE +#elif defined(_WIN32) && !defined(__MINGW32__) +#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11 +#elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) && \ + (__GOOGLE_GRTE_VERSION__ >= 20140228L) +// Support for async-safe TLS was specifically added in GRTEv4. It's not +// present in the upstream eglibc. +// Note: Current default for production systems. +#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_TLS +#else +#define ABSL_THREAD_IDENTITY_MODE \ + ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC +#endif + +#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ + ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 + +#if ABSL_PER_THREAD_TLS +ABSL_CONST_INIT extern ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* + thread_identity_ptr; +#elif defined(ABSL_HAVE_THREAD_LOCAL) +ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr; +#else +#error Thread-local storage not detected on this platform +#endif + +// thread_local variables cannot be in headers exposed by DLLs. However, it is +// important for performance reasons in general that +// `CurrentThreadIdentityIfPresent` be inlined. This is not possible across a +// DLL boundary so, with DLLs, we opt to have the function not be inlined. Note +// that `CurrentThreadIdentityIfPresent` is declared above so we can exclude +// this entire inline definition when compiling as a DLL. +#if !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL) +inline ThreadIdentity* CurrentThreadIdentityIfPresent() { + return thread_identity_ptr; +} +#endif + +#elif ABSL_THREAD_IDENTITY_MODE != \ + ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC +#error Unknown ABSL_THREAD_IDENTITY_MODE +#endif + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/throw_delegate.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/throw_delegate.h new file mode 100644 index 0000000000000000000000000000000000000000..075f5272543ab64d8c2e9a5ef7047c60f807f504 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/throw_delegate.h @@ -0,0 +1,75 @@ +// +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ +#define ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Helper functions that allow throwing exceptions consistently from anywhere. +// The main use case is for header-based libraries (eg templates), as they will +// be built by many different targets with their own compiler options. +// In particular, this will allow a safe way to throw exceptions even if the +// caller is compiled with -fno-exceptions. This is intended for implementing +// things like map<>::at(), which the standard documents as throwing an +// exception on error. +// +// Using other techniques like #if tricks could lead to ODR violations. +// +// You shouldn't use it unless you're writing code that you know will be built +// both with and without exceptions and you need to conform to an interface +// that uses exceptions. + +[[noreturn]] void ThrowStdLogicError(const std::string& what_arg); +[[noreturn]] void ThrowStdLogicError(const char* what_arg); +[[noreturn]] void ThrowStdInvalidArgument(const std::string& what_arg); +[[noreturn]] void ThrowStdInvalidArgument(const char* what_arg); +[[noreturn]] void ThrowStdDomainError(const std::string& what_arg); +[[noreturn]] void ThrowStdDomainError(const char* what_arg); +[[noreturn]] void ThrowStdLengthError(const std::string& what_arg); +[[noreturn]] void ThrowStdLengthError(const char* what_arg); +[[noreturn]] void ThrowStdOutOfRange(const std::string& what_arg); +[[noreturn]] void ThrowStdOutOfRange(const char* what_arg); +[[noreturn]] void ThrowStdRuntimeError(const std::string& what_arg); +[[noreturn]] void ThrowStdRuntimeError(const char* what_arg); +[[noreturn]] void ThrowStdRangeError(const std::string& what_arg); +[[noreturn]] void ThrowStdRangeError(const char* what_arg); +[[noreturn]] void ThrowStdOverflowError(const std::string& what_arg); +[[noreturn]] void ThrowStdOverflowError(const char* what_arg); +[[noreturn]] void ThrowStdUnderflowError(const std::string& what_arg); +[[noreturn]] void ThrowStdUnderflowError(const char* what_arg); + +[[noreturn]] void ThrowStdBadFunctionCall(); +[[noreturn]] void ThrowStdBadAlloc(); + +// ThrowStdBadArrayNewLength() cannot be consistently supported because +// std::bad_array_new_length is missing in libstdc++ until 4.9.0. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.3/libstdc++/api/a01379_source.html +// https://gcc.gnu.org/onlinedocs/gcc-4.9.0/libstdc++/api/a01327_source.html +// libcxx (as of 3.2) and msvc (as of 2015) both have it. +// [[noreturn]] void ThrowStdBadArrayNewLength(); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/tsan_mutex_interface.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/tsan_mutex_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..2a510603bc8c182f0560c6baac4e096bf522bbc8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/tsan_mutex_interface.h @@ -0,0 +1,66 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is intended solely for spinlock.h. +// It provides ThreadSanitizer annotations for custom mutexes. +// See for meaning of these annotations. + +#ifndef ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ +#define ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ + +// ABSL_INTERNAL_HAVE_TSAN_INTERFACE +// Macro intended only for internal use. +// +// Checks whether LLVM Thread Sanitizer interfaces are available. +// First made available in LLVM 5.0 (Sep 2017). +#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE +#error "ABSL_INTERNAL_HAVE_TSAN_INTERFACE cannot be directly set." +#endif + +#if defined(THREAD_SANITIZER) && defined(__has_include) +#if __has_include() +#define ABSL_INTERNAL_HAVE_TSAN_INTERFACE 1 +#endif +#endif + +#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE +#include + +#define ABSL_TSAN_MUTEX_CREATE __tsan_mutex_create +#define ABSL_TSAN_MUTEX_DESTROY __tsan_mutex_destroy +#define ABSL_TSAN_MUTEX_PRE_LOCK __tsan_mutex_pre_lock +#define ABSL_TSAN_MUTEX_POST_LOCK __tsan_mutex_post_lock +#define ABSL_TSAN_MUTEX_PRE_UNLOCK __tsan_mutex_pre_unlock +#define ABSL_TSAN_MUTEX_POST_UNLOCK __tsan_mutex_post_unlock +#define ABSL_TSAN_MUTEX_PRE_SIGNAL __tsan_mutex_pre_signal +#define ABSL_TSAN_MUTEX_POST_SIGNAL __tsan_mutex_post_signal +#define ABSL_TSAN_MUTEX_PRE_DIVERT __tsan_mutex_pre_divert +#define ABSL_TSAN_MUTEX_POST_DIVERT __tsan_mutex_post_divert + +#else + +#define ABSL_TSAN_MUTEX_CREATE(...) +#define ABSL_TSAN_MUTEX_DESTROY(...) +#define ABSL_TSAN_MUTEX_PRE_LOCK(...) +#define ABSL_TSAN_MUTEX_POST_LOCK(...) +#define ABSL_TSAN_MUTEX_PRE_UNLOCK(...) +#define ABSL_TSAN_MUTEX_POST_UNLOCK(...) +#define ABSL_TSAN_MUTEX_PRE_SIGNAL(...) +#define ABSL_TSAN_MUTEX_POST_SIGNAL(...) +#define ABSL_TSAN_MUTEX_PRE_DIVERT(...) +#define ABSL_TSAN_MUTEX_POST_DIVERT(...) + +#endif + +#endif // ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/unaligned_access.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/unaligned_access.h new file mode 100644 index 0000000000000000000000000000000000000000..6be56c865b3d3dd0f862ec99fc9ab95c017d52e4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/unaligned_access.h @@ -0,0 +1,158 @@ +// +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ +#define ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ + +#include + +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +// unaligned APIs + +// Portable handling of unaligned loads, stores, and copies. + +// The unaligned API is C++ only. The declarations use C++ features +// (namespaces, inline) which are absent or incompatible in C. +#if defined(__cplusplus) + +#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||\ + defined(MEMORY_SANITIZER) +// Consider we have an unaligned load/store of 4 bytes from address 0x...05. +// AddressSanitizer will treat it as a 3-byte access to the range 05:07 and +// will miss a bug if 08 is the first unaddressable byte. +// ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will +// miss a race between this access and some other accesses to 08. +// MemorySanitizer will correctly propagate the shadow on unaligned stores +// and correctly report bugs on unaligned loads, but it may not properly +// update and report the origin of the uninitialized memory. +// For all three tools, replacing an unaligned access with a tool-specific +// callback solves the problem. + +// Make sure uint16_t/uint32_t/uint64_t are defined. +#include + +extern "C" { +uint16_t __sanitizer_unaligned_load16(const void *p); +uint32_t __sanitizer_unaligned_load32(const void *p); +uint64_t __sanitizer_unaligned_load64(const void *p); +void __sanitizer_unaligned_store16(void *p, uint16_t v); +void __sanitizer_unaligned_store32(void *p, uint32_t v); +void __sanitizer_unaligned_store64(void *p, uint64_t v); +} // extern "C" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +inline uint16_t UnalignedLoad16(const void *p) { + return __sanitizer_unaligned_load16(p); +} + +inline uint32_t UnalignedLoad32(const void *p) { + return __sanitizer_unaligned_load32(p); +} + +inline uint64_t UnalignedLoad64(const void *p) { + return __sanitizer_unaligned_load64(p); +} + +inline void UnalignedStore16(void *p, uint16_t v) { + __sanitizer_unaligned_store16(p, v); +} + +inline void UnalignedStore32(void *p, uint32_t v) { + __sanitizer_unaligned_store32(p, v); +} + +inline void UnalignedStore64(void *p, uint64_t v) { + __sanitizer_unaligned_store64(p, v); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ + (absl::base_internal::UnalignedLoad16(_p)) +#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ + (absl::base_internal::UnalignedLoad32(_p)) +#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ + (absl::base_internal::UnalignedLoad64(_p)) + +#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ + (absl::base_internal::UnalignedStore16(_p, _val)) +#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ + (absl::base_internal::UnalignedStore32(_p, _val)) +#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ + (absl::base_internal::UnalignedStore64(_p, _val)) + +#else + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +inline uint16_t UnalignedLoad16(const void *p) { + uint16_t t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint32_t UnalignedLoad32(const void *p) { + uint32_t t; + memcpy(&t, p, sizeof t); + return t; +} + +inline uint64_t UnalignedLoad64(const void *p) { + uint64_t t; + memcpy(&t, p, sizeof t); + return t; +} + +inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); } + +inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); } + +inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ + (absl::base_internal::UnalignedLoad16(_p)) +#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ + (absl::base_internal::UnalignedLoad32(_p)) +#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ + (absl::base_internal::UnalignedLoad64(_p)) + +#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ + (absl::base_internal::UnalignedStore16(_p, _val)) +#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ + (absl::base_internal::UnalignedStore32(_p, _val)) +#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ + (absl::base_internal::UnalignedStore64(_p, _val)) + +#endif + +#endif // defined(__cplusplus), end of unaligned API + +#endif // ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/internal/unscaledcycleclock.h b/libs/or-tools-src-ubuntu/include/absl/base/internal/unscaledcycleclock.h new file mode 100644 index 0000000000000000000000000000000000000000..cdce9bf8a83c0ae7b238ba98c4fc22351ee58691 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/internal/unscaledcycleclock.h @@ -0,0 +1,124 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// UnscaledCycleClock +// An UnscaledCycleClock yields the value and frequency of a cycle counter +// that increments at a rate that is approximately constant. +// This class is for internal / whitelisted use only, you should consider +// using CycleClock instead. +// +// Notes: +// The cycle counter frequency is not necessarily the core clock frequency. +// That is, CycleCounter cycles are not necessarily "CPU cycles". +// +// An arbitrary offset may have been added to the counter at power on. +// +// On some platforms, the rate and offset of the counter may differ +// slightly when read from different CPUs of a multiprocessor. Usually, +// we try to ensure that the operating system adjusts values periodically +// so that values agree approximately. If you need stronger guarantees, +// consider using alternate interfaces. +// +// The CPU is not required to maintain the ordering of a cycle counter read +// with respect to surrounding instructions. + +#ifndef ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ +#define ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ + +#include + +#if defined(__APPLE__) +#include +#endif + +#include "absl/base/port.h" + +// The following platforms have an implementation of a hardware counter. +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ + defined(__powerpc__) || defined(__ppc__) || \ + defined(_M_IX86) || defined(_M_X64) +#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1 +#else +#define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0 +#endif + +// The following platforms often disable access to the hardware +// counter (through a sandbox) even if the underlying hardware has a +// usable counter. The CycleTimer interface also requires a *scaled* +// CycleClock that runs at atleast 1 MHz. We've found some Android +// ARM64 devices where this is not the case, so we disable it by +// default on Android ARM64. +#if defined(__native_client__) || \ + (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \ + (defined(__ANDROID__) && defined(__aarch64__)) +#define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 +#else +#define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 1 +#endif + +// UnscaledCycleClock is an optional internal feature. +// Use "#if ABSL_USE_UNSCALED_CYCLECLOCK" to test for its presence. +// Can be overridden at compile-time via -DABSL_USE_UNSCALED_CYCLECLOCK=0|1 +#if !defined(ABSL_USE_UNSCALED_CYCLECLOCK) +#define ABSL_USE_UNSCALED_CYCLECLOCK \ + (ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION && \ + ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT) +#endif + +#if ABSL_USE_UNSCALED_CYCLECLOCK + +// This macro can be used to test if UnscaledCycleClock::Frequency() +// is NominalCPUFrequency() on a particular platform. +#if (defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64)) +#define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace time_internal { +class UnscaledCycleClockWrapperForGetCurrentTime; +} // namespace time_internal + +namespace base_internal { +class CycleClock; +class UnscaledCycleClockWrapperForInitializeFrequency; + +class UnscaledCycleClock { + private: + UnscaledCycleClock() = delete; + + // Return the value of a cycle counter that counts at a rate that is + // approximately constant. + static int64_t Now(); + + // Return the how much UnscaledCycleClock::Now() increases per second. + // This is not necessarily the core CPU clock frequency. + // It may be the nominal value report by the kernel, rather than a measured + // value. + static double Frequency(); + + // Whitelisted friends. + friend class base_internal::CycleClock; + friend class time_internal::UnscaledCycleClockWrapperForGetCurrentTime; + friend class base_internal::UnscaledCycleClockWrapperForInitializeFrequency; +}; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_USE_UNSCALED_CYCLECLOCK + +#endif // ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/log_severity.h b/libs/or-tools-src-ubuntu/include/absl/base/log_severity.h new file mode 100644 index 0000000000000000000000000000000000000000..65a3b166727009a8bf464127de6b4a342dc3b39d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/log_severity.h @@ -0,0 +1,121 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ +#define ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// absl::LogSeverity +// +// Four severity levels are defined. Logging APIs should terminate the program +// when a message is logged at severity `kFatal`; the other levels have no +// special semantics. +// +// Values other than the four defined levels (e.g. produced by `static_cast`) +// are valid, but their semantics when passed to a function, macro, or flag +// depend on the function, macro, or flag. The usual behavior is to normalize +// such values to a defined severity level, however in some cases values other +// than the defined levels are useful for comparison. +// +// Exmaple: +// +// // Effectively disables all logging: +// SetMinLogLevel(static_cast(100)); +// +// Abseil flags may be defined with type `LogSeverity`. Dependency layering +// constraints require that the `AbslParseFlag()` overload be declared and +// defined in the flags library itself rather than here. The `AbslUnparseFlag()` +// overload is defined there as well for consistency. +// +// absl::LogSeverity Flag String Representation +// +// An `absl::LogSeverity` has a string representation used for parsing +// command-line flags based on the enumerator name (e.g. `kFatal`) or +// its unprefixed name (without the `k`) in any case-insensitive form. (E.g. +// "FATAL", "fatal" or "Fatal" are all valid.) Unparsing such flags produces an +// unprefixed string representation in all caps (e.g. "FATAL") or an integer. +// +// Additionally, the parser accepts arbitrary integers (as if the type were +// `int`). +// +// Examples: +// +// --my_log_level=kInfo +// --my_log_level=INFO +// --my_log_level=info +// --my_log_level=0 +// +// Unparsing a flag produces the same result as `absl::LogSeverityName()` for +// the standard levels and a base-ten integer otherwise. +enum class LogSeverity : int { + kInfo = 0, + kWarning = 1, + kError = 2, + kFatal = 3, +}; + +// LogSeverities() +// +// Returns an iterable of all standard `absl::LogSeverity` values, ordered from +// least to most severe. +constexpr std::array LogSeverities() { + return {{absl::LogSeverity::kInfo, absl::LogSeverity::kWarning, + absl::LogSeverity::kError, absl::LogSeverity::kFatal}}; +} + +// LogSeverityName() +// +// Returns the all-caps string representation (e.g. "INFO") of the specified +// severity level if it is one of the standard levels and "UNKNOWN" otherwise. +constexpr const char* LogSeverityName(absl::LogSeverity s) { + return s == absl::LogSeverity::kInfo + ? "INFO" + : s == absl::LogSeverity::kWarning + ? "WARNING" + : s == absl::LogSeverity::kError + ? "ERROR" + : s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN"; +} + +// NormalizeLogSeverity() +// +// Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal` +// normalize to `kError` (**NOT** `kFatal`). +constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) { + return s < absl::LogSeverity::kInfo + ? absl::LogSeverity::kInfo + : s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : s; +} +constexpr absl::LogSeverity NormalizeLogSeverity(int s) { + return absl::NormalizeLogSeverity(static_cast(s)); +} + +// operator<< +// +// The exact representation of a streamed `absl::LogSeverity` is deliberately +// unspecified; do not rely on it. +std::ostream& operator<<(std::ostream& os, absl::LogSeverity s); + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/macros.h b/libs/or-tools-src-ubuntu/include/absl/base/macros.h new file mode 100644 index 0000000000000000000000000000000000000000..547f93bafba97947c31bfc88517fe98ac0d2d84a --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/macros.h @@ -0,0 +1,220 @@ +// +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: macros.h +// ----------------------------------------------------------------------------- +// +// This header file defines the set of language macros used within Abseil code. +// For the set of macros used to determine supported compilers and platforms, +// see absl/base/config.h instead. +// +// This code is compiled directly on many platforms, including client +// platforms like Windows, Mac, and embedded systems. Before making +// any changes here, make sure that you're not breaking any platforms. + +#ifndef ABSL_BASE_MACROS_H_ +#define ABSL_BASE_MACROS_H_ + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" + +// ABSL_ARRAYSIZE() +// +// Returns the number of elements in an array as a compile-time constant, which +// can be used in defining new arrays. If you use this macro on a pointer by +// mistake, you will get a compile-time error. +#define ABSL_ARRAYSIZE(array) \ + (sizeof(::absl::macros_internal::ArraySizeHelper(array))) + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace macros_internal { +// Note: this internal template function declaration is used by ABSL_ARRAYSIZE. +// The function doesn't need a definition, as we only use its type. +template +auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; +} // namespace macros_internal +ABSL_NAMESPACE_END +} // namespace absl + +// kLinkerInitialized +// +// An enum used only as a constructor argument to indicate that a variable has +// static storage duration, and that the constructor should do nothing to its +// state. Use of this macro indicates to the reader that it is legal to +// declare a static instance of the class, provided the constructor is given +// the absl::base_internal::kLinkerInitialized argument. +// +// Normally, it is unsafe to declare a static variable that has a constructor or +// a destructor because invocation order is undefined. However, if the type can +// be zero-initialized (which the loader does for static variables) into a valid +// state and the type's destructor does not affect storage, then a constructor +// for static initialization can be declared. +// +// Example: +// // Declaration +// explicit MyClass(absl::base_internal:LinkerInitialized x) {} +// +// // Invocation +// static MyClass my_global(absl::base_internal::kLinkerInitialized); +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { +enum LinkerInitialized { + kLinkerInitialized = 0, +}; +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +// ABSL_FALLTHROUGH_INTENDED +// +// Annotates implicit fall-through between switch labels, allowing a case to +// indicate intentional fallthrough and turn off warnings about any lack of a +// `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by +// a semicolon and can be used in most places where `break` can, provided that +// no statements exist between it and the next switch label. +// +// Example: +// +// switch (x) { +// case 40: +// case 41: +// if (truth_is_out_there) { +// ++x; +// ABSL_FALLTHROUGH_INTENDED; // Use instead of/along with annotations +// // in comments +// } else { +// return x; +// } +// case 42: +// ... +// +// Notes: when compiled with clang in C++11 mode, the ABSL_FALLTHROUGH_INTENDED +// macro is expanded to the [[clang::fallthrough]] attribute, which is analysed +// when performing switch labels fall-through diagnostic +// (`-Wimplicit-fallthrough`). See clang documentation on language extensions +// for details: +// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough +// +// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro +// has no effect on diagnostics. In any case this macro has no effect on runtime +// behavior and performance of code. +#ifdef ABSL_FALLTHROUGH_INTENDED +#error "ABSL_FALLTHROUGH_INTENDED should not be defined." +#endif + +// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported. +#if defined(__clang__) && defined(__has_warning) +#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") +#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] +#endif +#elif defined(__GNUC__) && __GNUC__ >= 7 +#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] +#endif + +#ifndef ABSL_FALLTHROUGH_INTENDED +#define ABSL_FALLTHROUGH_INTENDED \ + do { \ + } while (0) +#endif + +// ABSL_DEPRECATED() +// +// Marks a deprecated class, struct, enum, function, method and variable +// declarations. The macro argument is used as a custom diagnostic message (e.g. +// suggestion of a better alternative). +// +// Examples: +// +// class ABSL_DEPRECATED("Use Bar instead") Foo {...}; +// +// ABSL_DEPRECATED("Use Baz() instead") void Bar() {...} +// +// template +// ABSL_DEPRECATED("Use DoThat() instead") +// void DoThis(); +// +// Every usage of a deprecated entity will trigger a warning when compiled with +// clang's `-Wdeprecated-declarations` option. This option is turned off by +// default, but the warnings will be reported by clang-tidy. +#if defined(__clang__) && __cplusplus >= 201103L +#define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) +#endif + +#ifndef ABSL_DEPRECATED +#define ABSL_DEPRECATED(message) +#endif + +// ABSL_BAD_CALL_IF() +// +// Used on a function overload to trap bad calls: any call that matches the +// overload will cause a compile-time error. This macro uses a clang-specific +// "enable_if" attribute, as described at +// https://clang.llvm.org/docs/AttributeReference.html#enable-if +// +// Overloads which use this macro should be bracketed by +// `#ifdef ABSL_BAD_CALL_IF`. +// +// Example: +// +// int isdigit(int c); +// #ifdef ABSL_BAD_CALL_IF +// int isdigit(int c) +// ABSL_BAD_CALL_IF(c <= -1 || c > 255, +// "'c' must have the value of an unsigned char or EOF"); +// #endif // ABSL_BAD_CALL_IF +#if ABSL_HAVE_ATTRIBUTE(enable_if) +#define ABSL_BAD_CALL_IF(expr, msg) \ + __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg))) +#endif + +// ABSL_ASSERT() +// +// In C++11, `assert` can't be used portably within constexpr functions. +// ABSL_ASSERT functions as a runtime assert but works in C++11 constexpr +// functions. Example: +// +// constexpr double Divide(double a, double b) { +// return ABSL_ASSERT(b != 0), a / b; +// } +// +// This macro is inspired by +// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ +#if defined(NDEBUG) +#define ABSL_ASSERT(expr) \ + (false ? static_cast(expr) : static_cast(0)) +#else +#define ABSL_ASSERT(expr) \ + (ABSL_PREDICT_TRUE((expr)) ? static_cast(0) \ + : [] { assert(false && #expr); }()) // NOLINT +#endif + +#ifdef ABSL_HAVE_EXCEPTIONS +#define ABSL_INTERNAL_TRY try +#define ABSL_INTERNAL_CATCH_ANY catch (...) +#define ABSL_INTERNAL_RETHROW do { throw; } while (false) +#else // ABSL_HAVE_EXCEPTIONS +#define ABSL_INTERNAL_TRY if (true) +#define ABSL_INTERNAL_CATCH_ANY else if (false) +#define ABSL_INTERNAL_RETHROW do {} while (false) +#endif // ABSL_HAVE_EXCEPTIONS + +#endif // ABSL_BASE_MACROS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/optimization.h b/libs/or-tools-src-ubuntu/include/absl/base/optimization.h new file mode 100644 index 0000000000000000000000000000000000000000..646523b34612254f0f32b2f2b1b5e8bc88c1c5fe --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/optimization.h @@ -0,0 +1,181 @@ +// +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: optimization.h +// ----------------------------------------------------------------------------- +// +// This header file defines portable macros for performance optimization. + +#ifndef ABSL_BASE_OPTIMIZATION_H_ +#define ABSL_BASE_OPTIMIZATION_H_ + +#include "absl/base/config.h" + +// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION +// +// Instructs the compiler to avoid optimizing tail-call recursion. Use of this +// macro is useful when you wish to preserve the existing function order within +// a stack trace for logging, debugging, or profiling purposes. +// +// Example: +// +// int f() { +// int result = g(); +// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); +// return result; +// } +#if defined(__pnacl__) +#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; } +#elif defined(__clang__) +// Clang will not tail call given inline volatile assembly. +#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("") +#elif defined(__GNUC__) +// GCC will not tail call given inline volatile assembly. +#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("") +#elif defined(_MSC_VER) +#include +// The __nop() intrinsic blocks the optimisation. +#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __nop() +#else +#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; } +#endif + +// ABSL_CACHELINE_SIZE +// +// Explicitly defines the size of the L1 cache for purposes of alignment. +// Setting the cacheline size allows you to specify that certain objects be +// aligned on a cacheline boundary with `ABSL_CACHELINE_ALIGNED` declarations. +// (See below.) +// +// NOTE: this macro should be replaced with the following C++17 features, when +// those are generally available: +// +// * `std::hardware_constructive_interference_size` +// * `std::hardware_destructive_interference_size` +// +// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html +// for more information. +#if defined(__GNUC__) +// Cache line alignment +#if defined(__i386__) || defined(__x86_64__) +#define ABSL_CACHELINE_SIZE 64 +#elif defined(__powerpc64__) +#define ABSL_CACHELINE_SIZE 128 +#elif defined(__aarch64__) +// We would need to read special register ctr_el0 to find out L1 dcache size. +// This value is a good estimate based on a real aarch64 machine. +#define ABSL_CACHELINE_SIZE 64 +#elif defined(__arm__) +// Cache line sizes for ARM: These values are not strictly correct since +// cache line sizes depend on implementations, not architectures. There +// are even implementations with cache line sizes configurable at boot +// time. +#if defined(__ARM_ARCH_5T__) +#define ABSL_CACHELINE_SIZE 32 +#elif defined(__ARM_ARCH_7A__) +#define ABSL_CACHELINE_SIZE 64 +#endif +#endif + +#ifndef ABSL_CACHELINE_SIZE +// A reasonable default guess. Note that overestimates tend to waste more +// space, while underestimates tend to waste more time. +#define ABSL_CACHELINE_SIZE 64 +#endif + +// ABSL_CACHELINE_ALIGNED +// +// Indicates that the declared object be cache aligned using +// `ABSL_CACHELINE_SIZE` (see above). Cacheline aligning objects allows you to +// load a set of related objects in the L1 cache for performance improvements. +// Cacheline aligning objects properly allows constructive memory sharing and +// prevents destructive (or "false") memory sharing. +// +// NOTE: this macro should be replaced with usage of `alignas()` using +// `std::hardware_constructive_interference_size` and/or +// `std::hardware_destructive_interference_size` when available within C++17. +// +// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html +// for more information. +// +// On some compilers, `ABSL_CACHELINE_ALIGNED` expands to an `__attribute__` +// or `__declspec` attribute. For compilers where this is not known to work, +// the macro expands to nothing. +// +// No further guarantees are made here. The result of applying the macro +// to variables and types is always implementation-defined. +// +// WARNING: It is easy to use this attribute incorrectly, even to the point +// of causing bugs that are difficult to diagnose, crash, etc. It does not +// of itself guarantee that objects are aligned to a cache line. +// +// NOTE: Some compilers are picky about the locations of annotations such as +// this attribute, so prefer to put it at the beginning of your declaration. +// For example, +// +// ABSL_CACHELINE_ALIGNED static Foo* foo = ... +// +// class ABSL_CACHELINE_ALIGNED Bar { ... +// +// Recommendations: +// +// 1) Consult compiler documentation; this comment is not kept in sync as +// toolchains evolve. +// 2) Verify your use has the intended effect. This often requires inspecting +// the generated machine code. +// 3) Prefer applying this attribute to individual variables. Avoid +// applying it to types. This tends to localize the effect. +#define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE))) +#elif defined(_MSC_VER) +#define ABSL_CACHELINE_SIZE 64 +#define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE)) +#else +#define ABSL_CACHELINE_SIZE 64 +#define ABSL_CACHELINE_ALIGNED +#endif + +// ABSL_PREDICT_TRUE, ABSL_PREDICT_FALSE +// +// Enables the compiler to prioritize compilation using static analysis for +// likely paths within a boolean branch. +// +// Example: +// +// if (ABSL_PREDICT_TRUE(expression)) { +// return result; // Faster if more likely +// } else { +// return 0; +// } +// +// Compilers can use the information that a certain branch is not likely to be +// taken (for instance, a CHECK failure) to optimize for the common case in +// the absence of better information (ie. compiling gcc with `-fprofile-arcs`). +// +// Recommendation: Modern CPUs dynamically predict branch execution paths, +// typically with accuracy greater than 97%. As a result, annotating every +// branch in a codebase is likely counterproductive; however, annotating +// specific branches that are both hot and consistently mispredicted is likely +// to yield performance improvements. +#if ABSL_HAVE_BUILTIN(__builtin_expect) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true)) +#else +#define ABSL_PREDICT_FALSE(x) (x) +#define ABSL_PREDICT_TRUE(x) (x) +#endif + +#endif // ABSL_BASE_OPTIMIZATION_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/options.h b/libs/or-tools-src-ubuntu/include/absl/base/options.h new file mode 100644 index 0000000000000000000000000000000000000000..50f26e24b41ce3612487850c8e92a7d7271889c0 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/options.h @@ -0,0 +1,211 @@ +#ifndef ABSL_BASE_OPTIONS_H_ +#define ABSL_BASE_OPTIONS_H_ + +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: options.h +// ----------------------------------------------------------------------------- +// +// This file contains Abseil configuration options for setting specific +// implementations instead of letting Abseil determine which implementation to +// use at compile-time. Setting these options may be useful for package or build +// managers who wish to guarantee ABI stability within binary builds (which are +// otherwise difficult to enforce). +// +// *** IMPORTANT NOTICE FOR PACKAGE MANAGERS: It is important that +// maintainers of package managers who wish to package Abseil read and +// understand this file! *** +// +// Abseil contains a number of possible configuration endpoints, based on +// parameters such as the detected platform, language version, or command-line +// flags used to invoke the underlying binary. As is the case with all +// libraries, binaries which contain Abseil code must ensure that separate +// packages use the same compiled copy of Abseil to avoid a diamond dependency +// problem, which can occur if two packages built with different Abseil +// configuration settings are linked together. Diamond dependency problems in +// C++ may manifest as violations to the One Definition Rule (ODR) (resulting in +// linker errors), or undefined behavior (resulting in crashes). +// +// Diamond dependency problems can be avoided if all packages utilize the same +// exact version of Abseil. Building from source code with the same compilation +// parameters is the easiest way to avoid such dependency problems. However, for +// package managers who cannot control such compilation parameters, we are +// providing the file to allow you to inject ABI (Application Binary Interface) +// stability across builds. Settings options in this file will neither change +// API nor ABI, providing a stable copy of Abseil between packages. +// +// Care must be taken to keep options within these configurations isolated +// from any other dynamic settings, such as command-line flags which could alter +// these options. This file is provided specifically to help build and package +// managers provide a stable copy of Abseil within their libraries and binaries; +// other developers should not have need to alter the contents of this file. +// +// ----------------------------------------------------------------------------- +// Usage +// ----------------------------------------------------------------------------- +// +// For any particular package release, set the appropriate definitions within +// this file to whatever value makes the most sense for your package(s). Note +// that, by default, most of these options, at the moment, affect the +// implementation of types; future options may affect other implementation +// details. +// +// NOTE: the defaults within this file all assume that Abseil can select the +// proper Abseil implementation at compile-time, which will not be sufficient +// to guarantee ABI stability to package managers. + +// Include a standard library header to allow configuration based on the +// standard library in use. +#ifdef __cplusplus +#include +#endif + +// ----------------------------------------------------------------------------- +// Type Compatibility Options +// ----------------------------------------------------------------------------- +// +// ABSL_OPTION_USE_STD_ANY +// +// This option controls whether absl::any is implemented as an alias to +// std::any, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::any. This requires that all code +// using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::any is available. This option is +// useful when you are building your entire program, including all of its +// dependencies, from source. It should not be used otherwise -- for example, +// if you are distributing Abseil in a binary package manager -- since in +// mode 2, absl::any will name a different type, with a different mangled name +// and binary layout, depending on the compiler flags passed by the end user. +// For more info, see https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::any is a typedef of std::any, use the feature macro ABSL_USES_STD_ANY. + +#define ABSL_OPTION_USE_STD_ANY 2 + + +// ABSL_OPTION_USE_STD_OPTIONAL +// +// This option controls whether absl::optional is implemented as an alias to +// std::optional, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::optional. This requires that all +// code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::optional is available. This option +// is useful when you are building your program from source. It should not be +// used otherwise -- for example, if you are distributing Abseil in a binary +// package manager -- since in mode 2, absl::optional will name a different +// type, with a different mangled name and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. + +// User code should not inspect this macro. To check in the preprocessor if +// absl::optional is a typedef of std::optional, use the feature macro +// ABSL_USES_STD_OPTIONAL. + +#define ABSL_OPTION_USE_STD_OPTIONAL 2 + + +// ABSL_OPTION_USE_STD_STRING_VIEW +// +// This option controls whether absl::string_view is implemented as an alias to +// std::string_view, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::string_view. This requires that +// all code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::string_view is available. This +// option is useful when you are building your program from source. It should +// not be used otherwise -- for example, if you are distributing Abseil in a +// binary package manager -- since in mode 2, absl::string_view will name a +// different type, with a different mangled name and binary layout, depending on +// the compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::string_view is a typedef of std::string_view, use the feature macro +// ABSL_USES_STD_STRING_VIEW. + +#define ABSL_OPTION_USE_STD_STRING_VIEW 2 + +// ABSL_OPTION_USE_STD_VARIANT +// +// This option controls whether absl::variant is implemented as an alias to +// std::variant, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::variant. This requires that all +// code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::variant is available. This option +// is useful when you are building your program from source. It should not be +// used otherwise -- for example, if you are distributing Abseil in a binary +// package manager -- since in mode 2, absl::variant will name a different +// type, with a different mangled name and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::variant is a typedef of std::variant, use the feature macro +// ABSL_USES_STD_VARIANT. + +#define ABSL_OPTION_USE_STD_VARIANT 2 + + +// ABSL_OPTION_USE_INLINE_NAMESPACE +// ABSL_OPTION_INLINE_NAMESPACE_NAME +// +// These options controls whether all entities in the absl namespace are +// contained within an inner inline namespace. This does not affect the +// user-visible API of Abseil, but it changes the mangled names of all symbols. +// +// This can be useful as a version tag if you are distributing Abseil in +// precompiled form. This will prevent a binary library build of Abseil with +// one inline namespace being used with headers configured with a different +// inline namespace name. Binary packagers are reminded that Abseil does not +// guarantee any ABI stability in Abseil, so any update of Abseil or +// configuration change in such a binary package should be combined with a +// new, unique value for the inline namespace name. +// +// A value of 0 means not to use inline namespaces. +// +// A value of 1 means to use an inline namespace with the given name inside +// namespace absl. If this is set, ABSL_OPTION_INLINE_NAMESPACE_NAME must also +// be changed to a new, unique identifier name. In particular "head" is not +// allowed. + +#define ABSL_OPTION_USE_INLINE_NAMESPACE 1 +#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_2020_02_25 + +#endif // ABSL_BASE_OPTIONS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/policy_checks.h b/libs/or-tools-src-ubuntu/include/absl/base/policy_checks.h new file mode 100644 index 0000000000000000000000000000000000000000..4dfa49e54ac28d719aee0562ee0af819fe01eb8e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/policy_checks.h @@ -0,0 +1,111 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: policy_checks.h +// ----------------------------------------------------------------------------- +// +// This header enforces a minimum set of policies at build time, such as the +// supported compiler and library versions. Unsupported configurations are +// reported with `#error`. This enforcement is best effort, so successfully +// compiling this header does not guarantee a supported configuration. + +#ifndef ABSL_BASE_POLICY_CHECKS_H_ +#define ABSL_BASE_POLICY_CHECKS_H_ + +// Included for the __GLIBC_PREREQ macro used below. +#include + +// Included for the _STLPORT_VERSION macro used below. +#if defined(__cplusplus) +#include +#endif + +// ----------------------------------------------------------------------------- +// Operating System Check +// ----------------------------------------------------------------------------- + +#if defined(__CYGWIN__) +#error "Cygwin is not supported." +#endif + +// ----------------------------------------------------------------------------- +// Compiler Check +// ----------------------------------------------------------------------------- + +// We support MSVC++ 14.0 update 2 and later. +// This minimum will go up. +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__) +#error "This package requires Visual Studio 2015 Update 2 or higher." +#endif + +// We support gcc 4.7 and later. +// This minimum will go up. +#if defined(__GNUC__) && !defined(__clang__) +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) +#error "This package requires gcc 4.7 or higher." +#endif +#endif + +// We support Apple Xcode clang 4.2.1 (version 421.11.65) and later. +// This corresponds to Apple Xcode version 4.5. +// This minimum will go up. +#if defined(__apple_build_version__) && __apple_build_version__ < 4211165 +#error "This package requires __apple_build_version__ of 4211165 or higher." +#endif + +// ----------------------------------------------------------------------------- +// C++ Version Check +// ----------------------------------------------------------------------------- + +// Enforce C++11 as the minimum. Note that Visual Studio has not +// advanced __cplusplus despite being good enough for our purposes, so +// so we exempt it from the check. +#if defined(__cplusplus) && !defined(_MSC_VER) +#if __cplusplus < 201103L +#error "C++ versions less than C++11 are not supported." +#endif +#endif + +// ----------------------------------------------------------------------------- +// Standard Library Check +// ----------------------------------------------------------------------------- + +#if defined(_STLPORT_VERSION) +#error "STLPort is not supported." +#endif + +// ----------------------------------------------------------------------------- +// `char` Size Check +// ----------------------------------------------------------------------------- + +// Abseil currently assumes CHAR_BIT == 8. If you would like to use Abseil on a +// platform where this is not the case, please provide us with the details about +// your platform so we can consider relaxing this requirement. +#if CHAR_BIT != 8 +#error "Abseil assumes CHAR_BIT == 8." +#endif + +// ----------------------------------------------------------------------------- +// `int` Size Check +// ----------------------------------------------------------------------------- + +// Abseil currently assumes that an int is 4 bytes. If you would like to use +// Abseil on a platform where this is not the case, please provide us with the +// details about your platform so we can consider relaxing this requirement. +#if INT_MAX < 2147483647 +#error "Abseil assumes that int is at least 4 bytes. " +#endif + +#endif // ABSL_BASE_POLICY_CHECKS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/port.h b/libs/or-tools-src-ubuntu/include/absl/base/port.h new file mode 100644 index 0000000000000000000000000000000000000000..6c28068d4f400e575a4548a6c2597ccb32bc4d32 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/port.h @@ -0,0 +1,26 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 files is a forwarding header for other headers containing various +// portability macros and functions. +// This file is used for both C and C++! + +#ifndef ABSL_BASE_PORT_H_ +#define ABSL_BASE_PORT_H_ + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" + +#endif // ABSL_BASE_PORT_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/base/thread_annotations.h b/libs/or-tools-src-ubuntu/include/absl/base/thread_annotations.h new file mode 100644 index 0000000000000000000000000000000000000000..5f51c0c2d2d5e31b643c0fd0636fdb600d77aaa1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/base/thread_annotations.h @@ -0,0 +1,280 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: thread_annotations.h +// ----------------------------------------------------------------------------- +// +// This header file contains macro definitions for thread safety annotations +// that allow developers to document the locking policies of multi-threaded +// code. The annotations can also help program analysis tools to identify +// potential thread safety issues. +// +// These annotations are implemented using compiler attributes. Using the macros +// defined here instead of raw attributes allow for portability and future +// compatibility. +// +// When referring to mutexes in the arguments of the attributes, you should +// use variable names or more complex expressions (e.g. my_object->mutex_) +// that evaluate to a concrete mutex object whenever possible. If the mutex +// you want to refer to is not in scope, you may use a member pointer +// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object. + +#ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_ +#define ABSL_BASE_THREAD_ANNOTATIONS_H_ + +#include "absl/base/config.h" +// TODO(mbonadei): Remove after the backward compatibility period. +#include "absl/base/internal/thread_annotations.h" // IWYU pragma: export + +#if defined(__clang__) +#define ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(x) __attribute__((x)) +#else +#define ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(x) // no-op +#endif + +// ABSL_GUARDED_BY() +// +// Documents if a shared field or global variable needs to be protected by a +// mutex. ABSL_GUARDED_BY() allows the user to specify a particular mutex that +// should be held when accessing the annotated variable. +// +// Although this annotation (and ABSL_PT_GUARDED_BY, below) cannot be applied to +// local variables, a local variable and its associated mutex can often be +// combined into a small class or struct, thereby allowing the annotation. +// +// Example: +// +// class Foo { +// Mutex mu_; +// int p1_ ABSL_GUARDED_BY(mu_); +// ... +// }; +#define ABSL_GUARDED_BY(x) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(guarded_by(x)) + +// ABSL_PT_GUARDED_BY() +// +// Documents if the memory location pointed to by a pointer should be guarded +// by a mutex when dereferencing the pointer. +// +// Example: +// class Foo { +// Mutex mu_; +// int *p1_ ABSL_PT_GUARDED_BY(mu_); +// ... +// }; +// +// Note that a pointer variable to a shared memory location could itself be a +// shared variable. +// +// Example: +// +// // `q_`, guarded by `mu1_`, points to a shared memory location that is +// // guarded by `mu2_`: +// int *q_ ABSL_GUARDED_BY(mu1_) ABSL_PT_GUARDED_BY(mu2_); +#define ABSL_PT_GUARDED_BY(x) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(pt_guarded_by(x)) + +// ABSL_ACQUIRED_AFTER() / ABSL_ACQUIRED_BEFORE() +// +// Documents the acquisition order between locks that can be held +// simultaneously by a thread. For any two locks that need to be annotated +// to establish an acquisition order, only one of them needs the annotation. +// (i.e. You don't have to annotate both locks with both ABSL_ACQUIRED_AFTER +// and ABSL_ACQUIRED_BEFORE.) +// +// As with ABSL_GUARDED_BY, this is only applicable to mutexes that are shared +// fields or global variables. +// +// Example: +// +// Mutex m1_; +// Mutex m2_ ABSL_ACQUIRED_AFTER(m1_); +#define ABSL_ACQUIRED_AFTER(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(acquired_after(__VA_ARGS__)) + +#define ABSL_ACQUIRED_BEFORE(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(acquired_before(__VA_ARGS__)) + +// ABSL_EXCLUSIVE_LOCKS_REQUIRED() / ABSL_SHARED_LOCKS_REQUIRED() +// +// Documents a function that expects a mutex to be held prior to entry. +// The mutex is expected to be held both on entry to, and exit from, the +// function. +// +// An exclusive lock allows read-write access to the guarded data member(s), and +// only one thread can acquire a lock exclusively at any one time. A shared lock +// allows read-only access, and any number of threads can acquire a shared lock +// concurrently. +// +// Generally, non-const methods should be annotated with +// ABSL_EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with +// ABSL_SHARED_LOCKS_REQUIRED. +// +// Example: +// +// Mutex mu1, mu2; +// int a ABSL_GUARDED_BY(mu1); +// int b ABSL_GUARDED_BY(mu2); +// +// void foo() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } +// void bar() const ABSL_SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } +#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + exclusive_locks_required(__VA_ARGS__)) + +#define ABSL_SHARED_LOCKS_REQUIRED(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(shared_locks_required(__VA_ARGS__)) + +// ABSL_LOCKS_EXCLUDED() +// +// Documents the locks acquired in the body of the function. These locks +// cannot be held when calling this function (as Abseil's `Mutex` locks are +// non-reentrant). +#define ABSL_LOCKS_EXCLUDED(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(locks_excluded(__VA_ARGS__)) + +// ABSL_LOCK_RETURNED() +// +// Documents a function that returns a mutex without acquiring it. For example, +// a public getter method that returns a pointer to a private mutex should +// be annotated with ABSL_LOCK_RETURNED. +#define ABSL_LOCK_RETURNED(x) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(lock_returned(x)) + +// ABSL_LOCKABLE +// +// Documents if a class/type is a lockable type (such as the `Mutex` class). +#define ABSL_LOCKABLE ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(lockable) + +// ABSL_SCOPED_LOCKABLE +// +// Documents if a class does RAII locking (such as the `MutexLock` class). +// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is +// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no +// arguments; the analysis will assume that the destructor unlocks whatever the +// constructor locked. +#define ABSL_SCOPED_LOCKABLE \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(scoped_lockable) + +// ABSL_EXCLUSIVE_LOCK_FUNCTION() +// +// Documents functions that acquire a lock in the body of a function, and do +// not release it. +#define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + exclusive_lock_function(__VA_ARGS__)) + +// ABSL_SHARED_LOCK_FUNCTION() +// +// Documents functions that acquire a shared (reader) lock in the body of a +// function, and do not release it. +#define ABSL_SHARED_LOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(shared_lock_function(__VA_ARGS__)) + +// ABSL_UNLOCK_FUNCTION() +// +// Documents functions that expect a lock to be held on entry to the function, +// and release it in the body of the function. +#define ABSL_UNLOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(unlock_function(__VA_ARGS__)) + +// ABSL_EXCLUSIVE_TRYLOCK_FUNCTION() / ABSL_SHARED_TRYLOCK_FUNCTION() +// +// Documents functions that try to acquire a lock, and return success or failure +// (or a non-boolean value that can be interpreted as a boolean). +// The first argument should be `true` for functions that return `true` on +// success, or `false` for functions that return `false` on success. The second +// argument specifies the mutex that is locked on success. If unspecified, this +// mutex is assumed to be `this`. +#define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + exclusive_trylock_function(__VA_ARGS__)) + +#define ABSL_SHARED_TRYLOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + shared_trylock_function(__VA_ARGS__)) + +// ABSL_ASSERT_EXCLUSIVE_LOCK() / ABSL_ASSERT_SHARED_LOCK() +// +// Documents functions that dynamically check to see if a lock is held, and fail +// if it is not held. +#define ABSL_ASSERT_EXCLUSIVE_LOCK(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(assert_exclusive_lock(__VA_ARGS__)) + +#define ABSL_ASSERT_SHARED_LOCK(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(assert_shared_lock(__VA_ARGS__)) + +// ABSL_NO_THREAD_SAFETY_ANALYSIS +// +// Turns off thread safety checking within the body of a particular function. +// This annotation is used to mark functions that are known to be correct, but +// the locking behavior is more complicated than the analyzer can handle. +#define ABSL_NO_THREAD_SAFETY_ANALYSIS \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) + +//------------------------------------------------------------------------------ +// Tool-Supplied Annotations +//------------------------------------------------------------------------------ + +// ABSL_TS_UNCHECKED should be placed around lock expressions that are not valid +// C++ syntax, but which are present for documentation purposes. These +// annotations will be ignored by the analysis. +#define ABSL_TS_UNCHECKED(x) "" + +// ABSL_TS_FIXME is used to mark lock expressions that are not valid C++ syntax. +// It is used by automated tools to mark and disable invalid expressions. +// The annotation should either be fixed, or changed to ABSL_TS_UNCHECKED. +#define ABSL_TS_FIXME(x) "" + +// Like ABSL_NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body +// of a particular function. However, this attribute is used to mark functions +// that are incorrect and need to be fixed. It is used by automated tools to +// avoid breaking the build when the analysis is updated. +// Code owners are expected to eventually fix the routine. +#define ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME ABSL_NO_THREAD_SAFETY_ANALYSIS + +// Similar to ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a +// ABSL_GUARDED_BY annotation that needs to be fixed, because it is producing +// thread safety warning. It disables the ABSL_GUARDED_BY. +#define ABSL_GUARDED_BY_FIXME(x) + +// Disables warnings for a single read operation. This can be used to avoid +// warnings when it is known that the read is not actually involved in a race, +// but the compiler cannot confirm that. +#define ABSL_TS_UNCHECKED_READ(x) absl::base_internal::ts_unchecked_read(x) + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// Takes a reference to a guarded data member, and returns an unguarded +// reference. +// Do not used this function directly, use ABSL_TS_UNCHECKED_READ instead. +template +inline const T& ts_unchecked_read(const T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +template +inline T& ts_unchecked_read(T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_THREAD_ANNOTATIONS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/btree_map.h b/libs/or-tools-src-ubuntu/include/absl/container/btree_map.h new file mode 100644 index 0000000000000000000000000000000000000000..d23f4ee5e648f645be25c58e77bed457f9e97363 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/btree_map.h @@ -0,0 +1,759 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: btree_map.h +// ----------------------------------------------------------------------------- +// +// This header file defines B-tree maps: sorted associative containers mapping +// keys to values. +// +// * `absl::btree_map<>` +// * `absl::btree_multimap<>` +// +// These B-tree types are similar to the corresponding types in the STL +// (`std::map` and `std::multimap`) and generally conform to the STL interfaces +// of those types. However, because they are implemented using B-trees, they +// are more efficient in most situations. +// +// Unlike `std::map` and `std::multimap`, which are commonly implemented using +// red-black tree nodes, B-tree maps use more generic B-tree nodes able to hold +// multiple values per node. Holding multiple values per node often makes +// B-tree maps perform better than their `std::map` counterparts, because +// multiple entries can be checked within the same cache hit. +// +// However, these types should not be considered drop-in replacements for +// `std::map` and `std::multimap` as there are some API differences, which are +// noted in this header file. +// +// Importantly, insertions and deletions may invalidate outstanding iterators, +// pointers, and references to elements. Such invalidations are typically only +// an issue if insertion and deletion operations are interleaved with the use of +// more than one iterator, pointer, or reference simultaneously. For this +// reason, `insert()` and `erase()` return a valid iterator at the current +// position. + +#ifndef ABSL_CONTAINER_BTREE_MAP_H_ +#define ABSL_CONTAINER_BTREE_MAP_H_ + +#include "absl/container/internal/btree.h" // IWYU pragma: export +#include "absl/container/internal/btree_container.h" // IWYU pragma: export + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// absl::btree_map<> +// +// An `absl::btree_map` is an ordered associative container of +// unique keys and associated values designed to be a more efficient replacement +// for `std::map` (in most cases). +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less`. +// +// An `absl::btree_map` uses a default allocator of +// `std::allocator>` to allocate (and deallocate) +// nodes, and construct and destruct values within those nodes. You may +// instead specify a custom allocator `A` (which in turn requires specifying a +// custom comparator `C`) as in `absl::btree_map`. +// +template , + typename Alloc = std::allocator>> +class btree_map + : public container_internal::btree_map_container< + container_internal::btree>> { + using Base = typename btree_map::btree_map_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_map` supports the same overload set as `std::map` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_map map1; + // + // * Initializer List constructor + // + // absl::btree_map map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::btree_map map3(map2); + // + // * Copy assignment operator + // + // absl::btree_map map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_map map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_map map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::btree_map map7(v.begin(), v.end()); + btree_map() {} + using Base::Base; + + // btree_map::begin() + // + // Returns an iterator to the beginning of the `btree_map`. + using Base::begin; + + // btree_map::cbegin() + // + // Returns a const iterator to the beginning of the `btree_map`. + using Base::cbegin; + + // btree_map::end() + // + // Returns an iterator to the end of the `btree_map`. + using Base::end; + + // btree_map::cend() + // + // Returns a const iterator to the end of the `btree_map`. + using Base::cend; + + // btree_map::empty() + // + // Returns whether or not the `btree_map` is empty. + using Base::empty; + + // btree_map::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_map` under current memory constraints. This value can be thought + // of as the largest value of `std::distance(begin(), end())` for a + // `btree_map`. + using Base::max_size; + + // btree_map::size() + // + // Returns the number of elements currently within the `btree_map`. + using Base::size; + + // btree_map::clear() + // + // Removes all elements from the `btree_map`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_map::erase() + // + // Erases elements within the `btree_map`. If an erase occurs, any references, + // pointers, or iterators are invalidated. + // Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_map`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template size_type erase(const K& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased. + using Base::erase; + + // btree_map::insert() + // + // Inserts an element of the specified value into the `btree_map`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If an insertion + // occurs, any references, pointers, or iterators are invalidated. + // Overloads are listed below. + // + // std::pair insert(const value_type& value): + // + // Inserts a value into the `btree_map`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(value_type&& value): + // + // Inserts a moveable value into the `btree_map`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_map::insert_or_assign() + // + // Inserts an element of the specified value into the `btree_map` provided + // that a value with the given key does not already exist, or replaces the + // corresponding mapped type with the forwarded `obj` argument if a key for + // that value already exists, returning an iterator pointing to the newly + // inserted element. Overloads are listed below. + // + // pair insert_or_assign(const key_type& k, M&& obj): + // pair insert_or_assign(key_type&& k, M&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `btree_map`. If the returned bool is true, insertion took place, and if + // it's false, assignment took place. + // + // iterator insert_or_assign(const_iterator hint, + // const key_type& k, M&& obj): + // iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `btree_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::insert_or_assign; + + // btree_map::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_map`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_map::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_map`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace_hint; + + // btree_map::try_emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_map`, provided that no element with the given key + // already exists. Unlike `emplace()`, if an element with the given key + // already exists, we guarantee that no element is constructed. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + // + // Overloads are listed below. + // + // std::pair try_emplace(const key_type& k, Args&&... args): + // std::pair try_emplace(key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `btree_map`. + // + // iterator try_emplace(const_iterator hint, + // const key_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `btree_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::try_emplace; + + // btree_map::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template node_type extract(const K& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_map` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_map::merge() + // + // Extracts elements from a given `source` btree_map into this + // `btree_map`. If the destination `btree_map` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // btree_map::swap(btree_map& other) + // + // Exchanges the contents of this `btree_map` with those of the `other` + // btree_map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `btree_map` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_map::at() + // + // Returns a reference to the mapped value of the element with key equivalent + // to the passed key. + using Base::at; + + // btree_map::contains() + // + // template bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_map`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::contains; + + // btree_map::count() + // + // template size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_map`. Note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `btree_map`. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::count; + + // btree_map::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_map`. + using Base::equal_range; + + // btree_map::find() + // + // template iterator find(const K& key): + // template const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_map`. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::find; + + // btree_map::operator[]() + // + // Returns a reference to the value mapped to the passed key within the + // `btree_map`, performing an `insert()` if the key does not already + // exist. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. Otherwise iterators are not affected and references are not + // invalidated. Overloads are listed below. + // + // T& operator[](key_type&& key): + // T& operator[](const key_type& key): + // + // Inserts a value_type object constructed in-place if the element with the + // given key does not exist. + using Base::operator[]; + + // btree_map::get_allocator() + // + // Returns the allocator function associated with this `btree_map`. + using Base::get_allocator; + + // btree_map::key_comp(); + // + // Returns the key comparator associated with this `btree_map`. + using Base::key_comp; + + // btree_map::value_comp(); + // + // Returns the value comparator associated with this `btree_map`. + using Base::value_comp; +}; + +// absl::swap(absl::btree_map<>, absl::btree_map<>) +// +// Swaps the contents of two `absl::btree_map` containers. +template +void swap(btree_map &x, btree_map &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_map<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +template +void erase_if(btree_map &map, Pred pred) { + for (auto it = map.begin(); it != map.end();) { + if (pred(*it)) { + it = map.erase(it); + } else { + ++it; + } + } +} + +// absl::btree_multimap +// +// An `absl::btree_multimap` is an ordered associative container of +// keys and associated values designed to be a more efficient replacement for +// `std::multimap` (in most cases). Unlike `absl::btree_map`, a B-tree multimap +// allows multiple elements with equivalent keys. +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less`. +// +// An `absl::btree_multimap` uses a default allocator of +// `std::allocator>` to allocate (and deallocate) +// nodes, and construct and destruct values within those nodes. You may +// instead specify a custom allocator `A` (which in turn requires specifying a +// custom comparator `C`) as in `absl::btree_multimap`. +// +template , + typename Alloc = std::allocator>> +class btree_multimap + : public container_internal::btree_multimap_container< + container_internal::btree>> { + using Base = typename btree_multimap::btree_multimap_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_multimap` supports the same overload set as `std::multimap` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_multimap map1; + // + // * Initializer List constructor + // + // absl::btree_multimap map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::btree_multimap map3(map2); + // + // * Copy assignment operator + // + // absl::btree_multimap map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_multimap map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_multimap map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::btree_multimap map7(v.begin(), v.end()); + btree_multimap() {} + using Base::Base; + + // btree_multimap::begin() + // + // Returns an iterator to the beginning of the `btree_multimap`. + using Base::begin; + + // btree_multimap::cbegin() + // + // Returns a const iterator to the beginning of the `btree_multimap`. + using Base::cbegin; + + // btree_multimap::end() + // + // Returns an iterator to the end of the `btree_multimap`. + using Base::end; + + // btree_multimap::cend() + // + // Returns a const iterator to the end of the `btree_multimap`. + using Base::cend; + + // btree_multimap::empty() + // + // Returns whether or not the `btree_multimap` is empty. + using Base::empty; + + // btree_multimap::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_multimap` under current memory constraints. This value can be + // thought of as the largest value of `std::distance(begin(), end())` for a + // `btree_multimap`. + using Base::max_size; + + // btree_multimap::size() + // + // Returns the number of elements currently within the `btree_multimap`. + using Base::size; + + // btree_multimap::clear() + // + // Removes all elements from the `btree_multimap`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_multimap::erase() + // + // Erases elements within the `btree_multimap`. If an erase occurs, any + // references, pointers, or iterators are invalidated. + // Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_multimap`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template size_type erase(const K& key): + // + // Erases the elements matching the key, if any exist, returning the + // number of elements erased. + using Base::erase; + + // btree_multimap::insert() + // + // Inserts an element of the specified value into the `btree_multimap`, + // returning an iterator pointing to the newly inserted element. + // Any references, pointers, or iterators are invalidated. Overloads are + // listed below. + // + // iterator insert(const value_type& value): + // + // Inserts a value into the `btree_multimap`, returning an iterator to the + // inserted element. + // + // iterator insert(value_type&& value): + // + // Inserts a moveable value into the `btree_multimap`, returning an iterator + // to the inserted element. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_multimap::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multimap`. Any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_multimap::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multimap`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search. + // + // Any references, pointers, or iterators are invalidated. + using Base::emplace_hint; + + // btree_multimap::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template node_type extract(const K& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_multimap` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_multimap::merge() + // + // Extracts elements from a given `source` btree_multimap into this + // `btree_multimap`. If the destination `btree_multimap` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // btree_multimap::swap(btree_multimap& other) + // + // Exchanges the contents of this `btree_multimap` with those of the `other` + // btree_multimap, avoiding invocation of any move, copy, or swap operations + // on individual elements. + // + // All iterators and references on the `btree_multimap` remain valid, + // excepting for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_multimap::contains() + // + // template bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_multimap`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::contains; + + // btree_multimap::count() + // + // template size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::count; + + // btree_multimap::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_multimap`. + using Base::equal_range; + + // btree_multimap::find() + // + // template iterator find(const K& key): + // template const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map is provided a + // compatible heterogeneous comparator. + using Base::find; + + // btree_multimap::get_allocator() + // + // Returns the allocator function associated with this `btree_multimap`. + using Base::get_allocator; + + // btree_multimap::key_comp(); + // + // Returns the key comparator associated with this `btree_multimap`. + using Base::key_comp; + + // btree_multimap::value_comp(); + // + // Returns the value comparator associated with this `btree_multimap`. + using Base::value_comp; +}; + +// absl::swap(absl::btree_multimap<>, absl::btree_multimap<>) +// +// Swaps the contents of two `absl::btree_multimap` containers. +template +void swap(btree_multimap &x, btree_multimap &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_multimap<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +template +void erase_if(btree_multimap &map, Pred pred) { + for (auto it = map.begin(); it != map.end();) { + if (pred(*it)) { + it = map.erase(it); + } else { + ++it; + } + } +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_BTREE_MAP_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/btree_set.h b/libs/or-tools-src-ubuntu/include/absl/container/btree_set.h new file mode 100644 index 0000000000000000000000000000000000000000..127fb940d40e0c044ab0a746b2d719f76b7ae0c9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/btree_set.h @@ -0,0 +1,683 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: btree_set.h +// ----------------------------------------------------------------------------- +// +// This header file defines B-tree sets: sorted associative containers of +// values. +// +// * `absl::btree_set<>` +// * `absl::btree_multiset<>` +// +// These B-tree types are similar to the corresponding types in the STL +// (`std::set` and `std::multiset`) and generally conform to the STL interfaces +// of those types. However, because they are implemented using B-trees, they +// are more efficient in most situations. +// +// Unlike `std::set` and `std::multiset`, which are commonly implemented using +// red-black tree nodes, B-tree sets use more generic B-tree nodes able to hold +// multiple values per node. Holding multiple values per node often makes +// B-tree sets perform better than their `std::set` counterparts, because +// multiple entries can be checked within the same cache hit. +// +// However, these types should not be considered drop-in replacements for +// `std::set` and `std::multiset` as there are some API differences, which are +// noted in this header file. +// +// Importantly, insertions and deletions may invalidate outstanding iterators, +// pointers, and references to elements. Such invalidations are typically only +// an issue if insertion and deletion operations are interleaved with the use of +// more than one iterator, pointer, or reference simultaneously. For this +// reason, `insert()` and `erase()` return a valid iterator at the current +// position. + +#ifndef ABSL_CONTAINER_BTREE_SET_H_ +#define ABSL_CONTAINER_BTREE_SET_H_ + +#include "absl/container/internal/btree.h" // IWYU pragma: export +#include "absl/container/internal/btree_container.h" // IWYU pragma: export + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// absl::btree_set<> +// +// An `absl::btree_set` is an ordered associative container of unique key +// values designed to be a more efficient replacement for `std::set` (in most +// cases). +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less`. +// +// An `absl::btree_set` uses a default allocator of `std::allocator` to +// allocate (and deallocate) nodes, and construct and destruct values within +// those nodes. You may instead specify a custom allocator `A` (which in turn +// requires specifying a custom comparator `C`) as in +// `absl::btree_set`. +// +template , + typename Alloc = std::allocator> +class btree_set + : public container_internal::btree_set_container< + container_internal::btree>> { + using Base = typename btree_set::btree_set_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_set` supports the same overload set as `std::set` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_set set1; + // + // * Initializer List constructor + // + // absl::btree_set set2 = + // {{"huey"}, {"dewey"}, {"louie"},}; + // + // * Copy constructor + // + // absl::btree_set set3(set2); + // + // * Copy assignment operator + // + // absl::btree_set set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_set set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_set set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector v = {"a", "b"}; + // absl::btree_set set7(v.begin(), v.end()); + btree_set() {} + using Base::Base; + + // btree_set::begin() + // + // Returns an iterator to the beginning of the `btree_set`. + using Base::begin; + + // btree_set::cbegin() + // + // Returns a const iterator to the beginning of the `btree_set`. + using Base::cbegin; + + // btree_set::end() + // + // Returns an iterator to the end of the `btree_set`. + using Base::end; + + // btree_set::cend() + // + // Returns a const iterator to the end of the `btree_set`. + using Base::cend; + + // btree_set::empty() + // + // Returns whether or not the `btree_set` is empty. + using Base::empty; + + // btree_set::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_set` under current memory constraints. This value can be thought + // of as the largest value of `std::distance(begin(), end())` for a + // `btree_set`. + using Base::max_size; + + // btree_set::size() + // + // Returns the number of elements currently within the `btree_set`. + using Base::size; + + // btree_set::clear() + // + // Removes all elements from the `btree_set`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_set::erase() + // + // Erases elements within the `btree_set`. Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_set`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template size_type erase(const K& key): + // + // Erases the element with the matching key, if it exists, returning the + // number of elements erased. + using Base::erase; + + // btree_set::insert() + // + // Inserts an element of the specified value into the `btree_set`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If an insertion + // occurs, any references, pointers, or iterators are invalidated. + // Overloads are listed below. + // + // std::pair insert(const value_type& value): + // + // Inserts a value into the `btree_set`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(value_type&& value): + // + // Inserts a moveable value into the `btree_set`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_set::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_set`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_set::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_set`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If an insertion occurs, any references, pointers, or iterators are + // invalidated. + using Base::emplace_hint; + + // btree_set::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template node_type extract(const K& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_set` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_set::merge() + // + // Extracts elements from a given `source` btree_set into this + // `btree_set`. If the destination `btree_set` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // btree_set::swap(btree_set& other) + // + // Exchanges the contents of this `btree_set` with those of the `other` + // btree_set, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `btree_set` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_set::contains() + // + // template bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_set`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::contains; + + // btree_set::count() + // + // template size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_set`. Note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `btree_set`. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::count; + + // btree_set::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_set`. + using Base::equal_range; + + // btree_set::find() + // + // template iterator find(const K& key): + // template const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_set`. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::find; + + // btree_set::get_allocator() + // + // Returns the allocator function associated with this `btree_set`. + using Base::get_allocator; + + // btree_set::key_comp(); + // + // Returns the key comparator associated with this `btree_set`. + using Base::key_comp; + + // btree_set::value_comp(); + // + // Returns the value comparator associated with this `btree_set`. The keys to + // sort the elements are the values themselves, therefore `value_comp` and its + // sibling member function `key_comp` are equivalent. + using Base::value_comp; +}; + +// absl::swap(absl::btree_set<>, absl::btree_set<>) +// +// Swaps the contents of two `absl::btree_set` containers. +template +void swap(btree_set &x, btree_set &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_set<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +template +void erase_if(btree_set &set, Pred pred) { + for (auto it = set.begin(); it != set.end();) { + if (pred(*it)) { + it = set.erase(it); + } else { + ++it; + } + } +} + +// absl::btree_multiset<> +// +// An `absl::btree_multiset` is an ordered associative container of +// keys and associated values designed to be a more efficient replacement +// for `std::multiset` (in most cases). Unlike `absl::btree_set`, a B-tree +// multiset allows equivalent elements. +// +// Keys are sorted using an (optional) comparison function, which defaults to +// `std::less`. +// +// An `absl::btree_multiset` uses a default allocator of `std::allocator` +// to allocate (and deallocate) nodes, and construct and destruct values within +// those nodes. You may instead specify a custom allocator `A` (which in turn +// requires specifying a custom comparator `C`) as in +// `absl::btree_multiset`. +// +template , + typename Alloc = std::allocator> +class btree_multiset + : public container_internal::btree_multiset_container< + container_internal::btree>> { + using Base = typename btree_multiset::btree_multiset_container; + + public: + // Constructors and Assignment Operators + // + // A `btree_multiset` supports the same overload set as `std::set` + // for construction and assignment: + // + // * Default constructor + // + // absl::btree_multiset set1; + // + // * Initializer List constructor + // + // absl::btree_multiset set2 = + // {{"huey"}, {"dewey"}, {"louie"},}; + // + // * Copy constructor + // + // absl::btree_multiset set3(set2); + // + // * Copy assignment operator + // + // absl::btree_multiset set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::btree_multiset set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::btree_multiset set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector v = {"a", "b"}; + // absl::btree_multiset set7(v.begin(), v.end()); + btree_multiset() {} + using Base::Base; + + // btree_multiset::begin() + // + // Returns an iterator to the beginning of the `btree_multiset`. + using Base::begin; + + // btree_multiset::cbegin() + // + // Returns a const iterator to the beginning of the `btree_multiset`. + using Base::cbegin; + + // btree_multiset::end() + // + // Returns an iterator to the end of the `btree_multiset`. + using Base::end; + + // btree_multiset::cend() + // + // Returns a const iterator to the end of the `btree_multiset`. + using Base::cend; + + // btree_multiset::empty() + // + // Returns whether or not the `btree_multiset` is empty. + using Base::empty; + + // btree_multiset::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `btree_multiset` under current memory constraints. This value can be + // thought of as the largest value of `std::distance(begin(), end())` for a + // `btree_multiset`. + using Base::max_size; + + // btree_multiset::size() + // + // Returns the number of elements currently within the `btree_multiset`. + using Base::size; + + // btree_multiset::clear() + // + // Removes all elements from the `btree_multiset`. Invalidates any references, + // pointers, or iterators referring to contained elements. + using Base::clear; + + // btree_multiset::erase() + // + // Erases elements within the `btree_multiset`. Overloads are listed below. + // + // iterator erase(iterator position): + // iterator erase(const_iterator position): + // + // Erases the element at `position` of the `btree_multiset`, returning + // the iterator pointing to the element after the one that was erased + // (or end() if none exists). + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning + // the iterator pointing to the element after the interval that was erased + // (or end() if none exists). + // + // template size_type erase(const K& key): + // + // Erases the elements matching the key, if any exist, returning the + // number of elements erased. + using Base::erase; + + // btree_multiset::insert() + // + // Inserts an element of the specified value into the `btree_multiset`, + // returning an iterator pointing to the newly inserted element. + // Any references, pointers, or iterators are invalidated. Overloads are + // listed below. + // + // iterator insert(const value_type& value): + // + // Inserts a value into the `btree_multiset`, returning an iterator to the + // inserted element. + // + // iterator insert(value_type&& value): + // + // Inserts a moveable value into the `btree_multiset`, returning an iterator + // to the inserted element. + // + // iterator insert(const_iterator hint, const value_type& value): + // iterator insert(const_iterator hint, value_type&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + using Base::insert; + + // btree_multiset::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multiset`. Any references, pointers, or iterators are + // invalidated. + using Base::emplace; + + // btree_multiset::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `btree_multiset`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search. + // + // Any references, pointers, or iterators are invalidated. + using Base::emplace_hint; + + // btree_multiset::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // template node_type extract(const K& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `btree_multiset` + // does not contain an element with a matching key, this function returns an + // empty node handle. + // + // NOTE: In this context, `node_type` refers to the C++17 concept of a + // move-only type that owns and provides access to the elements in associative + // containers (https://en.cppreference.com/w/cpp/container/node_handle). + // It does NOT refer to the data layout of the underlying btree. + using Base::extract; + + // btree_multiset::merge() + // + // Extracts elements from a given `source` btree_multiset into this + // `btree_multiset`. If the destination `btree_multiset` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // btree_multiset::swap(btree_multiset& other) + // + // Exchanges the contents of this `btree_multiset` with those of the `other` + // btree_multiset, avoiding invocation of any move, copy, or swap operations + // on individual elements. + // + // All iterators and references on the `btree_multiset` remain valid, + // excepting for the past-the-end iterator, which is invalidated. + using Base::swap; + + // btree_multiset::contains() + // + // template bool contains(const K& key) const: + // + // Determines whether an element comparing equal to the given `key` exists + // within the `btree_multiset`, returning `true` if so or `false` otherwise. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::contains; + + // btree_multiset::count() + // + // template size_type count(const K& key) const: + // + // Returns the number of elements comparing equal to the given `key` within + // the `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::count; + + // btree_multiset::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `btree_multiset`. + using Base::equal_range; + + // btree_multiset::find() + // + // template iterator find(const K& key): + // template const_iterator find(const K& key) const: + // + // Finds an element with the passed `key` within the `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set is provided a + // compatible heterogeneous comparator. + using Base::find; + + // btree_multiset::get_allocator() + // + // Returns the allocator function associated with this `btree_multiset`. + using Base::get_allocator; + + // btree_multiset::key_comp(); + // + // Returns the key comparator associated with this `btree_multiset`. + using Base::key_comp; + + // btree_multiset::value_comp(); + // + // Returns the value comparator associated with this `btree_multiset`. The + // keys to sort the elements are the values themselves, therefore `value_comp` + // and its sibling member function `key_comp` are equivalent. + using Base::value_comp; +}; + +// absl::swap(absl::btree_multiset<>, absl::btree_multiset<>) +// +// Swaps the contents of two `absl::btree_multiset` containers. +template +void swap(btree_multiset &x, btree_multiset &y) { + return x.swap(y); +} + +// absl::erase_if(absl::btree_multiset<>, Pred) +// +// Erases all elements that satisfy the predicate pred from the container. +template +void erase_if(btree_multiset &set, Pred pred) { + for (auto it = set.begin(); it != set.end();) { + if (pred(*it)) { + it = set.erase(it); + } else { + ++it; + } + } +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_BTREE_SET_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/btree_test.h b/libs/or-tools-src-ubuntu/include/absl/container/btree_test.h new file mode 100644 index 0000000000000000000000000000000000000000..218ba41dc2e9d2f6158abf05ae54deca9a1b6531 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/btree_test.h @@ -0,0 +1,155 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_BTREE_TEST_H_ +#define ABSL_CONTAINER_BTREE_TEST_H_ + +#include +#include +#include +#include +#include +#include + +#include "absl/container/btree_map.h" +#include "absl/container/btree_set.h" +#include "absl/container/flat_hash_set.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Like remove_const but propagates the removal through std::pair. +template +struct remove_pair_const { + using type = typename std::remove_const::type; +}; +template +struct remove_pair_const > { + using type = std::pair::type, + typename remove_pair_const::type>; +}; + +// Utility class to provide an accessor for a key given a value. The default +// behavior is to treat the value as a pair and return the first element. +template +struct KeyOfValue { + struct type { + const K& operator()(const V& p) const { return p.first; } + }; +}; + +// Partial specialization of KeyOfValue class for when the key and value are +// the same type such as in set<> and btree_set<>. +template +struct KeyOfValue { + struct type { + const K& operator()(const K& k) const { return k; } + }; +}; + +inline char* GenerateDigits(char buf[16], unsigned val, unsigned maxval) { + assert(val <= maxval); + constexpr unsigned kBase = 64; // avoid integer division. + unsigned p = 15; + buf[p--] = 0; + while (maxval > 0) { + buf[p--] = ' ' + (val % kBase); + val /= kBase; + maxval /= kBase; + } + return buf + p + 1; +} + +template +struct Generator { + int maxval; + explicit Generator(int m) : maxval(m) {} + K operator()(int i) const { + assert(i <= maxval); + return K(i); + } +}; + +template <> +struct Generator { + int maxval; + explicit Generator(int m) : maxval(m) {} + absl::Time operator()(int i) const { return absl::FromUnixMillis(i); } +}; + +template <> +struct Generator { + int maxval; + explicit Generator(int m) : maxval(m) {} + std::string operator()(int i) const { + char buf[16]; + return GenerateDigits(buf, i, maxval); + } +}; + +template +struct Generator > { + Generator::type> tgen; + Generator::type> ugen; + + explicit Generator(int m) : tgen(m), ugen(m) {} + std::pair operator()(int i) const { + return std::make_pair(tgen(i), ugen(i)); + } +}; + +// Generate n values for our tests and benchmarks. Value range is [0, maxval]. +inline std::vector GenerateNumbersWithSeed(int n, int maxval, int seed) { + // NOTE: Some tests rely on generated numbers not changing between test runs. + // We use std::minstd_rand0 because it is well-defined, but don't use + // std::uniform_int_distribution because platforms use different algorithms. + std::minstd_rand0 rng(seed); + + std::vector values; + absl::flat_hash_set unique_values; + if (values.size() < n) { + for (int i = values.size(); i < n; i++) { + int value; + do { + value = static_cast(rng()) % (maxval + 1); + } while (!unique_values.insert(value).second); + + values.push_back(value); + } + } + return values; +} + +// Generates n values in the range [0, maxval]. +template +std::vector GenerateValuesWithSeed(int n, int maxval, int seed) { + const std::vector nums = GenerateNumbersWithSeed(n, maxval, seed); + Generator gen(maxval); + std::vector vec; + + vec.reserve(n); + for (int i = 0; i < n; i++) { + vec.push_back(gen(nums[i])); + } + + return vec; +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_BTREE_TEST_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/fixed_array.h b/libs/or-tools-src-ubuntu/include/absl/container/fixed_array.h new file mode 100644 index 0000000000000000000000000000000000000000..a9ce99bafd3255aa8a73e979a7cec9682e289aec --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/fixed_array.h @@ -0,0 +1,515 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: fixed_array.h +// ----------------------------------------------------------------------------- +// +// A `FixedArray` represents a non-resizable array of `T` where the length of +// the array can be determined at run-time. It is a good replacement for +// non-standard and deprecated uses of `alloca()` and variable length arrays +// within the GCC extension. (See +// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html). +// +// `FixedArray` allocates small arrays inline, keeping performance fast by +// avoiding heap operations. It also helps reduce the chances of +// accidentally overflowing your stack if large input is passed to +// your function. + +#ifndef ABSL_CONTAINER_FIXED_ARRAY_H_ +#define ABSL_CONTAINER_FIXED_ARRAY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/algorithm.h" +#include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/base/macros.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +constexpr static auto kFixedArrayUseDefault = static_cast(-1); + +// ----------------------------------------------------------------------------- +// FixedArray +// ----------------------------------------------------------------------------- +// +// A `FixedArray` provides a run-time fixed-size array, allocating a small array +// inline for efficiency. +// +// Most users should not specify an `inline_elements` argument and let +// `FixedArray` automatically determine the number of elements +// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the +// `FixedArray` implementation will use inline storage for arrays with a +// length <= `inline_elements`. +// +// Note that a `FixedArray` constructed with a `size_type` argument will +// default-initialize its values by leaving trivially constructible types +// uninitialized (e.g. int, int[4], double), and others default-constructed. +// This matches the behavior of c-style arrays and `std::array`, but not +// `std::vector`. +// +// Note that `FixedArray` does not provide a public allocator; if it requires a +// heap allocation, it will do so with global `::operator new[]()` and +// `::operator delete[]()`, even if T provides class-scope overrides for these +// operators. +template > +class FixedArray { + static_assert(!std::is_array::value || std::extent::value > 0, + "Arrays with unknown bounds cannot be used with FixedArray."); + + static constexpr size_t kInlineBytesDefault = 256; + + using AllocatorTraits = std::allocator_traits; + // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17, + // but this seems to be mostly pedantic. + template + using EnableIfForwardIterator = absl::enable_if_t::iterator_category, + std::forward_iterator_tag>::value>; + static constexpr bool NoexceptCopyable() { + return std::is_nothrow_copy_constructible::value && + absl::allocator_is_nothrow::value; + } + static constexpr bool NoexceptMovable() { + return std::is_nothrow_move_constructible::value && + absl::allocator_is_nothrow::value; + } + static constexpr bool DefaultConstructorIsNonTrivial() { + return !absl::is_trivially_default_constructible::value; + } + + public: + using allocator_type = typename AllocatorTraits::allocator_type; + using value_type = typename allocator_type::value_type; + using pointer = typename allocator_type::pointer; + using const_pointer = typename allocator_type::const_pointer; + using reference = typename allocator_type::reference; + using const_reference = typename allocator_type::const_reference; + using size_type = typename allocator_type::size_type; + using difference_type = typename allocator_type::difference_type; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + static constexpr size_type inline_elements = + (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type) + : static_cast(N)); + + FixedArray( + const FixedArray& other, + const allocator_type& a = allocator_type()) noexcept(NoexceptCopyable()) + : FixedArray(other.begin(), other.end(), a) {} + + FixedArray( + FixedArray&& other, + const allocator_type& a = allocator_type()) noexcept(NoexceptMovable()) + : FixedArray(std::make_move_iterator(other.begin()), + std::make_move_iterator(other.end()), a) {} + + // Creates an array object that can store `n` elements. + // Note that trivially constructible elements will be uninitialized. + explicit FixedArray(size_type n, const allocator_type& a = allocator_type()) + : storage_(n, a) { + if (DefaultConstructorIsNonTrivial()) { + memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), + storage_.end()); + } + } + + // Creates an array initialized with `n` copies of `val`. + FixedArray(size_type n, const value_type& val, + const allocator_type& a = allocator_type()) + : storage_(n, a) { + memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), + storage_.end(), val); + } + + // Creates an array initialized with the size and contents of `init_list`. + FixedArray(std::initializer_list init_list, + const allocator_type& a = allocator_type()) + : FixedArray(init_list.begin(), init_list.end(), a) {} + + // Creates an array initialized with the elements from the input + // range. The array's size will always be `std::distance(first, last)`. + // REQUIRES: Iterator must be a forward_iterator or better. + template * = nullptr> + FixedArray(Iterator first, Iterator last, + const allocator_type& a = allocator_type()) + : storage_(std::distance(first, last), a) { + memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last); + } + + ~FixedArray() noexcept { + for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) { + AllocatorTraits::destroy(storage_.alloc(), cur); + } + } + + // Assignments are deleted because they break the invariant that the size of a + // `FixedArray` never changes. + void operator=(FixedArray&&) = delete; + void operator=(const FixedArray&) = delete; + + // FixedArray::size() + // + // Returns the length of the fixed array. + size_type size() const { return storage_.size(); } + + // FixedArray::max_size() + // + // Returns the largest possible value of `std::distance(begin(), end())` for a + // `FixedArray`. This is equivalent to the most possible addressable bytes + // over the number of bytes taken by T. + constexpr size_type max_size() const { + return (std::numeric_limits::max)() / sizeof(value_type); + } + + // FixedArray::empty() + // + // Returns whether or not the fixed array is empty. + bool empty() const { return size() == 0; } + + // FixedArray::memsize() + // + // Returns the memory size of the fixed array in bytes. + size_t memsize() const { return size() * sizeof(value_type); } + + // FixedArray::data() + // + // Returns a const T* pointer to elements of the `FixedArray`. This pointer + // can be used to access (but not modify) the contained elements. + const_pointer data() const { return AsValueType(storage_.begin()); } + + // Overload of FixedArray::data() to return a T* pointer to elements of the + // fixed array. This pointer can be used to access and modify the contained + // elements. + pointer data() { return AsValueType(storage_.begin()); } + + // FixedArray::operator[] + // + // Returns a reference the ith element of the fixed array. + // REQUIRES: 0 <= i < size() + reference operator[](size_type i) { + assert(i < size()); + return data()[i]; + } + + // Overload of FixedArray::operator()[] to return a const reference to the + // ith element of the fixed array. + // REQUIRES: 0 <= i < size() + const_reference operator[](size_type i) const { + assert(i < size()); + return data()[i]; + } + + // FixedArray::at + // + // Bounds-checked access. Returns a reference to the ith element of the + // fiexed array, or throws std::out_of_range + reference at(size_type i) { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); + } + return data()[i]; + } + + // Overload of FixedArray::at() to return a const reference to the ith element + // of the fixed array. + const_reference at(size_type i) const { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); + } + return data()[i]; + } + + // FixedArray::front() + // + // Returns a reference to the first element of the fixed array. + reference front() { return *begin(); } + + // Overload of FixedArray::front() to return a reference to the first element + // of a fixed array of const values. + const_reference front() const { return *begin(); } + + // FixedArray::back() + // + // Returns a reference to the last element of the fixed array. + reference back() { return *(end() - 1); } + + // Overload of FixedArray::back() to return a reference to the last element + // of a fixed array of const values. + const_reference back() const { return *(end() - 1); } + + // FixedArray::begin() + // + // Returns an iterator to the beginning of the fixed array. + iterator begin() { return data(); } + + // Overload of FixedArray::begin() to return a const iterator to the + // beginning of the fixed array. + const_iterator begin() const { return data(); } + + // FixedArray::cbegin() + // + // Returns a const iterator to the beginning of the fixed array. + const_iterator cbegin() const { return begin(); } + + // FixedArray::end() + // + // Returns an iterator to the end of the fixed array. + iterator end() { return data() + size(); } + + // Overload of FixedArray::end() to return a const iterator to the end of the + // fixed array. + const_iterator end() const { return data() + size(); } + + // FixedArray::cend() + // + // Returns a const iterator to the end of the fixed array. + const_iterator cend() const { return end(); } + + // FixedArray::rbegin() + // + // Returns a reverse iterator from the end of the fixed array. + reverse_iterator rbegin() { return reverse_iterator(end()); } + + // Overload of FixedArray::rbegin() to return a const reverse iterator from + // the end of the fixed array. + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + + // FixedArray::crbegin() + // + // Returns a const reverse iterator from the end of the fixed array. + const_reverse_iterator crbegin() const { return rbegin(); } + + // FixedArray::rend() + // + // Returns a reverse iterator from the beginning of the fixed array. + reverse_iterator rend() { return reverse_iterator(begin()); } + + // Overload of FixedArray::rend() for returning a const reverse iterator + // from the beginning of the fixed array. + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // FixedArray::crend() + // + // Returns a reverse iterator from the beginning of the fixed array. + const_reverse_iterator crend() const { return rend(); } + + // FixedArray::fill() + // + // Assigns the given `value` to all elements in the fixed array. + void fill(const value_type& val) { std::fill(begin(), end(), val); } + + // Relational operators. Equality operators are elementwise using + // `operator==`, while order operators order FixedArrays lexicographically. + friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) { + return absl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) { + return !(lhs == rhs); + } + + friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) { + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), + rhs.end()); + } + + friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) { + return rhs < lhs; + } + + friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) { + return !(rhs < lhs); + } + + friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) { + return !(lhs < rhs); + } + + template + friend H AbslHashValue(H h, const FixedArray& v) { + return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()), + v.size()); + } + + private: + // StorageElement + // + // For FixedArrays with a C-style-array value_type, StorageElement is a POD + // wrapper struct called StorageElementWrapper that holds the value_type + // instance inside. This is needed for construction and destruction of the + // entire array regardless of how many dimensions it has. For all other cases, + // StorageElement is just an alias of value_type. + // + // Maintainer's Note: The simpler solution would be to simply wrap value_type + // in a struct whether it's an array or not. That causes some paranoid + // diagnostics to misfire, believing that 'data()' returns a pointer to a + // single element, rather than the packed array that it really is. + // e.g.: + // + // FixedArray buf(1); + // sprintf(buf.data(), "foo"); + // + // error: call to int __builtin___sprintf_chk(etc...) + // will always overflow destination buffer [-Werror] + // + template , + size_t InnerN = std::extent::value> + struct StorageElementWrapper { + InnerT array[InnerN]; + }; + + using StorageElement = + absl::conditional_t::value, + StorageElementWrapper, value_type>; + + static pointer AsValueType(pointer ptr) { return ptr; } + static pointer AsValueType(StorageElementWrapper* ptr) { + return std::addressof(ptr->array); + } + + static_assert(sizeof(StorageElement) == sizeof(value_type), ""); + static_assert(alignof(StorageElement) == alignof(value_type), ""); + + class NonEmptyInlinedStorage { + public: + StorageElement* data() { return reinterpret_cast(buff_); } + void AnnotateConstruct(size_type n); + void AnnotateDestruct(size_type n); + +#ifdef ADDRESS_SANITIZER + void* RedzoneBegin() { return &redzone_begin_; } + void* RedzoneEnd() { return &redzone_end_ + 1; } +#endif // ADDRESS_SANITIZER + + private: + ADDRESS_SANITIZER_REDZONE(redzone_begin_); + alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])]; + ADDRESS_SANITIZER_REDZONE(redzone_end_); + }; + + class EmptyInlinedStorage { + public: + StorageElement* data() { return nullptr; } + void AnnotateConstruct(size_type) {} + void AnnotateDestruct(size_type) {} + }; + + using InlinedStorage = + absl::conditional_t; + + // Storage + // + // An instance of Storage manages the inline and out-of-line memory for + // instances of FixedArray. This guarantees that even when construction of + // individual elements fails in the FixedArray constructor body, the + // destructor for Storage will still be called and out-of-line memory will be + // properly deallocated. + // + class Storage : public InlinedStorage { + public: + Storage(size_type n, const allocator_type& a) + : size_alloc_(n, a), data_(InitializeData()) {} + + ~Storage() noexcept { + if (UsingInlinedStorage(size())) { + InlinedStorage::AnnotateDestruct(size()); + } else { + AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size()); + } + } + + size_type size() const { return size_alloc_.template get<0>(); } + StorageElement* begin() const { return data_; } + StorageElement* end() const { return begin() + size(); } + allocator_type& alloc() { return size_alloc_.template get<1>(); } + + private: + static bool UsingInlinedStorage(size_type n) { + return n <= inline_elements; + } + + StorageElement* InitializeData() { + if (UsingInlinedStorage(size())) { + InlinedStorage::AnnotateConstruct(size()); + return InlinedStorage::data(); + } else { + return reinterpret_cast( + AllocatorTraits::allocate(alloc(), size())); + } + } + + // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s + container_internal::CompressedTuple size_alloc_; + StorageElement* data_; + }; + + Storage storage_; +}; + +template +constexpr size_t FixedArray::kInlineBytesDefault; + +template +constexpr typename FixedArray::size_type + FixedArray::inline_elements; + +template +void FixedArray::NonEmptyInlinedStorage::AnnotateConstruct( + typename FixedArray::size_type n) { +#ifdef ADDRESS_SANITIZER + if (!n) return; + ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), data() + n); + ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), RedzoneBegin()); +#endif // ADDRESS_SANITIZER + static_cast(n); // Mark used when not in asan mode +} + +template +void FixedArray::NonEmptyInlinedStorage::AnnotateDestruct( + typename FixedArray::size_type n) { +#ifdef ADDRESS_SANITIZER + if (!n) return; + ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, RedzoneEnd()); + ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), data()); +#endif // ADDRESS_SANITIZER + static_cast(n); // Mark used when not in asan mode +} +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_FIXED_ARRAY_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/flat_hash_map.h b/libs/or-tools-src-ubuntu/include/absl/container/flat_hash_map.h new file mode 100644 index 0000000000000000000000000000000000000000..fcb70d861f39ce6a7f1dee21b384b7a7fe9d0d22 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/flat_hash_map.h @@ -0,0 +1,600 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: flat_hash_map.h +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_map` is an unordered associative container of +// unique keys and associated values designed to be a more efficient replacement +// for `std::unordered_map`. Like `unordered_map`, search, insertion, and +// deletion of map elements can be done as an `O(1)` operation. However, +// `flat_hash_map` (and other unordered associative containers known as the +// collection of Abseil "Swiss tables") contain other optimizations that result +// in both memory and computation advantages. +// +// In most cases, your default choice for a hash map should be a map of type +// `flat_hash_map`. + +#ifndef ABSL_CONTAINER_FLAT_HASH_MAP_H_ +#define ABSL_CONTAINER_FLAT_HASH_MAP_H_ + +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +template +struct FlatHashMapPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::flat_hash_map +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_map` is an unordered associative container which +// has been optimized for both speed and memory footprint in most common use +// cases. Its interface is similar to that of `std::unordered_map` with +// the following notable differences: +// +// * Requires keys that are CopyConstructible +// * Requires values that are MoveConstructible +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the map is provided a compatible heterogeneous +// hashing function and equality operator. +// * Invalidates any references and pointers to elements within the table after +// `rehash()`. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash map. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `flat_hash_map` uses the `absl::Hash` hashing framework. +// All fundamental and Abseil types that support the `absl::Hash` framework have +// a compatible equality operator for comparing insertions into `flat_hash_map`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// NOTE: A `flat_hash_map` stores its value types directly inside its +// implementation array to avoid memory indirection. Because a `flat_hash_map` +// is designed to move data when rehashed, map values will not retain pointer +// stability. If you require pointer stability, or if your values are large, +// consider using `absl::flat_hash_map>` instead. +// If your types are not moveable or you require pointer stability for keys, +// consider `absl::node_hash_map`. +// +// Example: +// +// // Create a flat hash map of three strings (that map to strings) +// absl::flat_hash_map ducks = +// {{"a", "huey"}, {"b", "dewey"}, {"c", "louie"}}; +// +// // Insert a new element into the flat hash map +// ducks.insert({"d", "donald"}); +// +// // Force a rehash of the flat hash map +// ducks.rehash(0); +// +// // Find the element with the key "b" +// std::string search_key = "b"; +// auto result = ducks.find(search_key); +// if (result != ducks.end()) { +// std::cout << "Result: " << result->second << std::endl; +// } +template , + class Eq = absl::container_internal::hash_default_eq, + class Allocator = std::allocator>> +class flat_hash_map : public absl::container_internal::raw_hash_map< + absl::container_internal::FlatHashMapPolicy, + Hash, Eq, Allocator> { + using Base = typename flat_hash_map::raw_hash_map; + + public: + // Constructors and Assignment Operators + // + // A flat_hash_map supports the same overload set as `std::unordered_map` + // for construction and assignment: + // + // * Default constructor + // + // // No allocation for the table's elements is made. + // absl::flat_hash_map map1; + // + // * Initializer List constructor + // + // absl::flat_hash_map map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::flat_hash_map map3(map2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::flat_hash_map map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::flat_hash_map map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::flat_hash_map map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::flat_hash_map map7(v.begin(), v.end()); + flat_hash_map() {} + using Base::Base; + + // flat_hash_map::begin() + // + // Returns an iterator to the beginning of the `flat_hash_map`. + using Base::begin; + + // flat_hash_map::cbegin() + // + // Returns a const iterator to the beginning of the `flat_hash_map`. + using Base::cbegin; + + // flat_hash_map::cend() + // + // Returns a const iterator to the end of the `flat_hash_map`. + using Base::cend; + + // flat_hash_map::end() + // + // Returns an iterator to the end of the `flat_hash_map`. + using Base::end; + + // flat_hash_map::capacity() + // + // Returns the number of element slots (assigned, deleted, and empty) + // available within the `flat_hash_map`. + // + // NOTE: this member function is particular to `absl::flat_hash_map` and is + // not provided in the `std::unordered_map` API. + using Base::capacity; + + // flat_hash_map::empty() + // + // Returns whether or not the `flat_hash_map` is empty. + using Base::empty; + + // flat_hash_map::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `flat_hash_map` under current memory constraints. This value can be thought + // of the largest value of `std::distance(begin(), end())` for a + // `flat_hash_map`. + using Base::max_size; + + // flat_hash_map::size() + // + // Returns the number of elements currently within the `flat_hash_map`. + using Base::size; + + // flat_hash_map::clear() + // + // Removes all elements from the `flat_hash_map`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // flat_hash_map::erase() + // + // Erases elements within the `flat_hash_map`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // Erases the element at `position` of the `flat_hash_map`, returning + // `void`. + // + // NOTE: returning `void` in this case is different than that of STL + // containers in general and `std::unordered_map` in particular (which + // return an iterator to the element following the erased element). If that + // iterator is needed, simply post increment the iterator: + // + // map.erase(it++); + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists. + using Base::erase; + + // flat_hash_map::insert() + // + // Inserts an element of the specified value into the `flat_hash_map`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair insert(const init_type& value): + // + // Inserts a value into the `flat_hash_map`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(T&& value): + // std::pair insert(init_type&& value): + // + // Inserts a moveable value into the `flat_hash_map`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const init_type& value): + // iterator insert(const_iterator hint, T&& value): + // iterator insert(const_iterator hint, init_type&& value); + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently, for `flat_hash_map` we guarantee the + // first match is inserted. + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently within the initializer list, for + // `flat_hash_map` we guarantee the first match is inserted. + using Base::insert; + + // flat_hash_map::insert_or_assign() + // + // Inserts an element of the specified value into the `flat_hash_map` provided + // that a value with the given key does not already exist, or replaces it with + // the element value if a key for that value already exists, returning an + // iterator pointing to the newly inserted element. If rehashing occurs due + // to the insertion, all existing iterators are invalidated. Overloads are + // listed below. + // + // pair insert_or_assign(const init_type& k, T&& obj): + // pair insert_or_assign(init_type&& k, T&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `flat_hash_map`. + // + // iterator insert_or_assign(const_iterator hint, + // const init_type& k, T&& obj): + // iterator insert_or_assign(const_iterator hint, init_type&& k, T&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `flat_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::insert_or_assign; + + // flat_hash_map::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_map`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace; + + // flat_hash_map::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_map`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace_hint; + + // flat_hash_map::try_emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_map`, provided that no element with the given key + // already exists. Unlike `emplace()`, if an element with the given key + // already exists, we guarantee that no element is constructed. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + // Overloads are listed below. + // + // pair try_emplace(const key_type& k, Args&&... args): + // pair try_emplace(key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `flat_hash_map`. + // + // iterator try_emplace(const_iterator hint, + // const init_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, init_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `flat_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + // + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. + using Base::try_emplace; + + // flat_hash_map::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the key,value pair of the element at the indicated position and + // returns a node handle owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the key,value pair of the element with a key matching the passed + // key value and returns a node handle owning that extracted data. If the + // `flat_hash_map` does not contain an element with a matching key, this + // function returns an empty node handle. + using Base::extract; + + // flat_hash_map::merge() + // + // Extracts elements from a given `source` flat hash map into this + // `flat_hash_map`. If the destination `flat_hash_map` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // flat_hash_map::swap(flat_hash_map& other) + // + // Exchanges the contents of this `flat_hash_map` with those of the `other` + // flat hash map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `flat_hash_map` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the flat hash map's hashing and key equivalence + // functions be Swappable, and are exchanged using unqualified calls to + // non-member `swap()`. If the map's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // flat_hash_map::rehash(count) + // + // Rehashes the `flat_hash_map`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + // + // NOTE: unlike behavior in `std::unordered_map`, references are also + // invalidated upon a `rehash()`. + using Base::rehash; + + // flat_hash_map::reserve(count) + // + // Sets the number of slots in the `flat_hash_map` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // flat_hash_map::at() + // + // Returns a reference to the mapped value of the element with key equivalent + // to the passed key. + using Base::at; + + // flat_hash_map::contains() + // + // Determines whether an element with a key comparing equal to the given `key` + // exists within the `flat_hash_map`, returning `true` if so or `false` + // otherwise. + using Base::contains; + + // flat_hash_map::count(const Key& key) const + // + // Returns the number of elements with a key comparing equal to the given + // `key` within the `flat_hash_map`. note that this function will return + // either `1` or `0` since duplicate keys are not allowed within a + // `flat_hash_map`. + using Base::count; + + // flat_hash_map::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `flat_hash_map`. + using Base::equal_range; + + // flat_hash_map::find() + // + // Finds an element with the passed `key` within the `flat_hash_map`. + using Base::find; + + // flat_hash_map::operator[]() + // + // Returns a reference to the value mapped to the passed key within the + // `flat_hash_map`, performing an `insert()` if the key does not already + // exist. + // + // If an insertion occurs and results in a rehashing of the container, all + // iterators are invalidated. Otherwise iterators are not affected and + // references are not invalidated. Overloads are listed below. + // + // T& operator[](const Key& key): + // + // Inserts an init_type object constructed in-place if the element with the + // given key does not exist. + // + // T& operator[](Key&& key): + // + // Inserts an init_type object constructed in-place provided that an element + // with the given key does not exist. + using Base::operator[]; + + // flat_hash_map::bucket_count() + // + // Returns the number of "buckets" within the `flat_hash_map`. Note that + // because a flat hash map contains all elements within its internal storage, + // this value simply equals the current capacity of the `flat_hash_map`. + using Base::bucket_count; + + // flat_hash_map::load_factor() + // + // Returns the current load factor of the `flat_hash_map` (the average number + // of slots occupied with a value within the hash map). + using Base::load_factor; + + // flat_hash_map::max_load_factor() + // + // Manages the maximum load factor of the `flat_hash_map`. Overloads are + // listed below. + // + // float flat_hash_map::max_load_factor() + // + // Returns the current maximum load factor of the `flat_hash_map`. + // + // void flat_hash_map::max_load_factor(float ml) + // + // Sets the maximum load factor of the `flat_hash_map` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `flat_hash_map` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // flat_hash_map::get_allocator() + // + // Returns the allocator function associated with this `flat_hash_map`. + using Base::get_allocator; + + // flat_hash_map::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `flat_hash_map`. + using Base::hash_function; + + // flat_hash_map::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; +}; + +// erase_if(flat_hash_map<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template +void erase_if(flat_hash_map& c, Predicate pred) { + container_internal::EraseIf(pred, &c); +} + +namespace container_internal { + +template +struct FlatHashMapPolicy { + using slot_policy = container_internal::map_slot_policy; + using slot_type = typename slot_policy::slot_type; + using key_type = K; + using mapped_type = V; + using init_type = std::pair; + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + slot_policy::construct(alloc, slot, std::forward(args)...); + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + slot_policy::destroy(alloc, slot); + } + + template + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + slot_policy::transfer(alloc, new_slot, old_slot); + } + + template + static decltype(absl::container_internal::DecomposePair( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposePair(std::forward(f), + std::forward(args)...); + } + + static size_t space_used(const slot_type*) { return 0; } + + static std::pair& element(slot_type* slot) { return slot->value; } + + static V& value(std::pair* kv) { return kv->second; } + static const V& value(const std::pair* kv) { return kv->second; } +}; + +} // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template +struct IsUnorderedContainer< + absl::flat_hash_map> : std::true_type {}; + +} // namespace container_algorithm_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/flat_hash_set.h b/libs/or-tools-src-ubuntu/include/absl/container/flat_hash_set.h new file mode 100644 index 0000000000000000000000000000000000000000..94be6e3d13f1bc0c34c6e0b5233ab34265c5db5c --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/flat_hash_set.h @@ -0,0 +1,503 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: flat_hash_set.h +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_set` is an unordered associative container designed to +// be a more efficient replacement for `std::unordered_set`. Like +// `unordered_set`, search, insertion, and deletion of set elements can be done +// as an `O(1)` operation. However, `flat_hash_set` (and other unordered +// associative containers known as the collection of Abseil "Swiss tables") +// contain other optimizations that result in both memory and computation +// advantages. +// +// In most cases, your default choice for a hash set should be a set of type +// `flat_hash_set`. +#ifndef ABSL_CONTAINER_FLAT_HASH_SET_H_ +#define ABSL_CONTAINER_FLAT_HASH_SET_H_ + +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/base/macros.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +template +struct FlatHashSetPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::flat_hash_set +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_set` is an unordered associative container which has +// been optimized for both speed and memory footprint in most common use cases. +// Its interface is similar to that of `std::unordered_set` with the +// following notable differences: +// +// * Requires keys that are CopyConstructible +// * Supports heterogeneous lookup, through `find()` and `insert()`, provided +// that the set is provided a compatible heterogeneous hashing function and +// equality operator. +// * Invalidates any references and pointers to elements within the table after +// `rehash()`. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash set. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `flat_hash_set` uses the `absl::Hash` hashing framework. All +// fundamental and Abseil types that support the `absl::Hash` framework have a +// compatible equality operator for comparing insertions into `flat_hash_map`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// NOTE: A `flat_hash_set` stores its keys directly inside its implementation +// array to avoid memory indirection. Because a `flat_hash_set` is designed to +// move data when rehashed, set keys will not retain pointer stability. If you +// require pointer stability, consider using +// `absl::flat_hash_set>`. If your type is not moveable and +// you require pointer stability, consider `absl::node_hash_set` instead. +// +// Example: +// +// // Create a flat hash set of three strings +// absl::flat_hash_set ducks = +// {"huey", "dewey", "louie"}; +// +// // Insert a new element into the flat hash set +// ducks.insert("donald"); +// +// // Force a rehash of the flat hash set +// ducks.rehash(0); +// +// // See if "dewey" is present +// if (ducks.contains("dewey")) { +// std::cout << "We found dewey!" << std::endl; +// } +template , + class Eq = absl::container_internal::hash_default_eq, + class Allocator = std::allocator> +class flat_hash_set + : public absl::container_internal::raw_hash_set< + absl::container_internal::FlatHashSetPolicy, Hash, Eq, Allocator> { + using Base = typename flat_hash_set::raw_hash_set; + + public: + // Constructors and Assignment Operators + // + // A flat_hash_set supports the same overload set as `std::unordered_map` + // for construction and assignment: + // + // * Default constructor + // + // // No allocation for the table's elements is made. + // absl::flat_hash_set set1; + // + // * Initializer List constructor + // + // absl::flat_hash_set set2 = + // {{"huey"}, {"dewey"}, {"louie"},}; + // + // * Copy constructor + // + // absl::flat_hash_set set3(set2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::flat_hash_set set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::flat_hash_set set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::flat_hash_set set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector v = {"a", "b"}; + // absl::flat_hash_set set7(v.begin(), v.end()); + flat_hash_set() {} + using Base::Base; + + // flat_hash_set::begin() + // + // Returns an iterator to the beginning of the `flat_hash_set`. + using Base::begin; + + // flat_hash_set::cbegin() + // + // Returns a const iterator to the beginning of the `flat_hash_set`. + using Base::cbegin; + + // flat_hash_set::cend() + // + // Returns a const iterator to the end of the `flat_hash_set`. + using Base::cend; + + // flat_hash_set::end() + // + // Returns an iterator to the end of the `flat_hash_set`. + using Base::end; + + // flat_hash_set::capacity() + // + // Returns the number of element slots (assigned, deleted, and empty) + // available within the `flat_hash_set`. + // + // NOTE: this member function is particular to `absl::flat_hash_set` and is + // not provided in the `std::unordered_map` API. + using Base::capacity; + + // flat_hash_set::empty() + // + // Returns whether or not the `flat_hash_set` is empty. + using Base::empty; + + // flat_hash_set::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `flat_hash_set` under current memory constraints. This value can be thought + // of the largest value of `std::distance(begin(), end())` for a + // `flat_hash_set`. + using Base::max_size; + + // flat_hash_set::size() + // + // Returns the number of elements currently within the `flat_hash_set`. + using Base::size; + + // flat_hash_set::clear() + // + // Removes all elements from the `flat_hash_set`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // flat_hash_set::erase() + // + // Erases elements within the `flat_hash_set`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // Erases the element at `position` of the `flat_hash_set`, returning + // `void`. + // + // NOTE: returning `void` in this case is different than that of STL + // containers in general and `std::unordered_set` in particular (which + // return an iterator to the element following the erased element). If that + // iterator is needed, simply post increment the iterator: + // + // set.erase(it++); + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists. + using Base::erase; + + // flat_hash_set::insert() + // + // Inserts an element of the specified value into the `flat_hash_set`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair insert(const T& value): + // + // Inserts a value into the `flat_hash_set`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(T&& value): + // + // Inserts a moveable value into the `flat_hash_set`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const T& value): + // iterator insert(const_iterator hint, T&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently, for `flat_hash_set` we guarantee the + // first match is inserted. + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently within the initializer list, for + // `flat_hash_set` we guarantee the first match is inserted. + using Base::insert; + + // flat_hash_set::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_set`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace; + + // flat_hash_set::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_set`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace_hint; + + // flat_hash_set::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `flat_hash_set` + // does not contain an element with a matching key, this function returns an + // empty node handle. + using Base::extract; + + // flat_hash_set::merge() + // + // Extracts elements from a given `source` flat hash map into this + // `flat_hash_set`. If the destination `flat_hash_set` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // flat_hash_set::swap(flat_hash_set& other) + // + // Exchanges the contents of this `flat_hash_set` with those of the `other` + // flat hash map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `flat_hash_set` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the flat hash set's hashing and key equivalence + // functions be Swappable, and are exchaged using unqualified calls to + // non-member `swap()`. If the map's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // flat_hash_set::rehash(count) + // + // Rehashes the `flat_hash_set`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + // + // NOTE: unlike behavior in `std::unordered_set`, references are also + // invalidated upon a `rehash()`. + using Base::rehash; + + // flat_hash_set::reserve(count) + // + // Sets the number of slots in the `flat_hash_set` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // flat_hash_set::contains() + // + // Determines whether an element comparing equal to the given `key` exists + // within the `flat_hash_set`, returning `true` if so or `false` otherwise. + using Base::contains; + + // flat_hash_set::count(const Key& key) const + // + // Returns the number of elements comparing equal to the given `key` within + // the `flat_hash_set`. note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `flat_hash_set`. + using Base::count; + + // flat_hash_set::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `flat_hash_set`. + using Base::equal_range; + + // flat_hash_set::find() + // + // Finds an element with the passed `key` within the `flat_hash_set`. + using Base::find; + + // flat_hash_set::bucket_count() + // + // Returns the number of "buckets" within the `flat_hash_set`. Note that + // because a flat hash map contains all elements within its internal storage, + // this value simply equals the current capacity of the `flat_hash_set`. + using Base::bucket_count; + + // flat_hash_set::load_factor() + // + // Returns the current load factor of the `flat_hash_set` (the average number + // of slots occupied with a value within the hash map). + using Base::load_factor; + + // flat_hash_set::max_load_factor() + // + // Manages the maximum load factor of the `flat_hash_set`. Overloads are + // listed below. + // + // float flat_hash_set::max_load_factor() + // + // Returns the current maximum load factor of the `flat_hash_set`. + // + // void flat_hash_set::max_load_factor(float ml) + // + // Sets the maximum load factor of the `flat_hash_set` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `flat_hash_set` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // flat_hash_set::get_allocator() + // + // Returns the allocator function associated with this `flat_hash_set`. + using Base::get_allocator; + + // flat_hash_set::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `flat_hash_set`. + using Base::hash_function; + + // flat_hash_set::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; +}; + +// erase_if(flat_hash_set<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template +void erase_if(flat_hash_set& c, Predicate pred) { + container_internal::EraseIf(pred, &c); +} + +namespace container_internal { + +template +struct FlatHashSetPolicy { + using slot_type = T; + using key_type = T; + using init_type = T; + using constant_iterators = std::true_type; + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + absl::allocator_traits::construct(*alloc, slot, + std::forward(args)...); + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + absl::allocator_traits::destroy(*alloc, slot); + } + + template + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + construct(alloc, new_slot, std::move(*old_slot)); + destroy(alloc, old_slot); + } + + static T& element(slot_type* slot) { return *slot; } + + template + static decltype(absl::container_internal::DecomposeValue( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposeValue( + std::forward(f), std::forward(args)...); + } + + static size_t space_used(const T*) { return 0; } +}; +} // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template +struct IsUnorderedContainer> + : std::true_type {}; + +} // namespace container_algorithm_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_FLAT_HASH_SET_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/inlined_vector.h b/libs/or-tools-src-ubuntu/include/absl/container/inlined_vector.h new file mode 100644 index 0000000000000000000000000000000000000000..2388d471dcc8192f4374d4614da4c425fd7d8f13 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/inlined_vector.h @@ -0,0 +1,848 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: inlined_vector.h +// ----------------------------------------------------------------------------- +// +// This header file contains the declaration and definition of an "inlined +// vector" which behaves in an equivalent fashion to a `std::vector`, except +// that storage for small sequences of the vector are provided inline without +// requiring any heap allocation. +// +// An `absl::InlinedVector` specifies the default capacity `N` as one of +// its template parameters. Instances where `size() <= N` hold contained +// elements in inline space. Typically `N` is very small so that sequences that +// are expected to be short do not require allocations. +// +// An `absl::InlinedVector` does not usually require a specific allocator. If +// the inlined vector grows beyond its initial constraints, it will need to +// allocate (as any normal `std::vector` would). This is usually performed with +// the default allocator (defined as `std::allocator`). Optionally, a custom +// allocator type may be specified as `A` in `absl::InlinedVector`. + +#ifndef ABSL_CONTAINER_INLINED_VECTOR_H_ +#define ABSL_CONTAINER_INLINED_VECTOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/algorithm.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" +#include "absl/container/internal/inlined_vector.h" +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +// ----------------------------------------------------------------------------- +// InlinedVector +// ----------------------------------------------------------------------------- +// +// An `absl::InlinedVector` is designed to be a drop-in replacement for +// `std::vector` for use cases where the vector's size is sufficiently small +// that it can be inlined. If the inlined vector does grow beyond its estimated +// capacity, it will trigger an initial allocation on the heap, and will behave +// as a `std:vector`. The API of the `absl::InlinedVector` within this file is +// designed to cover the same API footprint as covered by `std::vector`. +template > +class InlinedVector { + static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity."); + + using Storage = inlined_vector_internal::Storage; + + using AllocatorTraits = typename Storage::AllocatorTraits; + using RValueReference = typename Storage::RValueReference; + using MoveIterator = typename Storage::MoveIterator; + using IsMemcpyOk = typename Storage::IsMemcpyOk; + + template + using IteratorValueAdapter = + typename Storage::template IteratorValueAdapter; + using CopyValueAdapter = typename Storage::CopyValueAdapter; + using DefaultValueAdapter = typename Storage::DefaultValueAdapter; + + template + using EnableIfAtLeastForwardIterator = absl::enable_if_t< + inlined_vector_internal::IsAtLeastForwardIterator::value>; + template + using DisableIfAtLeastForwardIterator = absl::enable_if_t< + !inlined_vector_internal::IsAtLeastForwardIterator::value>; + + public: + using allocator_type = typename Storage::allocator_type; + using value_type = typename Storage::value_type; + using pointer = typename Storage::pointer; + using const_pointer = typename Storage::const_pointer; + using size_type = typename Storage::size_type; + using difference_type = typename Storage::difference_type; + using reference = typename Storage::reference; + using const_reference = typename Storage::const_reference; + using iterator = typename Storage::iterator; + using const_iterator = typename Storage::const_iterator; + using reverse_iterator = typename Storage::reverse_iterator; + using const_reverse_iterator = typename Storage::const_reverse_iterator; + + // --------------------------------------------------------------------------- + // InlinedVector Constructors and Destructor + // --------------------------------------------------------------------------- + + // Creates an empty inlined vector with a value-initialized allocator. + InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {} + + // Creates an empty inlined vector with a copy of `alloc`. + explicit InlinedVector(const allocator_type& alloc) noexcept + : storage_(alloc) {} + + // Creates an inlined vector with `n` copies of `value_type()`. + explicit InlinedVector(size_type n, + const allocator_type& alloc = allocator_type()) + : storage_(alloc) { + storage_.Initialize(DefaultValueAdapter(), n); + } + + // Creates an inlined vector with `n` copies of `v`. + InlinedVector(size_type n, const_reference v, + const allocator_type& alloc = allocator_type()) + : storage_(alloc) { + storage_.Initialize(CopyValueAdapter(v), n); + } + + // Creates an inlined vector with copies of the elements of `list`. + InlinedVector(std::initializer_list list, + const allocator_type& alloc = allocator_type()) + : InlinedVector(list.begin(), list.end(), alloc) {} + + // Creates an inlined vector with elements constructed from the provided + // forward iterator range [`first`, `last`). + // + // NOTE: the `enable_if` prevents ambiguous interpretation between a call to + // this constructor with two integral arguments and a call to the above + // `InlinedVector(size_type, const_reference)` constructor. + template * = nullptr> + InlinedVector(ForwardIterator first, ForwardIterator last, + const allocator_type& alloc = allocator_type()) + : storage_(alloc) { + storage_.Initialize(IteratorValueAdapter(first), + std::distance(first, last)); + } + + // Creates an inlined vector with elements constructed from the provided input + // iterator range [`first`, `last`). + template * = nullptr> + InlinedVector(InputIterator first, InputIterator last, + const allocator_type& alloc = allocator_type()) + : storage_(alloc) { + std::copy(first, last, std::back_inserter(*this)); + } + + // Creates an inlined vector by copying the contents of `other` using + // `other`'s allocator. + InlinedVector(const InlinedVector& other) + : InlinedVector(other, *other.storage_.GetAllocPtr()) {} + + // Creates an inlined vector by copying the contents of `other` using `alloc`. + InlinedVector(const InlinedVector& other, const allocator_type& alloc) + : storage_(alloc) { + if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) { + storage_.MemcpyFrom(other.storage_); + } else { + storage_.Initialize(IteratorValueAdapter(other.data()), + other.size()); + } + } + + // Creates an inlined vector by moving in the contents of `other` without + // allocating. If `other` contains allocated memory, the newly-created inlined + // vector will take ownership of that memory. However, if `other` does not + // contain allocated memory, the newly-created inlined vector will perform + // element-wise move construction of the contents of `other`. + // + // NOTE: since no allocation is performed for the inlined vector in either + // case, the `noexcept(...)` specification depends on whether moving the + // underlying objects can throw. It is assumed assumed that... + // a) move constructors should only throw due to allocation failure. + // b) if `value_type`'s move constructor allocates, it uses the same + // allocation function as the inlined vector's allocator. + // Thus, the move constructor is non-throwing if the allocator is non-throwing + // or `value_type`'s move constructor is specified as `noexcept`. + InlinedVector(InlinedVector&& other) noexcept( + absl::allocator_is_nothrow::value || + std::is_nothrow_move_constructible::value) + : storage_(*other.storage_.GetAllocPtr()) { + if (IsMemcpyOk::value) { + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else if (other.storage_.GetIsAllocated()) { + storage_.SetAllocatedData(other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()); + storage_.SetAllocatedSize(other.storage_.GetSize()); + + other.storage_.SetInlinedSize(0); + } else { + IteratorValueAdapter other_values( + MoveIterator(other.storage_.GetInlinedData())); + + inlined_vector_internal::ConstructElements( + storage_.GetAllocPtr(), storage_.GetInlinedData(), &other_values, + other.storage_.GetSize()); + + storage_.SetInlinedSize(other.storage_.GetSize()); + } + } + + // Creates an inlined vector by moving in the contents of `other` with a copy + // of `alloc`. + // + // NOTE: if `other`'s allocator is not equal to `alloc`, even if `other` + // contains allocated memory, this move constructor will still allocate. Since + // allocation is performed, this constructor can only be `noexcept` if the + // specified allocator is also `noexcept`. + InlinedVector(InlinedVector&& other, const allocator_type& alloc) noexcept( + absl::allocator_is_nothrow::value) + : storage_(alloc) { + if (IsMemcpyOk::value) { + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else if ((*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) && + other.storage_.GetIsAllocated()) { + storage_.SetAllocatedData(other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()); + storage_.SetAllocatedSize(other.storage_.GetSize()); + + other.storage_.SetInlinedSize(0); + } else { + storage_.Initialize( + IteratorValueAdapter(MoveIterator(other.data())), + other.size()); + } + } + + ~InlinedVector() {} + + // --------------------------------------------------------------------------- + // InlinedVector Member Accessors + // --------------------------------------------------------------------------- + + // `InlinedVector::empty()` + // + // Returns whether the inlined vector contains no elements. + bool empty() const noexcept { return !size(); } + + // `InlinedVector::size()` + // + // Returns the number of elements in the inlined vector. + size_type size() const noexcept { return storage_.GetSize(); } + + // `InlinedVector::max_size()` + // + // Returns the maximum number of elements the inlined vector can hold. + size_type max_size() const noexcept { + // One bit of the size storage is used to indicate whether the inlined + // vector contains allocated memory. As a result, the maximum size that the + // inlined vector can express is half of the max for `size_type`. + return (std::numeric_limits::max)() / 2; + } + + // `InlinedVector::capacity()` + // + // Returns the number of elements that could be stored in the inlined vector + // without requiring a reallocation. + // + // NOTE: for most inlined vectors, `capacity()` should be equal to the + // template parameter `N`. For inlined vectors which exceed this capacity, + // they will no longer be inlined and `capacity()` will equal the capactity of + // the allocated memory. + size_type capacity() const noexcept { + return storage_.GetIsAllocated() ? storage_.GetAllocatedCapacity() + : storage_.GetInlinedCapacity(); + } + + // `InlinedVector::data()` + // + // Returns a `pointer` to the elements of the inlined vector. This pointer + // can be used to access and modify the contained elements. + // + // NOTE: only elements within [`data()`, `data() + size()`) are valid. + pointer data() noexcept { + return storage_.GetIsAllocated() ? storage_.GetAllocatedData() + : storage_.GetInlinedData(); + } + + // Overload of `InlinedVector::data()` that returns a `const_pointer` to the + // elements of the inlined vector. This pointer can be used to access but not + // modify the contained elements. + // + // NOTE: only elements within [`data()`, `data() + size()`) are valid. + const_pointer data() const noexcept { + return storage_.GetIsAllocated() ? storage_.GetAllocatedData() + : storage_.GetInlinedData(); + } + + // `InlinedVector::operator[](...)` + // + // Returns a `reference` to the `i`th element of the inlined vector. + reference operator[](size_type i) { + assert(i < size()); + + return data()[i]; + } + + // Overload of `InlinedVector::operator[](...)` that returns a + // `const_reference` to the `i`th element of the inlined vector. + const_reference operator[](size_type i) const { + assert(i < size()); + + return data()[i]; + } + + // `InlinedVector::at(...)` + // + // Returns a `reference` to the `i`th element of the inlined vector. + // + // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, + // in both debug and non-debug builds, `std::out_of_range` will be thrown. + reference at(size_type i) { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange( + "`InlinedVector::at(size_type)` failed bounds check"); + } + + return data()[i]; + } + + // Overload of `InlinedVector::at(...)` that returns a `const_reference` to + // the `i`th element of the inlined vector. + // + // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, + // in both debug and non-debug builds, `std::out_of_range` will be thrown. + const_reference at(size_type i) const { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange( + "`InlinedVector::at(size_type) const` failed bounds check"); + } + + return data()[i]; + } + + // `InlinedVector::front()` + // + // Returns a `reference` to the first element of the inlined vector. + reference front() { + assert(!empty()); + + return at(0); + } + + // Overload of `InlinedVector::front()` that returns a `const_reference` to + // the first element of the inlined vector. + const_reference front() const { + assert(!empty()); + + return at(0); + } + + // `InlinedVector::back()` + // + // Returns a `reference` to the last element of the inlined vector. + reference back() { + assert(!empty()); + + return at(size() - 1); + } + + // Overload of `InlinedVector::back()` that returns a `const_reference` to the + // last element of the inlined vector. + const_reference back() const { + assert(!empty()); + + return at(size() - 1); + } + + // `InlinedVector::begin()` + // + // Returns an `iterator` to the beginning of the inlined vector. + iterator begin() noexcept { return data(); } + + // Overload of `InlinedVector::begin()` that returns a `const_iterator` to + // the beginning of the inlined vector. + const_iterator begin() const noexcept { return data(); } + + // `InlinedVector::end()` + // + // Returns an `iterator` to the end of the inlined vector. + iterator end() noexcept { return data() + size(); } + + // Overload of `InlinedVector::end()` that returns a `const_iterator` to the + // end of the inlined vector. + const_iterator end() const noexcept { return data() + size(); } + + // `InlinedVector::cbegin()` + // + // Returns a `const_iterator` to the beginning of the inlined vector. + const_iterator cbegin() const noexcept { return begin(); } + + // `InlinedVector::cend()` + // + // Returns a `const_iterator` to the end of the inlined vector. + const_iterator cend() const noexcept { return end(); } + + // `InlinedVector::rbegin()` + // + // Returns a `reverse_iterator` from the end of the inlined vector. + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + + // Overload of `InlinedVector::rbegin()` that returns a + // `const_reverse_iterator` from the end of the inlined vector. + const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); + } + + // `InlinedVector::rend()` + // + // Returns a `reverse_iterator` from the beginning of the inlined vector. + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + + // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator` + // from the beginning of the inlined vector. + const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); + } + + // `InlinedVector::crbegin()` + // + // Returns a `const_reverse_iterator` from the end of the inlined vector. + const_reverse_iterator crbegin() const noexcept { return rbegin(); } + + // `InlinedVector::crend()` + // + // Returns a `const_reverse_iterator` from the beginning of the inlined + // vector. + const_reverse_iterator crend() const noexcept { return rend(); } + + // `InlinedVector::get_allocator()` + // + // Returns a copy of the inlined vector's allocator. + allocator_type get_allocator() const { return *storage_.GetAllocPtr(); } + + // --------------------------------------------------------------------------- + // InlinedVector Member Mutators + // --------------------------------------------------------------------------- + + // `InlinedVector::operator=(...)` + // + // Replaces the elements of the inlined vector with copies of the elements of + // `list`. + InlinedVector& operator=(std::initializer_list list) { + assign(list.begin(), list.end()); + + return *this; + } + + // Overload of `InlinedVector::operator=(...)` that replaces the elements of + // the inlined vector with copies of the elements of `other`. + InlinedVector& operator=(const InlinedVector& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + const_pointer other_data = other.data(); + assign(other_data, other_data + other.size()); + } + + return *this; + } + + // Overload of `InlinedVector::operator=(...)` that moves the elements of + // `other` into the inlined vector. + // + // NOTE: as a result of calling this overload, `other` is left in a valid but + // unspecified state. + InlinedVector& operator=(InlinedVector&& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) { + inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), + size()); + storage_.DeallocateIfAllocated(); + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else { + storage_.Assign(IteratorValueAdapter( + MoveIterator(other.storage_.GetInlinedData())), + other.size()); + } + } + + return *this; + } + + // `InlinedVector::assign(...)` + // + // Replaces the contents of the inlined vector with `n` copies of `v`. + void assign(size_type n, const_reference v) { + storage_.Assign(CopyValueAdapter(v), n); + } + + // Overload of `InlinedVector::assign(...)` that replaces the contents of the + // inlined vector with copies of the elements of `list`. + void assign(std::initializer_list list) { + assign(list.begin(), list.end()); + } + + // Overload of `InlinedVector::assign(...)` to replace the contents of the + // inlined vector with the range [`first`, `last`). + // + // NOTE: this overload is for iterators that are "forward" category or better. + template * = nullptr> + void assign(ForwardIterator first, ForwardIterator last) { + storage_.Assign(IteratorValueAdapter(first), + std::distance(first, last)); + } + + // Overload of `InlinedVector::assign(...)` to replace the contents of the + // inlined vector with the range [`first`, `last`). + // + // NOTE: this overload is for iterators that are "input" category. + template * = nullptr> + void assign(InputIterator first, InputIterator last) { + size_type i = 0; + for (; i < size() && first != last; ++i, static_cast(++first)) { + at(i) = *first; + } + + erase(data() + i, data() + size()); + std::copy(first, last, std::back_inserter(*this)); + } + + // `InlinedVector::resize(...)` + // + // Resizes the inlined vector to contain `n` elements. + // + // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` + // is larger than `size()`, new elements are value-initialized. + void resize(size_type n) { storage_.Resize(DefaultValueAdapter(), n); } + + // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to + // contain `n` elements. + // + // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` + // is larger than `size()`, new elements are copied-constructed from `v`. + void resize(size_type n, const_reference v) { + storage_.Resize(CopyValueAdapter(v), n); + } + + // `InlinedVector::insert(...)` + // + // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly + // inserted element. + iterator insert(const_iterator pos, const_reference v) { + return emplace(pos, v); + } + + // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using + // move semantics, returning an `iterator` to the newly inserted element. + iterator insert(const_iterator pos, RValueReference v) { + return emplace(pos, std::move(v)); + } + + // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies + // of `v` starting at `pos`, returning an `iterator` pointing to the first of + // the newly inserted elements. + iterator insert(const_iterator pos, size_type n, const_reference v) { + assert(pos >= begin()); + assert(pos <= end()); + + if (ABSL_PREDICT_TRUE(n != 0)) { + value_type dealias = v; + return storage_.Insert(pos, CopyValueAdapter(dealias), n); + } else { + return const_cast(pos); + } + } + + // Overload of `InlinedVector::insert(...)` that inserts copies of the + // elements of `list` starting at `pos`, returning an `iterator` pointing to + // the first of the newly inserted elements. + iterator insert(const_iterator pos, std::initializer_list list) { + return insert(pos, list.begin(), list.end()); + } + + // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, + // `last`) starting at `pos`, returning an `iterator` pointing to the first + // of the newly inserted elements. + // + // NOTE: this overload is for iterators that are "forward" category or better. + template * = nullptr> + iterator insert(const_iterator pos, ForwardIterator first, + ForwardIterator last) { + assert(pos >= begin()); + assert(pos <= end()); + + if (ABSL_PREDICT_TRUE(first != last)) { + return storage_.Insert(pos, IteratorValueAdapter(first), + std::distance(first, last)); + } else { + return const_cast(pos); + } + } + + // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, + // `last`) starting at `pos`, returning an `iterator` pointing to the first + // of the newly inserted elements. + // + // NOTE: this overload is for iterators that are "input" category. + template * = nullptr> + iterator insert(const_iterator pos, InputIterator first, InputIterator last) { + assert(pos >= begin()); + assert(pos <= end()); + + size_type index = std::distance(cbegin(), pos); + for (size_type i = index; first != last; ++i, static_cast(++first)) { + insert(data() + i, *first); + } + + return iterator(data() + index); + } + + // `InlinedVector::emplace(...)` + // + // Constructs and inserts an element using `args...` in the inlined vector at + // `pos`, returning an `iterator` pointing to the newly emplaced element. + template + iterator emplace(const_iterator pos, Args&&... args) { + assert(pos >= begin()); + assert(pos <= end()); + + value_type dealias(std::forward(args)...); + return storage_.Insert(pos, + IteratorValueAdapter( + MoveIterator(std::addressof(dealias))), + 1); + } + + // `InlinedVector::emplace_back(...)` + // + // Constructs and inserts an element using `args...` in the inlined vector at + // `end()`, returning a `reference` to the newly emplaced element. + template + reference emplace_back(Args&&... args) { + return storage_.EmplaceBack(std::forward(args)...); + } + + // `InlinedVector::push_back(...)` + // + // Inserts a copy of `v` in the inlined vector at `end()`. + void push_back(const_reference v) { static_cast(emplace_back(v)); } + + // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` + // using move semantics. + void push_back(RValueReference v) { + static_cast(emplace_back(std::move(v))); + } + + // `InlinedVector::pop_back()` + // + // Destroys the element at `back()`, reducing the size by `1`. + void pop_back() noexcept { + assert(!empty()); + + AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1)); + storage_.SubtractSize(1); + } + + // `InlinedVector::erase(...)` + // + // Erases the element at `pos`, returning an `iterator` pointing to where the + // erased element was located. + // + // NOTE: may return `end()`, which is not dereferencable. + iterator erase(const_iterator pos) { + assert(pos >= begin()); + assert(pos < end()); + + return storage_.Erase(pos, pos + 1); + } + + // Overload of `InlinedVector::erase(...)` that erases every element in the + // range [`from`, `to`), returning an `iterator` pointing to where the first + // erased element was located. + // + // NOTE: may return `end()`, which is not dereferencable. + iterator erase(const_iterator from, const_iterator to) { + assert(from >= begin()); + assert(from <= to); + assert(to <= end()); + + if (ABSL_PREDICT_TRUE(from != to)) { + return storage_.Erase(from, to); + } else { + return const_cast(from); + } + } + + // `InlinedVector::clear()` + // + // Destroys all elements in the inlined vector, setting the size to `0` and + // deallocating any held memory. + void clear() noexcept { + inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), + size()); + storage_.DeallocateIfAllocated(); + + storage_.SetInlinedSize(0); + } + + // `InlinedVector::reserve(...)` + // + // Ensures that there is enough room for at least `n` elements. + void reserve(size_type n) { storage_.Reserve(n); } + + // `InlinedVector::shrink_to_fit()` + // + // Reduces memory usage by freeing unused memory. After being called, calls to + // `capacity()` will be equal to `max(N, size())`. + // + // If `size() <= N` and the inlined vector contains allocated memory, the + // elements will all be moved to the inlined space and the allocated memory + // will be deallocated. + // + // If `size() > N` and `size() < capacity()`, the elements will be moved to a + // smaller allocation. + void shrink_to_fit() { + if (storage_.GetIsAllocated()) { + storage_.ShrinkToFit(); + } + } + + // `InlinedVector::swap(...)` + // + // Swaps the contents of the inlined vector with `other`. + void swap(InlinedVector& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + storage_.Swap(std::addressof(other.storage_)); + } + } + + private: + template + friend H AbslHashValue(H h, const absl::InlinedVector& a); + + Storage storage_; +}; + +// ----------------------------------------------------------------------------- +// InlinedVector Non-Member Functions +// ----------------------------------------------------------------------------- + +// `swap(...)` +// +// Swaps the contents of two inlined vectors. +template +void swap(absl::InlinedVector& a, + absl::InlinedVector& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); +} + +// `operator==(...)` +// +// Tests for value-equality of two inlined vectors. +template +bool operator==(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + auto a_data = a.data(); + auto b_data = b.data(); + return absl::equal(a_data, a_data + a.size(), b_data, b_data + b.size()); +} + +// `operator!=(...)` +// +// Tests for value-inequality of two inlined vectors. +template +bool operator!=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return !(a == b); +} + +// `operator<(...)` +// +// Tests whether the value of an inlined vector is less than the value of +// another inlined vector using a lexicographical comparison algorithm. +template +bool operator<(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + auto a_data = a.data(); + auto b_data = b.data(); + return std::lexicographical_compare(a_data, a_data + a.size(), b_data, + b_data + b.size()); +} + +// `operator>(...)` +// +// Tests whether the value of an inlined vector is greater than the value of +// another inlined vector using a lexicographical comparison algorithm. +template +bool operator>(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return b < a; +} + +// `operator<=(...)` +// +// Tests whether the value of an inlined vector is less than or equal to the +// value of another inlined vector using a lexicographical comparison algorithm. +template +bool operator<=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return !(b < a); +} + +// `operator>=(...)` +// +// Tests whether the value of an inlined vector is greater than or equal to the +// value of another inlined vector using a lexicographical comparison algorithm. +template +bool operator>=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + return !(a < b); +} + +// `AbslHashValue(...)` +// +// Provides `absl::Hash` support for `absl::InlinedVector`. It is uncommon to +// call this directly. +template +H AbslHashValue(H h, const absl::InlinedVector& a) { + auto size = a.size(); + return H::combine(H::combine_contiguous(std::move(h), a.data(), size), size); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INLINED_VECTOR_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/btree.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/btree.h new file mode 100644 index 0000000000000000000000000000000000000000..fd5c0e7aba9c6e52aae1b7e6083d6923f7b5b330 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/btree.h @@ -0,0 +1,2614 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// A btree implementation of the STL set and map interfaces. A btree is smaller +// and generally also faster than STL set/map (refer to the benchmarks below). +// The red-black tree implementation of STL set/map has an overhead of 3 +// pointers (left, right and parent) plus the node color information for each +// stored value. So a set consumes 40 bytes for each value stored in +// 64-bit mode. This btree implementation stores multiple values on fixed +// size nodes (usually 256 bytes) and doesn't store child pointers for leaf +// nodes. The result is that a btree_set may use much less memory per +// stored value. For the random insertion benchmark in btree_bench.cc, a +// btree_set with node-size of 256 uses 5.1 bytes per stored value. +// +// The packing of multiple values on to each node of a btree has another effect +// besides better space utilization: better cache locality due to fewer cache +// lines being accessed. Better cache locality translates into faster +// operations. +// +// CAVEATS +// +// Insertions and deletions on a btree can cause splitting, merging or +// rebalancing of btree nodes. And even without these operations, insertions +// and deletions on a btree will move values around within a node. In both +// cases, the result is that insertions and deletions can invalidate iterators +// pointing to values other than the one being inserted/deleted. Therefore, this +// container does not provide pointer stability. This is notably different from +// STL set/map which takes care to not invalidate iterators on insert/erase +// except, of course, for iterators pointing to the value being erased. A +// partial workaround when erasing is available: erase() returns an iterator +// pointing to the item just after the one that was erased (or end() if none +// exists). + +#ifndef ABSL_CONTAINER_INTERNAL_BTREE_H_ +#define ABSL_CONTAINER_INTERNAL_BTREE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/macros.h" +#include "absl/container/internal/common.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/layout.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" +#include "absl/types/compare.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A helper class that indicates if the Compare parameter is a key-compare-to +// comparator. +template +using btree_is_key_compare_to = + std::is_convertible, + absl::weak_ordering>; + +struct StringBtreeDefaultLess { + using is_transparent = void; + + StringBtreeDefaultLess() = default; + + // Compatibility constructor. + StringBtreeDefaultLess(std::less) {} // NOLINT + StringBtreeDefaultLess(std::less) {} // NOLINT + + absl::weak_ordering operator()(absl::string_view lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(lhs.compare(rhs)); + } +}; + +struct StringBtreeDefaultGreater { + using is_transparent = void; + + StringBtreeDefaultGreater() = default; + + StringBtreeDefaultGreater(std::greater) {} // NOLINT + StringBtreeDefaultGreater(std::greater) {} // NOLINT + + absl::weak_ordering operator()(absl::string_view lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(rhs.compare(lhs)); + } +}; + +// A helper class to convert a boolean comparison into a three-way "compare-to" +// comparison that returns a negative value to indicate less-than, zero to +// indicate equality and a positive value to indicate greater-than. This helper +// class is specialized for less, greater, +// less, and greater. +// +// key_compare_to_adapter is provided so that btree users +// automatically get the more efficient compare-to code when using common +// google string types with common comparison functors. +// These string-like specializations also turn on heterogeneous lookup by +// default. +template +struct key_compare_to_adapter { + using type = Compare; +}; + +template <> +struct key_compare_to_adapter> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_to_adapter> { + using type = StringBtreeDefaultGreater; +}; + +template <> +struct key_compare_to_adapter> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_to_adapter> { + using type = StringBtreeDefaultGreater; +}; + +template +struct common_params { + // If Compare is a common comparator for a std::string-like type, then we adapt it + // to use heterogeneous lookup and to be a key-compare-to comparator. + using key_compare = typename key_compare_to_adapter::type; + // A type which indicates if we have a key-compare-to functor or a plain old + // key-compare functor. + using is_key_compare_to = btree_is_key_compare_to; + + using allocator_type = Alloc; + using key_type = Key; + using size_type = std::make_signed::type; + using difference_type = ptrdiff_t; + + // True if this is a multiset or multimap. + using is_multi_container = std::integral_constant; + + using slot_policy = SlotPolicy; + using slot_type = typename slot_policy::slot_type; + using value_type = typename slot_policy::value_type; + using init_type = typename slot_policy::mutable_value_type; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + + enum { + kTargetNodeSize = TargetNodeSize, + + // Upper bound for the available space for values. This is largest for leaf + // nodes, which have overhead of at least a pointer + 4 bytes (for storing + // 3 field_types and an enum). + kNodeValueSpace = + TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4), + }; + + // This is an integral type large enough to hold as many + // ValueSize-values as will fit a node of TargetNodeSize bytes. + using node_count_type = + absl::conditional_t<(kNodeValueSpace / sizeof(value_type) > + (std::numeric_limits::max)()), + uint16_t, uint8_t>; // NOLINT + + // The following methods are necessary for passing this struct as PolicyTraits + // for node_handle and/or are used within btree. + static value_type &element(slot_type *slot) { + return slot_policy::element(slot); + } + static const value_type &element(const slot_type *slot) { + return slot_policy::element(slot); + } + template + static void construct(Alloc *alloc, slot_type *slot, Args &&... args) { + slot_policy::construct(alloc, slot, std::forward(args)...); + } + static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { + slot_policy::construct(alloc, slot, other); + } + static void destroy(Alloc *alloc, slot_type *slot) { + slot_policy::destroy(alloc, slot); + } + static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) { + construct(alloc, new_slot, old_slot); + destroy(alloc, old_slot); + } + static void swap(Alloc *alloc, slot_type *a, slot_type *b) { + slot_policy::swap(alloc, a, b); + } + static void move(Alloc *alloc, slot_type *src, slot_type *dest) { + slot_policy::move(alloc, src, dest); + } + static void move(Alloc *alloc, slot_type *first, slot_type *last, + slot_type *result) { + slot_policy::move(alloc, first, last, result); + } +}; + +// A parameters structure for holding the type parameters for a btree_map. +// Compare and Alloc should be nothrow copy-constructible. +template +struct map_params : common_params> { + using super_type = typename map_params::common_params; + using mapped_type = Data; + // This type allows us to move keys when it is safe to do so. It is safe + // for maps in which value_type and mutable_value_type are layout compatible. + using slot_policy = typename super_type::slot_policy; + using slot_type = typename super_type::slot_type; + using value_type = typename super_type::value_type; + using init_type = typename super_type::init_type; + + using key_compare = typename super_type::key_compare; + // Inherit from key_compare for empty base class optimization. + struct value_compare : private key_compare { + value_compare() = default; + explicit value_compare(const key_compare &cmp) : key_compare(cmp) {} + + template + auto operator()(const T &left, const U &right) const + -> decltype(std::declval()(left.first, right.first)) { + return key_compare::operator()(left.first, right.first); + } + }; + using is_map_container = std::true_type; + + static const Key &key(const value_type &x) { return x.first; } + static const Key &key(const init_type &x) { return x.first; } + static const Key &key(const slot_type *x) { return slot_policy::key(x); } + static mapped_type &value(value_type *value) { return value->second; } +}; + +// This type implements the necessary functions from the +// absl::container_internal::slot_type interface. +template +struct set_slot_policy { + using slot_type = Key; + using value_type = Key; + using mutable_value_type = Key; + + static value_type &element(slot_type *slot) { return *slot; } + static const value_type &element(const slot_type *slot) { return *slot; } + + template + static void construct(Alloc *alloc, slot_type *slot, Args &&... args) { + absl::allocator_traits::construct(*alloc, slot, + std::forward(args)...); + } + + template + static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { + absl::allocator_traits::construct(*alloc, slot, std::move(*other)); + } + + template + static void destroy(Alloc *alloc, slot_type *slot) { + absl::allocator_traits::destroy(*alloc, slot); + } + + template + static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) { + using std::swap; + swap(*a, *b); + } + + template + static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) { + *dest = std::move(*src); + } + + template + static void move(Alloc *alloc, slot_type *first, slot_type *last, + slot_type *result) { + for (slot_type *src = first, *dest = result; src != last; ++src, ++dest) + move(alloc, src, dest); + } +}; + +// A parameters structure for holding the type parameters for a btree_set. +// Compare and Alloc should be nothrow copy-constructible. +template +struct set_params : common_params> { + using value_type = Key; + using slot_type = typename set_params::common_params::slot_type; + using value_compare = typename set_params::common_params::key_compare; + using is_map_container = std::false_type; + + static const Key &key(const value_type &x) { return x; } + static const Key &key(const slot_type *x) { return *x; } +}; + +// An adapter class that converts a lower-bound compare into an upper-bound +// compare. Note: there is no need to make a version of this adapter specialized +// for key-compare-to functors because the upper-bound (the first value greater +// than the input) is never an exact match. +template +struct upper_bound_adapter { + explicit upper_bound_adapter(const Compare &c) : comp(c) {} + template + bool operator()(const K &a, const LK &b) const { + // Returns true when a is not greater than b. + return !compare_internal::compare_result_as_less_than(comp(b, a)); + } + + private: + Compare comp; +}; + +enum class MatchKind : uint8_t { kEq, kNe }; + +template +struct SearchResult { + V value; + MatchKind match; + + static constexpr bool HasMatch() { return true; } + bool IsEq() const { return match == MatchKind::kEq; } +}; + +// When we don't use CompareTo, `match` is not present. +// This ensures that callers can't use it accidentally when it provides no +// useful information. +template +struct SearchResult { + V value; + + static constexpr bool HasMatch() { return false; } + static constexpr bool IsEq() { return false; } +}; + +// A node in the btree holding. The same node type is used for both internal +// and leaf nodes in the btree, though the nodes are allocated in such a way +// that the children array is only valid in internal nodes. +template +class btree_node { + using is_key_compare_to = typename Params::is_key_compare_to; + using is_multi_container = typename Params::is_multi_container; + using field_type = typename Params::node_count_type; + using allocator_type = typename Params::allocator_type; + using slot_type = typename Params::slot_type; + + public: + using params_type = Params; + using key_type = typename Params::key_type; + using value_type = typename Params::value_type; + using pointer = typename Params::pointer; + using const_pointer = typename Params::const_pointer; + using reference = typename Params::reference; + using const_reference = typename Params::const_reference; + using key_compare = typename Params::key_compare; + using size_type = typename Params::size_type; + using difference_type = typename Params::difference_type; + + // Btree decides whether to use linear node search as follows: + // - If the key is arithmetic and the comparator is std::less or + // std::greater, choose linear. + // - Otherwise, choose binary. + // TODO(ezb): Might make sense to add condition(s) based on node-size. + using use_linear_search = std::integral_constant< + bool, + std::is_arithmetic::value && + (std::is_same, key_compare>::value || + std::is_same, key_compare>::value)>; + + // This class is organized by gtl::Layout as if it had the following + // structure: + // // A pointer to the node's parent. + // btree_node *parent; + // + // // The position of the node in the node's parent. + // field_type position; + // // The index of the first populated value in `values`. + // // TODO(ezb): right now, `start` is always 0. Update insertion/merge + // // logic to allow for floating storage within nodes. + // field_type start; + // // The index after the last populated value in `values`. Currently, this + // // is the same as the count of values. + // field_type finish; + // // The maximum number of values the node can hold. This is an integer in + // // [1, kNodeValues] for root leaf nodes, kNodeValues for non-root leaf + // // nodes, and kInternalNodeMaxCount (as a sentinel value) for internal + // // nodes (even though there are still kNodeValues values in the node). + // // TODO(ezb): make max_count use only 4 bits and record log2(capacity) + // // to free extra bits for is_root, etc. + // field_type max_count; + // + // // The array of values. The capacity is `max_count` for leaf nodes and + // // kNodeValues for internal nodes. Only the values in + // // [start, finish) have been initialized and are valid. + // slot_type values[max_count]; + // + // // The array of child pointers. The keys in children[i] are all less + // // than key(i). The keys in children[i + 1] are all greater than key(i). + // // There are 0 children for leaf nodes and kNodeValues + 1 children for + // // internal nodes. + // btree_node *children[kNodeValues + 1]; + // + // This class is only constructed by EmptyNodeType. Normally, pointers to the + // layout above are allocated, cast to btree_node*, and de-allocated within + // the btree implementation. + ~btree_node() = default; + btree_node(btree_node const &) = delete; + btree_node &operator=(btree_node const &) = delete; + + // Public for EmptyNodeType. + constexpr static size_type Alignment() { + static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(), + "Alignment of all nodes must be equal."); + return InternalLayout().Alignment(); + } + + protected: + btree_node() = default; + + private: + using layout_type = absl::container_internal::Layout; + constexpr static size_type SizeWithNValues(size_type n) { + return layout_type(/*parent*/ 1, + /*position, start, finish, max_count*/ 4, + /*values*/ n, + /*children*/ 0) + .AllocSize(); + } + // A lower bound for the overhead of fields other than values in a leaf node. + constexpr static size_type MinimumOverhead() { + return SizeWithNValues(1) - sizeof(value_type); + } + + // Compute how many values we can fit onto a leaf node taking into account + // padding. + constexpr static size_type NodeTargetValues(const int begin, const int end) { + return begin == end ? begin + : SizeWithNValues((begin + end) / 2 + 1) > + params_type::kTargetNodeSize + ? NodeTargetValues(begin, (begin + end) / 2) + : NodeTargetValues((begin + end) / 2 + 1, end); + } + + enum { + kTargetNodeSize = params_type::kTargetNodeSize, + kNodeTargetValues = NodeTargetValues(0, params_type::kTargetNodeSize), + + // We need a minimum of 3 values per internal node in order to perform + // splitting (1 value for the two nodes involved in the split and 1 value + // propagated to the parent as the delimiter for the split). + kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3, + + // The node is internal (i.e. is not a leaf node) if and only if `max_count` + // has this value. + kInternalNodeMaxCount = 0, + }; + + // Leaves can have less than kNodeValues values. + constexpr static layout_type LeafLayout(const int max_values = kNodeValues) { + return layout_type(/*parent*/ 1, + /*position, start, finish, max_count*/ 4, + /*values*/ max_values, + /*children*/ 0); + } + constexpr static layout_type InternalLayout() { + return layout_type(/*parent*/ 1, + /*position, start, finish, max_count*/ 4, + /*values*/ kNodeValues, + /*children*/ kNodeValues + 1); + } + constexpr static size_type LeafSize(const int max_values = kNodeValues) { + return LeafLayout(max_values).AllocSize(); + } + constexpr static size_type InternalSize() { + return InternalLayout().AllocSize(); + } + + // N is the index of the type in the Layout definition. + // ElementType is the Nth type in the Layout definition. + template + inline typename layout_type::template ElementType *GetField() { + // We assert that we don't read from values that aren't there. + assert(N < 3 || !leaf()); + return InternalLayout().template Pointer(reinterpret_cast(this)); + } + template + inline const typename layout_type::template ElementType *GetField() const { + assert(N < 3 || !leaf()); + return InternalLayout().template Pointer( + reinterpret_cast(this)); + } + void set_parent(btree_node *p) { *GetField<0>() = p; } + field_type &mutable_finish() { return GetField<1>()[2]; } + slot_type *slot(int i) { return &GetField<2>()[i]; } + slot_type *start_slot() { return slot(start()); } + slot_type *finish_slot() { return slot(finish()); } + const slot_type *slot(int i) const { return &GetField<2>()[i]; } + void set_position(field_type v) { GetField<1>()[0] = v; } + void set_start(field_type v) { GetField<1>()[1] = v; } + void set_finish(field_type v) { GetField<1>()[2] = v; } + // This method is only called by the node init methods. + void set_max_count(field_type v) { GetField<1>()[3] = v; } + + public: + // Whether this is a leaf node or not. This value doesn't change after the + // node is created. + bool leaf() const { return GetField<1>()[3] != kInternalNodeMaxCount; } + + // Getter for the position of this node in its parent. + field_type position() const { return GetField<1>()[0]; } + + // Getter for the offset of the first value in the `values` array. + field_type start() const { + // TODO(ezb): when floating storage is implemented, return GetField<1>()[1]; + assert(GetField<1>()[1] == 0); + return 0; + } + + // Getter for the offset after the last value in the `values` array. + field_type finish() const { return GetField<1>()[2]; } + + // Getters for the number of values stored in this node. + field_type count() const { + assert(finish() >= start()); + return finish() - start(); + } + field_type max_count() const { + // Internal nodes have max_count==kInternalNodeMaxCount. + // Leaf nodes have max_count in [1, kNodeValues]. + const field_type max_count = GetField<1>()[3]; + return max_count == field_type{kInternalNodeMaxCount} + ? field_type{kNodeValues} + : max_count; + } + + // Getter for the parent of this node. + btree_node *parent() const { return *GetField<0>(); } + // Getter for whether the node is the root of the tree. The parent of the + // root of the tree is the leftmost node in the tree which is guaranteed to + // be a leaf. + bool is_root() const { return parent()->leaf(); } + void make_root() { + assert(parent()->is_root()); + set_parent(parent()->parent()); + } + + // Getters for the key/value at position i in the node. + const key_type &key(int i) const { return params_type::key(slot(i)); } + reference value(int i) { return params_type::element(slot(i)); } + const_reference value(int i) const { return params_type::element(slot(i)); } + + // Getters/setter for the child at position i in the node. + btree_node *child(int i) const { return GetField<3>()[i]; } + btree_node *start_child() const { return child(start()); } + btree_node *&mutable_child(int i) { return GetField<3>()[i]; } + void clear_child(int i) { + absl::container_internal::SanitizerPoisonObject(&mutable_child(i)); + } + void set_child(int i, btree_node *c) { + absl::container_internal::SanitizerUnpoisonObject(&mutable_child(i)); + mutable_child(i) = c; + c->set_position(i); + } + void init_child(int i, btree_node *c) { + set_child(i, c); + c->set_parent(this); + } + + // Returns the position of the first value whose key is not less than k. + template + SearchResult lower_bound( + const K &k, const key_compare &comp) const { + return use_linear_search::value ? linear_search(k, comp) + : binary_search(k, comp); + } + // Returns the position of the first value whose key is greater than k. + template + int upper_bound(const K &k, const key_compare &comp) const { + auto upper_compare = upper_bound_adapter(comp); + return use_linear_search::value ? linear_search(k, upper_compare).value + : binary_search(k, upper_compare).value; + } + + template + SearchResult::value> + linear_search(const K &k, const Compare &comp) const { + return linear_search_impl(k, start(), finish(), comp, + btree_is_key_compare_to()); + } + + template + SearchResult::value> + binary_search(const K &k, const Compare &comp) const { + return binary_search_impl(k, start(), finish(), comp, + btree_is_key_compare_to()); + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using plain compare. + template + SearchResult linear_search_impl( + const K &k, int s, const int e, const Compare &comp, + std::false_type /* IsCompareTo */) const { + while (s < e) { + if (!comp(key(s), k)) { + break; + } + ++s; + } + return {s}; + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using compare-to. + template + SearchResult linear_search_impl( + const K &k, int s, const int e, const Compare &comp, + std::true_type /* IsCompareTo */) const { + while (s < e) { + const absl::weak_ordering c = comp(key(s), k); + if (c == 0) { + return {s, MatchKind::kEq}; + } else if (c > 0) { + break; + } + ++s; + } + return {s, MatchKind::kNe}; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using plain compare. + template + SearchResult binary_search_impl( + const K &k, int s, int e, const Compare &comp, + std::false_type /* IsCompareTo */) const { + while (s != e) { + const int mid = (s + e) >> 1; + if (comp(key(mid), k)) { + s = mid + 1; + } else { + e = mid; + } + } + return {s}; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using compare-to. + template + SearchResult binary_search_impl( + const K &k, int s, int e, const CompareTo &comp, + std::true_type /* IsCompareTo */) const { + if (is_multi_container::value) { + MatchKind exact_match = MatchKind::kNe; + while (s != e) { + const int mid = (s + e) >> 1; + const absl::weak_ordering c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else { + e = mid; + if (c == 0) { + // Need to return the first value whose key is not less than k, + // which requires continuing the binary search if this is a + // multi-container. + exact_match = MatchKind::kEq; + } + } + } + return {s, exact_match}; + } else { // Not a multi-container. + while (s != e) { + const int mid = (s + e) >> 1; + const absl::weak_ordering c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else if (c > 0) { + e = mid; + } else { + return {mid, MatchKind::kEq}; + } + } + return {s, MatchKind::kNe}; + } + } + + // Emplaces a value at position i, shifting all existing values and + // children at positions >= i to the right by 1. + template + void emplace_value(size_type i, allocator_type *alloc, Args &&... args); + + // Removes the value at position i, shifting all existing values and children + // at positions > i to the left by 1. + void remove_value(int i, allocator_type *alloc); + + // Removes the values at positions [i, i + to_erase), shifting all values + // after that range to the left by to_erase. Does not change children at all. + void remove_values_ignore_children(int i, int to_erase, + allocator_type *alloc); + + // Rebalances a node with its right sibling. + void rebalance_right_to_left(int to_move, btree_node *right, + allocator_type *alloc); + void rebalance_left_to_right(int to_move, btree_node *right, + allocator_type *alloc); + + // Splits a node, moving a portion of the node's values to its right sibling. + void split(int insert_position, btree_node *dest, allocator_type *alloc); + + // Merges a node with its right sibling, moving all of the values and the + // delimiting key in the parent node onto itself. + void merge(btree_node *sibling, allocator_type *alloc); + + // Swap the contents of "this" and "src". + void swap(btree_node *src, allocator_type *alloc); + + // Node allocation/deletion routines. + static btree_node *init_leaf(btree_node *n, btree_node *parent, + int max_count) { + n->set_parent(parent); + n->set_position(0); + n->set_start(0); + n->set_finish(0); + n->set_max_count(max_count); + absl::container_internal::SanitizerPoisonMemoryRegion( + n->start_slot(), max_count * sizeof(slot_type)); + return n; + } + static btree_node *init_internal(btree_node *n, btree_node *parent) { + init_leaf(n, parent, kNodeValues); + // Set `max_count` to a sentinel value to indicate that this node is + // internal. + n->set_max_count(kInternalNodeMaxCount); + absl::container_internal::SanitizerPoisonMemoryRegion( + &n->mutable_child(n->start()), + (kNodeValues + 1) * sizeof(btree_node *)); + return n; + } + void destroy(allocator_type *alloc) { + for (int i = start(); i < finish(); ++i) { + value_destroy(i, alloc); + } + } + + public: + // Exposed only for tests. + static bool testonly_uses_linear_node_search() { + return use_linear_search::value; + } + + private: + template + void value_init(const size_type i, allocator_type *alloc, Args &&... args) { + absl::container_internal::SanitizerUnpoisonObject(slot(i)); + params_type::construct(alloc, slot(i), std::forward(args)...); + } + void value_destroy(const size_type i, allocator_type *alloc) { + params_type::destroy(alloc, slot(i)); + absl::container_internal::SanitizerPoisonObject(slot(i)); + } + + // Move n values starting at value i in this node into the values starting at + // value j in node x. + void uninitialized_move_n(const size_type n, const size_type i, + const size_type j, btree_node *x, + allocator_type *alloc) { + absl::container_internal::SanitizerUnpoisonMemoryRegion( + x->slot(j), n * sizeof(slot_type)); + for (slot_type *src = slot(i), *end = src + n, *dest = x->slot(j); + src != end; ++src, ++dest) { + params_type::construct(alloc, dest, src); + } + } + + // Destroys a range of n values, starting at index i. + void value_destroy_n(const size_type i, const size_type n, + allocator_type *alloc) { + for (int j = 0; j < n; ++j) { + value_destroy(i + j, alloc); + } + } + + template + friend class btree; + template + friend struct btree_iterator; + friend class BtreeNodePeer; +}; + +template +struct btree_iterator { + private: + using key_type = typename Node::key_type; + using size_type = typename Node::size_type; + using params_type = typename Node::params_type; + + using node_type = Node; + using normal_node = typename std::remove_const::type; + using const_node = const Node; + using normal_pointer = typename params_type::pointer; + using normal_reference = typename params_type::reference; + using const_pointer = typename params_type::const_pointer; + using const_reference = typename params_type::const_reference; + using slot_type = typename params_type::slot_type; + + using iterator = + btree_iterator; + using const_iterator = + btree_iterator; + + public: + // These aliases are public for std::iterator_traits. + using difference_type = typename Node::difference_type; + using value_type = typename params_type::value_type; + using pointer = Pointer; + using reference = Reference; + using iterator_category = std::bidirectional_iterator_tag; + + btree_iterator() : node(nullptr), position(-1) {} + explicit btree_iterator(Node *n) : node(n), position(n->start()) {} + btree_iterator(Node *n, int p) : node(n), position(p) {} + + // NOTE: this SFINAE allows for implicit conversions from iterator to + // const_iterator, but it specifically avoids defining copy constructors so + // that btree_iterator can be trivially copyable. This is for performance and + // binary size reasons. + template , iterator>::value && + std::is_same::value, + int> = 0> + btree_iterator(const btree_iterator &x) // NOLINT + : node(x.node), position(x.position) {} + + private: + // This SFINAE allows explicit conversions from const_iterator to + // iterator, but also avoids defining a copy constructor. + // NOTE: the const_cast is safe because this constructor is only called by + // non-const methods and the container owns the nodes. + template , const_iterator>::value && + std::is_same::value, + int> = 0> + explicit btree_iterator(const btree_iterator &x) + : node(const_cast(x.node)), position(x.position) {} + + // Increment/decrement the iterator. + void increment() { + if (node->leaf() && ++position < node->finish()) { + return; + } + increment_slow(); + } + void increment_slow(); + + void decrement() { + if (node->leaf() && --position >= node->start()) { + return; + } + decrement_slow(); + } + void decrement_slow(); + + public: + bool operator==(const const_iterator &x) const { + return node == x.node && position == x.position; + } + bool operator!=(const const_iterator &x) const { + return node != x.node || position != x.position; + } + + // Accessors for the key/value the iterator is pointing at. + reference operator*() const { return node->value(position); } + pointer operator->() const { return &node->value(position); } + + btree_iterator &operator++() { + increment(); + return *this; + } + btree_iterator &operator--() { + decrement(); + return *this; + } + btree_iterator operator++(int) { + btree_iterator tmp = *this; + ++*this; + return tmp; + } + btree_iterator operator--(int) { + btree_iterator tmp = *this; + --*this; + return tmp; + } + + private: + template + friend class btree; + template + friend class btree_container; + template + friend class btree_set_container; + template + friend class btree_map_container; + template + friend class btree_multiset_container; + template + friend struct btree_iterator; + template + friend class base_checker; + + const key_type &key() const { return node->key(position); } + slot_type *slot() { return node->slot(position); } + + // The node in the tree the iterator is pointing at. + Node *node; + // The position within the node of the tree the iterator is pointing at. + // TODO(ezb): make this a field_type + int position; +}; + +template +class btree { + using node_type = btree_node; + using is_key_compare_to = typename Params::is_key_compare_to; + + // We use a static empty node for the root/leftmost/rightmost of empty btrees + // in order to avoid branching in begin()/end(). + struct alignas(node_type::Alignment()) EmptyNodeType : node_type { + using field_type = typename node_type::field_type; + node_type *parent; + field_type position = 0; + field_type start = 0; + field_type finish = 0; + // max_count must be != kInternalNodeMaxCount (so that this node is regarded + // as a leaf node). max_count() is never called when the tree is empty. + field_type max_count = node_type::kInternalNodeMaxCount + 1; + +#ifdef _MSC_VER + // MSVC has constexpr code generations bugs here. + EmptyNodeType() : parent(this) {} +#else + constexpr EmptyNodeType(node_type *p) : parent(p) {} +#endif + }; + + static node_type *EmptyNode() { +#ifdef _MSC_VER + static EmptyNodeType *empty_node = new EmptyNodeType; + // This assert fails on some other construction methods. + assert(empty_node->parent == empty_node); + return empty_node; +#else + static constexpr EmptyNodeType empty_node( + const_cast(&empty_node)); + return const_cast(&empty_node); +#endif + } + + enum { + kNodeValues = node_type::kNodeValues, + kMinNodeValues = kNodeValues / 2, + }; + + struct node_stats { + using size_type = typename Params::size_type; + + node_stats(size_type l, size_type i) : leaf_nodes(l), internal_nodes(i) {} + + node_stats &operator+=(const node_stats &x) { + leaf_nodes += x.leaf_nodes; + internal_nodes += x.internal_nodes; + return *this; + } + + size_type leaf_nodes; + size_type internal_nodes; + }; + + public: + using key_type = typename Params::key_type; + using value_type = typename Params::value_type; + using size_type = typename Params::size_type; + using difference_type = typename Params::difference_type; + using key_compare = typename Params::key_compare; + using value_compare = typename Params::value_compare; + using allocator_type = typename Params::allocator_type; + using reference = typename Params::reference; + using const_reference = typename Params::const_reference; + using pointer = typename Params::pointer; + using const_pointer = typename Params::const_pointer; + using iterator = btree_iterator; + using const_iterator = typename iterator::const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using node_handle_type = node_handle; + + // Internal types made public for use by btree_container types. + using params_type = Params; + using slot_type = typename Params::slot_type; + + private: + // For use in copy_or_move_values_in_order. + const value_type &maybe_move_from_iterator(const_iterator x) { return *x; } + value_type &&maybe_move_from_iterator(iterator x) { return std::move(*x); } + + // Copies or moves (depending on the template parameter) the values in + // x into this btree in their order in x. This btree must be empty before this + // method is called. This method is used in copy construction, copy + // assignment, and move assignment. + template + void copy_or_move_values_in_order(Btree *x); + + // Validates that various assumptions/requirements are true at compile time. + constexpr static bool static_assert_validation(); + + public: + btree(const key_compare &comp, const allocator_type &alloc); + + btree(const btree &x); + btree(btree &&x) noexcept + : root_(std::move(x.root_)), + rightmost_(absl::exchange(x.rightmost_, EmptyNode())), + size_(absl::exchange(x.size_, 0)) { + x.mutable_root() = EmptyNode(); + } + + ~btree() { + // Put static_asserts in destructor to avoid triggering them before the type + // is complete. + static_assert(static_assert_validation(), "This call must be elided."); + clear(); + } + + // Assign the contents of x to *this. + btree &operator=(const btree &x); + btree &operator=(btree &&x) noexcept; + + iterator begin() { return iterator(leftmost()); } + const_iterator begin() const { return const_iterator(leftmost()); } + iterator end() { return iterator(rightmost_, rightmost_->finish()); } + const_iterator end() const { + return const_iterator(rightmost_, rightmost_->finish()); + } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // Finds the first element whose key is not less than key. + template + iterator lower_bound(const K &key) { + return internal_end(internal_lower_bound(key)); + } + template + const_iterator lower_bound(const K &key) const { + return internal_end(internal_lower_bound(key)); + } + + // Finds the first element whose key is greater than key. + template + iterator upper_bound(const K &key) { + return internal_end(internal_upper_bound(key)); + } + template + const_iterator upper_bound(const K &key) const { + return internal_end(internal_upper_bound(key)); + } + + // Finds the range of values which compare equal to key. The first member of + // the returned pair is equal to lower_bound(key). The second member pair of + // the pair is equal to upper_bound(key). + template + std::pair equal_range(const K &key) { + return {lower_bound(key), upper_bound(key)}; + } + template + std::pair equal_range(const K &key) const { + return {lower_bound(key), upper_bound(key)}; + } + + // Inserts a value into the btree only if it does not already exist. The + // boolean return value indicates whether insertion succeeded or failed. + // Requirement: if `key` already exists in the btree, does not consume `args`. + // Requirement: `key` is never referenced after consuming `args`. + template + std::pair insert_unique(const key_type &key, Args &&... args); + + // Inserts with hint. Checks to see if the value should be placed immediately + // before `position` in the tree. If so, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_unique() were made. + // Requirement: if `key` already exists in the btree, does not consume `args`. + // Requirement: `key` is never referenced after consuming `args`. + template + std::pair insert_hint_unique(iterator position, + const key_type &key, + Args &&... args); + + // Insert a range of values into the btree. + template + void insert_iterator_unique(InputIterator b, InputIterator e); + + // Inserts a value into the btree. + template + iterator insert_multi(const key_type &key, ValueType &&v); + + // Inserts a value into the btree. + template + iterator insert_multi(ValueType &&v) { + return insert_multi(params_type::key(v), std::forward(v)); + } + + // Insert with hint. Check to see if the value should be placed immediately + // before position in the tree. If it does, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_multi(v) were made. + template + iterator insert_hint_multi(iterator position, ValueType &&v); + + // Insert a range of values into the btree. + template + void insert_iterator_multi(InputIterator b, InputIterator e); + + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + // Requirement: does not read the value at `*iter`. + iterator erase(iterator iter); + + // Erases range. Returns the number of keys erased and an iterator pointing + // to the element after the last erased element. + std::pair erase_range(iterator begin, iterator end); + + // Erases the specified key from the btree. Returns 1 if an element was + // erased and 0 otherwise. + template + size_type erase_unique(const K &key); + + // Erases all of the entries matching the specified key from the + // btree. Returns the number of elements erased. + template + size_type erase_multi(const K &key); + + // Finds the iterator corresponding to a key or returns end() if the key is + // not present. + template + iterator find(const K &key) { + return internal_end(internal_find(key)); + } + template + const_iterator find(const K &key) const { + return internal_end(internal_find(key)); + } + + // Returns a count of the number of times the key appears in the btree. + template + size_type count_unique(const K &key) const { + const iterator begin = internal_find(key); + if (begin.node == nullptr) { + // The key doesn't exist in the tree. + return 0; + } + return 1; + } + // Returns a count of the number of times the key appears in the btree. + template + size_type count_multi(const K &key) const { + const auto range = equal_range(key); + return std::distance(range.first, range.second); + } + + // Clear the btree, deleting all of the values it contains. + void clear(); + + // Swap the contents of *this and x. + void swap(btree &x); + + const key_compare &key_comp() const noexcept { + return root_.template get<0>(); + } + template + bool compare_keys(const K &x, const LK &y) const { + return compare_internal::compare_result_as_less_than(key_comp()(x, y)); + } + + value_compare value_comp() const { return value_compare(key_comp()); } + + // Verifies the structure of the btree. + void verify() const; + + // Size routines. + size_type size() const { return size_; } + size_type max_size() const { return (std::numeric_limits::max)(); } + bool empty() const { return size_ == 0; } + + // The height of the btree. An empty tree will have height 0. + size_type height() const { + size_type h = 0; + if (!empty()) { + // Count the length of the chain from the leftmost node up to the + // root. We actually count from the root back around to the level below + // the root, but the calculation is the same because of the circularity + // of that traversal. + const node_type *n = root(); + do { + ++h; + n = n->parent(); + } while (n != root()); + } + return h; + } + + // The number of internal, leaf and total nodes used by the btree. + size_type leaf_nodes() const { return internal_stats(root()).leaf_nodes; } + size_type internal_nodes() const { + return internal_stats(root()).internal_nodes; + } + size_type nodes() const { + node_stats stats = internal_stats(root()); + return stats.leaf_nodes + stats.internal_nodes; + } + + // The total number of bytes used by the btree. + size_type bytes_used() const { + node_stats stats = internal_stats(root()); + if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) { + return sizeof(*this) + node_type::LeafSize(root()->max_count()); + } else { + return sizeof(*this) + stats.leaf_nodes * node_type::LeafSize() + + stats.internal_nodes * node_type::InternalSize(); + } + } + + // The average number of bytes used per value stored in the btree. + static double average_bytes_per_value() { + // Returns the number of bytes per value on a leaf node that is 75% + // full. Experimentally, this matches up nicely with the computed number of + // bytes per value in trees that had their values inserted in random order. + return node_type::LeafSize() / (kNodeValues * 0.75); + } + + // The fullness of the btree. Computed as the number of elements in the btree + // divided by the maximum number of elements a tree with the current number + // of nodes could hold. A value of 1 indicates perfect space + // utilization. Smaller values indicate space wastage. + // Returns 0 for empty trees. + double fullness() const { + if (empty()) return 0.0; + return static_cast(size()) / (nodes() * kNodeValues); + } + // The overhead of the btree structure in bytes per node. Computed as the + // total number of bytes used by the btree minus the number of bytes used for + // storing elements divided by the number of elements. + // Returns 0 for empty trees. + double overhead() const { + if (empty()) return 0.0; + return (bytes_used() - size() * sizeof(value_type)) / + static_cast(size()); + } + + // The allocator used by the btree. + allocator_type get_allocator() const { return allocator(); } + + private: + // Internal accessor routines. + node_type *root() { return root_.template get<2>(); } + const node_type *root() const { return root_.template get<2>(); } + node_type *&mutable_root() noexcept { return root_.template get<2>(); } + key_compare *mutable_key_comp() noexcept { return &root_.template get<0>(); } + + // The leftmost node is stored as the parent of the root node. + node_type *leftmost() { return root()->parent(); } + const node_type *leftmost() const { return root()->parent(); } + + // Allocator routines. + allocator_type *mutable_allocator() noexcept { + return &root_.template get<1>(); + } + const allocator_type &allocator() const noexcept { + return root_.template get<1>(); + } + + // Allocates a correctly aligned node of at least size bytes using the + // allocator. + node_type *allocate(const size_type size) { + return reinterpret_cast( + absl::container_internal::Allocate( + mutable_allocator(), size)); + } + + // Node creation/deletion routines. + node_type *new_internal_node(node_type *parent) { + node_type *p = allocate(node_type::InternalSize()); + return node_type::init_internal(p, parent); + } + node_type *new_leaf_node(node_type *parent) { + node_type *p = allocate(node_type::LeafSize()); + return node_type::init_leaf(p, parent, kNodeValues); + } + node_type *new_leaf_root_node(const int max_count) { + node_type *p = allocate(node_type::LeafSize(max_count)); + return node_type::init_leaf(p, p, max_count); + } + + // Deletion helper routines. + void erase_same_node(iterator begin, iterator end); + iterator erase_from_leaf_node(iterator begin, size_type to_erase); + iterator rebalance_after_delete(iterator iter); + + // Deallocates a node of a certain size in bytes using the allocator. + void deallocate(const size_type size, node_type *node) { + absl::container_internal::Deallocate( + mutable_allocator(), node, size); + } + + void delete_internal_node(node_type *node) { + node->destroy(mutable_allocator()); + deallocate(node_type::InternalSize(), node); + } + void delete_leaf_node(node_type *node) { + node->destroy(mutable_allocator()); + deallocate(node_type::LeafSize(node->max_count()), node); + } + + // Rebalances or splits the node iter points to. + void rebalance_or_split(iterator *iter); + + // Merges the values of left, right and the delimiting key on their parent + // onto left, removing the delimiting key and deleting right. + void merge_nodes(node_type *left, node_type *right); + + // Tries to merge node with its left or right sibling, and failing that, + // rebalance with its left or right sibling. Returns true if a merge + // occurred, at which point it is no longer valid to access node. Returns + // false if no merging took place. + bool try_merge_or_rebalance(iterator *iter); + + // Tries to shrink the height of the tree by 1. + void try_shrink(); + + iterator internal_end(iterator iter) { + return iter.node != nullptr ? iter : end(); + } + const_iterator internal_end(const_iterator iter) const { + return iter.node != nullptr ? iter : end(); + } + + // Emplaces a value into the btree immediately before iter. Requires that + // key(v) <= iter.key() and (--iter).key() <= key(v). + template + iterator internal_emplace(iterator iter, Args &&... args); + + // Returns an iterator pointing to the first value >= the value "iter" is + // pointing at. Note that "iter" might be pointing to an invalid location such + // as iter.position == iter.node->finish(). This routine simply moves iter up + // in the tree to a valid location. + // Requires: iter.node is non-null. + template + static IterType internal_last(IterType iter); + + // Returns an iterator pointing to the leaf position at which key would + // reside in the tree. We provide 2 versions of internal_locate. The first + // version uses a less-than comparator and is incapable of distinguishing when + // there is an exact match. The second version is for the key-compare-to + // specialization and distinguishes exact matches. The key-compare-to + // specialization allows the caller to avoid a subsequent comparison to + // determine if an exact match was made, which is important for keys with + // expensive comparison, such as strings. + template + SearchResult internal_locate( + const K &key) const; + + template + SearchResult internal_locate_impl( + const K &key, std::false_type /* IsCompareTo */) const; + + template + SearchResult internal_locate_impl( + const K &key, std::true_type /* IsCompareTo */) const; + + // Internal routine which implements lower_bound(). + template + iterator internal_lower_bound(const K &key) const; + + // Internal routine which implements upper_bound(). + template + iterator internal_upper_bound(const K &key) const; + + // Internal routine which implements find(). + template + iterator internal_find(const K &key) const; + + // Deletes a node and all of its children. + void internal_clear(node_type *node); + + // Verifies the tree structure of node. + int internal_verify(const node_type *node, const key_type *lo, + const key_type *hi) const; + + node_stats internal_stats(const node_type *node) const { + // The root can be a static empty node. + if (node == nullptr || (node == root() && empty())) { + return node_stats(0, 0); + } + if (node->leaf()) { + return node_stats(1, 0); + } + node_stats res(0, 1); + for (int i = node->start(); i <= node->finish(); ++i) { + res += internal_stats(node->child(i)); + } + return res; + } + + public: + // Exposed only for tests. + static bool testonly_uses_linear_node_search() { + return node_type::testonly_uses_linear_node_search(); + } + + private: + // We use compressed tuple in order to save space because key_compare and + // allocator_type are usually empty. + absl::container_internal::CompressedTuple + root_; + + // A pointer to the rightmost node. Note that the leftmost node is stored as + // the root's parent. + node_type *rightmost_; + + // Number of values. + size_type size_; +}; + +//// +// btree_node methods +template +template +inline void btree_node

::emplace_value(const size_type i, + allocator_type *alloc, + Args &&... args) { + assert(i >= start()); + assert(i <= finish()); + // Shift old values to create space for new value and then construct it in + // place. + if (i < finish()) { + value_init(finish(), alloc, slot(finish() - 1)); + for (size_type j = finish() - 1; j > i; --j) + params_type::move(alloc, slot(j - 1), slot(j)); + value_destroy(i, alloc); + } + value_init(i, alloc, std::forward(args)...); + set_finish(finish() + 1); + + if (!leaf() && finish() > i + 1) { + for (int j = finish(); j > i + 1; --j) { + set_child(j, child(j - 1)); + } + clear_child(i + 1); + } +} + +template +inline void btree_node

::remove_value(const int i, allocator_type *alloc) { + if (!leaf() && finish() > i + 1) { + assert(child(i + 1)->count() == 0); + for (size_type j = i + 1; j < finish(); ++j) { + set_child(j, child(j + 1)); + } + clear_child(finish()); + } + + remove_values_ignore_children(i, /*to_erase=*/1, alloc); +} + +template +inline void btree_node

::remove_values_ignore_children( + const int i, const int to_erase, allocator_type *alloc) { + params_type::move(alloc, slot(i + to_erase), finish_slot(), slot(i)); + value_destroy_n(finish() - to_erase, to_erase, alloc); + set_finish(finish() - to_erase); +} + +template +void btree_node

::rebalance_right_to_left(const int to_move, + btree_node *right, + allocator_type *alloc) { + assert(parent() == right->parent()); + assert(position() + 1 == right->position()); + assert(right->count() >= count()); + assert(to_move >= 1); + assert(to_move <= right->count()); + + // 1) Move the delimiting value in the parent to the left node. + value_init(finish(), alloc, parent()->slot(position())); + + // 2) Move the (to_move - 1) values from the right node to the left node. + right->uninitialized_move_n(to_move - 1, right->start(), finish() + 1, this, + alloc); + + // 3) Move the new delimiting value to the parent from the right node. + params_type::move(alloc, right->slot(to_move - 1), + parent()->slot(position())); + + // 4) Shift the values in the right node to their correct position. + params_type::move(alloc, right->slot(to_move), right->finish_slot(), + right->start_slot()); + + // 5) Destroy the now-empty to_move entries in the right node. + right->value_destroy_n(right->finish() - to_move, to_move, alloc); + + if (!leaf()) { + // Move the child pointers from the right to the left node. + for (int i = 0; i < to_move; ++i) { + init_child(finish() + i + 1, right->child(i)); + } + for (int i = right->start(); i <= right->finish() - to_move; ++i) { + assert(i + to_move <= right->max_count()); + right->init_child(i, right->child(i + to_move)); + right->clear_child(i + to_move); + } + } + + // Fixup `finish` on the left and right nodes. + set_finish(finish() + to_move); + right->set_finish(right->finish() - to_move); +} + +template +void btree_node

::rebalance_left_to_right(const int to_move, + btree_node *right, + allocator_type *alloc) { + assert(parent() == right->parent()); + assert(position() + 1 == right->position()); + assert(count() >= right->count()); + assert(to_move >= 1); + assert(to_move <= count()); + + // Values in the right node are shifted to the right to make room for the + // new to_move values. Then, the delimiting value in the parent and the + // other (to_move - 1) values in the left node are moved into the right node. + // Lastly, a new delimiting value is moved from the left node into the + // parent, and the remaining empty left node entries are destroyed. + + if (right->count() >= to_move) { + // The original location of the right->count() values are sufficient to hold + // the new to_move entries from the parent and left node. + + // 1) Shift existing values in the right node to their correct positions. + right->uninitialized_move_n(to_move, right->finish() - to_move, + right->finish(), right, alloc); + for (slot_type *src = right->slot(right->finish() - to_move - 1), + *dest = right->slot(right->finish() - 1), + *end = right->start_slot(); + src >= end; --src, --dest) { + params_type::move(alloc, src, dest); + } + + // 2) Move the delimiting value in the parent to the right node. + params_type::move(alloc, parent()->slot(position()), + right->slot(to_move - 1)); + + // 3) Move the (to_move - 1) values from the left node to the right node. + params_type::move(alloc, slot(finish() - (to_move - 1)), finish_slot(), + right->start_slot()); + } else { + // The right node does not have enough initialized space to hold the new + // to_move entries, so part of them will move to uninitialized space. + + // 1) Shift existing values in the right node to their correct positions. + right->uninitialized_move_n(right->count(), right->start(), + right->start() + to_move, right, alloc); + + // 2) Move the delimiting value in the parent to the right node. + right->value_init(to_move - 1, alloc, parent()->slot(position())); + + // 3) Move the (to_move - 1) values from the left node to the right node. + const size_type uninitialized_remaining = to_move - right->count() - 1; + uninitialized_move_n(uninitialized_remaining, + finish() - uninitialized_remaining, right->finish(), + right, alloc); + params_type::move(alloc, slot(finish() - (to_move - 1)), + slot(finish() - uninitialized_remaining), + right->start_slot()); + } + + // 4) Move the new delimiting value to the parent from the left node. + params_type::move(alloc, slot(finish() - to_move), + parent()->slot(position())); + + // 5) Destroy the now-empty to_move entries in the left node. + value_destroy_n(finish() - to_move, to_move, alloc); + + if (!leaf()) { + // Move the child pointers from the left to the right node. + for (int i = right->finish(); i >= right->start(); --i) { + right->init_child(i + to_move, right->child(i)); + right->clear_child(i); + } + for (int i = 1; i <= to_move; ++i) { + right->init_child(i - 1, child(finish() - to_move + i)); + clear_child(finish() - to_move + i); + } + } + + // Fixup the counts on the left and right nodes. + set_finish(finish() - to_move); + right->set_finish(right->finish() + to_move); +} + +template +void btree_node

::split(const int insert_position, btree_node *dest, + allocator_type *alloc) { + assert(dest->count() == 0); + assert(max_count() == kNodeValues); + + // We bias the split based on the position being inserted. If we're + // inserting at the beginning of the left node then bias the split to put + // more values on the right node. If we're inserting at the end of the + // right node then bias the split to put more values on the left node. + if (insert_position == start()) { + dest->set_finish(dest->start() + finish() - 1); + } else if (insert_position == kNodeValues) { + dest->set_finish(dest->start()); + } else { + dest->set_finish(dest->start() + count() / 2); + } + set_finish(finish() - dest->count()); + assert(count() >= 1); + + // Move values from the left sibling to the right sibling. + uninitialized_move_n(dest->count(), finish(), dest->start(), dest, alloc); + + // Destroy the now-empty entries in the left node. + value_destroy_n(finish(), dest->count(), alloc); + + // The split key is the largest value in the left sibling. + --mutable_finish(); + parent()->emplace_value(position(), alloc, finish_slot()); + value_destroy(finish(), alloc); + parent()->init_child(position() + 1, dest); + + if (!leaf()) { + for (int i = dest->start(), j = finish() + 1; i <= dest->finish(); + ++i, ++j) { + assert(child(j) != nullptr); + dest->init_child(i, child(j)); + clear_child(j); + } + } +} + +template +void btree_node

::merge(btree_node *src, allocator_type *alloc) { + assert(parent() == src->parent()); + assert(position() + 1 == src->position()); + + // Move the delimiting value to the left node. + value_init(finish(), alloc, parent()->slot(position())); + + // Move the values from the right to the left node. + src->uninitialized_move_n(src->count(), src->start(), finish() + 1, this, + alloc); + + // Destroy the now-empty entries in the right node. + src->value_destroy_n(src->start(), src->count(), alloc); + + if (!leaf()) { + // Move the child pointers from the right to the left node. + for (int i = src->start(), j = finish() + 1; i <= src->finish(); ++i, ++j) { + init_child(j, src->child(i)); + src->clear_child(i); + } + } + + // Fixup `finish` on the src and dest nodes. + set_finish(start() + 1 + count() + src->count()); + src->set_finish(src->start()); + + // Remove the value on the parent node. + parent()->remove_value(position(), alloc); +} + +template +void btree_node

::swap(btree_node *x, allocator_type *alloc) { + using std::swap; + assert(leaf() == x->leaf()); + + // Determine which is the smaller/larger node. + btree_node *smaller = this, *larger = x; + if (smaller->count() > larger->count()) { + swap(smaller, larger); + } + + // Swap the values. + for (slot_type *a = smaller->start_slot(), *b = larger->start_slot(), + *end = smaller->finish_slot(); + a != end; ++a, ++b) { + params_type::swap(alloc, a, b); + } + + // Move values that can't be swapped. + const size_type to_move = larger->count() - smaller->count(); + larger->uninitialized_move_n(to_move, smaller->finish(), smaller->finish(), + smaller, alloc); + larger->value_destroy_n(smaller->finish(), to_move, alloc); + + if (!leaf()) { + // Swap the child pointers. + std::swap_ranges(&smaller->mutable_child(smaller->start()), + &smaller->mutable_child(smaller->finish() + 1), + &larger->mutable_child(larger->start())); + // Update swapped children's parent pointers. + int i = smaller->start(); + int j = larger->start(); + for (; i <= smaller->finish(); ++i, ++j) { + smaller->child(i)->set_parent(smaller); + larger->child(j)->set_parent(larger); + } + // Move the child pointers that couldn't be swapped. + for (; j <= larger->finish(); ++i, ++j) { + smaller->init_child(i, larger->child(j)); + larger->clear_child(j); + } + } + + // Swap the `finish`s. + // TODO(ezb): with floating storage, will also need to swap starts. + swap(mutable_finish(), x->mutable_finish()); +} + +//// +// btree_iterator methods +template +void btree_iterator::increment_slow() { + if (node->leaf()) { + assert(position >= node->finish()); + btree_iterator save(*this); + while (position == node->finish() && !node->is_root()) { + assert(node->parent()->child(node->position()) == node); + position = node->position(); + node = node->parent(); + } + if (position == node->finish()) { + *this = save; + } + } else { + assert(position < node->finish()); + node = node->child(position + 1); + while (!node->leaf()) { + node = node->start_child(); + } + position = node->start(); + } +} + +template +void btree_iterator::decrement_slow() { + if (node->leaf()) { + assert(position <= -1); + btree_iterator save(*this); + while (position < node->start() && !node->is_root()) { + assert(node->parent()->child(node->position()) == node); + position = node->position() - 1; + node = node->parent(); + } + if (position < node->start()) { + *this = save; + } + } else { + assert(position >= node->start()); + node = node->child(position); + while (!node->leaf()) { + node = node->child(node->finish()); + } + position = node->finish() - 1; + } +} + +//// +// btree methods +template +template +void btree

::copy_or_move_values_in_order(Btree *x) { + static_assert(std::is_same::value || + std::is_same::value, + "Btree type must be same or const."); + assert(empty()); + + // We can avoid key comparisons because we know the order of the + // values is the same order we'll store them in. + auto iter = x->begin(); + if (iter == x->end()) return; + insert_multi(maybe_move_from_iterator(iter)); + ++iter; + for (; iter != x->end(); ++iter) { + // If the btree is not empty, we can just insert the new value at the end + // of the tree. + internal_emplace(end(), maybe_move_from_iterator(iter)); + } +} + +template +constexpr bool btree

::static_assert_validation() { + static_assert(std::is_nothrow_copy_constructible::value, + "Key comparison must be nothrow copy constructible"); + static_assert(std::is_nothrow_copy_constructible::value, + "Allocator must be nothrow copy constructible"); + static_assert(type_traits_internal::is_trivially_copyable::value, + "iterator not trivially copyable."); + + // Note: We assert that kTargetValues, which is computed from + // Params::kTargetNodeSize, must fit the node_type::field_type. + static_assert( + kNodeValues < (1 << (8 * sizeof(typename node_type::field_type))), + "target node size too large"); + + // Verify that key_compare returns an absl::{weak,strong}_ordering or bool. + using compare_result_type = + absl::result_of_t; + static_assert( + std::is_same::value || + std::is_convertible::value, + "key comparison function must return absl::{weak,strong}_ordering or " + "bool."); + + // Test the assumption made in setting kNodeValueSpace. + static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4, + "node space assumption incorrect"); + + return true; +} + +template +btree

::btree(const key_compare &comp, const allocator_type &alloc) + : root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {} + +template +btree

::btree(const btree &x) : btree(x.key_comp(), x.allocator()) { + copy_or_move_values_in_order(&x); +} + +template +template +auto btree

::insert_unique(const key_type &key, Args &&... args) + -> std::pair { + if (empty()) { + mutable_root() = rightmost_ = new_leaf_root_node(1); + } + + auto res = internal_locate(key); + iterator &iter = res.value; + + if (res.HasMatch()) { + if (res.IsEq()) { + // The key already exists in the tree, do nothing. + return {iter, false}; + } + } else { + iterator last = internal_last(iter); + if (last.node && !compare_keys(key, last.key())) { + // The key already exists in the tree, do nothing. + return {last, false}; + } + } + return {internal_emplace(iter, std::forward(args)...), true}; +} + +template +template +inline auto btree

::insert_hint_unique(iterator position, const key_type &key, + Args &&... args) + -> std::pair { + if (!empty()) { + if (position == end() || compare_keys(key, position.key())) { + if (position == begin() || compare_keys(std::prev(position).key(), key)) { + // prev.key() < key < position.key() + return {internal_emplace(position, std::forward(args)...), true}; + } + } else if (compare_keys(position.key(), key)) { + ++position; + if (position == end() || compare_keys(key, position.key())) { + // {original `position`}.key() < key < {current `position`}.key() + return {internal_emplace(position, std::forward(args)...), true}; + } + } else { + // position.key() == key + return {position, false}; + } + } + return insert_unique(key, std::forward(args)...); +} + +template +template +void btree

::insert_iterator_unique(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_hint_unique(end(), params_type::key(*b), *b); + } +} + +template +template +auto btree

::insert_multi(const key_type &key, ValueType &&v) -> iterator { + if (empty()) { + mutable_root() = rightmost_ = new_leaf_root_node(1); + } + + iterator iter = internal_upper_bound(key); + if (iter.node == nullptr) { + iter = end(); + } + return internal_emplace(iter, std::forward(v)); +} + +template +template +auto btree

::insert_hint_multi(iterator position, ValueType &&v) -> iterator { + if (!empty()) { + const key_type &key = params_type::key(v); + if (position == end() || !compare_keys(position.key(), key)) { + if (position == begin() || + !compare_keys(key, std::prev(position).key())) { + // prev.key() <= key <= position.key() + return internal_emplace(position, std::forward(v)); + } + } else { + ++position; + if (position == end() || !compare_keys(position.key(), key)) { + // {original `position`}.key() < key < {current `position`}.key() + return internal_emplace(position, std::forward(v)); + } + } + } + return insert_multi(std::forward(v)); +} + +template +template +void btree

::insert_iterator_multi(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_hint_multi(end(), *b); + } +} + +template +auto btree

::operator=(const btree &x) -> btree & { + if (this != &x) { + clear(); + + *mutable_key_comp() = x.key_comp(); + if (absl::allocator_traits< + allocator_type>::propagate_on_container_copy_assignment::value) { + *mutable_allocator() = x.allocator(); + } + + copy_or_move_values_in_order(&x); + } + return *this; +} + +template +auto btree

::operator=(btree &&x) noexcept -> btree & { + if (this != &x) { + clear(); + + using std::swap; + if (absl::allocator_traits< + allocator_type>::propagate_on_container_copy_assignment::value) { + // Note: `root_` also contains the allocator and the key comparator. + swap(root_, x.root_); + swap(rightmost_, x.rightmost_); + swap(size_, x.size_); + } else { + if (allocator() == x.allocator()) { + swap(mutable_root(), x.mutable_root()); + swap(*mutable_key_comp(), *x.mutable_key_comp()); + swap(rightmost_, x.rightmost_); + swap(size_, x.size_); + } else { + // We aren't allowed to propagate the allocator and the allocator is + // different so we can't take over its memory. We must move each element + // individually. We need both `x` and `this` to have `x`s key comparator + // while moving the values so we can't swap the key comparators. + *mutable_key_comp() = x.key_comp(); + copy_or_move_values_in_order(&x); + } + } + } + return *this; +} + +template +auto btree

::erase(iterator iter) -> iterator { + bool internal_delete = false; + if (!iter.node->leaf()) { + // Deletion of a value on an internal node. First, move the largest value + // from our left child here, then delete that position (in remove_value() + // below). We can get to the largest value from our left child by + // decrementing iter. + iterator internal_iter(iter); + --iter; + assert(iter.node->leaf()); + params_type::move(mutable_allocator(), iter.node->slot(iter.position), + internal_iter.node->slot(internal_iter.position)); + internal_delete = true; + } + + // Delete the key from the leaf. + iter.node->remove_value(iter.position, mutable_allocator()); + --size_; + + // We want to return the next value after the one we just erased. If we + // erased from an internal node (internal_delete == true), then the next + // value is ++(++iter). If we erased from a leaf node (internal_delete == + // false) then the next value is ++iter. Note that ++iter may point to an + // internal node and the value in the internal node may move to a leaf node + // (iter.node) when rebalancing is performed at the leaf level. + + iterator res = rebalance_after_delete(iter); + + // If we erased from an internal node, advance the iterator. + if (internal_delete) { + ++res; + } + return res; +} + +template +auto btree

::rebalance_after_delete(iterator iter) -> iterator { + // Merge/rebalance as we walk back up the tree. + iterator res(iter); + bool first_iteration = true; + for (;;) { + if (iter.node == root()) { + try_shrink(); + if (empty()) { + return end(); + } + break; + } + if (iter.node->count() >= kMinNodeValues) { + break; + } + bool merged = try_merge_or_rebalance(&iter); + // On the first iteration, we should update `res` with `iter` because `res` + // may have been invalidated. + if (first_iteration) { + res = iter; + first_iteration = false; + } + if (!merged) { + break; + } + iter.position = iter.node->position(); + iter.node = iter.node->parent(); + } + + // Adjust our return value. If we're pointing at the end of a node, advance + // the iterator. + if (res.position == res.node->finish()) { + res.position = res.node->finish() - 1; + ++res; + } + + return res; +} + +template +auto btree

::erase_range(iterator begin, iterator end) + -> std::pair { + difference_type count = std::distance(begin, end); + assert(count >= 0); + + if (count == 0) { + return {0, begin}; + } + + if (count == size_) { + clear(); + return {count, this->end()}; + } + + if (begin.node == end.node) { + erase_same_node(begin, end); + size_ -= count; + return {count, rebalance_after_delete(begin)}; + } + + const size_type target_size = size_ - count; + while (size_ > target_size) { + if (begin.node->leaf()) { + const size_type remaining_to_erase = size_ - target_size; + const size_type remaining_in_node = begin.node->finish() - begin.position; + begin = erase_from_leaf_node( + begin, (std::min)(remaining_to_erase, remaining_in_node)); + } else { + begin = erase(begin); + } + } + return {count, begin}; +} + +template +void btree

::erase_same_node(iterator begin, iterator end) { + assert(begin.node == end.node); + assert(end.position > begin.position); + + node_type *node = begin.node; + size_type to_erase = end.position - begin.position; + if (!node->leaf()) { + // Delete all children between begin and end. + for (size_type i = 0; i < to_erase; ++i) { + internal_clear(node->child(begin.position + i + 1)); + } + // Rotate children after end into new positions. + for (size_type i = begin.position + to_erase + 1; i <= node->finish(); + ++i) { + node->set_child(i - to_erase, node->child(i)); + node->clear_child(i); + } + } + node->remove_values_ignore_children(begin.position, to_erase, + mutable_allocator()); + + // Do not need to update rightmost_, because + // * either end == this->end(), and therefore node == rightmost_, and still + // exists + // * or end != this->end(), and therefore rightmost_ hasn't been erased, since + // it wasn't covered in [begin, end) +} + +template +auto btree

::erase_from_leaf_node(iterator begin, size_type to_erase) + -> iterator { + node_type *node = begin.node; + assert(node->leaf()); + assert(node->finish() > begin.position); + assert(begin.position + to_erase <= node->finish()); + + node->remove_values_ignore_children(begin.position, to_erase, + mutable_allocator()); + + size_ -= to_erase; + + return rebalance_after_delete(begin); +} + +template +template +auto btree

::erase_unique(const K &key) -> size_type { + const iterator iter = internal_find(key); + if (iter.node == nullptr) { + // The key doesn't exist in the tree, return nothing done. + return 0; + } + erase(iter); + return 1; +} + +template +template +auto btree

::erase_multi(const K &key) -> size_type { + const iterator begin = internal_lower_bound(key); + if (begin.node == nullptr) { + // The key doesn't exist in the tree, return nothing done. + return 0; + } + // Delete all of the keys between begin and upper_bound(key). + const iterator end = internal_end(internal_upper_bound(key)); + return erase_range(begin, end).first; +} + +template +void btree

::clear() { + if (!empty()) { + internal_clear(root()); + } + mutable_root() = EmptyNode(); + rightmost_ = EmptyNode(); + size_ = 0; +} + +template +void btree

::swap(btree &x) { + using std::swap; + if (absl::allocator_traits< + allocator_type>::propagate_on_container_swap::value) { + // Note: `root_` also contains the allocator and the key comparator. + swap(root_, x.root_); + } else { + // It's undefined behavior if the allocators are unequal here. + assert(allocator() == x.allocator()); + swap(mutable_root(), x.mutable_root()); + swap(*mutable_key_comp(), *x.mutable_key_comp()); + } + swap(rightmost_, x.rightmost_); + swap(size_, x.size_); +} + +template +void btree

::verify() const { + assert(root() != nullptr); + assert(leftmost() != nullptr); + assert(rightmost_ != nullptr); + assert(empty() || size() == internal_verify(root(), nullptr, nullptr)); + assert(leftmost() == (++const_iterator(root(), -1)).node); + assert(rightmost_ == (--const_iterator(root(), root()->finish())).node); + assert(leftmost()->leaf()); + assert(rightmost_->leaf()); +} + +template +void btree

::rebalance_or_split(iterator *iter) { + node_type *&node = iter->node; + int &insert_position = iter->position; + assert(node->count() == node->max_count()); + assert(kNodeValues == node->max_count()); + + // First try to make room on the node by rebalancing. + node_type *parent = node->parent(); + if (node != root()) { + if (node->position() > parent->start()) { + // Try rebalancing with our left sibling. + node_type *left = parent->child(node->position() - 1); + assert(left->max_count() == kNodeValues); + if (left->count() < kNodeValues) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the end of the right node then we bias rebalancing to + // fill up the left node. + int to_move = (kNodeValues - left->count()) / + (1 + (insert_position < kNodeValues)); + to_move = (std::max)(1, to_move); + + if (insert_position - to_move >= node->start() || + left->count() + to_move < kNodeValues) { + left->rebalance_right_to_left(to_move, node, mutable_allocator()); + + assert(node->max_count() - node->count() == to_move); + insert_position = insert_position - to_move; + if (insert_position < node->start()) { + insert_position = insert_position + left->count() + 1; + node = left; + } + + assert(node->count() < node->max_count()); + return; + } + } + } + + if (node->position() < parent->finish()) { + // Try rebalancing with our right sibling. + node_type *right = parent->child(node->position() + 1); + assert(right->max_count() == kNodeValues); + if (right->count() < kNodeValues) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the beginning of the left node then we bias rebalancing + // to fill up the right node. + int to_move = (kNodeValues - right->count()) / + (1 + (insert_position > node->start())); + to_move = (std::max)(1, to_move); + + if (insert_position <= node->finish() - to_move || + right->count() + to_move < kNodeValues) { + node->rebalance_left_to_right(to_move, right, mutable_allocator()); + + if (insert_position > node->finish()) { + insert_position = insert_position - node->count() - 1; + node = right; + } + + assert(node->count() < node->max_count()); + return; + } + } + } + + // Rebalancing failed, make sure there is room on the parent node for a new + // value. + assert(parent->max_count() == kNodeValues); + if (parent->count() == kNodeValues) { + iterator parent_iter(node->parent(), node->position()); + rebalance_or_split(&parent_iter); + } + } else { + // Rebalancing not possible because this is the root node. + // Create a new root node and set the current root node as the child of the + // new root. + parent = new_internal_node(parent); + parent->init_child(parent->start(), root()); + mutable_root() = parent; + // If the former root was a leaf node, then it's now the rightmost node. + assert(!parent->start_child()->leaf() || + parent->start_child() == rightmost_); + } + + // Split the node. + node_type *split_node; + if (node->leaf()) { + split_node = new_leaf_node(parent); + node->split(insert_position, split_node, mutable_allocator()); + if (rightmost_ == node) rightmost_ = split_node; + } else { + split_node = new_internal_node(parent); + node->split(insert_position, split_node, mutable_allocator()); + } + + if (insert_position > node->finish()) { + insert_position = insert_position - node->count() - 1; + node = split_node; + } +} + +template +void btree

::merge_nodes(node_type *left, node_type *right) { + left->merge(right, mutable_allocator()); + if (right->leaf()) { + if (rightmost_ == right) rightmost_ = left; + delete_leaf_node(right); + } else { + delete_internal_node(right); + } +} + +template +bool btree

::try_merge_or_rebalance(iterator *iter) { + node_type *parent = iter->node->parent(); + if (iter->node->position() > parent->start()) { + // Try merging with our left sibling. + node_type *left = parent->child(iter->node->position() - 1); + assert(left->max_count() == kNodeValues); + if (1 + left->count() + iter->node->count() <= kNodeValues) { + iter->position += 1 + left->count(); + merge_nodes(left, iter->node); + iter->node = left; + return true; + } + } + if (iter->node->position() < parent->finish()) { + // Try merging with our right sibling. + node_type *right = parent->child(iter->node->position() + 1); + assert(right->max_count() == kNodeValues); + if (1 + iter->node->count() + right->count() <= kNodeValues) { + merge_nodes(iter->node, right); + return true; + } + // Try rebalancing with our right sibling. We don't perform rebalancing if + // we deleted the first element from iter->node and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the front of the tree. + if (right->count() > kMinNodeValues && + (iter->node->count() == 0 || iter->position > iter->node->start())) { + int to_move = (right->count() - iter->node->count()) / 2; + to_move = (std::min)(to_move, right->count() - 1); + iter->node->rebalance_right_to_left(to_move, right, mutable_allocator()); + return false; + } + } + if (iter->node->position() > parent->start()) { + // Try rebalancing with our left sibling. We don't perform rebalancing if + // we deleted the last element from iter->node and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the back of the tree. + node_type *left = parent->child(iter->node->position() - 1); + if (left->count() > kMinNodeValues && + (iter->node->count() == 0 || iter->position < iter->node->finish())) { + int to_move = (left->count() - iter->node->count()) / 2; + to_move = (std::min)(to_move, left->count() - 1); + left->rebalance_left_to_right(to_move, iter->node, mutable_allocator()); + iter->position += to_move; + return false; + } + } + return false; +} + +template +void btree

::try_shrink() { + if (root()->count() > 0) { + return; + } + // Deleted the last item on the root node, shrink the height of the tree. + if (root()->leaf()) { + assert(size() == 0); + delete_leaf_node(root()); + mutable_root() = EmptyNode(); + rightmost_ = EmptyNode(); + } else { + node_type *child = root()->start_child(); + child->make_root(); + delete_internal_node(root()); + mutable_root() = child; + } +} + +template +template +inline IterType btree

::internal_last(IterType iter) { + assert(iter.node != nullptr); + while (iter.position == iter.node->finish()) { + iter.position = iter.node->position(); + iter.node = iter.node->parent(); + if (iter.node->leaf()) { + iter.node = nullptr; + break; + } + } + return iter; +} + +template +template +inline auto btree

::internal_emplace(iterator iter, Args &&... args) + -> iterator { + if (!iter.node->leaf()) { + // We can't insert on an internal node. Instead, we'll insert after the + // previous value which is guaranteed to be on a leaf node. + --iter; + ++iter.position; + } + const int max_count = iter.node->max_count(); + if (iter.node->count() == max_count) { + // Make room in the leaf for the new item. + if (max_count < kNodeValues) { + // Insertion into the root where the root is smaller than the full node + // size. Simply grow the size of the root node. + assert(iter.node == root()); + iter.node = + new_leaf_root_node((std::min)(kNodeValues, 2 * max_count)); + iter.node->swap(root(), mutable_allocator()); + delete_leaf_node(root()); + mutable_root() = iter.node; + rightmost_ = iter.node; + } else { + rebalance_or_split(&iter); + } + } + iter.node->emplace_value(iter.position, mutable_allocator(), + std::forward(args)...); + ++size_; + return iter; +} + +template +template +inline auto btree

::internal_locate(const K &key) const + -> SearchResult { + return internal_locate_impl(key, is_key_compare_to()); +} + +template +template +inline auto btree

::internal_locate_impl( + const K &key, std::false_type /* IsCompareTo */) const + -> SearchResult { + iterator iter(const_cast(root())); + for (;;) { + iter.position = iter.node->lower_bound(key, key_comp()).value; + // NOTE: we don't need to walk all the way down the tree if the keys are + // equal, but determining equality would require doing an extra comparison + // on each node on the way down, and we will need to go all the way to the + // leaf node in the expected case. + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return {iter}; +} + +template +template +inline auto btree

::internal_locate_impl( + const K &key, std::true_type /* IsCompareTo */) const + -> SearchResult { + iterator iter(const_cast(root())); + for (;;) { + SearchResult res = iter.node->lower_bound(key, key_comp()); + iter.position = res.value; + if (res.match == MatchKind::kEq) { + return {iter, MatchKind::kEq}; + } + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return {iter, MatchKind::kNe}; +} + +template +template +auto btree

::internal_lower_bound(const K &key) const -> iterator { + iterator iter(const_cast(root())); + for (;;) { + iter.position = iter.node->lower_bound(key, key_comp()).value; + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return internal_last(iter); +} + +template +template +auto btree

::internal_upper_bound(const K &key) const -> iterator { + iterator iter(const_cast(root())); + for (;;) { + iter.position = iter.node->upper_bound(key, key_comp()); + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return internal_last(iter); +} + +template +template +auto btree

::internal_find(const K &key) const -> iterator { + auto res = internal_locate(key); + if (res.HasMatch()) { + if (res.IsEq()) { + return res.value; + } + } else { + const iterator iter = internal_last(res.value); + if (iter.node != nullptr && !compare_keys(key, iter.key())) { + return iter; + } + } + return {nullptr, 0}; +} + +template +void btree

::internal_clear(node_type *node) { + if (!node->leaf()) { + for (int i = node->start(); i <= node->finish(); ++i) { + internal_clear(node->child(i)); + } + delete_internal_node(node); + } else { + delete_leaf_node(node); + } +} + +template +int btree

::internal_verify(const node_type *node, const key_type *lo, + const key_type *hi) const { + assert(node->count() > 0); + assert(node->count() <= node->max_count()); + if (lo) { + assert(!compare_keys(node->key(node->start()), *lo)); + } + if (hi) { + assert(!compare_keys(*hi, node->key(node->finish() - 1))); + } + for (int i = node->start() + 1; i < node->finish(); ++i) { + assert(!compare_keys(node->key(i), node->key(i - 1))); + } + int count = node->count(); + if (!node->leaf()) { + for (int i = node->start(); i <= node->finish(); ++i) { + assert(node->child(i) != nullptr); + assert(node->child(i)->parent() == node); + assert(node->child(i)->position() == i); + count += internal_verify(node->child(i), + i == node->start() ? lo : &node->key(i - 1), + i == node->finish() ? hi : &node->key(i)); + } + } + return count; +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_BTREE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/btree_container.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/btree_container.h new file mode 100644 index 0000000000000000000000000000000000000000..f2e4c3a5358bfe1bf146daf6f66b7f948a7786b8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/btree_container.h @@ -0,0 +1,672 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ +#define ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ + +#include +#include +#include +#include + +#include "absl/base/internal/throw_delegate.h" +#include "absl/container/internal/btree.h" // IWYU pragma: export +#include "absl/container/internal/common.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A common base class for btree_set, btree_map, btree_multiset, and +// btree_multimap. +template +class btree_container { + using params_type = typename Tree::params_type; + + protected: + // Alias used for heterogeneous lookup functions. + // `key_arg` evaluates to `K` when the functors are transparent and to + // `key_type` otherwise. It permits template argument deduction on `K` for the + // transparent case. + template + using key_arg = + typename KeyArg::value>:: + template type; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using difference_type = typename Tree::difference_type; + using key_compare = typename Tree::key_compare; + using value_compare = typename Tree::value_compare; + using allocator_type = typename Tree::allocator_type; + using reference = typename Tree::reference; + using const_reference = typename Tree::const_reference; + using pointer = typename Tree::pointer; + using const_pointer = typename Tree::const_pointer; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using reverse_iterator = typename Tree::reverse_iterator; + using const_reverse_iterator = typename Tree::const_reverse_iterator; + using node_type = typename Tree::node_handle_type; + + // Constructors/assignments. + btree_container() : tree_(key_compare(), allocator_type()) {} + explicit btree_container(const key_compare &comp, + const allocator_type &alloc = allocator_type()) + : tree_(comp, alloc) {} + btree_container(const btree_container &x) = default; + btree_container(btree_container &&x) noexcept = default; + btree_container &operator=(const btree_container &x) = default; + btree_container &operator=(btree_container &&x) noexcept( + std::is_nothrow_move_assignable::value) = default; + + // Iterator routines. + iterator begin() { return tree_.begin(); } + const_iterator begin() const { return tree_.begin(); } + const_iterator cbegin() const { return tree_.begin(); } + iterator end() { return tree_.end(); } + const_iterator end() const { return tree_.end(); } + const_iterator cend() const { return tree_.end(); } + reverse_iterator rbegin() { return tree_.rbegin(); } + const_reverse_iterator rbegin() const { return tree_.rbegin(); } + const_reverse_iterator crbegin() const { return tree_.rbegin(); } + reverse_iterator rend() { return tree_.rend(); } + const_reverse_iterator rend() const { return tree_.rend(); } + const_reverse_iterator crend() const { return tree_.rend(); } + + // Lookup routines. + template + iterator find(const key_arg &key) { + return tree_.find(key); + } + template + const_iterator find(const key_arg &key) const { + return tree_.find(key); + } + template + bool contains(const key_arg &key) const { + return find(key) != end(); + } + template + iterator lower_bound(const key_arg &key) { + return tree_.lower_bound(key); + } + template + const_iterator lower_bound(const key_arg &key) const { + return tree_.lower_bound(key); + } + template + iterator upper_bound(const key_arg &key) { + return tree_.upper_bound(key); + } + template + const_iterator upper_bound(const key_arg &key) const { + return tree_.upper_bound(key); + } + template + std::pair equal_range(const key_arg &key) { + return tree_.equal_range(key); + } + template + std::pair equal_range( + const key_arg &key) const { + return tree_.equal_range(key); + } + + // Deletion routines. Note that there is also a deletion routine that is + // specific to btree_set_container/btree_multiset_container. + + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + iterator erase(const_iterator iter) { return tree_.erase(iterator(iter)); } + iterator erase(iterator iter) { return tree_.erase(iter); } + iterator erase(const_iterator first, const_iterator last) { + return tree_.erase_range(iterator(first), iterator(last)).second; + } + + // Extract routines. + node_type extract(iterator position) { + // Use Move instead of Transfer, because the rebalancing code expects to + // have a valid object to scribble metadata bits on top of. + auto node = CommonAccess::Move(get_allocator(), position.slot()); + erase(position); + return node; + } + node_type extract(const_iterator position) { + return extract(iterator(position)); + } + + public: + // Utility routines. + void clear() { tree_.clear(); } + void swap(btree_container &x) { tree_.swap(x.tree_); } + void verify() const { tree_.verify(); } + + // Size routines. + size_type size() const { return tree_.size(); } + size_type max_size() const { return tree_.max_size(); } + bool empty() const { return tree_.empty(); } + + friend bool operator==(const btree_container &x, const btree_container &y) { + if (x.size() != y.size()) return false; + return std::equal(x.begin(), x.end(), y.begin()); + } + + friend bool operator!=(const btree_container &x, const btree_container &y) { + return !(x == y); + } + + friend bool operator<(const btree_container &x, const btree_container &y) { + return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); + } + + friend bool operator>(const btree_container &x, const btree_container &y) { + return y < x; + } + + friend bool operator<=(const btree_container &x, const btree_container &y) { + return !(y < x); + } + + friend bool operator>=(const btree_container &x, const btree_container &y) { + return !(x < y); + } + + // The allocator used by the btree. + allocator_type get_allocator() const { return tree_.get_allocator(); } + + // The key comparator used by the btree. + key_compare key_comp() const { return tree_.key_comp(); } + value_compare value_comp() const { return tree_.value_comp(); } + + // Support absl::Hash. + template + friend State AbslHashValue(State h, const btree_container &b) { + for (const auto &v : b) { + h = State::combine(std::move(h), v); + } + return State::combine(std::move(h), b.size()); + } + + protected: + Tree tree_; +}; + +// A common base class for btree_set and btree_map. +template +class btree_set_container : public btree_container { + using super_type = btree_container; + using params_type = typename Tree::params_type; + using init_type = typename params_type::init_type; + using is_key_compare_to = typename params_type::is_key_compare_to; + friend class BtreeNodePeer; + + protected: + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using key_compare = typename Tree::key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using node_type = typename super_type::node_type; + using insert_return_type = InsertReturnType; + + // Inherit constructors. + using super_type::super_type; + btree_set_container() {} + + // Range constructor. + template + btree_set_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + + // Initializer list constructor. + btree_set_container(std::initializer_list init, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : btree_set_container(init.begin(), init.end(), comp, alloc) {} + + // Lookup routines. + template + size_type count(const key_arg &key) const { + return this->tree_.count_unique(key); + } + + // Insertion routines. + std::pair insert(const value_type &x) { + return this->tree_.insert_unique(params_type::key(x), x); + } + std::pair insert(value_type &&x) { + return this->tree_.insert_unique(params_type::key(x), std::move(x)); + } + template + std::pair emplace(Args &&... args) { + init_type v(std::forward(args)...); + return this->tree_.insert_unique(params_type::key(v), std::move(v)); + } + iterator insert(const_iterator position, const value_type &x) { + return this->tree_ + .insert_hint_unique(iterator(position), params_type::key(x), x) + .first; + } + iterator insert(const_iterator position, value_type &&x) { + return this->tree_ + .insert_hint_unique(iterator(position), params_type::key(x), + std::move(x)) + .first; + } + template + iterator emplace_hint(const_iterator position, Args &&... args) { + init_type v(std::forward(args)...); + return this->tree_ + .insert_hint_unique(iterator(position), params_type::key(v), + std::move(v)) + .first; + } + template + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_iterator_unique(b, e); + } + void insert(std::initializer_list init) { + this->tree_.insert_iterator_unique(init.begin(), init.end()); + } + insert_return_type insert(node_type &&node) { + if (!node) return {this->end(), false, node_type()}; + std::pair res = + this->tree_.insert_unique(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + if (res.second) { + CommonAccess::Destroy(&node); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + iterator insert(const_iterator hint, node_type &&node) { + if (!node) return this->end(); + std::pair res = this->tree_.insert_hint_unique( + iterator(hint), params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + if (res.second) CommonAccess::Destroy(&node); + return res.first; + } + + // Deletion routines. + template + size_type erase(const key_arg &key) { + return this->tree_.erase_unique(key); + } + using super_type::erase; + + // Node extraction routines. + template + node_type extract(const key_arg &key) { + auto it = this->find(key); + return it == this->end() ? node_type() : extract(it); + } + using super_type::extract; + + // Merge routines. + // Moves elements from `src` into `this`. If the element already exists in + // `this`, it is left unmodified in `src`. + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &src) { // NOLINT + for (auto src_it = src.begin(); src_it != src.end();) { + if (insert(std::move(*src_it)).second) { + src_it = src.erase(src_it); + } else { + ++src_it; + } + } + } + + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &&src) { + merge(src); + } +}; + +// Base class for btree_map. +template +class btree_map_container : public btree_set_container { + using super_type = btree_set_container; + using params_type = typename Tree::params_type; + + private: + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using mapped_type = typename params_type::mapped_type; + using value_type = typename Tree::value_type; + using key_compare = typename Tree::key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + + // Inherit constructors. + using super_type::super_type; + btree_map_container() {} + + // Insertion routines. + // Note: the nullptr template arguments and extra `const M&` overloads allow + // for supporting bitfield arguments. + // Note: when we call `std::forward(obj)` twice, it's safe because + // insert_unique/insert_hint_unique are guaranteed to not consume `obj` when + // `ret.second` is false. + template + std::pair insert_or_assign(const key_type &k, const M &obj) { + const std::pair ret = this->tree_.insert_unique(k, k, obj); + if (!ret.second) ret.first->second = obj; + return ret; + } + template + std::pair insert_or_assign(key_type &&k, const M &obj) { + const std::pair ret = + this->tree_.insert_unique(k, std::move(k), obj); + if (!ret.second) ret.first->second = obj; + return ret; + } + template + std::pair insert_or_assign(const key_type &k, M &&obj) { + const std::pair ret = + this->tree_.insert_unique(k, k, std::forward(obj)); + if (!ret.second) ret.first->second = std::forward(obj); + return ret; + } + template + std::pair insert_or_assign(key_type &&k, M &&obj) { + const std::pair ret = + this->tree_.insert_unique(k, std::move(k), std::forward(obj)); + if (!ret.second) ret.first->second = std::forward(obj); + return ret; + } + template + iterator insert_or_assign(const_iterator position, const key_type &k, + const M &obj) { + const std::pair ret = + this->tree_.insert_hint_unique(iterator(position), k, k, obj); + if (!ret.second) ret.first->second = obj; + return ret.first; + } + template + iterator insert_or_assign(const_iterator position, key_type &&k, + const M &obj) { + const std::pair ret = this->tree_.insert_hint_unique( + iterator(position), k, std::move(k), obj); + if (!ret.second) ret.first->second = obj; + return ret.first; + } + template + iterator insert_or_assign(const_iterator position, const key_type &k, + M &&obj) { + const std::pair ret = this->tree_.insert_hint_unique( + iterator(position), k, k, std::forward(obj)); + if (!ret.second) ret.first->second = std::forward(obj); + return ret.first; + } + template + iterator insert_or_assign(const_iterator position, key_type &&k, M &&obj) { + const std::pair ret = this->tree_.insert_hint_unique( + iterator(position), k, std::move(k), std::forward(obj)); + if (!ret.second) ret.first->second = std::forward(obj); + return ret.first; + } + template + std::pair try_emplace(const key_type &k, Args &&... args) { + return this->tree_.insert_unique( + k, std::piecewise_construct, std::forward_as_tuple(k), + std::forward_as_tuple(std::forward(args)...)); + } + template + std::pair try_emplace(key_type &&k, Args &&... args) { + // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k` + // and then using `k` unsequenced. This is safe because the move is into a + // forwarding reference and insert_unique guarantees that `key` is never + // referenced after consuming `args`. + const key_type &key_ref = k; + return this->tree_.insert_unique( + key_ref, std::piecewise_construct, std::forward_as_tuple(std::move(k)), + std::forward_as_tuple(std::forward(args)...)); + } + template + iterator try_emplace(const_iterator hint, const key_type &k, + Args &&... args) { + return this->tree_ + .insert_hint_unique(iterator(hint), k, std::piecewise_construct, + std::forward_as_tuple(k), + std::forward_as_tuple(std::forward(args)...)) + .first; + } + template + iterator try_emplace(const_iterator hint, key_type &&k, Args &&... args) { + // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k` + // and then using `k` unsequenced. This is safe because the move is into a + // forwarding reference and insert_hint_unique guarantees that `key` is + // never referenced after consuming `args`. + const key_type &key_ref = k; + return this->tree_ + .insert_hint_unique(iterator(hint), key_ref, std::piecewise_construct, + std::forward_as_tuple(std::move(k)), + std::forward_as_tuple(std::forward(args)...)) + .first; + } + mapped_type &operator[](const key_type &k) { + return try_emplace(k).first->second; + } + mapped_type &operator[](key_type &&k) { + return try_emplace(std::move(k)).first->second; + } + + template + mapped_type &at(const key_arg &key) { + auto it = this->find(key); + if (it == this->end()) + base_internal::ThrowStdOutOfRange("absl::btree_map::at"); + return it->second; + } + template + const mapped_type &at(const key_arg &key) const { + auto it = this->find(key); + if (it == this->end()) + base_internal::ThrowStdOutOfRange("absl::btree_map::at"); + return it->second; + } +}; + +// A common base class for btree_multiset and btree_multimap. +template +class btree_multiset_container : public btree_container { + using super_type = btree_container; + using params_type = typename Tree::params_type; + using init_type = typename params_type::init_type; + using is_key_compare_to = typename params_type::is_key_compare_to; + + template + using key_arg = typename super_type::template key_arg; + + public: + using key_type = typename Tree::key_type; + using value_type = typename Tree::value_type; + using size_type = typename Tree::size_type; + using key_compare = typename Tree::key_compare; + using allocator_type = typename Tree::allocator_type; + using iterator = typename Tree::iterator; + using const_iterator = typename Tree::const_iterator; + using node_type = typename super_type::node_type; + + // Inherit constructors. + using super_type::super_type; + btree_multiset_container() {} + + // Range constructor. + template + btree_multiset_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + + // Initializer list constructor. + btree_multiset_container(std::initializer_list init, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : btree_multiset_container(init.begin(), init.end(), comp, alloc) {} + + // Lookup routines. + template + size_type count(const key_arg &key) const { + return this->tree_.count_multi(key); + } + + // Insertion routines. + iterator insert(const value_type &x) { return this->tree_.insert_multi(x); } + iterator insert(value_type &&x) { + return this->tree_.insert_multi(std::move(x)); + } + iterator insert(const_iterator position, const value_type &x) { + return this->tree_.insert_hint_multi(iterator(position), x); + } + iterator insert(const_iterator position, value_type &&x) { + return this->tree_.insert_hint_multi(iterator(position), std::move(x)); + } + template + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_iterator_multi(b, e); + } + void insert(std::initializer_list init) { + this->tree_.insert_iterator_multi(init.begin(), init.end()); + } + template + iterator emplace(Args &&... args) { + return this->tree_.insert_multi(init_type(std::forward(args)...)); + } + template + iterator emplace_hint(const_iterator position, Args &&... args) { + return this->tree_.insert_hint_multi( + iterator(position), init_type(std::forward(args)...)); + } + iterator insert(node_type &&node) { + if (!node) return this->end(); + iterator res = + this->tree_.insert_multi(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + CommonAccess::Destroy(&node); + return res; + } + iterator insert(const_iterator hint, node_type &&node) { + if (!node) return this->end(); + iterator res = this->tree_.insert_hint_multi( + iterator(hint), + std::move(params_type::element(CommonAccess::GetSlot(node)))); + CommonAccess::Destroy(&node); + return res; + } + + // Deletion routines. + template + size_type erase(const key_arg &key) { + return this->tree_.erase_multi(key); + } + using super_type::erase; + + // Node extraction routines. + template + node_type extract(const key_arg &key) { + auto it = this->find(key); + return it == this->end() ? node_type() : extract(it); + } + using super_type::extract; + + // Merge routines. + // Moves all elements from `src` into `this`. + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &src) { // NOLINT + insert(std::make_move_iterator(src.begin()), + std::make_move_iterator(src.end())); + src.clear(); + } + + template < + typename T, + typename absl::enable_if_t< + absl::conjunction< + std::is_same, + std::is_same, + std::is_same>::value, + int> = 0> + void merge(btree_container &&src) { + merge(src); + } +}; + +// A base class for btree_multimap. +template +class btree_multimap_container : public btree_multiset_container { + using super_type = btree_multiset_container; + using params_type = typename Tree::params_type; + + public: + using mapped_type = typename params_type::mapped_type; + + // Inherit constructors. + using super_type::super_type; + btree_multimap_container() {} +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/common.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/common.h new file mode 100644 index 0000000000000000000000000000000000000000..5037d80316cc98388955c1a559087984f660f57e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/common.h @@ -0,0 +1,202 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_CONTAINER_H_ +#define ABSL_CONTAINER_INTERNAL_CONTAINER_H_ + +#include +#include + +#include "absl/meta/type_traits.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct IsTransparent : std::false_type {}; +template +struct IsTransparent> + : std::true_type {}; + +template +struct KeyArg { + // Transparent. Forward `K`. + template + using type = K; +}; + +template <> +struct KeyArg { + // Not transparent. Always use `key_type`. + template + using type = key_type; +}; + +// The node_handle concept from C++17. +// We specialize node_handle for sets and maps. node_handle_base holds the +// common API of both. +template +class node_handle_base { + protected: + using slot_type = typename PolicyTraits::slot_type; + + public: + using allocator_type = Alloc; + + constexpr node_handle_base() = default; + node_handle_base(node_handle_base&& other) noexcept { + *this = std::move(other); + } + ~node_handle_base() { destroy(); } + node_handle_base& operator=(node_handle_base&& other) noexcept { + destroy(); + if (!other.empty()) { + alloc_ = other.alloc_; + PolicyTraits::transfer(alloc(), slot(), other.slot()); + other.reset(); + } + return *this; + } + + bool empty() const noexcept { return !alloc_; } + explicit operator bool() const noexcept { return !empty(); } + allocator_type get_allocator() const { return *alloc_; } + + protected: + friend struct CommonAccess; + + struct transfer_tag_t {}; + node_handle_base(transfer_tag_t, const allocator_type& a, slot_type* s) + : alloc_(a) { + PolicyTraits::transfer(alloc(), slot(), s); + } + + struct move_tag_t {}; + node_handle_base(move_tag_t, const allocator_type& a, slot_type* s) + : alloc_(a) { + PolicyTraits::construct(alloc(), slot(), s); + } + + void destroy() { + if (!empty()) { + PolicyTraits::destroy(alloc(), slot()); + reset(); + } + } + + void reset() { + assert(alloc_.has_value()); + alloc_ = absl::nullopt; + } + + slot_type* slot() const { + assert(!empty()); + return reinterpret_cast(std::addressof(slot_space_)); + } + allocator_type* alloc() { return std::addressof(*alloc_); } + + private: + absl::optional alloc_ = {}; + alignas(slot_type) mutable unsigned char slot_space_[sizeof(slot_type)] = {}; +}; + +// For sets. +template +class node_handle : public node_handle_base { + using Base = node_handle_base; + + public: + using value_type = typename PolicyTraits::value_type; + + constexpr node_handle() {} + + value_type& value() const { return PolicyTraits::element(this->slot()); } + + private: + friend struct CommonAccess; + + using Base::Base; +}; + +// For maps. +template +class node_handle> + : public node_handle_base { + using Base = node_handle_base; + + public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + + constexpr node_handle() {} + + auto key() const -> decltype(PolicyTraits::key(this->slot())) { + return PolicyTraits::key(this->slot()); + } + + mapped_type& mapped() const { + return PolicyTraits::value(&PolicyTraits::element(this->slot())); + } + + private: + friend struct CommonAccess; + + using Base::Base; +}; + +// Provide access to non-public node-handle functions. +struct CommonAccess { + template + static auto GetSlot(const Node& node) -> decltype(node.slot()) { + return node.slot(); + } + + template + static void Destroy(Node* node) { + node->destroy(); + } + + template + static void Reset(Node* node) { + node->reset(); + } + + template + static T Transfer(Args&&... args) { + return T(typename T::transfer_tag_t{}, std::forward(args)...); + } + + template + static T Move(Args&&... args) { + return T(typename T::move_tag_t{}, std::forward(args)...); + } +}; + +// Implement the insert_return_type<> concept of C++17. +template +struct InsertReturnType { + Iterator position; + bool inserted; + NodeType node; +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/compressed_tuple.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/compressed_tuple.h new file mode 100644 index 0000000000000000000000000000000000000000..4bfe92fd991f54b7aa77aa24b25e66f3cdca4dec --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/compressed_tuple.h @@ -0,0 +1,265 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Helper class to perform the Empty Base Optimization. +// Ts can contain classes and non-classes, empty or not. For the ones that +// are empty classes, we perform the optimization. If all types in Ts are empty +// classes, then CompressedTuple is itself an empty class. +// +// To access the members, use member get() function. +// +// Eg: +// absl::container_internal::CompressedTuple value(7, t1, t2, +// t3); +// assert(value.get<0>() == 7); +// T1& t1 = value.get<1>(); +// const T2& t2 = value.get<2>(); +// ... +// +// https://en.cppreference.com/w/cpp/language/ebo + +#ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ +#define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ + +#include +#include +#include +#include + +#include "absl/utility/utility.h" + +#if defined(_MSC_VER) && !defined(__NVCC__) +// We need to mark these classes with this declspec to ensure that +// CompressedTuple happens. +#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases) +#else +#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class CompressedTuple; + +namespace internal_compressed_tuple { + +template +struct Elem; +template +struct Elem, I> + : std::tuple_element> {}; +template +using ElemT = typename Elem::type; + +// Use the __is_final intrinsic if available. Where it's not available, classes +// declared with the 'final' specifier cannot be used as CompressedTuple +// elements. +// TODO(sbenza): Replace this with std::is_final in C++14. +template +constexpr bool IsFinal() { +#if defined(__clang__) || defined(__GNUC__) + return __is_final(T); +#else + return false; +#endif +} + +// We can't use EBCO on other CompressedTuples because that would mean that we +// derive from multiple Storage<> instantiations with the same I parameter, +// and potentially from multiple identical Storage<> instantiations. So anytime +// we use type inheritance rather than encapsulation, we mark +// CompressedTupleImpl, to make this easy to detect. +struct uses_inheritance {}; + +template +constexpr bool ShouldUseBase() { + return std::is_class::value && std::is_empty::value && !IsFinal() && + !std::is_base_of::value; +} + +// The storage class provides two specializations: +// - For empty classes, it stores T as a base class. +// - For everything else, it stores T as a member. +template ::type>()> +#else + bool UseBase = ShouldUseBase()> +#endif +struct Storage { + T value; + constexpr Storage() = default; + template + explicit constexpr Storage(absl::in_place_t, V&& v) + : value(absl::forward(v)) {} + constexpr const T& get() const& { return value; } + T& get() & { return value; } + constexpr const T&& get() const&& { return absl::move(*this).value; } + T&& get() && { return std::move(*this).value; } +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage : T { + constexpr Storage() = default; + + template + explicit constexpr Storage(absl::in_place_t, V&& v) + : T(absl::forward(v)) {} + + constexpr const T& get() const& { return *this; } + T& get() & { return *this; } + constexpr const T&& get() const&& { return absl::move(*this); } + T&& get() && { return std::move(*this); } +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< + CompressedTuple, absl::index_sequence, ShouldAnyUseBase> + // We use the dummy identity function through std::integral_constant to + // convince MSVC of accepting and expanding I in that context. Without it + // you would get: + // error C3548: 'I': parameter pack cannot be used in this context + : uses_inheritance, + Storage::value>... { + constexpr CompressedTupleImpl() = default; + template + explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) + : Storage(absl::in_place, absl::forward(args))... {} + friend CompressedTuple; +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< + CompressedTuple, absl::index_sequence, false> + // We use the dummy identity function as above... + : Storage::value, false>... { + constexpr CompressedTupleImpl() = default; + template + explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) + : Storage(absl::in_place, absl::forward(args))... {} + friend CompressedTuple; +}; + +std::false_type Or(std::initializer_list); +std::true_type Or(std::initializer_list); + +// MSVC requires this to be done separately rather than within the declaration +// of CompressedTuple below. +template +constexpr bool ShouldAnyUseBase() { + return decltype( + Or({std::integral_constant()>()...})){}; +} + +template +using TupleMoveConstructible = typename std::conditional< + std::is_reference::value, std::is_convertible, + std::is_constructible>::type; + +} // namespace internal_compressed_tuple + +// Helper class to perform the Empty Base Class Optimization. +// Ts can contain classes and non-classes, empty or not. For the ones that +// are empty classes, we perform the CompressedTuple. If all types in Ts are +// empty classes, then CompressedTuple is itself an empty class. (This +// does not apply when one or more of those empty classes is itself an empty +// CompressedTuple.) +// +// To access the members, use member .get() function. +// +// Eg: +// absl::container_internal::CompressedTuple value(7, t1, t2, +// t3); +// assert(value.get<0>() == 7); +// T1& t1 = value.get<1>(); +// const T2& t2 = value.get<2>(); +// ... +// +// https://en.cppreference.com/w/cpp/language/ebo +template +class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple + : private internal_compressed_tuple::CompressedTupleImpl< + CompressedTuple, absl::index_sequence_for, + internal_compressed_tuple::ShouldAnyUseBase()> { + private: + template + using ElemT = internal_compressed_tuple::ElemT; + + template + using StorageT = internal_compressed_tuple::Storage, I>; + + public: + // There seems to be a bug in MSVC dealing in which using '=default' here will + // cause the compiler to ignore the body of other constructors. The work- + // around is to explicitly implement the default constructor. +#if defined(_MSC_VER) + constexpr CompressedTuple() : CompressedTuple::CompressedTupleImpl() {} +#else + constexpr CompressedTuple() = default; +#endif + explicit constexpr CompressedTuple(const Ts&... base) + : CompressedTuple::CompressedTupleImpl(absl::in_place, base...) {} + + template ...)>>, + internal_compressed_tuple::TupleMoveConstructible< + Ts, Vs&&>...>::value, + bool> = true> + explicit constexpr CompressedTuple(Vs&&... base) + : CompressedTuple::CompressedTupleImpl(absl::in_place, + absl::forward(base)...) {} + + template + ElemT& get() & { + return internal_compressed_tuple::Storage, I>::get(); + } + + template + constexpr const ElemT& get() const& { + return StorageT::get(); + } + + template + ElemT&& get() && { + return std::move(*this).StorageT::get(); + } + + template + constexpr const ElemT&& get() const&& { + return absl::move(*this).StorageT::get(); + } +}; + +// Explicit specialization for a zero-element tuple +// (needed to avoid ambiguous overloads for the default constructor). +template <> +class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC + +#endif // ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/container_memory.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/container_memory.h new file mode 100644 index 0000000000000000000000000000000000000000..d24b0f8413845312201aeb5ecc56dba329566002 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/container_memory.h @@ -0,0 +1,440 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ +#define ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ + +#ifdef ADDRESS_SANITIZER +#include +#endif + +#ifdef MEMORY_SANITIZER +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Allocates at least n bytes aligned to the specified alignment. +// Alignment must be a power of 2. It must be positive. +// +// Note that many allocators don't honor alignment requirements above certain +// threshold (usually either alignof(std::max_align_t) or alignof(void*)). +// Allocate() doesn't apply alignment corrections. If the underlying allocator +// returns insufficiently alignment pointer, that's what you are going to get. +template +void* Allocate(Alloc* alloc, size_t n) { + static_assert(Alignment > 0, ""); + assert(n && "n must be positive"); + struct alignas(Alignment) M {}; + using A = typename absl::allocator_traits::template rebind_alloc; + using AT = typename absl::allocator_traits::template rebind_traits; + A mem_alloc(*alloc); + void* p = AT::allocate(mem_alloc, (n + sizeof(M) - 1) / sizeof(M)); + assert(reinterpret_cast(p) % Alignment == 0 && + "allocator does not respect alignment"); + return p; +} + +// The pointer must have been previously obtained by calling +// Allocate(alloc, n). +template +void Deallocate(Alloc* alloc, void* p, size_t n) { + static_assert(Alignment > 0, ""); + assert(n && "n must be positive"); + struct alignas(Alignment) M {}; + using A = typename absl::allocator_traits::template rebind_alloc; + using AT = typename absl::allocator_traits::template rebind_traits; + A mem_alloc(*alloc); + AT::deallocate(mem_alloc, static_cast(p), + (n + sizeof(M) - 1) / sizeof(M)); +} + +namespace memory_internal { + +// Constructs T into uninitialized storage pointed by `ptr` using the args +// specified in the tuple. +template +void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t, + absl::index_sequence) { + absl::allocator_traits::construct( + *alloc, ptr, std::get(std::forward(t))...); +} + +template +struct WithConstructedImplF { + template + decltype(std::declval()(std::declval())) operator()( + Args&&... args) const { + return std::forward(f)(T(std::forward(args)...)); + } + F&& f; +}; + +template +decltype(std::declval()(std::declval())) WithConstructedImpl( + Tuple&& t, absl::index_sequence, F&& f) { + return WithConstructedImplF{std::forward(f)}( + std::get(std::forward(t))...); +} + +template +auto TupleRefImpl(T&& t, absl::index_sequence) + -> decltype(std::forward_as_tuple(std::get(std::forward(t))...)) { + return std::forward_as_tuple(std::get(std::forward(t))...); +} + +// Returns a tuple of references to the elements of the input tuple. T must be a +// tuple. +template +auto TupleRef(T&& t) -> decltype( + TupleRefImpl(std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>())) { + return TupleRefImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>()); +} + +template +decltype(std::declval()(std::declval(), std::piecewise_construct, + std::declval>(), std::declval())) +DecomposePairImpl(F&& f, std::pair, V> p) { + const auto& key = std::get<0>(p.first); + return std::forward(f)(key, std::piecewise_construct, std::move(p.first), + std::move(p.second)); +} + +} // namespace memory_internal + +// Constructs T into uninitialized storage pointed by `ptr` using the args +// specified in the tuple. +template +void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) { + memory_internal::ConstructFromTupleImpl( + alloc, ptr, std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>()); +} + +// Constructs T using the args specified in the tuple and calls F with the +// constructed value. +template +decltype(std::declval()(std::declval())) WithConstructed( + Tuple&& t, F&& f) { + return memory_internal::WithConstructedImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>(), + std::forward(f)); +} + +// Given arguments of an std::pair's consructor, PairArgs() returns a pair of +// tuples with references to the passed arguments. The tuples contain +// constructor arguments for the first and the second elements of the pair. +// +// The following two snippets are equivalent. +// +// 1. std::pair p(args...); +// +// 2. auto a = PairArgs(args...); +// std::pair p(std::piecewise_construct, +// std::move(p.first), std::move(p.second)); +inline std::pair, std::tuple<>> PairArgs() { return {}; } +template +std::pair, std::tuple> PairArgs(F&& f, S&& s) { + return {std::piecewise_construct, std::forward_as_tuple(std::forward(f)), + std::forward_as_tuple(std::forward(s))}; +} +template +std::pair, std::tuple> PairArgs( + const std::pair& p) { + return PairArgs(p.first, p.second); +} +template +std::pair, std::tuple> PairArgs(std::pair&& p) { + return PairArgs(std::forward(p.first), std::forward(p.second)); +} +template +auto PairArgs(std::piecewise_construct_t, F&& f, S&& s) + -> decltype(std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s)))) { + return std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s))); +} + +// A helper function for implementing apply() in map policies. +template +auto DecomposePair(F&& f, Args&&... args) + -> decltype(memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...))) { + return memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...)); +} + +// A helper function for implementing apply() in set policies. +template +decltype(std::declval()(std::declval(), std::declval())) +DecomposeValue(F&& f, Arg&& arg) { + const auto& key = arg; + return std::forward(f)(key, std::forward(arg)); +} + +// Helper functions for asan and msan. +inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) { +#ifdef ADDRESS_SANITIZER + ASAN_POISON_MEMORY_REGION(m, s); +#endif +#ifdef MEMORY_SANITIZER + __msan_poison(m, s); +#endif + (void)m; + (void)s; +} + +inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) { +#ifdef ADDRESS_SANITIZER + ASAN_UNPOISON_MEMORY_REGION(m, s); +#endif +#ifdef MEMORY_SANITIZER + __msan_unpoison(m, s); +#endif + (void)m; + (void)s; +} + +template +inline void SanitizerPoisonObject(const T* object) { + SanitizerPoisonMemoryRegion(object, sizeof(T)); +} + +template +inline void SanitizerUnpoisonObject(const T* object) { + SanitizerUnpoisonMemoryRegion(object, sizeof(T)); +} + +namespace memory_internal { + +// If Pair is a standard-layout type, OffsetOf::kFirst and +// OffsetOf::kSecond are equivalent to offsetof(Pair, first) and +// offsetof(Pair, second) respectively. Otherwise they are -1. +// +// The purpose of OffsetOf is to avoid calling offsetof() on non-standard-layout +// type, which is non-portable. +template +struct OffsetOf { + static constexpr size_t kFirst = -1; + static constexpr size_t kSecond = -1; +}; + +template +struct OffsetOf::type> { + static constexpr size_t kFirst = offsetof(Pair, first); + static constexpr size_t kSecond = offsetof(Pair, second); +}; + +template +struct IsLayoutCompatible { + private: + struct Pair { + K first; + V second; + }; + + // Is P layout-compatible with Pair? + template + static constexpr bool LayoutCompatible() { + return std::is_standard_layout

() && sizeof(P) == sizeof(Pair) && + alignof(P) == alignof(Pair) && + memory_internal::OffsetOf

::kFirst == + memory_internal::OffsetOf::kFirst && + memory_internal::OffsetOf

::kSecond == + memory_internal::OffsetOf::kSecond; + } + + public: + // Whether pair and pair are layout-compatible. If they are, + // then it is safe to store them in a union and read from either. + static constexpr bool value = std::is_standard_layout() && + std::is_standard_layout() && + memory_internal::OffsetOf::kFirst == 0 && + LayoutCompatible>() && + LayoutCompatible>(); +}; + +} // namespace memory_internal + +// The internal storage type for key-value containers like flat_hash_map. +// +// It is convenient for the value_type of a flat_hash_map to be +// pair; the "const K" prevents accidental modification of the key +// when dealing with the reference returned from find() and similar methods. +// However, this creates other problems; we want to be able to emplace(K, V) +// efficiently with move operations, and similarly be able to move a +// pair in insert(). +// +// The solution is this union, which aliases the const and non-const versions +// of the pair. This also allows flat_hash_map to work, even though +// that has the same efficiency issues with move in emplace() and insert() - +// but people do it anyway. +// +// If kMutableKeys is false, only the value member can be accessed. +// +// If kMutableKeys is true, key can be accessed through all slots while value +// and mutable_value must be accessed only via INITIALIZED slots. Slots are +// created and destroyed via mutable_value so that the key can be moved later. +// +// Accessing one of the union fields while the other is active is safe as +// long as they are layout-compatible, which is guaranteed by the definition of +// kMutableKeys. For C++11, the relevant section of the standard is +// https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19) +template +union map_slot_type { + map_slot_type() {} + ~map_slot_type() = delete; + using value_type = std::pair; + using mutable_value_type = std::pair; + + value_type value; + mutable_value_type mutable_value; + K key; +}; + +template +struct map_slot_policy { + using slot_type = map_slot_type; + using value_type = std::pair; + using mutable_value_type = std::pair; + + private: + static void emplace(slot_type* slot) { + // The construction of union doesn't do anything at runtime but it allows us + // to access its members without violating aliasing rules. + new (slot) slot_type; + } + // If pair and pair are layout-compatible, we can accept one + // or the other via slot_type. We are also free to access the key via + // slot_type::key in this case. + using kMutableKeys = memory_internal::IsLayoutCompatible; + + public: + static value_type& element(slot_type* slot) { return slot->value; } + static const value_type& element(const slot_type* slot) { + return slot->value; + } + + static const K& key(const slot_type* slot) { + return kMutableKeys::value ? slot->key : slot->value.first; + } + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + emplace(slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct(*alloc, &slot->mutable_value, + std::forward(args)...); + } else { + absl::allocator_traits::construct(*alloc, &slot->value, + std::forward(args)...); + } + } + + // Construct this slot by moving from another slot. + template + static void construct(Allocator* alloc, slot_type* slot, slot_type* other) { + emplace(slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct( + *alloc, &slot->mutable_value, std::move(other->mutable_value)); + } else { + absl::allocator_traits::construct(*alloc, &slot->value, + std::move(other->value)); + } + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + if (kMutableKeys::value) { + absl::allocator_traits::destroy(*alloc, &slot->mutable_value); + } else { + absl::allocator_traits::destroy(*alloc, &slot->value); + } + } + + template + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + emplace(new_slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct( + *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value)); + } else { + absl::allocator_traits::construct(*alloc, &new_slot->value, + std::move(old_slot->value)); + } + destroy(alloc, old_slot); + } + + template + static void swap(Allocator* alloc, slot_type* a, slot_type* b) { + if (kMutableKeys::value) { + using std::swap; + swap(a->mutable_value, b->mutable_value); + } else { + value_type tmp = std::move(a->value); + absl::allocator_traits::destroy(*alloc, &a->value); + absl::allocator_traits::construct(*alloc, &a->value, + std::move(b->value)); + absl::allocator_traits::destroy(*alloc, &b->value); + absl::allocator_traits::construct(*alloc, &b->value, + std::move(tmp)); + } + } + + template + static void move(Allocator* alloc, slot_type* src, slot_type* dest) { + if (kMutableKeys::value) { + dest->mutable_value = std::move(src->mutable_value); + } else { + absl::allocator_traits::destroy(*alloc, &dest->value); + absl::allocator_traits::construct(*alloc, &dest->value, + std::move(src->value)); + } + } + + template + static void move(Allocator* alloc, slot_type* first, slot_type* last, + slot_type* result) { + for (slot_type *src = first, *dest = result; src != last; ++src, ++dest) + move(alloc, src, dest); + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/counting_allocator.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/counting_allocator.h new file mode 100644 index 0000000000000000000000000000000000000000..9efdc662136322a88e33c32a385ed6cfac4a3d18 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/counting_allocator.h @@ -0,0 +1,83 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ +#define ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ + +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// This is a stateful allocator, but the state lives outside of the +// allocator (in whatever test is using the allocator). This is odd +// but helps in tests where the allocator is propagated into nested +// containers - that chain of allocators uses the same state and is +// thus easier to query for aggregate allocation information. +template +class CountingAllocator : public std::allocator { + public: + using Alloc = std::allocator; + using pointer = typename Alloc::pointer; + using size_type = typename Alloc::size_type; + + CountingAllocator() : bytes_used_(nullptr) {} + explicit CountingAllocator(int64_t* b) : bytes_used_(b) {} + + template + CountingAllocator(const CountingAllocator& x) + : Alloc(x), bytes_used_(x.bytes_used_) {} + + pointer allocate(size_type n, + std::allocator::const_pointer hint = nullptr) { + assert(bytes_used_ != nullptr); + *bytes_used_ += n * sizeof(T); + return Alloc::allocate(n, hint); + } + + void deallocate(pointer p, size_type n) { + Alloc::deallocate(p, n); + assert(bytes_used_ != nullptr); + *bytes_used_ -= n * sizeof(T); + } + + template + class rebind { + public: + using other = CountingAllocator; + }; + + friend bool operator==(const CountingAllocator& a, + const CountingAllocator& b) { + return a.bytes_used_ == b.bytes_used_; + } + + friend bool operator!=(const CountingAllocator& a, + const CountingAllocator& b) { + return !(a == b); + } + + int64_t* bytes_used_; +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_function_defaults.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_function_defaults.h new file mode 100644 index 0000000000000000000000000000000000000000..401ddf4d83c6bf8dfe4b6784faf6ff3028a110cd --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_function_defaults.h @@ -0,0 +1,146 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Define the default Hash and Eq functions for SwissTable containers. +// +// std::hash and std::equal_to are not appropriate hash and equal +// functions for SwissTable containers. There are two reasons for this. +// +// SwissTable containers are power of 2 sized containers: +// +// This means they use the lower bits of the hash value to find the slot for +// each entry. The typical hash function for integral types is the identity. +// This is a very weak hash function for SwissTable and any power of 2 sized +// hashtable implementation which will lead to excessive collisions. For +// SwissTable we use murmur3 style mixing to reduce collisions to a minimum. +// +// SwissTable containers support heterogeneous lookup: +// +// In order to make heterogeneous lookup work, hash and equal functions must be +// polymorphic. At the same time they have to satisfy the same requirements the +// C++ standard imposes on hash functions and equality operators. That is: +// +// if hash_default_eq(a, b) returns true for any a and b of type T, then +// hash_default_hash(a) must equal hash_default_hash(b) +// +// For SwissTable containers this requirement is relaxed to allow a and b of +// any and possibly different types. Note that like the standard the hash and +// equal functions are still bound to T. This is important because some type U +// can be hashed by/tested for equality differently depending on T. A notable +// example is `const char*`. `const char*` is treated as a c-style string when +// the hash function is hash but as a pointer when the hash +// function is hash. +// +#ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/hash/hash.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// The hash of an object of type T is computed by using absl::Hash. +template +struct HashEq { + using Hash = absl::Hash; + using Eq = std::equal_to; +}; + +struct StringHash { + using is_transparent = void; + + size_t operator()(absl::string_view v) const { + return absl::Hash{}(v); + } +}; + +// Supports heterogeneous lookup for string-like elements. +struct StringHashEq { + using Hash = StringHash; + struct Eq { + using is_transparent = void; + bool operator()(absl::string_view lhs, absl::string_view rhs) const { + return lhs == rhs; + } + }; +}; + +template <> +struct HashEq : StringHashEq {}; +template <> +struct HashEq : StringHashEq {}; + +// Supports heterogeneous lookup for pointers and smart pointers. +template +struct HashEq { + struct Hash { + using is_transparent = void; + template + size_t operator()(const U& ptr) const { + return absl::Hash{}(HashEq::ToPtr(ptr)); + } + }; + struct Eq { + using is_transparent = void; + template + bool operator()(const A& a, const B& b) const { + return HashEq::ToPtr(a) == HashEq::ToPtr(b); + } + }; + + private: + static const T* ToPtr(const T* ptr) { return ptr; } + template + static const T* ToPtr(const std::unique_ptr& ptr) { + return ptr.get(); + } + template + static const T* ToPtr(const std::shared_ptr& ptr) { + return ptr.get(); + } +}; + +template +struct HashEq> : HashEq {}; +template +struct HashEq> : HashEq {}; + +// This header's visibility is restricted. If you need to access the default +// hasher please use the container's ::hasher alias instead. +// +// Example: typename Hash = typename absl::flat_hash_map::hasher +template +using hash_default_hash = typename container_internal::HashEq::Hash; + +// This header's visibility is restricted. If you need to access the default +// key equal please use the container's ::key_equal alias instead. +// +// Example: typename Eq = typename absl::flat_hash_map::key_equal +template +using hash_default_eq = typename container_internal::HashEq::Eq; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_generator_testing.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_generator_testing.h new file mode 100644 index 0000000000000000000000000000000000000000..6869fe45e8c8d48495038f82243abc26594ed1a7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_generator_testing.h @@ -0,0 +1,161 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Generates random values for testing. Specialized only for the few types we +// care about. + +#ifndef ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ + +#include + +#include +#include +#include +#include +#include +#include + +#include "absl/container/internal/hash_policy_testing.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace hash_internal { +namespace generator_internal { + +template +struct IsMap : std::false_type {}; + +template +struct IsMap> : std::true_type {}; + +} // namespace generator_internal + +std::mt19937_64* GetSharedRng(); + +enum Enum { + kEnumEmpty, + kEnumDeleted, +}; + +enum class EnumClass : uint64_t { + kEmpty, + kDeleted, +}; + +inline std::ostream& operator<<(std::ostream& o, const EnumClass& ec) { + return o << static_cast(ec); +} + +template +struct Generator; + +template +struct Generator::value>::type> { + T operator()() const { + std::uniform_int_distribution dist; + return dist(*GetSharedRng()); + } +}; + +template <> +struct Generator { + Enum operator()() const { + std::uniform_int_distribution::type> + dist; + while (true) { + auto variate = dist(*GetSharedRng()); + if (variate != kEnumEmpty && variate != kEnumDeleted) + return static_cast(variate); + } + } +}; + +template <> +struct Generator { + EnumClass operator()() const { + std::uniform_int_distribution< + typename std::underlying_type::type> + dist; + while (true) { + EnumClass variate = static_cast(dist(*GetSharedRng())); + if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted) + return static_cast(variate); + } + } +}; + +template <> +struct Generator { + std::string operator()() const; +}; + +template <> +struct Generator { + absl::string_view operator()() const; +}; + +template <> +struct Generator { + NonStandardLayout operator()() const { + return NonStandardLayout(Generator()()); + } +}; + +template +struct Generator> { + std::pair operator()() const { + return std::pair(Generator::type>()(), + Generator::type>()()); + } +}; + +template +struct Generator> { + std::tuple operator()() const { + return std::tuple(Generator::type>()()...); + } +}; + +template +struct Generator> { + std::unique_ptr operator()() const { + return absl::make_unique(Generator()()); + } +}; + +template +struct Generator().key()), + decltype(std::declval().value())>> + : Generator().key())>::type, + typename std::decay().value())>::type>> {}; + +template +using GeneratedType = decltype( + std::declval::value, + typename Container::value_type, + typename Container::key_type>::type>&>()()); + +} // namespace hash_internal +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_policy_testing.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_policy_testing.h new file mode 100644 index 0000000000000000000000000000000000000000..01c40d2e5cff126f06db66d3b8f4088c598e7b91 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_policy_testing.h @@ -0,0 +1,184 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 to help tests verify that hash tables properly handle stateful +// allocators and hash functions. + +#ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/hash/hash.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace hash_testing_internal { + +template +struct WithId { + WithId() : id_(next_id()) {} + WithId(const WithId& that) : id_(that.id_) {} + WithId(WithId&& that) : id_(that.id_) { that.id_ = 0; } + WithId& operator=(const WithId& that) { + id_ = that.id_; + return *this; + } + WithId& operator=(WithId&& that) { + id_ = that.id_; + that.id_ = 0; + return *this; + } + + size_t id() const { return id_; } + + friend bool operator==(const WithId& a, const WithId& b) { + return a.id_ == b.id_; + } + friend bool operator!=(const WithId& a, const WithId& b) { return !(a == b); } + + protected: + explicit WithId(size_t id) : id_(id) {} + + private: + size_t id_; + + template + static size_t next_id() { + // 0 is reserved for moved from state. + static size_t gId = 1; + return gId++; + } +}; + +} // namespace hash_testing_internal + +struct NonStandardLayout { + NonStandardLayout() {} + explicit NonStandardLayout(std::string s) : value(std::move(s)) {} + virtual ~NonStandardLayout() {} + + friend bool operator==(const NonStandardLayout& a, + const NonStandardLayout& b) { + return a.value == b.value; + } + friend bool operator!=(const NonStandardLayout& a, + const NonStandardLayout& b) { + return a.value != b.value; + } + + template + friend H AbslHashValue(H h, const NonStandardLayout& v) { + return H::combine(std::move(h), v.value); + } + + std::string value; +}; + +struct StatefulTestingHash + : absl::container_internal::hash_testing_internal::WithId< + StatefulTestingHash> { + template + size_t operator()(const T& t) const { + return absl::Hash{}(t); + } +}; + +struct StatefulTestingEqual + : absl::container_internal::hash_testing_internal::WithId< + StatefulTestingEqual> { + template + bool operator()(const T& t, const U& u) const { + return t == u; + } +}; + +// It is expected that Alloc() == Alloc() for all allocators so we cannot use +// WithId base. We need to explicitly assign ids. +template +struct Alloc : std::allocator { + using propagate_on_container_swap = std::true_type; + + // Using old paradigm for this to ensure compatibility. + explicit Alloc(size_t id = 0) : id_(id) {} + + Alloc(const Alloc&) = default; + Alloc& operator=(const Alloc&) = default; + + template + Alloc(const Alloc& that) : std::allocator(that), id_(that.id()) {} + + template + struct rebind { + using other = Alloc; + }; + + size_t id() const { return id_; } + + friend bool operator==(const Alloc& a, const Alloc& b) { + return a.id_ == b.id_; + } + friend bool operator!=(const Alloc& a, const Alloc& b) { return !(a == b); } + + private: + size_t id_ = (std::numeric_limits::max)(); +}; + +template +auto items(const Map& m) -> std::vector< + std::pair> { + using std::get; + std::vector> res; + res.reserve(m.size()); + for (const auto& v : m) res.emplace_back(get<0>(v), get<1>(v)); + return res; +} + +template +auto keys(const Set& s) + -> std::vector::type> { + std::vector::type> res; + res.reserve(s.size()); + for (const auto& v : s) res.emplace_back(v); + return res; +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +// ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions +// where the unordered containers are missing certain constructors that +// take allocator arguments. This test is defined ad-hoc for the platforms +// we care about (notably Crosstool 17) because libstdcxx's useless +// versioning scheme precludes a more principled solution. +// From GCC-4.9 Changelog: (src: https://gcc.gnu.org/gcc-4.9/changes.html) +// "the unordered associative containers in and +// meet the allocator-aware container requirements;" +#if (defined(__GLIBCXX__) && __GLIBCXX__ <= 20140425 ) || \ +( __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9 )) +#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 0 +#else +#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 1 +#endif + +#endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_policy_traits.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_policy_traits.h new file mode 100644 index 0000000000000000000000000000000000000000..3e1209c6ebec893f3047a016b2ec3f94fba2ce93 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/hash_policy_traits.h @@ -0,0 +1,191 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ + +#include +#include +#include +#include + +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Defines how slots are initialized/destroyed/moved. +template +struct hash_policy_traits { + private: + struct ReturnKey { + // We return `Key` here. + // When Key=T&, we forward the lvalue reference. + // When Key=T, we return by value to avoid a dangling reference. + // eg, for string_hash_map. + template + Key operator()(Key&& k, const Args&...) const { + return std::forward(k); + } + }; + + template + struct ConstantIteratorsImpl : std::false_type {}; + + template + struct ConstantIteratorsImpl> + : P::constant_iterators {}; + + public: + // The actual object stored in the hash table. + using slot_type = typename Policy::slot_type; + + // The type of the keys stored in the hashtable. + using key_type = typename Policy::key_type; + + // The argument type for insertions into the hashtable. This is different + // from value_type for increased performance. See initializer_list constructor + // and insert() member functions for more details. + using init_type = typename Policy::init_type; + + using reference = decltype(Policy::element(std::declval())); + using pointer = typename std::remove_reference::type*; + using value_type = typename std::remove_reference::type; + + // Policies can set this variable to tell raw_hash_set that all iterators + // should be constant, even `iterator`. This is useful for set-like + // containers. + // Defaults to false if not provided by the policy. + using constant_iterators = ConstantIteratorsImpl<>; + + // PRECONDITION: `slot` is UNINITIALIZED + // POSTCONDITION: `slot` is INITIALIZED + template + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + Policy::construct(alloc, slot, std::forward(args)...); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is UNINITIALIZED + template + static void destroy(Alloc* alloc, slot_type* slot) { + Policy::destroy(alloc, slot); + } + + // Transfers the `old_slot` to `new_slot`. Any memory allocated by the + // allocator inside `old_slot` to `new_slot` can be transferred. + // + // OPTIONAL: defaults to: + // + // clone(new_slot, std::move(*old_slot)); + // destroy(old_slot); + // + // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED + // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is + // UNINITIALIZED + template + static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) { + transfer_impl(alloc, new_slot, old_slot, 0); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is INITIALIZED + template + static auto element(slot_type* slot) -> decltype(P::element(slot)) { + return P::element(slot); + } + + // Returns the amount of memory owned by `slot`, exclusive of `sizeof(*slot)`. + // + // If `slot` is nullptr, returns the constant amount of memory owned by any + // full slot or -1 if slots own variable amounts of memory. + // + // PRECONDITION: `slot` is INITIALIZED or nullptr + template + static size_t space_used(const slot_type* slot) { + return P::space_used(slot); + } + + // Provides generalized access to the key for elements, both for elements in + // the table and for elements that have not yet been inserted (or even + // constructed). We would like an API that allows us to say: `key(args...)` + // but we cannot do that for all cases, so we use this more general API that + // can be used for many things, including the following: + // + // - Given an element in a table, get its key. + // - Given an element initializer, get its key. + // - Given `emplace()` arguments, get the element key. + // + // Implementations of this must adhere to a very strict technical + // specification around aliasing and consuming arguments: + // + // Let `value_type` be the result type of `element()` without ref- and + // cv-qualifiers. The first argument is a functor, the rest are constructor + // arguments for `value_type`. Returns `std::forward(f)(k, xs...)`, where + // `k` is the element key, and `xs...` are the new constructor arguments for + // `value_type`. It's allowed for `k` to alias `xs...`, and for both to alias + // `ts...`. The key won't be touched once `xs...` are used to construct an + // element; `ts...` won't be touched at all, which allows `apply()` to consume + // any rvalues among them. + // + // If `value_type` is constructible from `Ts&&...`, `Policy::apply()` must not + // trigger a hard compile error unless it originates from `f`. In other words, + // `Policy::apply()` must be SFINAE-friendly. If `value_type` is not + // constructible from `Ts&&...`, either SFINAE or a hard compile error is OK. + // + // If `Ts...` is `[cv] value_type[&]` or `[cv] init_type[&]`, + // `Policy::apply()` must work. A compile error is not allowed, SFINAE or not. + template + static auto apply(F&& f, Ts&&... ts) + -> decltype(P::apply(std::forward(f), std::forward(ts)...)) { + return P::apply(std::forward(f), std::forward(ts)...); + } + + // Returns the "key" portion of the slot. + // Used for node handle manipulation. + template + static auto key(slot_type* slot) + -> decltype(P::apply(ReturnKey(), element(slot))) { + return P::apply(ReturnKey(), element(slot)); + } + + // Returns the "value" (as opposed to the "key") portion of the element. Used + // by maps to implement `operator[]`, `at()` and `insert_or_assign()`. + template + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + + private: + // Use auto -> decltype as an enabler. + template + static auto transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, int) + -> decltype((void)P::transfer(alloc, new_slot, old_slot)) { + P::transfer(alloc, new_slot, old_slot); + } + template + static void transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, char) { + construct(alloc, new_slot, std::move(element(old_slot))); + destroy(alloc, old_slot); + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/hashtable_debug.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/hashtable_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..19d52121d68857735197a3d6b2738c1a3367057d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/hashtable_debug.h @@ -0,0 +1,110 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 library provides APIs to debug the probing behavior of hash tables. +// +// In general, the probing behavior is a black box for users and only the +// side effects can be measured in the form of performance differences. +// These APIs give a glimpse on the actual behavior of the probing algorithms in +// these hashtables given a specified hash function and a set of elements. +// +// The probe count distribution can be used to assess the quality of the hash +// function for that particular hash table. Note that a hash function that +// performs well in one hash table implementation does not necessarily performs +// well in a different one. +// +// This library supports std::unordered_{set,map}, dense_hash_{set,map} and +// absl::{flat,node,string}_hash_{set,map}. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ + +#include +#include +#include +#include + +#include "absl/container/internal/hashtable_debug_hooks.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Returns the number of probes required to lookup `key`. Returns 0 for a +// search with no collisions. Higher values mean more hash collisions occurred; +// however, the exact meaning of this number varies according to the container +// type. +template +size_t GetHashtableDebugNumProbes( + const C& c, const typename C::key_type& key) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::GetNumProbes(c, key); +} + +// Gets a histogram of the number of probes for each elements in the container. +// The sum of all the values in the vector is equal to container.size(). +template +std::vector GetHashtableDebugNumProbesHistogram(const C& container) { + std::vector v; + for (auto it = container.begin(); it != container.end(); ++it) { + size_t num_probes = GetHashtableDebugNumProbes( + container, + absl::container_internal::hashtable_debug_internal::GetKey(*it, 0)); + v.resize((std::max)(v.size(), num_probes + 1)); + v[num_probes]++; + } + return v; +} + +struct HashtableDebugProbeSummary { + size_t total_elements; + size_t total_num_probes; + double mean; +}; + +// Gets a summary of the probe count distribution for the elements in the +// container. +template +HashtableDebugProbeSummary GetHashtableDebugProbeSummary(const C& container) { + auto probes = GetHashtableDebugNumProbesHistogram(container); + HashtableDebugProbeSummary summary = {}; + for (size_t i = 0; i < probes.size(); ++i) { + summary.total_elements += probes[i]; + summary.total_num_probes += probes[i] * i; + } + summary.mean = 1.0 * summary.total_num_probes / summary.total_elements; + return summary; +} + +// Returns the number of bytes requested from the allocator by the container +// and not freed. +template +size_t AllocatedByteSize(const C& c) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::AllocatedByteSize(c); +} + +// Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type `C` +// and `c.size()` is equal to `num_elements`. +template +size_t LowerBoundAllocatedByteSize(size_t num_elements) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::LowerBoundAllocatedByteSize(num_elements); +} + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/hashtable_debug_hooks.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/hashtable_debug_hooks.h new file mode 100644 index 0000000000000000000000000000000000000000..3e9ea5954e0a19ba625d5af7398b6ac8117a7c23 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/hashtable_debug_hooks.h @@ -0,0 +1,85 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Provides the internal API for hashtable_debug.h. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ + +#include + +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace hashtable_debug_internal { + +// If it is a map, call get<0>(). +using std::get; +template +auto GetKey(const typename T::value_type& pair, int) -> decltype(get<0>(pair)) { + return get<0>(pair); +} + +// If it is not a map, return the value directly. +template +const typename T::key_type& GetKey(const typename T::key_type& key, char) { + return key; +} + +// Containers should specialize this to provide debug information for that +// container. +template +struct HashtableDebugAccess { + // Returns the number of probes required to find `key` in `c`. The "number of + // probes" is a concept that can vary by container. Implementations should + // return 0 when `key` was found in the minimum number of operations and + // should increment the result for each non-trivial operation required to find + // `key`. + // + // The default implementation uses the bucket api from the standard and thus + // works for `std::unordered_*` containers. + static size_t GetNumProbes(const Container& c, + const typename Container::key_type& key) { + if (!c.bucket_count()) return {}; + size_t num_probes = 0; + size_t bucket = c.bucket(key); + for (auto it = c.begin(bucket), e = c.end(bucket);; ++it, ++num_probes) { + if (it == e) return num_probes; + if (c.key_eq()(key, GetKey(*it, 0))) return num_probes; + } + } + + // Returns the number of bytes requested from the allocator by the container + // and not freed. + // + // static size_t AllocatedByteSize(const Container& c); + + // Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type + // `Container` and `c.size()` is equal to `num_elements`. + // + // static size_t LowerBoundAllocatedByteSize(size_t num_elements); +}; + +} // namespace hashtable_debug_internal +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/hashtablez_sampler.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/hashtablez_sampler.h new file mode 100644 index 0000000000000000000000000000000000000000..34d5e5723c0f9877380c0fc80e4ad6bdb273666b --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/hashtablez_sampler.h @@ -0,0 +1,297 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: hashtablez_sampler.h +// ----------------------------------------------------------------------------- +// +// This header file defines the API for a low level library to sample hashtables +// and collect runtime statistics about them. +// +// `HashtablezSampler` controls the lifecycle of `HashtablezInfo` objects which +// store information about a single sample. +// +// `Record*` methods store information into samples. +// `Sample()` and `Unsample()` make use of a single global sampler with +// properties controlled by the flags hashtablez_enabled, +// hashtablez_sample_rate, and hashtablez_max_samples. +// +// WARNING +// +// Using this sampling API may cause sampled Swiss tables to use the global +// allocator (operator `new`) in addition to any custom allocator. If you +// are using a table in an unusual circumstance where allocation or calling a +// linux syscall is unacceptable, this could interfere. +// +// This utility is internal-only. Use at your own risk. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ + +#include +#include +#include +#include + +#include "absl/base/internal/per_thread_tls.h" +#include "absl/base/optimization.h" +#include "absl/container/internal/have_sse.h" +#include "absl/synchronization/mutex.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// Stores information about a sampled hashtable. All mutations to this *must* +// be made through `Record*` functions below. All reads from this *must* only +// occur in the callback to `HashtablezSampler::Iterate`. +struct HashtablezInfo { + // Constructs the object but does not fill in any fields. + HashtablezInfo(); + ~HashtablezInfo(); + HashtablezInfo(const HashtablezInfo&) = delete; + HashtablezInfo& operator=(const HashtablezInfo&) = delete; + + // Puts the object into a clean state, fills in the logically `const` members, + // blocking for any readers that are currently sampling the object. + void PrepareForSampling() ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu); + + // These fields are mutated by the various Record* APIs and need to be + // thread-safe. + std::atomic capacity; + std::atomic size; + std::atomic num_erases; + std::atomic max_probe_length; + std::atomic total_probe_length; + std::atomic hashes_bitwise_or; + std::atomic hashes_bitwise_and; + + // `HashtablezSampler` maintains intrusive linked lists for all samples. See + // comments on `HashtablezSampler::all_` for details on these. `init_mu` + // guards the ability to restore the sample to a pristine state. This + // prevents races with sampling and resurrecting an object. + absl::Mutex init_mu; + HashtablezInfo* next; + HashtablezInfo* dead ABSL_GUARDED_BY(init_mu); + + // All of the fields below are set by `PrepareForSampling`, they must not be + // mutated in `Record*` functions. They are logically `const` in that sense. + // These are guarded by init_mu, but that is not externalized to clients, who + // can only read them during `HashtablezSampler::Iterate` which will hold the + // lock. + static constexpr int kMaxStackDepth = 64; + absl::Time create_time; + int32_t depth; + void* stack[kMaxStackDepth]; +}; + +inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { +#if SWISSTABLE_HAVE_SSE2 + total_probe_length /= 16; +#else + total_probe_length /= 8; +#endif + info->total_probe_length.store(total_probe_length, std::memory_order_relaxed); + info->num_erases.store(0, std::memory_order_relaxed); +} + +inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, + size_t capacity) { + info->size.store(size, std::memory_order_relaxed); + info->capacity.store(capacity, std::memory_order_relaxed); + if (size == 0) { + // This is a clear, reset the total/num_erases too. + RecordRehashSlow(info, 0); + } +} + +void RecordInsertSlow(HashtablezInfo* info, size_t hash, + size_t distance_from_desired); + +inline void RecordEraseSlow(HashtablezInfo* info) { + info->size.fetch_sub(1, std::memory_order_relaxed); + info->num_erases.fetch_add(1, std::memory_order_relaxed); +} + +HashtablezInfo* SampleSlow(int64_t* next_sample); +void UnsampleSlow(HashtablezInfo* info); + +class HashtablezInfoHandle { + public: + explicit HashtablezInfoHandle() : info_(nullptr) {} + explicit HashtablezInfoHandle(HashtablezInfo* info) : info_(info) {} + ~HashtablezInfoHandle() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + UnsampleSlow(info_); + } + + HashtablezInfoHandle(const HashtablezInfoHandle&) = delete; + HashtablezInfoHandle& operator=(const HashtablezInfoHandle&) = delete; + + HashtablezInfoHandle(HashtablezInfoHandle&& o) noexcept + : info_(absl::exchange(o.info_, nullptr)) {} + HashtablezInfoHandle& operator=(HashtablezInfoHandle&& o) noexcept { + if (ABSL_PREDICT_FALSE(info_ != nullptr)) { + UnsampleSlow(info_); + } + info_ = absl::exchange(o.info_, nullptr); + return *this; + } + + inline void RecordStorageChanged(size_t size, size_t capacity) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordStorageChangedSlow(info_, size, capacity); + } + + inline void RecordRehash(size_t total_probe_length) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordRehashSlow(info_, total_probe_length); + } + + inline void RecordInsert(size_t hash, size_t distance_from_desired) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordInsertSlow(info_, hash, distance_from_desired); + } + + inline void RecordErase() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordEraseSlow(info_); + } + + friend inline void swap(HashtablezInfoHandle& lhs, + HashtablezInfoHandle& rhs) { + std::swap(lhs.info_, rhs.info_); + } + + private: + friend class HashtablezInfoHandlePeer; + HashtablezInfo* info_; +}; + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +#error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +#if (ABSL_PER_THREAD_TLS == 1) && !defined(ABSL_BUILD_DLL) && \ + !defined(ABSL_CONSUME_DLL) +#define ABSL_INTERNAL_HASHTABLEZ_SAMPLE +#endif + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample; +#endif // ABSL_PER_THREAD_TLS + +// Returns an RAII sampling handle that manages registration and unregistation +// with the global sampler. +inline HashtablezInfoHandle Sample() { +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) { + return HashtablezInfoHandle(nullptr); + } + return HashtablezInfoHandle(SampleSlow(&global_next_sample)); +#else + return HashtablezInfoHandle(nullptr); +#endif // !ABSL_PER_THREAD_TLS +} + +// Holds samples and their associated stack traces with a soft limit of +// `SetHashtablezMaxSamples()`. +// +// Thread safe. +class HashtablezSampler { + public: + // Returns a global Sampler. + static HashtablezSampler& Global(); + + HashtablezSampler(); + ~HashtablezSampler(); + + // Registers for sampling. Returns an opaque registration info. + HashtablezInfo* Register(); + + // Unregisters the sample. + void Unregister(HashtablezInfo* sample); + + // The dispose callback will be called on all samples the moment they are + // being unregistered. Only affects samples that are unregistered after the + // callback has been set. + // Returns the previous callback. + using DisposeCallback = void (*)(const HashtablezInfo&); + DisposeCallback SetDisposeCallback(DisposeCallback f); + + // Iterates over all the registered `StackInfo`s. Returning the number of + // samples that have been dropped. + int64_t Iterate(const std::function& f); + + private: + void PushNew(HashtablezInfo* sample); + void PushDead(HashtablezInfo* sample); + HashtablezInfo* PopDead(); + + std::atomic dropped_samples_; + std::atomic size_estimate_; + + // Intrusive lock free linked lists for tracking samples. + // + // `all_` records all samples (they are never removed from this list) and is + // terminated with a `nullptr`. + // + // `graveyard_.dead` is a circular linked list. When it is empty, + // `graveyard_.dead == &graveyard`. The list is circular so that + // every item on it (even the last) has a non-null dead pointer. This allows + // `Iterate` to determine if a given sample is live or dead using only + // information on the sample itself. + // + // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead + // looks like this (G is the Graveyard): + // + // +---+ +---+ +---+ +---+ +---+ + // all -->| A |--->| B |--->| C |--->| D |--->| E | + // | | | | | | | | | | + // +---+ | | +->| |-+ | | +->| |-+ | | + // | G | +---+ | +---+ | +---+ | +---+ | +---+ + // | | | | | | + // | | --------+ +--------+ | + // +---+ | + // ^ | + // +--------------------------------------+ + // + std::atomic all_; + HashtablezInfo graveyard_; + + std::atomic dispose_; +}; + +// Enables or disables sampling for Swiss tables. +void SetHashtablezEnabled(bool enabled); + +// Sets the rate at which Swiss tables will be sampled. +void SetHashtablezSampleParameter(int32_t rate); + +// Sets a soft max for the number of samples that will be kept. +void SetHashtablezMaxSamples(int32_t max); + +// Configuration override. +// This allows process-wide sampling without depending on order of +// initialization of static storage duration objects. +// The definition of this constant is weak, which allows us to inject a +// different value for it at link time. +extern "C" bool AbslContainerInternalSampleEverything(); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/have_sse.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/have_sse.h new file mode 100644 index 0000000000000000000000000000000000000000..43414418dba064efaa87862a7ce8c1adce98d704 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/have_sse.h @@ -0,0 +1,49 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Shared config probing for SSE instructions used in Swiss tables. +#ifndef ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ +#define ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ + +#ifndef SWISSTABLE_HAVE_SSE2 +#if defined(__SSE2__) || \ + (defined(_MSC_VER) && \ + (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2))) +#define SWISSTABLE_HAVE_SSE2 1 +#else +#define SWISSTABLE_HAVE_SSE2 0 +#endif +#endif + +#ifndef SWISSTABLE_HAVE_SSSE3 +#ifdef __SSSE3__ +#define SWISSTABLE_HAVE_SSSE3 1 +#else +#define SWISSTABLE_HAVE_SSSE3 0 +#endif +#endif + +#if SWISSTABLE_HAVE_SSSE3 && !SWISSTABLE_HAVE_SSE2 +#error "Bad configuration!" +#endif + +#if SWISSTABLE_HAVE_SSE2 +#include +#endif + +#if SWISSTABLE_HAVE_SSSE3 +#include +#endif + +#endif // ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/inlined_vector.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/inlined_vector.h new file mode 100644 index 0000000000000000000000000000000000000000..4d80b727bf4ccc461d35f718103ccea2d2885a8d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/inlined_vector.h @@ -0,0 +1,892 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ +#define ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/macros.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace inlined_vector_internal { + +template +using IsAtLeastForwardIterator = std::is_convertible< + typename std::iterator_traits::iterator_category, + std::forward_iterator_tag>; + +template ::value_type> +using IsMemcpyOk = + absl::conjunction>, + absl::is_trivially_copy_constructible, + absl::is_trivially_copy_assignable, + absl::is_trivially_destructible>; + +template +void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first, + SizeType destroy_size) { + using AllocatorTraits = absl::allocator_traits; + + if (destroy_first != nullptr) { + for (auto i = destroy_size; i != 0;) { + --i; + AllocatorTraits::destroy(*alloc_ptr, destroy_first + i); + } + +#if !defined(NDEBUG) + { + using ValueType = typename AllocatorTraits::value_type; + + // Overwrite unused memory with `0xab` so we can catch uninitialized + // usage. + // + // Cast to `void*` to tell the compiler that we don't care that we might + // be scribbling on a vtable pointer. + void* memory_ptr = destroy_first; + auto memory_size = destroy_size * sizeof(ValueType); + std::memset(memory_ptr, 0xab, memory_size); + } +#endif // !defined(NDEBUG) + } +} + +template +void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first, + ValueAdapter* values_ptr, SizeType construct_size) { + for (SizeType i = 0; i < construct_size; ++i) { + ABSL_INTERNAL_TRY { + values_ptr->ConstructNext(alloc_ptr, construct_first + i); + } + ABSL_INTERNAL_CATCH_ANY { + inlined_vector_internal::DestroyElements(alloc_ptr, construct_first, i); + ABSL_INTERNAL_RETHROW; + } + } +} + +template +void AssignElements(Pointer assign_first, ValueAdapter* values_ptr, + SizeType assign_size) { + for (SizeType i = 0; i < assign_size; ++i) { + values_ptr->AssignNext(assign_first + i); + } +} + +template +struct StorageView { + using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; + + Pointer data; + SizeType size; + SizeType capacity; +}; + +template +class IteratorValueAdapter { + using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; + + public: + explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} + + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at, *it_); + ++it_; + } + + void AssignNext(Pointer assign_at) { + *assign_at = *it_; + ++it_; + } + + private: + Iterator it_; +}; + +template +class CopyValueAdapter { + using AllocatorTraits = absl::allocator_traits; + using ValueType = typename AllocatorTraits::value_type; + using Pointer = typename AllocatorTraits::pointer; + using ConstPointer = typename AllocatorTraits::const_pointer; + + public: + explicit CopyValueAdapter(const ValueType& v) : ptr_(std::addressof(v)) {} + + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_); + } + + void AssignNext(Pointer assign_at) { *assign_at = *ptr_; } + + private: + ConstPointer ptr_; +}; + +template +class DefaultValueAdapter { + using AllocatorTraits = absl::allocator_traits; + using ValueType = typename AllocatorTraits::value_type; + using Pointer = typename AllocatorTraits::pointer; + + public: + explicit DefaultValueAdapter() {} + + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at); + } + + void AssignNext(Pointer assign_at) { *assign_at = ValueType(); } +}; + +template +class AllocationTransaction { + using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; + + public: + explicit AllocationTransaction(AllocatorType* alloc_ptr) + : alloc_data_(*alloc_ptr, nullptr) {} + + ~AllocationTransaction() { + if (DidAllocate()) { + AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity()); + } + } + + AllocationTransaction(const AllocationTransaction&) = delete; + void operator=(const AllocationTransaction&) = delete; + + AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } + Pointer& GetData() { return alloc_data_.template get<1>(); } + SizeType& GetCapacity() { return capacity_; } + + bool DidAllocate() { return GetData() != nullptr; } + Pointer Allocate(SizeType capacity) { + GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); + GetCapacity() = capacity; + return GetData(); + } + + void Reset() { + GetData() = nullptr; + GetCapacity() = 0; + } + + private: + container_internal::CompressedTuple alloc_data_; + SizeType capacity_ = 0; +}; + +template +class ConstructionTransaction { + using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; + + public: + explicit ConstructionTransaction(AllocatorType* alloc_ptr) + : alloc_data_(*alloc_ptr, nullptr) {} + + ~ConstructionTransaction() { + if (DidConstruct()) { + inlined_vector_internal::DestroyElements(std::addressof(GetAllocator()), + GetData(), GetSize()); + } + } + + ConstructionTransaction(const ConstructionTransaction&) = delete; + void operator=(const ConstructionTransaction&) = delete; + + AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } + Pointer& GetData() { return alloc_data_.template get<1>(); } + SizeType& GetSize() { return size_; } + + bool DidConstruct() { return GetData() != nullptr; } + template + void Construct(Pointer data, ValueAdapter* values_ptr, SizeType size) { + inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()), + data, values_ptr, size); + GetData() = data; + GetSize() = size; + } + void Commit() { + GetData() = nullptr; + GetSize() = 0; + } + + private: + container_internal::CompressedTuple alloc_data_; + SizeType size_ = 0; +}; + +template +class Storage { + public: + using AllocatorTraits = absl::allocator_traits; + using allocator_type = typename AllocatorTraits::allocator_type; + using value_type = typename AllocatorTraits::value_type; + using pointer = typename AllocatorTraits::pointer; + using const_pointer = typename AllocatorTraits::const_pointer; + using size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; + + using reference = value_type&; + using const_reference = const value_type&; + using RValueReference = value_type&&; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using MoveIterator = std::move_iterator; + using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk; + + using StorageView = inlined_vector_internal::StorageView; + + template + using IteratorValueAdapter = + inlined_vector_internal::IteratorValueAdapter; + using CopyValueAdapter = + inlined_vector_internal::CopyValueAdapter; + using DefaultValueAdapter = + inlined_vector_internal::DefaultValueAdapter; + + using AllocationTransaction = + inlined_vector_internal::AllocationTransaction; + using ConstructionTransaction = + inlined_vector_internal::ConstructionTransaction; + + static size_type NextCapacity(size_type current_capacity) { + return current_capacity * 2; + } + + static size_type ComputeCapacity(size_type current_capacity, + size_type requested_capacity) { + return (std::max)(NextCapacity(current_capacity), requested_capacity); + } + + // --------------------------------------------------------------------------- + // Storage Constructors and Destructor + // --------------------------------------------------------------------------- + + Storage() : metadata_() {} + + explicit Storage(const allocator_type& alloc) : metadata_(alloc, {}) {} + + ~Storage() { + pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); + inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize()); + DeallocateIfAllocated(); + } + + // --------------------------------------------------------------------------- + // Storage Member Accessors + // --------------------------------------------------------------------------- + + size_type& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } + + const size_type& GetSizeAndIsAllocated() const { + return metadata_.template get<1>(); + } + + size_type GetSize() const { return GetSizeAndIsAllocated() >> 1; } + + bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } + + pointer GetAllocatedData() { return data_.allocated.allocated_data; } + + const_pointer GetAllocatedData() const { + return data_.allocated.allocated_data; + } + + pointer GetInlinedData() { + return reinterpret_cast( + std::addressof(data_.inlined.inlined_data[0])); + } + + const_pointer GetInlinedData() const { + return reinterpret_cast( + std::addressof(data_.inlined.inlined_data[0])); + } + + size_type GetAllocatedCapacity() const { + return data_.allocated.allocated_capacity; + } + + size_type GetInlinedCapacity() const { return static_cast(N); } + + StorageView MakeStorageView() { + return GetIsAllocated() + ? StorageView{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()} + : StorageView{GetInlinedData(), GetSize(), GetInlinedCapacity()}; + } + + allocator_type* GetAllocPtr() { + return std::addressof(metadata_.template get<0>()); + } + + const allocator_type* GetAllocPtr() const { + return std::addressof(metadata_.template get<0>()); + } + + // --------------------------------------------------------------------------- + // Storage Member Mutators + // --------------------------------------------------------------------------- + + template + void Initialize(ValueAdapter values, size_type new_size); + + template + void Assign(ValueAdapter values, size_type new_size); + + template + void Resize(ValueAdapter values, size_type new_size); + + template + iterator Insert(const_iterator pos, ValueAdapter values, + size_type insert_count); + + template + reference EmplaceBack(Args&&... args); + + iterator Erase(const_iterator from, const_iterator to); + + void Reserve(size_type requested_capacity); + + void ShrinkToFit(); + + void Swap(Storage* other_storage_ptr); + + void SetIsAllocated() { + GetSizeAndIsAllocated() |= static_cast(1); + } + + void UnsetIsAllocated() { + GetSizeAndIsAllocated() &= ((std::numeric_limits::max)() - 1); + } + + void SetSize(size_type size) { + GetSizeAndIsAllocated() = + (size << 1) | static_cast(GetIsAllocated()); + } + + void SetAllocatedSize(size_type size) { + GetSizeAndIsAllocated() = (size << 1) | static_cast(1); + } + + void SetInlinedSize(size_type size) { + GetSizeAndIsAllocated() = size << static_cast(1); + } + + void AddSize(size_type count) { + GetSizeAndIsAllocated() += count << static_cast(1); + } + + void SubtractSize(size_type count) { + assert(count <= GetSize()); + + GetSizeAndIsAllocated() -= count << static_cast(1); + } + + void SetAllocatedData(pointer data, size_type capacity) { + data_.allocated.allocated_data = data; + data_.allocated.allocated_capacity = capacity; + } + + void AcquireAllocatedData(AllocationTransaction* allocation_tx_ptr) { + SetAllocatedData(allocation_tx_ptr->GetData(), + allocation_tx_ptr->GetCapacity()); + + allocation_tx_ptr->Reset(); + } + + void MemcpyFrom(const Storage& other_storage) { + assert(IsMemcpyOk::value || other_storage.GetIsAllocated()); + + GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); + data_ = other_storage.data_; + } + + void DeallocateIfAllocated() { + if (GetIsAllocated()) { + AllocatorTraits::deallocate(*GetAllocPtr(), GetAllocatedData(), + GetAllocatedCapacity()); + } + } + + private: + using Metadata = + container_internal::CompressedTuple; + + struct Allocated { + pointer allocated_data; + size_type allocated_capacity; + }; + + struct Inlined { + alignas(value_type) char inlined_data[sizeof(value_type[N])]; + }; + + union Data { + Allocated allocated; + Inlined inlined; + }; + + Metadata metadata_; + Data data_; +}; + +template +template +auto Storage::Initialize(ValueAdapter values, size_type new_size) + -> void { + // Only callable from constructors! + assert(!GetIsAllocated()); + assert(GetSize() == 0); + + pointer construct_data; + if (new_size > GetInlinedCapacity()) { + // Because this is only called from the `InlinedVector` constructors, it's + // safe to take on the allocation with size `0`. If `ConstructElements(...)` + // throws, deallocation will be automatically handled by `~Storage()`. + size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size); + construct_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); + SetAllocatedData(construct_data, new_capacity); + SetIsAllocated(); + } else { + construct_data = GetInlinedData(); + } + + inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, + &values, new_size); + + // Since the initial size was guaranteed to be `0` and the allocated bit is + // already correct for either case, *adding* `new_size` gives us the correct + // result faster than setting it directly. + AddSize(new_size); +} + +template +template +auto Storage::Assign(ValueAdapter values, size_type new_size) -> void { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocPtr()); + + absl::Span assign_loop; + absl::Span construct_loop; + absl::Span destroy_loop; + + if (new_size > storage_view.capacity) { + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + construct_loop = {allocation_tx.Allocate(new_capacity), new_size}; + destroy_loop = {storage_view.data, storage_view.size}; + } else if (new_size > storage_view.size) { + assign_loop = {storage_view.data, storage_view.size}; + construct_loop = {storage_view.data + storage_view.size, + new_size - storage_view.size}; + } else { + assign_loop = {storage_view.data, new_size}; + destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; + } + + inlined_vector_internal::AssignElements(assign_loop.data(), &values, + assign_loop.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), construct_loop.data(), &values, construct_loop.size()); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), + destroy_loop.size()); + + if (allocation_tx.DidAllocate()) { + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + + SetSize(new_size); +} + +template +template +auto Storage::Resize(ValueAdapter values, size_type new_size) -> void { + StorageView storage_view = MakeStorageView(); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + AllocationTransaction allocation_tx(GetAllocPtr()); + ConstructionTransaction construction_tx(GetAllocPtr()); + + absl::Span construct_loop; + absl::Span move_construct_loop; + absl::Span destroy_loop; + + if (new_size > storage_view.capacity) { + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + pointer new_data = allocation_tx.Allocate(new_capacity); + construct_loop = {new_data + storage_view.size, + new_size - storage_view.size}; + move_construct_loop = {new_data, storage_view.size}; + destroy_loop = {storage_view.data, storage_view.size}; + } else if (new_size > storage_view.size) { + construct_loop = {storage_view.data + storage_view.size, + new_size - storage_view.size}; + } else { + destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; + } + + construction_tx.Construct(construct_loop.data(), &values, + construct_loop.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), move_construct_loop.data(), &move_values, + move_construct_loop.size()); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), + destroy_loop.size()); + + construction_tx.Commit(); + if (allocation_tx.DidAllocate()) { + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + + SetSize(new_size); +} + +template +template +auto Storage::Insert(const_iterator pos, ValueAdapter values, + size_type insert_count) -> iterator { + StorageView storage_view = MakeStorageView(); + + size_type insert_index = + std::distance(const_iterator(storage_view.data), pos); + size_type insert_end_index = insert_index + insert_count; + size_type new_size = storage_view.size + insert_count; + + if (new_size > storage_view.capacity) { + AllocationTransaction allocation_tx(GetAllocPtr()); + ConstructionTransaction construction_tx(GetAllocPtr()); + ConstructionTransaction move_construciton_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + pointer new_data = allocation_tx.Allocate(new_capacity); + + construction_tx.Construct(new_data + insert_index, &values, insert_count); + + move_construciton_tx.Construct(new_data, &move_values, insert_index); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), new_data + insert_end_index, &move_values, + storage_view.size - insert_index); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + construction_tx.Commit(); + move_construciton_tx.Commit(); + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + + SetAllocatedSize(new_size); + return iterator(new_data + insert_index); + } else { + size_type move_construction_destination_index = + (std::max)(insert_end_index, storage_view.size); + + ConstructionTransaction move_construction_tx(GetAllocPtr()); + + IteratorValueAdapter move_construction_values( + MoveIterator(storage_view.data + + (move_construction_destination_index - insert_count))); + absl::Span move_construction = { + storage_view.data + move_construction_destination_index, + new_size - move_construction_destination_index}; + + pointer move_assignment_values = storage_view.data + insert_index; + absl::Span move_assignment = { + storage_view.data + insert_end_index, + move_construction_destination_index - insert_end_index}; + + absl::Span insert_assignment = {move_assignment_values, + move_construction.size()}; + + absl::Span insert_construction = { + insert_assignment.data() + insert_assignment.size(), + insert_count - insert_assignment.size()}; + + move_construction_tx.Construct(move_construction.data(), + &move_construction_values, + move_construction.size()); + + for (pointer destination = move_assignment.data() + move_assignment.size(), + last_destination = move_assignment.data(), + source = move_assignment_values + move_assignment.size(); + ;) { + --destination; + --source; + if (destination < last_destination) break; + *destination = std::move(*source); + } + + inlined_vector_internal::AssignElements(insert_assignment.data(), &values, + insert_assignment.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), insert_construction.data(), &values, + insert_construction.size()); + + move_construction_tx.Commit(); + + AddSize(insert_count); + return iterator(storage_view.data + insert_index); + } +} + +template +template +auto Storage::EmplaceBack(Args&&... args) -> reference { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + pointer construct_data; + if (storage_view.size == storage_view.capacity) { + size_type new_capacity = NextCapacity(storage_view.capacity); + construct_data = allocation_tx.Allocate(new_capacity); + } else { + construct_data = storage_view.data; + } + + pointer last_ptr = construct_data + storage_view.size; + + AllocatorTraits::construct(*GetAllocPtr(), last_ptr, + std::forward(args)...); + + if (allocation_tx.DidAllocate()) { + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements( + GetAllocPtr(), allocation_tx.GetData(), &move_values, + storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + AllocatorTraits::destroy(*GetAllocPtr(), last_ptr); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + + AddSize(1); + return *last_ptr; +} + +template +auto Storage::Erase(const_iterator from, const_iterator to) + -> iterator { + StorageView storage_view = MakeStorageView(); + + size_type erase_size = std::distance(from, to); + size_type erase_index = + std::distance(const_iterator(storage_view.data), from); + size_type erase_end_index = erase_index + erase_size; + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data + erase_end_index)); + + inlined_vector_internal::AssignElements(storage_view.data + erase_index, + &move_values, + storage_view.size - erase_end_index); + + inlined_vector_internal::DestroyElements( + GetAllocPtr(), storage_view.data + (storage_view.size - erase_size), + erase_size); + + SubtractSize(erase_size); + return iterator(storage_view.data + erase_index); +} + +template +auto Storage::Reserve(size_type requested_capacity) -> void { + StorageView storage_view = MakeStorageView(); + + if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + size_type new_capacity = + ComputeCapacity(storage_view.capacity, requested_capacity); + pointer new_data = allocation_tx.Allocate(new_capacity); + + inlined_vector_internal::ConstructElements(GetAllocPtr(), new_data, + &move_values, storage_view.size); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); +} + +template +auto Storage::ShrinkToFit() -> void { + // May only be called on allocated instances! + assert(GetIsAllocated()); + + StorageView storage_view{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()}; + + if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + pointer construct_data; + if (storage_view.size > GetInlinedCapacity()) { + size_type new_capacity = storage_view.size; + construct_data = allocation_tx.Allocate(new_capacity); + } else { + construct_data = GetInlinedData(); + } + + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, + &move_values, storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + SetAllocatedData(storage_view.data, storage_view.capacity); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data, + storage_view.capacity); + + if (allocation_tx.DidAllocate()) { + AcquireAllocatedData(&allocation_tx); + } else { + UnsetIsAllocated(); + } +} + +template +auto Storage::Swap(Storage* other_storage_ptr) -> void { + using std::swap; + assert(this != other_storage_ptr); + + if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) { + swap(data_.allocated, other_storage_ptr->data_.allocated); + } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) { + Storage* small_ptr = this; + Storage* large_ptr = other_storage_ptr; + if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr); + + for (size_type i = 0; i < small_ptr->GetSize(); ++i) { + swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]); + } + + IteratorValueAdapter move_values( + MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize())); + + inlined_vector_internal::ConstructElements( + large_ptr->GetAllocPtr(), + small_ptr->GetInlinedData() + small_ptr->GetSize(), &move_values, + large_ptr->GetSize() - small_ptr->GetSize()); + + inlined_vector_internal::DestroyElements( + large_ptr->GetAllocPtr(), + large_ptr->GetInlinedData() + small_ptr->GetSize(), + large_ptr->GetSize() - small_ptr->GetSize()); + } else { + Storage* allocated_ptr = this; + Storage* inlined_ptr = other_storage_ptr; + if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr); + + StorageView allocated_storage_view{allocated_ptr->GetAllocatedData(), + allocated_ptr->GetSize(), + allocated_ptr->GetAllocatedCapacity()}; + + IteratorValueAdapter move_values( + MoveIterator(inlined_ptr->GetInlinedData())); + + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements( + inlined_ptr->GetAllocPtr(), allocated_ptr->GetInlinedData(), + &move_values, inlined_ptr->GetSize()); + } + ABSL_INTERNAL_CATCH_ANY { + allocated_ptr->SetAllocatedData(allocated_storage_view.data, + allocated_storage_view.capacity); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(inlined_ptr->GetAllocPtr(), + inlined_ptr->GetInlinedData(), + inlined_ptr->GetSize()); + + inlined_ptr->SetAllocatedData(allocated_storage_view.data, + allocated_storage_view.capacity); + } + + swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); + swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr()); +} + +} // namespace inlined_vector_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/layout.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/layout.h new file mode 100644 index 0000000000000000000000000000000000000000..69cc85dd6679d6071b7cd8d727e77988ceeac6e5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/layout.h @@ -0,0 +1,741 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// MOTIVATION AND TUTORIAL +// +// If you want to put in a single heap allocation N doubles followed by M ints, +// it's easy if N and M are known at compile time. +// +// struct S { +// double a[N]; +// int b[M]; +// }; +// +// S* p = new S; +// +// But what if N and M are known only in run time? Class template Layout to the +// rescue! It's a portable generalization of the technique known as struct hack. +// +// // This object will tell us everything we need to know about the memory +// // layout of double[N] followed by int[M]. It's structurally identical to +// // size_t[2] that stores N and M. It's very cheap to create. +// const Layout layout(N, M); +// +// // Allocate enough memory for both arrays. `AllocSize()` tells us how much +// // memory is needed. We are free to use any allocation function we want as +// // long as it returns aligned memory. +// std::unique_ptr p(new unsigned char[layout.AllocSize()]); +// +// // Obtain the pointer to the array of doubles. +// // Equivalent to `reinterpret_cast(p.get())`. +// // +// // We could have written layout.Pointer<0>(p) instead. If all the types are +// // unique you can use either form, but if some types are repeated you must +// // use the index form. +// double* a = layout.Pointer(p.get()); +// +// // Obtain the pointer to the array of ints. +// // Equivalent to `reinterpret_cast(p.get() + N * 8)`. +// int* b = layout.Pointer(p); +// +// If we are unable to specify sizes of all fields, we can pass as many sizes as +// we can to `Partial()`. In return, it'll allow us to access the fields whose +// locations and sizes can be computed from the provided information. +// `Partial()` comes in handy when the array sizes are embedded into the +// allocation. +// +// // size_t[1] containing N, size_t[1] containing M, double[N], int[M]. +// using L = Layout; +// +// unsigned char* Allocate(size_t n, size_t m) { +// const L layout(1, 1, n, m); +// unsigned char* p = new unsigned char[layout.AllocSize()]; +// *layout.Pointer<0>(p) = n; +// *layout.Pointer<1>(p) = m; +// return p; +// } +// +// void Use(unsigned char* p) { +// // First, extract N and M. +// // Specify that the first array has only one element. Using `prefix` we +// // can access the first two arrays but not more. +// constexpr auto prefix = L::Partial(1); +// size_t n = *prefix.Pointer<0>(p); +// size_t m = *prefix.Pointer<1>(p); +// +// // Now we can get pointers to the payload. +// const L layout(1, 1, n, m); +// double* a = layout.Pointer(p); +// int* b = layout.Pointer(p); +// } +// +// The layout we used above combines fixed-size with dynamically-sized fields. +// This is quite common. Layout is optimized for this use case and generates +// optimal code. All computations that can be performed at compile time are +// indeed performed at compile time. +// +// Efficiency tip: The order of fields matters. In `Layout` try to +// ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no +// padding in between arrays. +// +// You can manually override the alignment of an array by wrapping the type in +// `Aligned`. `Layout<..., Aligned, ...>` has exactly the same API +// and behavior as `Layout<..., T, ...>` except that the first element of the +// array of `T` is aligned to `N` (the rest of the elements follow without +// padding). `N` cannot be less than `alignof(T)`. +// +// `AllocSize()` and `Pointer()` are the most basic methods for dealing with +// memory layouts. Check out the reference or code below to discover more. +// +// EXAMPLE +// +// // Immutable move-only string with sizeof equal to sizeof(void*). The +// // string size and the characters are kept in the same heap allocation. +// class CompactString { +// public: +// CompactString(const char* s = "") { +// const size_t size = strlen(s); +// // size_t[1] followed by char[size + 1]. +// const L layout(1, size + 1); +// p_.reset(new unsigned char[layout.AllocSize()]); +// // If running under ASAN, mark the padding bytes, if any, to catch +// // memory errors. +// layout.PoisonPadding(p_.get()); +// // Store the size in the allocation. +// *layout.Pointer(p_.get()) = size; +// // Store the characters in the allocation. +// memcpy(layout.Pointer(p_.get()), s, size + 1); +// } +// +// size_t size() const { +// // Equivalent to reinterpret_cast(*p). +// return *L::Partial().Pointer(p_.get()); +// } +// +// const char* c_str() const { +// // Equivalent to reinterpret_cast(p.get() + sizeof(size_t)). +// // The argument in Partial(1) specifies that we have size_t[1] in front +// // of the characters. +// return L::Partial(1).Pointer(p_.get()); +// } +// +// private: +// // Our heap allocation contains a size_t followed by an array of chars. +// using L = Layout; +// std::unique_ptr p_; +// }; +// +// int main() { +// CompactString s = "hello"; +// assert(s.size() == 5); +// assert(strcmp(s.c_str(), "hello") == 0); +// } +// +// DOCUMENTATION +// +// The interface exported by this file consists of: +// - class `Layout<>` and its public members. +// - The public members of class `internal_layout::LayoutImpl<>`. That class +// isn't intended to be used directly, and its name and template parameter +// list are internal implementation details, but the class itself provides +// most of the functionality in this file. See comments on its members for +// detailed documentation. +// +// `Layout::Partial(count1,..., countm)` (where `m` <= `n`) returns a +// `LayoutImpl<>` object. `Layout layout(count1,..., countn)` +// creates a `Layout` object, which exposes the same functionality by inheriting +// from `LayoutImpl<>`. + +#ifndef ABSL_CONTAINER_INTERNAL_LAYOUT_H_ +#define ABSL_CONTAINER_INTERNAL_LAYOUT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ADDRESS_SANITIZER +#include +#endif + +#include "absl/meta/type_traits.h" +#include "absl/strings/str_cat.h" +#include "absl/types/span.h" +#include "absl/utility/utility.h" + +#if defined(__GXX_RTTI) +#define ABSL_INTERNAL_HAS_CXA_DEMANGLE +#endif + +#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE +#include +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A type wrapper that instructs `Layout` to use the specific alignment for the +// array. `Layout<..., Aligned, ...>` has exactly the same API +// and behavior as `Layout<..., T, ...>` except that the first element of the +// array of `T` is aligned to `N` (the rest of the elements follow without +// padding). +// +// Requires: `N >= alignof(T)` and `N` is a power of 2. +template +struct Aligned; + +namespace internal_layout { + +template +struct NotAligned {}; + +template +struct NotAligned> { + static_assert(sizeof(T) == 0, "Aligned cannot be const-qualified"); +}; + +template +using IntToSize = size_t; + +template +using TypeToSize = size_t; + +template +struct Type : NotAligned { + using type = T; +}; + +template +struct Type> { + using type = T; +}; + +template +struct SizeOf : NotAligned, std::integral_constant {}; + +template +struct SizeOf> : std::integral_constant {}; + +// Note: workaround for https://gcc.gnu.org/PR88115 +template +struct AlignOf : NotAligned { + static constexpr size_t value = alignof(T); +}; + +template +struct AlignOf> { + static_assert(N % alignof(T) == 0, + "Custom alignment can't be lower than the type's alignment"); + static constexpr size_t value = N; +}; + +// Does `Ts...` contain `T`? +template +using Contains = absl::disjunction...>; + +template +using CopyConst = + typename std::conditional::value, const To, To>::type; + +// Note: We're not qualifying this with absl:: because it doesn't compile under +// MSVC. +template +using SliceType = Span; + +// This namespace contains no types. It prevents functions defined in it from +// being found by ADL. +namespace adl_barrier { + +template +constexpr size_t Find(Needle, Needle, Ts...) { + static_assert(!Contains(), "Duplicate element type"); + return 0; +} + +template +constexpr size_t Find(Needle, T, Ts...) { + return adl_barrier::Find(Needle(), Ts()...) + 1; +} + +constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); } + +// Returns `q * m` for the smallest `q` such that `q * m >= n`. +// Requires: `m` is a power of two. It's enforced by IsLegalElementType below. +constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); } + +constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; } + +constexpr size_t Max(size_t a) { return a; } + +template +constexpr size_t Max(size_t a, size_t b, Ts... rest) { + return adl_barrier::Max(b < a ? a : b, rest...); +} + +template +std::string TypeName() { + std::string out; + int status = 0; + char* demangled = nullptr; +#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE + demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status); +#endif + if (status == 0 && demangled != nullptr) { // Demangling succeeded. + absl::StrAppend(&out, "<", demangled, ">"); + free(demangled); + } else { +#if defined(__GXX_RTTI) || defined(_CPPRTTI) + absl::StrAppend(&out, "<", typeid(T).name(), ">"); +#endif + } + return out; +} + +} // namespace adl_barrier + +template +using EnableIf = typename std::enable_if::type; + +// Can `T` be a template argument of `Layout`? +template +using IsLegalElementType = std::integral_constant< + bool, !std::is_reference::value && !std::is_volatile::value && + !std::is_reference::type>::value && + !std::is_volatile::type>::value && + adl_barrier::IsPow2(AlignOf::value)>; + +template +class LayoutImpl; + +// Public base class of `Layout` and the result type of `Layout::Partial()`. +// +// `Elements...` contains all template arguments of `Layout` that created this +// instance. +// +// `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is the number of arguments +// passed to `Layout::Partial()` or `Layout::Layout()`. +// +// `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is +// `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we +// can compute offsets). +template +class LayoutImpl, absl::index_sequence, + absl::index_sequence> { + private: + static_assert(sizeof...(Elements) > 0, "At least one field is required"); + static_assert(absl::conjunction...>::value, + "Invalid element type (see IsLegalElementType)"); + + enum { + NumTypes = sizeof...(Elements), + NumSizes = sizeof...(SizeSeq), + NumOffsets = sizeof...(OffsetSeq), + }; + + // These are guaranteed by `Layout`. + static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1), + "Internal error"); + static_assert(NumTypes > 0, "Internal error"); + + // Returns the index of `T` in `Elements...`. Results in a compilation error + // if `Elements...` doesn't contain exactly one instance of `T`. + template + static constexpr size_t ElementIndex() { + static_assert(Contains, Type::type>...>(), + "Type not found"); + return adl_barrier::Find(Type(), + Type::type>()...); + } + + template + using ElementAlignment = + AlignOf>::type>; + + public: + // Element types of all arrays packed in a tuple. + using ElementTypes = std::tuple::type...>; + + // Element type of the Nth array. + template + using ElementType = typename std::tuple_element::type; + + constexpr explicit LayoutImpl(IntToSize... sizes) + : size_{sizes...} {} + + // Alignment of the layout, equal to the strictest alignment of all elements. + // All pointers passed to the methods of layout must be aligned to this value. + static constexpr size_t Alignment() { + return adl_barrier::Max(AlignOf::value...); + } + + // Offset in bytes of the Nth array. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Offset<0>() == 0); // The ints starts from 0. + // assert(x.Offset<1>() == 16); // The doubles starts from 16. + // + // Requires: `N <= NumSizes && N < sizeof...(Ts)`. + template = 0> + constexpr size_t Offset() const { + return 0; + } + + template = 0> + constexpr size_t Offset() const { + static_assert(N < NumOffsets, "Index out of bounds"); + return adl_barrier::Align( + Offset() + SizeOf>() * size_[N - 1], + ElementAlignment::value); + } + + // Offset in bytes of the array with the specified element type. There must + // be exactly one such array and its zero-based index must be at most + // `NumSizes`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Offset() == 0); // The ints starts from 0. + // assert(x.Offset() == 16); // The doubles starts from 16. + template + constexpr size_t Offset() const { + return Offset()>(); + } + + // Offsets in bytes of all arrays for which the offsets are known. + constexpr std::array Offsets() const { + return {{Offset()...}}; + } + + // The number of elements in the Nth array. This is the Nth argument of + // `Layout::Partial()` or `Layout::Layout()` (zero-based). + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Size<0>() == 3); + // assert(x.Size<1>() == 4); + // + // Requires: `N < NumSizes`. + template + constexpr size_t Size() const { + static_assert(N < NumSizes, "Index out of bounds"); + return size_[N]; + } + + // The number of elements in the array with the specified element type. + // There must be exactly one such array and its zero-based index must be + // at most `NumSizes`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Size() == 3); + // assert(x.Size() == 4); + template + constexpr size_t Size() const { + return Size()>(); + } + + // The number of elements of all arrays for which they are known. + constexpr std::array Sizes() const { + return {{Size()...}}; + } + + // Pointer to the beginning of the Nth array. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // int* ints = x.Pointer<0>(p); + // double* doubles = x.Pointer<1>(p); + // + // Requires: `N <= NumSizes && N < sizeof...(Ts)`. + // Requires: `p` is aligned to `Alignment()`. + template + CopyConst>* Pointer(Char* p) const { + using C = typename std::remove_const::type; + static_assert( + std::is_same() || std::is_same() || + std::is_same(), + "The argument must be a pointer to [const] [signed|unsigned] char"); + constexpr size_t alignment = Alignment(); + (void)alignment; + assert(reinterpret_cast(p) % alignment == 0); + return reinterpret_cast>*>(p + Offset()); + } + + // Pointer to the beginning of the array with the specified element type. + // There must be exactly one such array and its zero-based index must be at + // most `NumSizes`. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // int* ints = x.Pointer(p); + // double* doubles = x.Pointer(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + CopyConst* Pointer(Char* p) const { + return Pointer()>(p); + } + + // Pointers to all arrays for which pointers are known. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // + // int* ints; + // double* doubles; + // std::tie(ints, doubles) = x.Pointers(p); + // + // Requires: `p` is aligned to `Alignment()`. + // + // Note: We're not using ElementType alias here because it does not compile + // under MSVC. + template + std::tuple::type>*...> + Pointers(Char* p) const { + return std::tuple>*...>( + Pointer(p)...); + } + + // The Nth array. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // Span ints = x.Slice<0>(p); + // Span doubles = x.Slice<1>(p); + // + // Requires: `N < NumSizes`. + // Requires: `p` is aligned to `Alignment()`. + template + SliceType>> Slice(Char* p) const { + return SliceType>>(Pointer(p), Size()); + } + + // The array with the specified element type. There must be exactly one + // such array and its zero-based index must be less than `NumSizes`. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // Span ints = x.Slice(p); + // Span doubles = x.Slice(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + SliceType> Slice(Char* p) const { + return Slice()>(p); + } + + // All arrays with known sizes. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // + // Span ints; + // Span doubles; + // std::tie(ints, doubles) = x.Slices(p); + // + // Requires: `p` is aligned to `Alignment()`. + // + // Note: We're not using ElementType alias here because it does not compile + // under MSVC. + template + std::tuple::type>>...> + Slices(Char* p) const { + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63875 (fixed + // in 6.1). + (void)p; + return std::tuple>>...>( + Slice(p)...); + } + + // The size of the allocation that fits all arrays. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; // 48 bytes + // + // Requires: `NumSizes == sizeof...(Ts)`. + constexpr size_t AllocSize() const { + static_assert(NumTypes == NumSizes, "You must specify sizes of all fields"); + return Offset() + + SizeOf>() * size_[NumTypes - 1]; + } + + // If built with --config=asan, poisons padding bytes (if any) in the + // allocation. The pointer must point to a memory block at least + // `AllocSize()` bytes in length. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // Requires: `p` is aligned to `Alignment()`. + template = 0> + void PoisonPadding(const Char* p) const { + Pointer<0>(p); // verify the requirements on `Char` and `p` + } + + template = 0> + void PoisonPadding(const Char* p) const { + static_assert(N < NumOffsets, "Index out of bounds"); + (void)p; +#ifdef ADDRESS_SANITIZER + PoisonPadding(p); + // The `if` is an optimization. It doesn't affect the observable behaviour. + if (ElementAlignment::value % ElementAlignment::value) { + size_t start = + Offset() + SizeOf>() * size_[N - 1]; + ASAN_POISON_MEMORY_REGION(p + start, Offset() - start); + } +#endif + } + + // Human-readable description of the memory layout. Useful for debugging. + // Slow. + // + // // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed + // // by an unknown number of doubles. + // auto x = Layout::Partial(5, 3); + // assert(x.DebugString() == + // "@0(1)[5]; @8(4)[3]; @24(8)"); + // + // Each field is in the following format: @offset(sizeof)[size] ( + // may be missing depending on the target platform). For example, + // @8(4)[3] means that at offset 8 we have an array of ints, where each + // int is 4 bytes, and we have 3 of those ints. The size of the last field may + // be missing (as in the example above). Only fields with known offsets are + // described. Type names may differ across platforms: one compiler might + // produce "unsigned*" where another produces "unsigned int *". + std::string DebugString() const { + const auto offsets = Offsets(); + const size_t sizes[] = {SizeOf>()...}; + const std::string types[] = { + adl_barrier::TypeName>()...}; + std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")"); + for (size_t i = 0; i != NumOffsets - 1; ++i) { + absl::StrAppend(&res, "[", size_[i], "]; @", offsets[i + 1], types[i + 1], + "(", sizes[i + 1], ")"); + } + // NumSizes is a constant that may be zero. Some compilers cannot see that + // inside the if statement "size_[NumSizes - 1]" must be valid. + int last = static_cast(NumSizes) - 1; + if (NumTypes == NumSizes && last >= 0) { + absl::StrAppend(&res, "[", size_[last], "]"); + } + return res; + } + + private: + // Arguments of `Layout::Partial()` or `Layout::Layout()`. + size_t size_[NumSizes > 0 ? NumSizes : 1]; +}; + +template +using LayoutType = LayoutImpl< + std::tuple, absl::make_index_sequence, + absl::make_index_sequence>; + +} // namespace internal_layout + +// Descriptor of arrays of various types and sizes laid out in memory one after +// another. See the top of the file for documentation. +// +// Check out the public API of internal_layout::LayoutImpl above. The type is +// internal to the library but its methods are public, and they are inherited +// by `Layout`. +template +class Layout : public internal_layout::LayoutType { + public: + static_assert(sizeof...(Ts) > 0, "At least one field is required"); + static_assert( + absl::conjunction...>::value, + "Invalid element type (see IsLegalElementType)"); + + // The result type of `Partial()` with `NumSizes` arguments. + template + using PartialType = internal_layout::LayoutType; + + // `Layout` knows the element types of the arrays we want to lay out in + // memory but not the number of elements in each array. + // `Partial(size1, ..., sizeN)` allows us to specify the latter. The + // resulting immutable object can be used to obtain pointers to the + // individual arrays. + // + // It's allowed to pass fewer array sizes than the number of arrays. E.g., + // if all you need is to the offset of the second array, you only need to + // pass one argument -- the number of elements in the first array. + // + // // int[3] followed by 4 bytes of padding and an unknown number of + // // doubles. + // auto x = Layout::Partial(3); + // // doubles start at byte 16. + // assert(x.Offset<1>() == 16); + // + // If you know the number of elements in all arrays, you can still call + // `Partial()` but it's more convenient to use the constructor of `Layout`. + // + // Layout x(3, 5); + // + // Note: The sizes of the arrays must be specified in number of elements, + // not in bytes. + // + // Requires: `sizeof...(Sizes) <= sizeof...(Ts)`. + // Requires: all arguments are convertible to `size_t`. + template + static constexpr PartialType Partial(Sizes&&... sizes) { + static_assert(sizeof...(Sizes) <= sizeof...(Ts), ""); + return PartialType(absl::forward(sizes)...); + } + + // Creates a layout with the sizes of all arrays specified. If you know + // only the sizes of the first N arrays (where N can be zero), you can use + // `Partial()` defined above. The constructor is essentially equivalent to + // calling `Partial()` and passing in all array sizes; the constructor is + // provided as a convenient abbreviation. + // + // Note: The sizes of the arrays must be specified in number of elements, + // not in bytes. + constexpr explicit Layout(internal_layout::TypeToSize... sizes) + : internal_layout::LayoutType(sizes...) {} +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/node_hash_policy.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/node_hash_policy.h new file mode 100644 index 0000000000000000000000000000000000000000..4617162f0b3271c6fcbed70c8e5cd515176c0fb6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/node_hash_policy.h @@ -0,0 +1,92 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Adapts a policy for nodes. +// +// The node policy should model: +// +// struct Policy { +// // Returns a new node allocated and constructed using the allocator, using +// // the specified arguments. +// template +// value_type* new_element(Alloc* alloc, Args&&... args) const; +// +// // Destroys and deallocates node using the allocator. +// template +// void delete_element(Alloc* alloc, value_type* node) const; +// }; +// +// It may also optionally define `value()` and `apply()`. For documentation on +// these, see hash_policy_traits.h. + +#ifndef ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ +#define ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct node_hash_policy { + static_assert(std::is_lvalue_reference::value, ""); + + using slot_type = typename std::remove_cv< + typename std::remove_reference::type>::type*; + + template + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + *slot = Policy::new_element(alloc, std::forward(args)...); + } + + template + static void destroy(Alloc* alloc, slot_type* slot) { + Policy::delete_element(alloc, *slot); + } + + template + static void transfer(Alloc*, slot_type* new_slot, slot_type* old_slot) { + *new_slot = *old_slot; + } + + static size_t space_used(const slot_type* slot) { + if (slot == nullptr) return Policy::element_space_used(nullptr); + return Policy::element_space_used(*slot); + } + + static Reference element(slot_type* slot) { return **slot; } + + template + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + + template + static auto apply(Ts&&... ts) -> decltype(P::apply(std::forward(ts)...)) { + return P::apply(std::forward(ts)...); + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/raw_hash_map.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/raw_hash_map.h new file mode 100644 index 0000000000000000000000000000000000000000..0a02757ddfb49258dbc1d174aaf820f909723c5e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/raw_hash_map.h @@ -0,0 +1,197 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ +#define ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ + +#include +#include +#include + +#include "absl/base/internal/throw_delegate.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class raw_hash_map : public raw_hash_set { + // P is Policy. It's passed as a template argument to support maps that have + // incomplete types as values, as in unordered_map. + // MappedReference<> may be a non-reference type. + template + using MappedReference = decltype(P::value( + std::addressof(std::declval()))); + + // MappedConstReference<> may be a non-reference type. + template + using MappedConstReference = decltype(P::value( + std::addressof(std::declval()))); + + using KeyArgImpl = + KeyArg::value && IsTransparent::value>; + + public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + template + using key_arg = typename KeyArgImpl::template type; + + static_assert(!std::is_reference::value, ""); + // TODO(alkis): remove this assertion and verify that reference mapped_type is + // supported. + static_assert(!std::is_reference::value, ""); + + using iterator = typename raw_hash_map::raw_hash_set::iterator; + using const_iterator = typename raw_hash_map::raw_hash_set::const_iterator; + + raw_hash_map() {} + using raw_hash_map::raw_hash_set::raw_hash_set; + + // The last two template parameters ensure that both arguments are rvalues + // (lvalue arguments are handled by the overloads below). This is necessary + // for supporting bitfield arguments. + // + // union { int n : 1; }; + // flat_hash_map m; + // m.insert_or_assign(n, n); + template + std::pair insert_or_assign(key_arg&& k, V&& v) { + return insert_or_assign_impl(std::forward(k), std::forward(v)); + } + + template + std::pair insert_or_assign(key_arg&& k, const V& v) { + return insert_or_assign_impl(std::forward(k), v); + } + + template + std::pair insert_or_assign(const key_arg& k, V&& v) { + return insert_or_assign_impl(k, std::forward(v)); + } + + template + std::pair insert_or_assign(const key_arg& k, const V& v) { + return insert_or_assign_impl(k, v); + } + + template + iterator insert_or_assign(const_iterator, key_arg&& k, V&& v) { + return insert_or_assign(std::forward(k), std::forward(v)).first; + } + + template + iterator insert_or_assign(const_iterator, key_arg&& k, const V& v) { + return insert_or_assign(std::forward(k), v).first; + } + + template + iterator insert_or_assign(const_iterator, const key_arg& k, V&& v) { + return insert_or_assign(k, std::forward(v)).first; + } + + template + iterator insert_or_assign(const_iterator, const key_arg& k, const V& v) { + return insert_or_assign(k, v).first; + } + + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. + template ::value, int>::type = 0, + K* = nullptr> + std::pair try_emplace(key_arg&& k, Args&&... args) { + return try_emplace_impl(std::forward(k), std::forward(args)...); + } + + template ::value, int>::type = 0> + std::pair try_emplace(const key_arg& k, Args&&... args) { + return try_emplace_impl(k, std::forward(args)...); + } + + template + iterator try_emplace(const_iterator, key_arg&& k, Args&&... args) { + return try_emplace(std::forward(k), std::forward(args)...).first; + } + + template + iterator try_emplace(const_iterator, const key_arg& k, Args&&... args) { + return try_emplace(k, std::forward(args)...).first; + } + + template + MappedReference

at(const key_arg& key) { + auto it = this->find(key); + if (it == this->end()) { + base_internal::ThrowStdOutOfRange( + "absl::container_internal::raw_hash_map<>::at"); + } + return Policy::value(&*it); + } + + template + MappedConstReference

at(const key_arg& key) const { + auto it = this->find(key); + if (it == this->end()) { + base_internal::ThrowStdOutOfRange( + "absl::container_internal::raw_hash_map<>::at"); + } + return Policy::value(&*it); + } + + template + MappedReference

operator[](key_arg&& key) { + return Policy::value(&*try_emplace(std::forward(key)).first); + } + + template + MappedReference

operator[](const key_arg& key) { + return Policy::value(&*try_emplace(key).first); + } + + private: + template + std::pair insert_or_assign_impl(K&& k, V&& v) { + auto res = this->find_or_prepare_insert(k); + if (res.second) + this->emplace_at(res.first, std::forward(k), std::forward(v)); + else + Policy::value(&*this->iterator_at(res.first)) = std::forward(v); + return {this->iterator_at(res.first), res.second}; + } + + template + std::pair try_emplace_impl(K&& k, Args&&... args) { + auto res = this->find_or_prepare_insert(k); + if (res.second) + this->emplace_at(res.first, std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)); + return {this->iterator_at(res.first), res.second}; + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/raw_hash_set.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/raw_hash_set.h new file mode 100644 index 0000000000000000000000000000000000000000..ca7be8d8681903c444c45e3288fa9a6488b149f3 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/raw_hash_set.h @@ -0,0 +1,1882 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// An open-addressing +// hashtable with quadratic probing. +// +// This is a low level hashtable on top of which different interfaces can be +// implemented, like flat_hash_set, node_hash_set, string_hash_set, etc. +// +// The table interface is similar to that of std::unordered_set. Notable +// differences are that most member functions support heterogeneous keys when +// BOTH the hash and eq functions are marked as transparent. They do so by +// providing a typedef called `is_transparent`. +// +// When heterogeneous lookup is enabled, functions that take key_type act as if +// they have an overload set like: +// +// iterator find(const key_type& key); +// template +// iterator find(const K& key); +// +// size_type erase(const key_type& key); +// template +// size_type erase(const K& key); +// +// std::pair equal_range(const key_type& key); +// template +// std::pair equal_range(const K& key); +// +// When heterogeneous lookup is disabled, only the explicit `key_type` overloads +// exist. +// +// find() also supports passing the hash explicitly: +// +// iterator find(const key_type& key, size_t hash); +// template +// iterator find(const U& key, size_t hash); +// +// In addition the pointer to element and iterator stability guarantees are +// weaker: all iterators and pointers are invalidated after a new element is +// inserted. +// +// IMPLEMENTATION DETAILS +// +// The table stores elements inline in a slot array. In addition to the slot +// array the table maintains some control state per slot. The extra state is one +// byte per slot and stores empty or deleted marks, or alternatively 7 bits from +// the hash of an occupied slot. The table is split into logical groups of +// slots, like so: +// +// Group 1 Group 2 Group 3 +// +---------------+---------------+---------------+ +// | | | | | | | | | | | | | | | | | | | | | | | | | +// +---------------+---------------+---------------+ +// +// On lookup the hash is split into two parts: +// - H2: 7 bits (those stored in the control bytes) +// - H1: the rest of the bits +// The groups are probed using H1. For each group the slots are matched to H2 in +// parallel. Because H2 is 7 bits (128 states) and the number of slots per group +// is low (8 or 16) in almost all cases a match in H2 is also a lookup hit. +// +// On insert, once the right group is found (as in lookup), its slots are +// filled in order. +// +// On erase a slot is cleared. In case the group did not have any empty slots +// before the erase, the erased slot is marked as deleted. +// +// Groups without empty slots (but maybe with deleted slots) extend the probe +// sequence. The probing algorithm is quadratic. Given N the number of groups, +// the probing function for the i'th probe is: +// +// P(0) = H1 % N +// +// P(i) = (P(i - 1) + i) % N +// +// This probing function guarantees that after N probes, all the groups of the +// table will be probed exactly once. + +#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ +#define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/internal/bits.h" +#include "absl/base/internal/endian.h" +#include "absl/base/port.h" +#include "absl/container/internal/common.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_policy_traits.h" +#include "absl/container/internal/hashtable_debug_hooks.h" +#include "absl/container/internal/hashtablez_sampler.h" +#include "absl/container/internal/have_sse.h" +#include "absl/container/internal/layout.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class probe_seq { + public: + probe_seq(size_t hash, size_t mask) { + assert(((mask + 1) & mask) == 0 && "not a mask"); + mask_ = mask; + offset_ = hash & mask_; + } + size_t offset() const { return offset_; } + size_t offset(size_t i) const { return (offset_ + i) & mask_; } + + void next() { + index_ += Width; + offset_ += index_; + offset_ &= mask_; + } + // 0-based probe index. The i-th probe in the probe sequence. + size_t index() const { return index_; } + + private: + size_t mask_; + size_t offset_; + size_t index_ = 0; +}; + +template +struct RequireUsableKey { + template + std::pair< + decltype(std::declval()(std::declval())), + decltype(std::declval()(std::declval(), + std::declval()))>* + operator()(const PassedKey&, const Args&...) const; +}; + +template +struct IsDecomposable : std::false_type {}; + +template +struct IsDecomposable< + absl::void_t(), + std::declval()...))>, + Policy, Hash, Eq, Ts...> : std::true_type {}; + +// TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it. +template +constexpr bool IsNoThrowSwappable() { + using std::swap; + return noexcept(swap(std::declval(), std::declval())); +} + +template +int TrailingZeros(T x) { + return sizeof(T) == 8 ? base_internal::CountTrailingZerosNonZero64( + static_cast(x)) + : base_internal::CountTrailingZerosNonZero32( + static_cast(x)); +} + +template +int LeadingZeros(T x) { + return sizeof(T) == 8 + ? base_internal::CountLeadingZeros64(static_cast(x)) + : base_internal::CountLeadingZeros32(static_cast(x)); +} + +// An abstraction over a bitmask. It provides an easy way to iterate through the +// indexes of the set bits of a bitmask. When Shift=0 (platforms with SSE), +// this is a true bitmask. On non-SSE, platforms the arithematic used to +// emulate the SSE behavior works in bytes (Shift=3) and leaves each bytes as +// either 0x00 or 0x80. +// +// For example: +// for (int i : BitMask(0x5)) -> yields 0, 2 +// for (int i : BitMask(0x0000000080800000)) -> yields 2, 3 +template +class BitMask { + static_assert(std::is_unsigned::value, ""); + static_assert(Shift == 0 || Shift == 3, ""); + + public: + // These are useful for unit tests (gunit). + using value_type = int; + using iterator = BitMask; + using const_iterator = BitMask; + + explicit BitMask(T mask) : mask_(mask) {} + BitMask& operator++() { + mask_ &= (mask_ - 1); + return *this; + } + explicit operator bool() const { return mask_ != 0; } + int operator*() const { return LowestBitSet(); } + int LowestBitSet() const { + return container_internal::TrailingZeros(mask_) >> Shift; + } + int HighestBitSet() const { + return (sizeof(T) * CHAR_BIT - container_internal::LeadingZeros(mask_) - + 1) >> + Shift; + } + + BitMask begin() const { return *this; } + BitMask end() const { return BitMask(0); } + + int TrailingZeros() const { + return container_internal::TrailingZeros(mask_) >> Shift; + } + + int LeadingZeros() const { + constexpr int total_significant_bits = SignificantBits << Shift; + constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits; + return container_internal::LeadingZeros(mask_ << extra_bits) >> Shift; + } + + private: + friend bool operator==(const BitMask& a, const BitMask& b) { + return a.mask_ == b.mask_; + } + friend bool operator!=(const BitMask& a, const BitMask& b) { + return a.mask_ != b.mask_; + } + + T mask_; +}; + +using ctrl_t = signed char; +using h2_t = uint8_t; + +// The values here are selected for maximum performance. See the static asserts +// below for details. +enum Ctrl : ctrl_t { + kEmpty = -128, // 0b10000000 + kDeleted = -2, // 0b11111110 + kSentinel = -1, // 0b11111111 +}; +static_assert( + kEmpty & kDeleted & kSentinel & 0x80, + "Special markers need to have the MSB to make checking for them efficient"); +static_assert(kEmpty < kSentinel && kDeleted < kSentinel, + "kEmpty and kDeleted must be smaller than kSentinel to make the " + "SIMD test of IsEmptyOrDeleted() efficient"); +static_assert(kSentinel == -1, + "kSentinel must be -1 to elide loading it from memory into SIMD " + "registers (pcmpeqd xmm, xmm)"); +static_assert(kEmpty == -128, + "kEmpty must be -128 to make the SIMD check for its " + "existence efficient (psignb xmm, xmm)"); +static_assert(~kEmpty & ~kDeleted & kSentinel & 0x7F, + "kEmpty and kDeleted must share an unset bit that is not shared " + "by kSentinel to make the scalar test for MatchEmptyOrDeleted() " + "efficient"); +static_assert(kDeleted == -2, + "kDeleted must be -2 to make the implementation of " + "ConvertSpecialToEmptyAndFullToDeleted efficient"); + +// A single block of empty control bytes for tables without any slots allocated. +// This enables removing a branch in the hot path of find(). +inline ctrl_t* EmptyGroup() { + alignas(16) static constexpr ctrl_t empty_group[] = { + kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, + kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty}; + return const_cast(empty_group); +} + +// Mixes a randomly generated per-process seed with `hash` and `ctrl` to +// randomize insertion order within groups. +bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl); + +// Returns a hash seed. +// +// The seed consists of the ctrl_ pointer, which adds enough entropy to ensure +// non-determinism of iteration order in most cases. +inline size_t HashSeed(const ctrl_t* ctrl) { + // The low bits of the pointer have little or no entropy because of + // alignment. We shift the pointer to try to use higher entropy bits. A + // good number seems to be 12 bits, because that aligns with page size. + return reinterpret_cast(ctrl) >> 12; +} + +inline size_t H1(size_t hash, const ctrl_t* ctrl) { + return (hash >> 7) ^ HashSeed(ctrl); +} +inline ctrl_t H2(size_t hash) { return hash & 0x7F; } + +inline bool IsEmpty(ctrl_t c) { return c == kEmpty; } +inline bool IsFull(ctrl_t c) { return c >= 0; } +inline bool IsDeleted(ctrl_t c) { return c == kDeleted; } +inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; } + +#if SWISSTABLE_HAVE_SSE2 + +// https://github.com/abseil/abseil-cpp/issues/209 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853 +// _mm_cmpgt_epi8 is broken under GCC with -funsigned-char +// Work around this by using the portable implementation of Group +// when using -funsigned-char under GCC. +inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) { +#if defined(__GNUC__) && !defined(__clang__) + if (std::is_unsigned::value) { + const __m128i mask = _mm_set1_epi8(0x80); + const __m128i diff = _mm_subs_epi8(b, a); + return _mm_cmpeq_epi8(_mm_and_si128(diff, mask), mask); + } +#endif + return _mm_cmpgt_epi8(a, b); +} + +struct GroupSse2Impl { + static constexpr size_t kWidth = 16; // the number of slots per group + + explicit GroupSse2Impl(const ctrl_t* pos) { + ctrl = _mm_loadu_si128(reinterpret_cast(pos)); + } + + // Returns a bitmask representing the positions of slots that match hash. + BitMask Match(h2_t hash) const { + auto match = _mm_set1_epi8(hash); + return BitMask( + _mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))); + } + + // Returns a bitmask representing the positions of empty slots. + BitMask MatchEmpty() const { +#if SWISSTABLE_HAVE_SSSE3 + // This only works because kEmpty is -128. + return BitMask( + _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))); +#else + return Match(static_cast(kEmpty)); +#endif + } + + // Returns a bitmask representing the positions of empty or deleted slots. + BitMask MatchEmptyOrDeleted() const { + auto special = _mm_set1_epi8(kSentinel); + return BitMask( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))); + } + + // Returns the number of trailing empty or deleted elements in the group. + uint32_t CountLeadingEmptyOrDeleted() const { + auto special = _mm_set1_epi8(kSentinel); + return TrailingZeros( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1); + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + auto msbs = _mm_set1_epi8(static_cast(-128)); + auto x126 = _mm_set1_epi8(126); +#if SWISSTABLE_HAVE_SSSE3 + auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs); +#else + auto zero = _mm_setzero_si128(); + auto special_mask = _mm_cmpgt_epi8_fixed(zero, ctrl); + auto res = _mm_or_si128(msbs, _mm_andnot_si128(special_mask, x126)); +#endif + _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), res); + } + + __m128i ctrl; +}; +#endif // SWISSTABLE_HAVE_SSE2 + +struct GroupPortableImpl { + static constexpr size_t kWidth = 8; + + explicit GroupPortableImpl(const ctrl_t* pos) + : ctrl(little_endian::Load64(pos)) {} + + BitMask Match(h2_t hash) const { + // For the technique, see: + // http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord + // (Determine if a word has a byte equal to n). + // + // Caveat: there are false positives but: + // - they only occur if there is a real match + // - they never occur on kEmpty, kDeleted, kSentinel + // - they will be handled gracefully by subsequent checks in code + // + // Example: + // v = 0x1716151413121110 + // hash = 0x12 + // retval = (v - lsbs) & ~v & msbs = 0x0000000080800000 + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl ^ (lsbs * hash); + return BitMask((x - lsbs) & ~x & msbs); + } + + BitMask MatchEmpty() const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + return BitMask((ctrl & (~ctrl << 6)) & msbs); + } + + BitMask MatchEmptyOrDeleted() const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + return BitMask((ctrl & (~ctrl << 7)) & msbs); + } + + uint32_t CountLeadingEmptyOrDeleted() const { + constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL; + return (TrailingZeros(((~ctrl & (ctrl >> 7)) | gaps) + 1) + 7) >> 3; + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl & msbs; + auto res = (~x + (x >> 7)) & ~lsbs; + little_endian::Store64(dst, res); + } + + uint64_t ctrl; +}; + +#if SWISSTABLE_HAVE_SSE2 +using Group = GroupSse2Impl; +#else +using Group = GroupPortableImpl; +#endif + +template +class raw_hash_set; + +inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } + +// PRECONDITION: +// IsValidCapacity(capacity) +// ctrl[capacity] == kSentinel +// ctrl[i] != kSentinel for all i < capacity +// Applies mapping for every byte in ctrl: +// DELETED -> EMPTY +// EMPTY -> EMPTY +// FULL -> DELETED +inline void ConvertDeletedToEmptyAndFullToDeleted( + ctrl_t* ctrl, size_t capacity) { + assert(ctrl[capacity] == kSentinel); + assert(IsValidCapacity(capacity)); + for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) { + Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos); + } + // Copy the cloned ctrl bytes. + std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth); + ctrl[capacity] = kSentinel; +} + +// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1. +inline size_t NormalizeCapacity(size_t n) { + return n ? ~size_t{} >> LeadingZeros(n) : 1; +} + +// We use 7/8th as maximum load factor. +// For 16-wide groups, that gives an average of two empty slots per group. +inline size_t CapacityToGrowth(size_t capacity) { + assert(IsValidCapacity(capacity)); + // `capacity*7/8` + if (Group::kWidth == 8 && capacity == 7) { + // x-x/8 does not work when x==7. + return 6; + } + return capacity - capacity / 8; +} +// From desired "growth" to a lowerbound of the necessary capacity. +// Might not be a valid one and required NormalizeCapacity(). +inline size_t GrowthToLowerboundCapacity(size_t growth) { + // `growth*8/7` + if (Group::kWidth == 8 && growth == 7) { + // x+(x-1)/7 does not work when x==7. + return 8; + } + return growth + static_cast((static_cast(growth) - 1) / 7); +} + +// Policy: a policy defines how to perform different operations on +// the slots of the hashtable (see hash_policy_traits.h for the full interface +// of policy). +// +// Hash: a (possibly polymorphic) functor that hashes keys of the hashtable. The +// functor should accept a key and return size_t as hash. For best performance +// it is important that the hash function provides high entropy across all bits +// of the hash. +// +// Eq: a (possibly polymorphic) functor that compares two keys for equality. It +// should accept two (of possibly different type) keys and return a bool: true +// if they are equal, false if they are not. If two keys compare equal, then +// their hash values as defined by Hash MUST be equal. +// +// Allocator: an Allocator [https://devdocs.io/cpp/concept/allocator] with which +// the storage of the hashtable will be allocated and the elements will be +// constructed and destroyed. +template +class raw_hash_set { + using PolicyTraits = hash_policy_traits; + using KeyArgImpl = + KeyArg::value && IsTransparent::value>; + + public: + using init_type = typename PolicyTraits::init_type; + using key_type = typename PolicyTraits::key_type; + // TODO(sbenza): Hide slot_type as it is an implementation detail. Needs user + // code fixes! + using slot_type = typename PolicyTraits::slot_type; + using allocator_type = Alloc; + using size_type = size_t; + using difference_type = ptrdiff_t; + using hasher = Hash; + using key_equal = Eq; + using policy_type = Policy; + using value_type = typename PolicyTraits::value_type; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename absl::allocator_traits< + allocator_type>::template rebind_traits::pointer; + using const_pointer = typename absl::allocator_traits< + allocator_type>::template rebind_traits::const_pointer; + + // Alias used for heterogeneous lookup functions. + // `key_arg` evaluates to `K` when the functors are transparent and to + // `key_type` otherwise. It permits template argument deduction on `K` for the + // transparent case. + template + using key_arg = typename KeyArgImpl::template type; + + private: + // Give an early error when key_type is not hashable/eq. + auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k)); + auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k)); + + using Layout = absl::container_internal::Layout; + + static Layout MakeLayout(size_t capacity) { + assert(IsValidCapacity(capacity)); + return Layout(capacity + Group::kWidth + 1, capacity); + } + + using AllocTraits = absl::allocator_traits; + using SlotAlloc = typename absl::allocator_traits< + allocator_type>::template rebind_alloc; + using SlotAllocTraits = typename absl::allocator_traits< + allocator_type>::template rebind_traits; + + static_assert(std::is_lvalue_reference::value, + "Policy::element() must return a reference"); + + template + struct SameAsElementReference + : std::is_same::type>::type, + typename std::remove_cv< + typename std::remove_reference::type>::type> {}; + + // An enabler for insert(T&&): T must be convertible to init_type or be the + // same as [cv] value_type [ref]. + // Note: we separate SameAsElementReference into its own type to avoid using + // reference unless we need to. MSVC doesn't seem to like it in some + // cases. + template + using RequiresInsertable = typename std::enable_if< + absl::disjunction, + SameAsElementReference>::value, + int>::type; + + // RequiresNotInit is a workaround for gcc prior to 7.1. + // See https://godbolt.org/g/Y4xsUh. + template + using RequiresNotInit = + typename std::enable_if::value, int>::type; + + template + using IsDecomposable = IsDecomposable; + + public: + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + + class iterator { + friend class raw_hash_set; + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = typename raw_hash_set::value_type; + using reference = + absl::conditional_t; + using pointer = absl::remove_reference_t*; + using difference_type = typename raw_hash_set::difference_type; + + iterator() {} + + // PRECONDITION: not an end() iterator. + reference operator*() const { + assert_is_full(); + return PolicyTraits::element(slot_); + } + + // PRECONDITION: not an end() iterator. + pointer operator->() const { return &operator*(); } + + // PRECONDITION: not an end() iterator. + iterator& operator++() { + assert_is_full(); + ++ctrl_; + ++slot_; + skip_empty_or_deleted(); + return *this; + } + // PRECONDITION: not an end() iterator. + iterator operator++(int) { + auto tmp = *this; + ++*this; + return tmp; + } + + friend bool operator==(const iterator& a, const iterator& b) { + a.assert_is_valid(); + b.assert_is_valid(); + return a.ctrl_ == b.ctrl_; + } + friend bool operator!=(const iterator& a, const iterator& b) { + return !(a == b); + } + + private: + iterator(ctrl_t* ctrl) : ctrl_(ctrl) {} // for end() + iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {} + + void assert_is_full() const { assert(IsFull(*ctrl_)); } + void assert_is_valid() const { + assert(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel); + } + + void skip_empty_or_deleted() { + while (IsEmptyOrDeleted(*ctrl_)) { + // ctrl is not necessarily aligned to Group::kWidth. It is also likely + // to read past the space for ctrl bytes and into slots. This is ok + // because ctrl has sizeof() == 1 and slot has sizeof() >= 1 so there + // is no way to read outside the combined slot array. + uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted(); + ctrl_ += shift; + slot_ += shift; + } + } + + ctrl_t* ctrl_ = nullptr; + // To avoid uninitialized member warnings, put slot_ in an anonymous union. + // The member is not initialized on singleton and end iterators. + union { + slot_type* slot_; + }; + }; + + class const_iterator { + friend class raw_hash_set; + + public: + using iterator_category = typename iterator::iterator_category; + using value_type = typename raw_hash_set::value_type; + using reference = typename raw_hash_set::const_reference; + using pointer = typename raw_hash_set::const_pointer; + using difference_type = typename raw_hash_set::difference_type; + + const_iterator() {} + // Implicit construction from iterator. + const_iterator(iterator i) : inner_(std::move(i)) {} + + reference operator*() const { return *inner_; } + pointer operator->() const { return inner_.operator->(); } + + const_iterator& operator++() { + ++inner_; + return *this; + } + const_iterator operator++(int) { return inner_++; } + + friend bool operator==(const const_iterator& a, const const_iterator& b) { + return a.inner_ == b.inner_; + } + friend bool operator!=(const const_iterator& a, const const_iterator& b) { + return !(a == b); + } + + private: + const_iterator(const ctrl_t* ctrl, const slot_type* slot) + : inner_(const_cast(ctrl), const_cast(slot)) {} + + iterator inner_; + }; + + using node_type = node_handle, Alloc>; + using insert_return_type = InsertReturnType; + + raw_hash_set() noexcept( + std::is_nothrow_default_constructible::value&& + std::is_nothrow_default_constructible::value&& + std::is_nothrow_default_constructible::value) {} + + explicit raw_hash_set(size_t bucket_count, const hasher& hash = hasher(), + const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : ctrl_(EmptyGroup()), settings_(0, hash, eq, alloc) { + if (bucket_count) { + capacity_ = NormalizeCapacity(bucket_count); + reset_growth_left(); + initialize_slots(); + } + } + + raw_hash_set(size_t bucket_count, const hasher& hash, + const allocator_type& alloc) + : raw_hash_set(bucket_count, hash, key_equal(), alloc) {} + + raw_hash_set(size_t bucket_count, const allocator_type& alloc) + : raw_hash_set(bucket_count, hasher(), key_equal(), alloc) {} + + explicit raw_hash_set(const allocator_type& alloc) + : raw_hash_set(0, hasher(), key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count = 0, + const hasher& hash = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(bucket_count, hash, eq, alloc) { + insert(first, last); + } + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count, + const hasher& hash, const allocator_type& alloc) + : raw_hash_set(first, last, bucket_count, hash, key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(first, last, bucket_count, hasher(), key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, const allocator_type& alloc) + : raw_hash_set(first, last, 0, hasher(), key_equal(), alloc) {} + + // Instead of accepting std::initializer_list as the first + // argument like std::unordered_set does, we have two overloads + // that accept std::initializer_list and std::initializer_list. + // This is advantageous for performance. + // + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. + // std::unordered_set s = {"abc", "def"}; + // + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. + // absl::flat_hash_set s = {"abc", "def"}; + // + // The same trick is used in insert(). + // + // The enabler is necessary to prevent this constructor from triggering where + // the copy constructor is meant to be called. + // + // absl::flat_hash_set a, b{a}; + // + // RequiresNotInit is a workaround for gcc prior to 7.1. + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count = 0, + const hasher& hash = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(init.begin(), init.end(), bucket_count, hash, eq, alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count = 0, + const hasher& hash = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(init.begin(), init.end(), bucket_count, hash, eq, alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count, + const hasher& hash, const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count, + const hasher& hash, const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, const allocator_type& alloc) + : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, + const allocator_type& alloc) + : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + raw_hash_set(const raw_hash_set& that) + : raw_hash_set(that, AllocTraits::select_on_container_copy_construction( + that.alloc_ref())) {} + + raw_hash_set(const raw_hash_set& that, const allocator_type& a) + : raw_hash_set(0, that.hash_ref(), that.eq_ref(), a) { + reserve(that.size()); + // Because the table is guaranteed to be empty, we can do something faster + // than a full `insert`. + for (const auto& v : that) { + const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v); + auto target = find_first_non_full(hash); + set_ctrl(target.offset, H2(hash)); + emplace_at(target.offset, v); + infoz_.RecordInsert(hash, target.probe_length); + } + size_ = that.size(); + growth_left() -= that.size(); + } + + raw_hash_set(raw_hash_set&& that) noexcept( + std::is_nothrow_copy_constructible::value&& + std::is_nothrow_copy_constructible::value&& + std::is_nothrow_copy_constructible::value) + : ctrl_(absl::exchange(that.ctrl_, EmptyGroup())), + slots_(absl::exchange(that.slots_, nullptr)), + size_(absl::exchange(that.size_, 0)), + capacity_(absl::exchange(that.capacity_, 0)), + infoz_(absl::exchange(that.infoz_, HashtablezInfoHandle())), + // Hash, equality and allocator are copied instead of moved because + // `that` must be left valid. If Hash is std::function, moving it + // would create a nullptr functor that cannot be called. + settings_(that.settings_) { + // growth_left was copied above, reset the one from `that`. + that.growth_left() = 0; + } + + raw_hash_set(raw_hash_set&& that, const allocator_type& a) + : ctrl_(EmptyGroup()), + slots_(nullptr), + size_(0), + capacity_(0), + settings_(0, that.hash_ref(), that.eq_ref(), a) { + if (a == that.alloc_ref()) { + std::swap(ctrl_, that.ctrl_); + std::swap(slots_, that.slots_); + std::swap(size_, that.size_); + std::swap(capacity_, that.capacity_); + std::swap(growth_left(), that.growth_left()); + std::swap(infoz_, that.infoz_); + } else { + reserve(that.size()); + // Note: this will copy elements of dense_set and unordered_set instead of + // moving them. This can be fixed if it ever becomes an issue. + for (auto& elem : that) insert(std::move(elem)); + } + } + + raw_hash_set& operator=(const raw_hash_set& that) { + raw_hash_set tmp(that, + AllocTraits::propagate_on_container_copy_assignment::value + ? that.alloc_ref() + : alloc_ref()); + swap(tmp); + return *this; + } + + raw_hash_set& operator=(raw_hash_set&& that) noexcept( + absl::allocator_traits::is_always_equal::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_assignable::value) { + // TODO(sbenza): We should only use the operations from the noexcept clause + // to make sure we actually adhere to that contract. + return move_assign( + std::move(that), + typename AllocTraits::propagate_on_container_move_assignment()); + } + + ~raw_hash_set() { destroy_slots(); } + + iterator begin() { + auto it = iterator_at(0); + it.skip_empty_or_deleted(); + return it; + } + iterator end() { return {ctrl_ + capacity_}; } + + const_iterator begin() const { + return const_cast(this)->begin(); + } + const_iterator end() const { return const_cast(this)->end(); } + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + + bool empty() const { return !size(); } + size_t size() const { return size_; } + size_t capacity() const { return capacity_; } + size_t max_size() const { return (std::numeric_limits::max)(); } + + ABSL_ATTRIBUTE_REINITIALIZES void clear() { + // Iterating over this container is O(bucket_count()). When bucket_count() + // is much greater than size(), iteration becomes prohibitively expensive. + // For clear() it is more important to reuse the allocated array when the + // container is small because allocation takes comparatively long time + // compared to destruction of the elements of the container. So we pick the + // largest bucket_count() threshold for which iteration is still fast and + // past that we simply deallocate the array. + if (capacity_ > 127) { + destroy_slots(); + } else if (capacity_) { + for (size_t i = 0; i != capacity_; ++i) { + if (IsFull(ctrl_[i])) { + PolicyTraits::destroy(&alloc_ref(), slots_ + i); + } + } + size_ = 0; + reset_ctrl(); + reset_growth_left(); + } + assert(empty()); + infoz_.RecordStorageChanged(0, capacity_); + } + + // This overload kicks in when the argument is an rvalue of insertable and + // decomposable type other than init_type. + // + // flat_hash_map m; + // m.insert(std::make_pair("abc", 42)); + // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc + // bug. + template = 0, + class T2 = T, + typename std::enable_if::value, int>::type = 0, + T* = nullptr> + std::pair insert(T&& value) { + return emplace(std::forward(value)); + } + + // This overload kicks in when the argument is a bitfield or an lvalue of + // insertable and decomposable type. + // + // union { int n : 1; }; + // flat_hash_set s; + // s.insert(n); + // + // flat_hash_set s; + // const char* p = "hello"; + // s.insert(p); + // + // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace + // RequiresInsertable with RequiresInsertable. + // We are hitting this bug: https://godbolt.org/g/1Vht4f. + template < + class T, RequiresInsertable = 0, + typename std::enable_if::value, int>::type = 0> + std::pair insert(const T& value) { + return emplace(value); + } + + // This overload kicks in when the argument is an rvalue of init_type. Its + // purpose is to handle brace-init-list arguments. + // + // flat_hash_map s; + // s.insert({"abc", 42}); + std::pair insert(init_type&& value) { + return emplace(std::move(value)); + } + + // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc + // bug. + template = 0, class T2 = T, + typename std::enable_if::value, int>::type = 0, + T* = nullptr> + iterator insert(const_iterator, T&& value) { + return insert(std::forward(value)).first; + } + + // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace + // RequiresInsertable with RequiresInsertable. + // We are hitting this bug: https://godbolt.org/g/1Vht4f. + template < + class T, RequiresInsertable = 0, + typename std::enable_if::value, int>::type = 0> + iterator insert(const_iterator, const T& value) { + return insert(value).first; + } + + iterator insert(const_iterator, init_type&& value) { + return insert(std::move(value)).first; + } + + template + void insert(InputIt first, InputIt last) { + for (; first != last; ++first) insert(*first); + } + + template = 0, RequiresInsertable = 0> + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + insert_return_type insert(node_type&& node) { + if (!node) return {end(), false, node_type()}; + const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node)); + auto res = PolicyTraits::apply( + InsertSlot{*this, std::move(*CommonAccess::GetSlot(node))}, + elem); + if (res.second) { + CommonAccess::Reset(&node); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + + iterator insert(const_iterator, node_type&& node) { + return insert(std::move(node)).first; + } + + // This overload kicks in if we can deduce the key from args. This enables us + // to avoid constructing value_type if an entry with the same key already + // exists. + // + // For example: + // + // flat_hash_map m = {{"abc", "def"}}; + // // Creates no std::string copies and makes no heap allocations. + // m.emplace("abc", "xyz"); + template ::value, int>::type = 0> + std::pair emplace(Args&&... args) { + return PolicyTraits::apply(EmplaceDecomposable{*this}, + std::forward(args)...); + } + + // This overload kicks in if we cannot deduce the key from args. It constructs + // value_type unconditionally and then either moves it into the table or + // destroys. + template ::value, int>::type = 0> + std::pair emplace(Args&&... args) { + alignas(slot_type) unsigned char raw[sizeof(slot_type)]; + slot_type* slot = reinterpret_cast(&raw); + + PolicyTraits::construct(&alloc_ref(), slot, std::forward(args)...); + const auto& elem = PolicyTraits::element(slot); + return PolicyTraits::apply(InsertSlot{*this, std::move(*slot)}, elem); + } + + template + iterator emplace_hint(const_iterator, Args&&... args) { + return emplace(std::forward(args)...).first; + } + + // Extension API: support for lazy emplace. + // + // Looks up key in the table. If found, returns the iterator to the element. + // Otherwise calls `f` with one argument of type `raw_hash_set::constructor`. + // + // `f` must abide by several restrictions: + // - it MUST call `raw_hash_set::constructor` with arguments as if a + // `raw_hash_set::value_type` is constructed, + // - it MUST NOT access the container before the call to + // `raw_hash_set::constructor`, and + // - it MUST NOT erase the lazily emplaced element. + // Doing any of these is undefined behavior. + // + // For example: + // + // std::unordered_set s; + // // Makes ArenaStr even if "abc" is in the map. + // s.insert(ArenaString(&arena, "abc")); + // + // flat_hash_set s; + // // Makes ArenaStr only if "abc" is not in the map. + // s.lazy_emplace("abc", [&](const constructor& ctor) { + // ctor(&arena, "abc"); + // }); + // + // WARNING: This API is currently experimental. If there is a way to implement + // the same thing with the rest of the API, prefer that. + class constructor { + friend class raw_hash_set; + + public: + template + void operator()(Args&&... args) const { + assert(*slot_); + PolicyTraits::construct(alloc_, *slot_, std::forward(args)...); + *slot_ = nullptr; + } + + private: + constructor(allocator_type* a, slot_type** slot) : alloc_(a), slot_(slot) {} + + allocator_type* alloc_; + slot_type** slot_; + }; + + template + iterator lazy_emplace(const key_arg& key, F&& f) { + auto res = find_or_prepare_insert(key); + if (res.second) { + slot_type* slot = slots_ + res.first; + std::forward(f)(constructor(&alloc_ref(), &slot)); + assert(!slot); + } + return iterator_at(res.first); + } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.erase("abc"); + // + // flat_hash_set s; + // // Uses "abc" directly without copying it into std::string. + // s.erase("abc"); + template + size_type erase(const key_arg& key) { + auto it = find(key); + if (it == end()) return 0; + erase(it); + return 1; + } + + // Erases the element pointed to by `it`. Unlike `std::unordered_set::erase`, + // this method returns void to reduce algorithmic complexity to O(1). The + // iterator is invalidated, so any increment should be done before calling + // erase. In order to erase while iterating across a map, use the following + // idiom (which also works for standard containers): + // + // for (auto it = m.begin(), end = m.end(); it != end;) { + // // `erase()` will invalidate `it`, so advance `it` first. + // auto copy_it = it++; + // if () { + // m.erase(copy_it); + // } + // } + void erase(const_iterator cit) { erase(cit.inner_); } + + // This overload is necessary because otherwise erase(const K&) would be + // a better match if non-const iterator is passed as an argument. + void erase(iterator it) { + it.assert_is_full(); + PolicyTraits::destroy(&alloc_ref(), it.slot_); + erase_meta_only(it); + } + + iterator erase(const_iterator first, const_iterator last) { + while (first != last) { + erase(first++); + } + return last.inner_; + } + + // Moves elements from `src` into `this`. + // If the element already exists in `this`, it is left unmodified in `src`. + template + void merge(raw_hash_set& src) { // NOLINT + assert(this != &src); + for (auto it = src.begin(), e = src.end(); it != e;) { + auto next = std::next(it); + if (PolicyTraits::apply(InsertSlot{*this, std::move(*it.slot_)}, + PolicyTraits::element(it.slot_)) + .second) { + src.erase_meta_only(it); + } + it = next; + } + } + + template + void merge(raw_hash_set&& src) { + merge(src); + } + + node_type extract(const_iterator position) { + position.inner_.assert_is_full(); + auto node = + CommonAccess::Transfer(alloc_ref(), position.inner_.slot_); + erase_meta_only(position); + return node; + } + + template < + class K = key_type, + typename std::enable_if::value, int>::type = 0> + node_type extract(const key_arg& key) { + auto it = find(key); + return it == end() ? node_type() : extract(const_iterator{it}); + } + + void swap(raw_hash_set& that) noexcept( + IsNoThrowSwappable() && IsNoThrowSwappable() && + (!AllocTraits::propagate_on_container_swap::value || + IsNoThrowSwappable())) { + using std::swap; + swap(ctrl_, that.ctrl_); + swap(slots_, that.slots_); + swap(size_, that.size_); + swap(capacity_, that.capacity_); + swap(growth_left(), that.growth_left()); + swap(hash_ref(), that.hash_ref()); + swap(eq_ref(), that.eq_ref()); + swap(infoz_, that.infoz_); + if (AllocTraits::propagate_on_container_swap::value) { + swap(alloc_ref(), that.alloc_ref()); + } else { + // If the allocators do not compare equal it is officially undefined + // behavior. We choose to do nothing. + } + } + + void rehash(size_t n) { + if (n == 0 && capacity_ == 0) return; + if (n == 0 && size_ == 0) { + destroy_slots(); + infoz_.RecordStorageChanged(0, 0); + return; + } + // bitor is a faster way of doing `max` here. We will round up to the next + // power-of-2-minus-1, so bitor is good enough. + auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size())); + // n == 0 unconditionally rehashes as per the standard. + if (n == 0 || m > capacity_) { + resize(m); + } + } + + void reserve(size_t n) { rehash(GrowthToLowerboundCapacity(n)); } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.count("abc"); + // + // ch_set s; + // // Uses "abc" directly without copying it into std::string. + // s.count("abc"); + template + size_t count(const key_arg& key) const { + return find(key) == end() ? 0 : 1; + } + + // Issues CPU prefetch instructions for the memory needed to find or insert + // a key. Like all lookup functions, this support heterogeneous keys. + // + // NOTE: This is a very low level operation and should not be used without + // specific benchmarks indicating its importance. + template + void prefetch(const key_arg& key) const { + (void)key; +#if defined(__GNUC__) + auto seq = probe(hash_ref()(key)); + __builtin_prefetch(static_cast(ctrl_ + seq.offset())); + __builtin_prefetch(static_cast(slots_ + seq.offset())); +#endif // __GNUC__ + } + + // The API of find() has two extensions. + // + // 1. The hash can be passed by the user. It must be equal to the hash of the + // key. + // + // 2. The type of the key argument doesn't have to be key_type. This is so + // called heterogeneous key support. + template + iterator find(const key_arg& key, size_t hash) { + auto seq = probe(hash); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int i : g.Match(H2(hash))) { + if (ABSL_PREDICT_TRUE(PolicyTraits::apply( + EqualElement{key, eq_ref()}, + PolicyTraits::element(slots_ + seq.offset(i))))) + return iterator_at(seq.offset(i)); + } + if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return end(); + seq.next(); + } + } + template + iterator find(const key_arg& key) { + return find(key, hash_ref()(key)); + } + + template + const_iterator find(const key_arg& key, size_t hash) const { + return const_cast(this)->find(key, hash); + } + template + const_iterator find(const key_arg& key) const { + return find(key, hash_ref()(key)); + } + + template + bool contains(const key_arg& key) const { + return find(key) != end(); + } + + template + std::pair equal_range(const key_arg& key) { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + template + std::pair equal_range( + const key_arg& key) const { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + + size_t bucket_count() const { return capacity_; } + float load_factor() const { + return capacity_ ? static_cast(size()) / capacity_ : 0.0; + } + float max_load_factor() const { return 1.0f; } + void max_load_factor(float) { + // Does nothing. + } + + hasher hash_function() const { return hash_ref(); } + key_equal key_eq() const { return eq_ref(); } + allocator_type get_allocator() const { return alloc_ref(); } + + friend bool operator==(const raw_hash_set& a, const raw_hash_set& b) { + if (a.size() != b.size()) return false; + const raw_hash_set* outer = &a; + const raw_hash_set* inner = &b; + if (outer->capacity() > inner->capacity()) std::swap(outer, inner); + for (const value_type& elem : *outer) + if (!inner->has_element(elem)) return false; + return true; + } + + friend bool operator!=(const raw_hash_set& a, const raw_hash_set& b) { + return !(a == b); + } + + friend void swap(raw_hash_set& a, + raw_hash_set& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); + } + + private: + template + friend struct absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess; + + struct FindElement { + template + const_iterator operator()(const K& key, Args&&...) const { + return s.find(key); + } + const raw_hash_set& s; + }; + + struct HashElement { + template + size_t operator()(const K& key, Args&&...) const { + return h(key); + } + const hasher& h; + }; + + template + struct EqualElement { + template + bool operator()(const K2& lhs, Args&&...) const { + return eq(lhs, rhs); + } + const K1& rhs; + const key_equal& eq; + }; + + struct EmplaceDecomposable { + template + std::pair operator()(const K& key, Args&&... args) const { + auto res = s.find_or_prepare_insert(key); + if (res.second) { + s.emplace_at(res.first, std::forward(args)...); + } + return {s.iterator_at(res.first), res.second}; + } + raw_hash_set& s; + }; + + template + struct InsertSlot { + template + std::pair operator()(const K& key, Args&&...) && { + auto res = s.find_or_prepare_insert(key); + if (res.second) { + PolicyTraits::transfer(&s.alloc_ref(), s.slots_ + res.first, &slot); + } else if (do_destroy) { + PolicyTraits::destroy(&s.alloc_ref(), &slot); + } + return {s.iterator_at(res.first), res.second}; + } + raw_hash_set& s; + // Constructed slot. Either moved into place or destroyed. + slot_type&& slot; + }; + + // "erases" the object from the container, except that it doesn't actually + // destroy the object. It only updates all the metadata of the class. + // This can be used in conjunction with Policy::transfer to move the object to + // another place. + void erase_meta_only(const_iterator it) { + assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator"); + --size_; + const size_t index = it.inner_.ctrl_ - ctrl_; + const size_t index_before = (index - Group::kWidth) & capacity_; + const auto empty_after = Group(it.inner_.ctrl_).MatchEmpty(); + const auto empty_before = Group(ctrl_ + index_before).MatchEmpty(); + + // We count how many consecutive non empties we have to the right and to the + // left of `it`. If the sum is >= kWidth then there is at least one probe + // window that might have seen a full group. + bool was_never_full = + empty_before && empty_after && + static_cast(empty_after.TrailingZeros() + + empty_before.LeadingZeros()) < Group::kWidth; + + set_ctrl(index, was_never_full ? kEmpty : kDeleted); + growth_left() += was_never_full; + infoz_.RecordErase(); + } + + void initialize_slots() { + assert(capacity_); + // Folks with custom allocators often make unwarranted assumptions about the + // behavior of their classes vis-a-vis trivial destructability and what + // calls they will or wont make. Avoid sampling for people with custom + // allocators to get us out of this mess. This is not a hard guarantee but + // a workaround while we plan the exact guarantee we want to provide. + // + // People are often sloppy with the exact type of their allocator (sometimes + // it has an extra const or is missing the pair, but rebinds made it work + // anyway). To avoid the ambiguity, we work off SlotAlloc which we have + // bound more carefully. + if (std::is_same>::value && + slots_ == nullptr) { + infoz_ = Sample(); + } + + auto layout = MakeLayout(capacity_); + char* mem = static_cast( + Allocate(&alloc_ref(), layout.AllocSize())); + ctrl_ = reinterpret_cast(layout.template Pointer<0>(mem)); + slots_ = layout.template Pointer<1>(mem); + reset_ctrl(); + reset_growth_left(); + infoz_.RecordStorageChanged(size_, capacity_); + } + + void destroy_slots() { + if (!capacity_) return; + for (size_t i = 0; i != capacity_; ++i) { + if (IsFull(ctrl_[i])) { + PolicyTraits::destroy(&alloc_ref(), slots_ + i); + } + } + auto layout = MakeLayout(capacity_); + // Unpoison before returning the memory to the allocator. + SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); + Deallocate(&alloc_ref(), ctrl_, layout.AllocSize()); + ctrl_ = EmptyGroup(); + slots_ = nullptr; + size_ = 0; + capacity_ = 0; + growth_left() = 0; + } + + void resize(size_t new_capacity) { + assert(IsValidCapacity(new_capacity)); + auto* old_ctrl = ctrl_; + auto* old_slots = slots_; + const size_t old_capacity = capacity_; + capacity_ = new_capacity; + initialize_slots(); + + size_t total_probe_length = 0; + for (size_t i = 0; i != old_capacity; ++i) { + if (IsFull(old_ctrl[i])) { + size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, + PolicyTraits::element(old_slots + i)); + auto target = find_first_non_full(hash); + size_t new_i = target.offset; + total_probe_length += target.probe_length; + set_ctrl(new_i, H2(hash)); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i); + } + } + if (old_capacity) { + SanitizerUnpoisonMemoryRegion(old_slots, + sizeof(slot_type) * old_capacity); + auto layout = MakeLayout(old_capacity); + Deallocate(&alloc_ref(), old_ctrl, + layout.AllocSize()); + } + infoz_.RecordRehash(total_probe_length); + } + + void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE { + assert(IsValidCapacity(capacity_)); + assert(!is_small()); + // Algorithm: + // - mark all DELETED slots as EMPTY + // - mark all FULL slots as DELETED + // - for each slot marked as DELETED + // hash = Hash(element) + // target = find_first_non_full(hash) + // if target is in the same group + // mark slot as FULL + // else if target is EMPTY + // transfer element to target + // mark slot as EMPTY + // mark target as FULL + // else if target is DELETED + // swap current element with target element + // mark target as FULL + // repeat procedure for current slot with moved from element (target) + ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_); + alignas(slot_type) unsigned char raw[sizeof(slot_type)]; + size_t total_probe_length = 0; + slot_type* slot = reinterpret_cast(&raw); + for (size_t i = 0; i != capacity_; ++i) { + if (!IsDeleted(ctrl_[i])) continue; + size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, + PolicyTraits::element(slots_ + i)); + auto target = find_first_non_full(hash); + size_t new_i = target.offset; + total_probe_length += target.probe_length; + + // Verify if the old and new i fall within the same group wrt the hash. + // If they do, we don't need to move the object as it falls already in the + // best probe we can. + const auto probe_index = [&](size_t pos) { + return ((pos - probe(hash).offset()) & capacity_) / Group::kWidth; + }; + + // Element doesn't move. + if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) { + set_ctrl(i, H2(hash)); + continue; + } + if (IsEmpty(ctrl_[new_i])) { + // Transfer element to the empty spot. + // set_ctrl poisons/unpoisons the slots so we have to call it at the + // right time. + set_ctrl(new_i, H2(hash)); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i); + set_ctrl(i, kEmpty); + } else { + assert(IsDeleted(ctrl_[new_i])); + set_ctrl(new_i, H2(hash)); + // Until we are done rehashing, DELETED marks previously FULL slots. + // Swap i and new_i elements. + PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i); + PolicyTraits::transfer(&alloc_ref(), slots_ + i, slots_ + new_i); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slot); + --i; // repeat + } + } + reset_growth_left(); + infoz_.RecordRehash(total_probe_length); + } + + void rehash_and_grow_if_necessary() { + if (capacity_ == 0) { + resize(1); + } else if (size() <= CapacityToGrowth(capacity()) / 2) { + // Squash DELETED without growing if there is enough capacity. + drop_deletes_without_resize(); + } else { + // Otherwise grow the container. + resize(capacity_ * 2 + 1); + } + } + + bool has_element(const value_type& elem) const { + size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem); + auto seq = probe(hash); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int i : g.Match(H2(hash))) { + if (ABSL_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset(i)) == + elem)) + return true; + } + if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return false; + seq.next(); + assert(seq.index() < capacity_ && "full table!"); + } + return false; + } + + // Probes the raw_hash_set with the probe sequence for hash and returns the + // pointer to the first empty or deleted slot. + // NOTE: this function must work with tables having both kEmpty and kDelete + // in one group. Such tables appears during drop_deletes_without_resize. + // + // This function is very useful when insertions happen and: + // - the input is already a set + // - there are enough slots + // - the element with the hash is not in the table + struct FindInfo { + size_t offset; + size_t probe_length; + }; + FindInfo find_first_non_full(size_t hash) { + auto seq = probe(hash); + while (true) { + Group g{ctrl_ + seq.offset()}; + auto mask = g.MatchEmptyOrDeleted(); + if (mask) { +#if !defined(NDEBUG) + // We want to add entropy even when ASLR is not enabled. + // In debug build we will randomly insert in either the front or back of + // the group. + // TODO(kfm,sbenza): revisit after we do unconditional mixing + if (!is_small() && ShouldInsertBackwards(hash, ctrl_)) { + return {seq.offset(mask.HighestBitSet()), seq.index()}; + } +#endif + return {seq.offset(mask.LowestBitSet()), seq.index()}; + } + assert(seq.index() < capacity_ && "full table!"); + seq.next(); + } + } + + // TODO(alkis): Optimize this assuming *this and that don't overlap. + raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) { + raw_hash_set tmp(std::move(that)); + swap(tmp); + return *this; + } + raw_hash_set& move_assign(raw_hash_set&& that, std::false_type) { + raw_hash_set tmp(std::move(that), alloc_ref()); + swap(tmp); + return *this; + } + + protected: + template + std::pair find_or_prepare_insert(const K& key) { + auto hash = hash_ref()(key); + auto seq = probe(hash); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int i : g.Match(H2(hash))) { + if (ABSL_PREDICT_TRUE(PolicyTraits::apply( + EqualElement{key, eq_ref()}, + PolicyTraits::element(slots_ + seq.offset(i))))) + return {seq.offset(i), false}; + } + if (ABSL_PREDICT_TRUE(g.MatchEmpty())) break; + seq.next(); + } + return {prepare_insert(hash), true}; + } + + size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE { + auto target = find_first_non_full(hash); + if (ABSL_PREDICT_FALSE(growth_left() == 0 && + !IsDeleted(ctrl_[target.offset]))) { + rehash_and_grow_if_necessary(); + target = find_first_non_full(hash); + } + ++size_; + growth_left() -= IsEmpty(ctrl_[target.offset]); + set_ctrl(target.offset, H2(hash)); + infoz_.RecordInsert(hash, target.probe_length); + return target.offset; + } + + // Constructs the value in the space pointed by the iterator. This only works + // after an unsuccessful find_or_prepare_insert() and before any other + // modifications happen in the raw_hash_set. + // + // PRECONDITION: i is an index returned from find_or_prepare_insert(k), where + // k is the key decomposed from `forward(args)...`, and the bool + // returned by find_or_prepare_insert(k) was true. + // POSTCONDITION: *m.iterator_at(i) == value_type(forward(args)...). + template + void emplace_at(size_t i, Args&&... args) { + PolicyTraits::construct(&alloc_ref(), slots_ + i, + std::forward(args)...); + + assert(PolicyTraits::apply(FindElement{*this}, *iterator_at(i)) == + iterator_at(i) && + "constructed value does not match the lookup key"); + } + + iterator iterator_at(size_t i) { return {ctrl_ + i, slots_ + i}; } + const_iterator iterator_at(size_t i) const { return {ctrl_ + i, slots_ + i}; } + + private: + friend struct RawHashSetTestOnlyAccess; + + probe_seq probe(size_t hash) const { + return probe_seq(H1(hash, ctrl_), capacity_); + } + + // Reset all ctrl bytes back to kEmpty, except the sentinel. + void reset_ctrl() { + std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth); + ctrl_[capacity_] = kSentinel; + SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); + } + + void reset_growth_left() { + growth_left() = CapacityToGrowth(capacity()) - size_; + } + + // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at + // the end too. + void set_ctrl(size_t i, ctrl_t h) { + assert(i < capacity_); + + if (IsFull(h)) { + SanitizerUnpoisonObject(slots_ + i); + } else { + SanitizerPoisonObject(slots_ + i); + } + + ctrl_[i] = h; + ctrl_[((i - Group::kWidth) & capacity_) + 1 + + ((Group::kWidth - 1) & capacity_)] = h; + } + + size_t& growth_left() { return settings_.template get<0>(); } + + // The representation of the object has two modes: + // - small: For capacities < kWidth-1 + // - large: For the rest. + // + // Differences: + // - In small mode we are able to use the whole capacity. The extra control + // bytes give us at least one "empty" control byte to stop the iteration. + // This is important to make 1 a valid capacity. + // + // - In small mode only the first `capacity()` control bytes after the + // sentinel are valid. The rest contain dummy kEmpty values that do not + // represent a real slot. This is important to take into account on + // find_first_non_full(), where we never try ShouldInsertBackwards() for + // small tables. + bool is_small() const { return capacity_ < Group::kWidth - 1; } + + hasher& hash_ref() { return settings_.template get<1>(); } + const hasher& hash_ref() const { return settings_.template get<1>(); } + key_equal& eq_ref() { return settings_.template get<2>(); } + const key_equal& eq_ref() const { return settings_.template get<2>(); } + allocator_type& alloc_ref() { return settings_.template get<3>(); } + const allocator_type& alloc_ref() const { + return settings_.template get<3>(); + } + + // TODO(alkis): Investigate removing some of these fields: + // - ctrl/slots can be derived from each other + // - size can be moved into the slot array + ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t] + slot_type* slots_ = nullptr; // [capacity * slot_type] + size_t size_ = 0; // number of full slots + size_t capacity_ = 0; // total number of slots + HashtablezInfoHandle infoz_; + absl::container_internal::CompressedTuple + settings_{0, hasher{}, key_equal{}, allocator_type{}}; +}; + +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template +void EraseIf(Predicate pred, raw_hash_set* c) { + for (auto it = c->begin(), last = c->end(); it != last;) { + auto copy_it = it++; + if (pred(*copy_it)) { + c->erase(copy_it); + } + } +} + +namespace hashtable_debug_internal { +template +struct HashtableDebugAccess> { + using Traits = typename Set::PolicyTraits; + using Slot = typename Traits::slot_type; + + static size_t GetNumProbes(const Set& set, + const typename Set::key_type& key) { + size_t num_probes = 0; + size_t hash = set.hash_ref()(key); + auto seq = set.probe(hash); + while (true) { + container_internal::Group g{set.ctrl_ + seq.offset()}; + for (int i : g.Match(container_internal::H2(hash))) { + if (Traits::apply( + typename Set::template EqualElement{ + key, set.eq_ref()}, + Traits::element(set.slots_ + seq.offset(i)))) + return num_probes; + ++num_probes; + } + if (g.MatchEmpty()) return num_probes; + seq.next(); + ++num_probes; + } + } + + static size_t AllocatedByteSize(const Set& c) { + size_t capacity = c.capacity_; + if (capacity == 0) return 0; + auto layout = Set::MakeLayout(capacity); + size_t m = layout.AllocSize(); + + size_t per_slot = Traits::space_used(static_cast(nullptr)); + if (per_slot != ~size_t{}) { + m += per_slot * c.size(); + } else { + for (size_t i = 0; i != capacity; ++i) { + if (container_internal::IsFull(c.ctrl_[i])) { + m += Traits::space_used(c.slots_ + i); + } + } + } + return m; + } + + static size_t LowerBoundAllocatedByteSize(size_t size) { + size_t capacity = GrowthToLowerboundCapacity(size); + if (capacity == 0) return 0; + auto layout = Set::MakeLayout(NormalizeCapacity(capacity)); + size_t m = layout.AllocSize(); + size_t per_slot = Traits::space_used(static_cast(nullptr)); + if (per_slot != ~size_t{}) { + m += per_slot * size; + } + return m; + } +}; + +} // namespace hashtable_debug_internal +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/test_instance_tracker.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/test_instance_tracker.h new file mode 100644 index 0000000000000000000000000000000000000000..5ff6fd714e2b347f375b0ee64f28cf3f287e86a6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/test_instance_tracker.h @@ -0,0 +1,274 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_ +#define ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_ + +#include +#include + +#include "absl/types/compare.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace test_internal { + +// A type that counts number of occurrences of the type, the live occurrences of +// the type, as well as the number of copies, moves, swaps, and comparisons that +// have occurred on the type. This is used as a base class for the copyable, +// copyable+movable, and movable types below that are used in actual tests. Use +// InstanceTracker in tests to track the number of instances. +class BaseCountedInstance { + public: + explicit BaseCountedInstance(int x) : value_(x) { + ++num_instances_; + ++num_live_instances_; + } + BaseCountedInstance(const BaseCountedInstance& x) + : value_(x.value_), is_live_(x.is_live_) { + ++num_instances_; + if (is_live_) ++num_live_instances_; + ++num_copies_; + } + BaseCountedInstance(BaseCountedInstance&& x) + : value_(x.value_), is_live_(x.is_live_) { + x.is_live_ = false; + ++num_instances_; + ++num_moves_; + } + ~BaseCountedInstance() { + --num_instances_; + if (is_live_) --num_live_instances_; + } + + BaseCountedInstance& operator=(const BaseCountedInstance& x) { + value_ = x.value_; + if (is_live_) --num_live_instances_; + is_live_ = x.is_live_; + if (is_live_) ++num_live_instances_; + ++num_copies_; + return *this; + } + BaseCountedInstance& operator=(BaseCountedInstance&& x) { + value_ = x.value_; + if (is_live_) --num_live_instances_; + is_live_ = x.is_live_; + x.is_live_ = false; + ++num_moves_; + return *this; + } + + bool operator==(const BaseCountedInstance& x) const { + ++num_comparisons_; + return value_ == x.value_; + } + + bool operator!=(const BaseCountedInstance& x) const { + ++num_comparisons_; + return value_ != x.value_; + } + + bool operator<(const BaseCountedInstance& x) const { + ++num_comparisons_; + return value_ < x.value_; + } + + bool operator>(const BaseCountedInstance& x) const { + ++num_comparisons_; + return value_ > x.value_; + } + + bool operator<=(const BaseCountedInstance& x) const { + ++num_comparisons_; + return value_ <= x.value_; + } + + bool operator>=(const BaseCountedInstance& x) const { + ++num_comparisons_; + return value_ >= x.value_; + } + + absl::weak_ordering compare(const BaseCountedInstance& x) const { + ++num_comparisons_; + return value_ < x.value_ + ? absl::weak_ordering::less + : value_ == x.value_ ? absl::weak_ordering::equivalent + : absl::weak_ordering::greater; + } + + int value() const { + if (!is_live_) std::abort(); + return value_; + } + + friend std::ostream& operator<<(std::ostream& o, + const BaseCountedInstance& v) { + return o << "[value:" << v.value() << "]"; + } + + // Implementation of efficient swap() that counts swaps. + static void SwapImpl( + BaseCountedInstance& lhs, // NOLINT(runtime/references) + BaseCountedInstance& rhs) { // NOLINT(runtime/references) + using std::swap; + swap(lhs.value_, rhs.value_); + swap(lhs.is_live_, rhs.is_live_); + ++BaseCountedInstance::num_swaps_; + } + + private: + friend class InstanceTracker; + + int value_; + + // Indicates if the value is live, ie it hasn't been moved away from. + bool is_live_ = true; + + // Number of instances. + static int num_instances_; + + // Number of live instances (those that have not been moved away from.) + static int num_live_instances_; + + // Number of times that BaseCountedInstance objects were moved. + static int num_moves_; + + // Number of times that BaseCountedInstance objects were copied. + static int num_copies_; + + // Number of times that BaseCountedInstance objects were swapped. + static int num_swaps_; + + // Number of times that BaseCountedInstance objects were compared. + static int num_comparisons_; +}; + +// Helper to track the BaseCountedInstance instance counters. Expects that the +// number of instances and live_instances are the same when it is constructed +// and when it is destructed. +class InstanceTracker { + public: + InstanceTracker() + : start_instances_(BaseCountedInstance::num_instances_), + start_live_instances_(BaseCountedInstance::num_live_instances_) { + ResetCopiesMovesSwaps(); + } + ~InstanceTracker() { + if (instances() != 0) std::abort(); + if (live_instances() != 0) std::abort(); + } + + // Returns the number of BaseCountedInstance instances both containing valid + // values and those moved away from compared to when the InstanceTracker was + // constructed + int instances() const { + return BaseCountedInstance::num_instances_ - start_instances_; + } + + // Returns the number of live BaseCountedInstance instances compared to when + // the InstanceTracker was constructed + int live_instances() const { + return BaseCountedInstance::num_live_instances_ - start_live_instances_; + } + + // Returns the number of moves on BaseCountedInstance objects since + // construction or since the last call to ResetCopiesMovesSwaps(). + int moves() const { return BaseCountedInstance::num_moves_ - start_moves_; } + + // Returns the number of copies on BaseCountedInstance objects since + // construction or the last call to ResetCopiesMovesSwaps(). + int copies() const { + return BaseCountedInstance::num_copies_ - start_copies_; + } + + // Returns the number of swaps on BaseCountedInstance objects since + // construction or the last call to ResetCopiesMovesSwaps(). + int swaps() const { return BaseCountedInstance::num_swaps_ - start_swaps_; } + + // Returns the number of comparisons on BaseCountedInstance objects since + // construction or the last call to ResetCopiesMovesSwaps(). + int comparisons() const { + return BaseCountedInstance::num_comparisons_ - start_comparisons_; + } + + // Resets the base values for moves, copies, comparisons, and swaps to the + // current values, so that subsequent Get*() calls for moves, copies, + // comparisons, and swaps will compare to the situation at the point of this + // call. + void ResetCopiesMovesSwaps() { + start_moves_ = BaseCountedInstance::num_moves_; + start_copies_ = BaseCountedInstance::num_copies_; + start_swaps_ = BaseCountedInstance::num_swaps_; + start_comparisons_ = BaseCountedInstance::num_comparisons_; + } + + private: + int start_instances_; + int start_live_instances_; + int start_moves_; + int start_copies_; + int start_swaps_; + int start_comparisons_; +}; + +// Copyable, not movable. +class CopyableOnlyInstance : public BaseCountedInstance { + public: + explicit CopyableOnlyInstance(int x) : BaseCountedInstance(x) {} + CopyableOnlyInstance(const CopyableOnlyInstance& rhs) = default; + CopyableOnlyInstance& operator=(const CopyableOnlyInstance& rhs) = default; + + friend void swap(CopyableOnlyInstance& lhs, CopyableOnlyInstance& rhs) { + BaseCountedInstance::SwapImpl(lhs, rhs); + } + + static bool supports_move() { return false; } +}; + +// Copyable and movable. +class CopyableMovableInstance : public BaseCountedInstance { + public: + explicit CopyableMovableInstance(int x) : BaseCountedInstance(x) {} + CopyableMovableInstance(const CopyableMovableInstance& rhs) = default; + CopyableMovableInstance(CopyableMovableInstance&& rhs) = default; + CopyableMovableInstance& operator=(const CopyableMovableInstance& rhs) = + default; + CopyableMovableInstance& operator=(CopyableMovableInstance&& rhs) = default; + + friend void swap(CopyableMovableInstance& lhs, CopyableMovableInstance& rhs) { + BaseCountedInstance::SwapImpl(lhs, rhs); + } + + static bool supports_move() { return true; } +}; + +// Only movable, not default-constructible. +class MovableOnlyInstance : public BaseCountedInstance { + public: + explicit MovableOnlyInstance(int x) : BaseCountedInstance(x) {} + MovableOnlyInstance(MovableOnlyInstance&& other) = default; + MovableOnlyInstance& operator=(MovableOnlyInstance&& other) = default; + + friend void swap(MovableOnlyInstance& lhs, MovableOnlyInstance& rhs) { + BaseCountedInstance::SwapImpl(lhs, rhs); + } + + static bool supports_move() { return true; } +}; + +} // namespace test_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/tracked.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/tracked.h new file mode 100644 index 0000000000000000000000000000000000000000..29f5829f71999a66dce926f255c01ad4c52ba2e1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/tracked.h @@ -0,0 +1,83 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_TRACKED_H_ +#define ABSL_CONTAINER_INTERNAL_TRACKED_H_ + +#include + +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +// A class that tracks its copies and moves so that it can be queried in tests. +template +class Tracked { + public: + Tracked() {} + // NOLINTNEXTLINE(runtime/explicit) + Tracked(const T& val) : val_(val) {} + Tracked(const Tracked& that) + : val_(that.val_), + num_moves_(that.num_moves_), + num_copies_(that.num_copies_) { + ++(*num_copies_); + } + Tracked(Tracked&& that) + : val_(std::move(that.val_)), + num_moves_(std::move(that.num_moves_)), + num_copies_(std::move(that.num_copies_)) { + ++(*num_moves_); + } + Tracked& operator=(const Tracked& that) { + val_ = that.val_; + num_moves_ = that.num_moves_; + num_copies_ = that.num_copies_; + ++(*num_copies_); + } + Tracked& operator=(Tracked&& that) { + val_ = std::move(that.val_); + num_moves_ = std::move(that.num_moves_); + num_copies_ = std::move(that.num_copies_); + ++(*num_moves_); + } + + const T& val() const { return val_; } + + friend bool operator==(const Tracked& a, const Tracked& b) { + return a.val_ == b.val_; + } + friend bool operator!=(const Tracked& a, const Tracked& b) { + return !(a == b); + } + + size_t num_copies() { return *num_copies_; } + size_t num_moves() { return *num_moves_; } + + private: + T val_; + std::shared_ptr num_moves_ = std::make_shared(0); + std::shared_ptr num_copies_ = std::make_shared(0); +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_TRACKED_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_constructor_test.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_constructor_test.h new file mode 100644 index 0000000000000000000000000000000000000000..76ee95e6abc574fa3e9845d47b3635e5679802af --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_constructor_test.h @@ -0,0 +1,489 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_ + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/internal/hash_generator_testing.h" +#include "absl/container/internal/hash_policy_testing.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class ConstructorTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(ConstructorTest); + +TYPED_TEST_P(ConstructorTest, NoArgs) { + TypeParam m; + EXPECT_TRUE(m.empty()); + EXPECT_THAT(m, ::testing::UnorderedElementsAre()); +} + +TYPED_TEST_P(ConstructorTest, BucketCount) { + TypeParam m(123); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(m, ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, BucketCountHash) { + using H = typename TypeParam::hasher; + H hasher; + TypeParam m(123, hasher); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(m, ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, BucketCountHashEqual) { + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + H hasher; + E equal; + TypeParam m(123, hasher, equal); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.key_eq(), equal); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(m, ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) { + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(123, hasher, equal, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.key_eq(), equal); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(m, ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +template +struct is_std_unordered_map : std::false_type {}; + +template +struct is_std_unordered_map> : std::true_type {}; + +#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +using has_cxx14_std_apis = std::true_type; +#else +using has_cxx14_std_apis = std::false_type; +#endif + +template +using expect_cxx14_apis = + absl::disjunction>, + has_cxx14_std_apis>; + +template +void BucketCountAllocTest(std::false_type) {} + +template +void BucketCountAllocTest(std::true_type) { + using A = typename TypeParam::allocator_type; + A alloc(0); + TypeParam m(123, alloc); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(m, ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { + BucketCountAllocTest(expect_cxx14_apis()); +} + +template +void BucketCountHashAllocTest(std::false_type) {} + +template +void BucketCountHashAllocTest(std::true_type) { + using H = typename TypeParam::hasher; + using A = typename TypeParam::allocator_type; + H hasher; + A alloc(0); + TypeParam m(123, hasher, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(m, ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { + BucketCountHashAllocTest(expect_cxx14_apis()); +} + +#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +using has_alloc_std_constructors = std::true_type; +#else +using has_alloc_std_constructors = std::false_type; +#endif + +template +using expect_alloc_constructors = + absl::disjunction>, + has_alloc_std_constructors>; + +template +void AllocTest(std::false_type) {} + +template +void AllocTest(std::true_type) { + using A = typename TypeParam::allocator_type; + A alloc(0); + TypeParam m(alloc); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(m, ::testing::UnorderedElementsAre()); +} + +TYPED_TEST_P(ConstructorTest, Alloc) { + AllocTest(expect_alloc_constructors()); +} + +TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.key_eq(), equal); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +template +void InputIteratorBucketAllocTest(std::false_type) {} + +template +void InputIteratorBucketAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using A = typename TypeParam::allocator_type; + A alloc(0); + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end(), 123, alloc); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { + InputIteratorBucketAllocTest(expect_cxx14_apis()); +} + +template +void InputIteratorBucketHashAllocTest(std::false_type) {} + +template +void InputIteratorBucketHashAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using A = typename TypeParam::allocator_type; + H hasher; + A alloc(0); + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end(), 123, hasher, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { + InputIteratorBucketHashAllocTest(expect_cxx14_apis()); +} + +TYPED_TEST_P(ConstructorTest, CopyConstructor) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(123, hasher, equal, alloc); + for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + TypeParam n(m); + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_EQ(m.get_allocator(), n.get_allocator()); + EXPECT_EQ(m, n); +} + +template +void CopyConstructorAllocTest(std::false_type) {} + +template +void CopyConstructorAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(123, hasher, equal, alloc); + for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + TypeParam n(m, A(11)); + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_NE(m.get_allocator(), n.get_allocator()); + EXPECT_EQ(m, n); +} + +TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { + CopyConstructorAllocTest(expect_alloc_constructors()); +} + +// TODO(alkis): Test non-propagating allocators on copy constructors. + +TYPED_TEST_P(ConstructorTest, MoveConstructor) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(123, hasher, equal, alloc); + for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + TypeParam t(m); + TypeParam n(std::move(t)); + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_EQ(m.get_allocator(), n.get_allocator()); + EXPECT_EQ(m, n); +} + +template +void MoveConstructorAllocTest(std::false_type) {} + +template +void MoveConstructorAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(123, hasher, equal, alloc); + for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + TypeParam t(m); + TypeParam n(std::move(t), A(1)); + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_NE(m.get_allocator(), n.get_allocator()); + EXPECT_EQ(m, n); +} + +TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { + MoveConstructorAllocTest(expect_alloc_constructors()); +} + +// TODO(alkis): Test non-propagating allocators on move constructors. + +TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(values, 123, hasher, equal, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.key_eq(), equal); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +template +void InitializerListBucketAllocTest(std::false_type) {} + +template +void InitializerListBucketAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using A = typename TypeParam::allocator_type; + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + A alloc(0); + TypeParam m(values, 123, alloc); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { + InitializerListBucketAllocTest(expect_cxx14_apis()); +} + +template +void InitializerListBucketHashAllocTest(std::false_type) {} + +template +void InitializerListBucketHashAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using A = typename TypeParam::allocator_type; + H hasher; + A alloc(0); + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + TypeParam m(values, 123, hasher, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { + InitializerListBucketHashAllocTest(expect_cxx14_apis()); +} + +TYPED_TEST_P(ConstructorTest, Assignment) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + hash_internal::Generator gen; + TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); + TypeParam n; + n = m; + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_EQ(m, n); +} + +// TODO(alkis): Test [non-]propagating allocators on move/copy assignments +// (it depends on traits). + +TYPED_TEST_P(ConstructorTest, MoveAssignment) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + hash_internal::Generator gen; + TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); + TypeParam t(m); + TypeParam n; + n = std::move(t); + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_EQ(m, n); +} + +TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + TypeParam m; + m = values; + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); +} + +TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + TypeParam m({gen(), gen(), gen()}); + TypeParam n({gen()}); + n = m; + EXPECT_EQ(m, n); +} + +TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + TypeParam m({gen(), gen(), gen()}); + TypeParam t(m); + TypeParam n({gen()}); + n = std::move(t); + EXPECT_EQ(m, n); +} + +TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + TypeParam m; + m = values; + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); +} + +TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + TypeParam m(values); + m = *&m; // Avoid -Wself-assign + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); +} + +// We cannot test self move as standard states that it leaves standard +// containers in unspecified state (and in practice in causes memory-leak +// according to heap-checker!). + +REGISTER_TYPED_TEST_CASE_P( + ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual, + BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc, + InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, + InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc, + MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc, + InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment, + MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting, + MoveAssignmentOverwritesExisting, + AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_lookup_test.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_lookup_test.h new file mode 100644 index 0000000000000000000000000000000000000000..e76421e508fe96149cf27816ddff3b74afd69302 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_lookup_test.h @@ -0,0 +1,117 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_ + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/internal/hash_generator_testing.h" +#include "absl/container/internal/hash_policy_testing.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class LookupTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(LookupTest); + +TYPED_TEST_P(LookupTest, At) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end()); + for (const auto& p : values) { + const auto& val = m.at(p.first); + EXPECT_EQ(p.second, val) << ::testing::PrintToString(p.first); + } +} + +TYPED_TEST_P(LookupTest, OperatorBracket) { + using T = hash_internal::GeneratedType; + using V = typename TypeParam::mapped_type; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m; + for (const auto& p : values) { + auto& val = m[p.first]; + EXPECT_EQ(V(), val) << ::testing::PrintToString(p.first); + val = p.second; + } + for (const auto& p : values) + EXPECT_EQ(p.second, m[p.first]) << ::testing::PrintToString(p.first); +} + +TYPED_TEST_P(LookupTest, Count) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m; + for (const auto& p : values) + EXPECT_EQ(0, m.count(p.first)) << ::testing::PrintToString(p.first); + m.insert(values.begin(), values.end()); + for (const auto& p : values) + EXPECT_EQ(1, m.count(p.first)) << ::testing::PrintToString(p.first); +} + +TYPED_TEST_P(LookupTest, Find) { + using std::get; + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m; + for (const auto& p : values) + EXPECT_TRUE(m.end() == m.find(p.first)) + << ::testing::PrintToString(p.first); + m.insert(values.begin(), values.end()); + for (const auto& p : values) { + auto it = m.find(p.first); + EXPECT_TRUE(m.end() != it) << ::testing::PrintToString(p.first); + EXPECT_EQ(p.second, get<1>(*it)) << ::testing::PrintToString(p.first); + } +} + +TYPED_TEST_P(LookupTest, EqualRange) { + using std::get; + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m; + for (const auto& p : values) { + auto r = m.equal_range(p.first); + ASSERT_EQ(0, std::distance(r.first, r.second)); + } + m.insert(values.begin(), values.end()); + for (const auto& p : values) { + auto r = m.equal_range(p.first); + ASSERT_EQ(1, std::distance(r.first, r.second)); + EXPECT_EQ(p.second, get<1>(*r.first)) << ::testing::PrintToString(p.first); + } +} + +REGISTER_TYPED_TEST_CASE_P(LookupTest, At, OperatorBracket, Count, Find, + EqualRange); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_members_test.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_members_test.h new file mode 100644 index 0000000000000000000000000000000000000000..7d48cdb890bba6f853a01501d1e1368631e400c5 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_members_test.h @@ -0,0 +1,87 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ + +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class MembersTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(MembersTest); + +template +void UseType() {} + +TYPED_TEST_P(MembersTest, Typedefs) { + EXPECT_TRUE((std::is_same, + typename TypeParam::value_type>())); + EXPECT_TRUE((absl::conjunction< + absl::negation>, + std::is_integral>())); + EXPECT_TRUE((absl::conjunction< + std::is_signed, + std::is_integral>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval()( + std::declval())), + size_t>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval()( + std::declval(), + std::declval())), + bool>())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same::pointer, + typename TypeParam::pointer>())); + EXPECT_TRUE( + (std::is_same::const_pointer, + typename TypeParam::const_pointer>())); +} + +TYPED_TEST_P(MembersTest, SimpleFunctions) { + EXPECT_GT(TypeParam().max_size(), 0); +} + +TYPED_TEST_P(MembersTest, BeginEnd) { + TypeParam t = {typename TypeParam::value_type{}}; + EXPECT_EQ(t.begin(), t.cbegin()); + EXPECT_EQ(t.end(), t.cend()); + EXPECT_NE(t.begin(), t.end()); + EXPECT_NE(t.cbegin(), t.cend()); +} + +REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_modifiers_test.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_modifiers_test.h new file mode 100644 index 0000000000000000000000000000000000000000..b8c513f1579d640117a3e118cbaeb72ceda9b314 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_map_modifiers_test.h @@ -0,0 +1,316 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_ + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/internal/hash_generator_testing.h" +#include "absl/container/internal/hash_policy_testing.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class ModifiersTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(ModifiersTest); + +TYPED_TEST_P(ModifiersTest, Clear) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end()); + ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); + m.clear(); + EXPECT_THAT(items(m), ::testing::UnorderedElementsAre()); + EXPECT_TRUE(m.empty()); +} + +TYPED_TEST_P(ModifiersTest, Insert) { + using T = hash_internal::GeneratedType; + using V = typename TypeParam::mapped_type; + T val = hash_internal::Generator()(); + TypeParam m; + auto p = m.insert(val); + EXPECT_TRUE(p.second); + EXPECT_EQ(val, *p.first); + T val2 = {val.first, hash_internal::Generator()()}; + p = m.insert(val2); + EXPECT_FALSE(p.second); + EXPECT_EQ(val, *p.first); +} + +TYPED_TEST_P(ModifiersTest, InsertHint) { + using T = hash_internal::GeneratedType; + using V = typename TypeParam::mapped_type; + T val = hash_internal::Generator()(); + TypeParam m; + auto it = m.insert(m.end(), val); + EXPECT_TRUE(it != m.end()); + EXPECT_EQ(val, *it); + T val2 = {val.first, hash_internal::Generator()()}; + it = m.insert(it, val2); + EXPECT_TRUE(it != m.end()); + EXPECT_EQ(val, *it); +} + +TYPED_TEST_P(ModifiersTest, InsertRange) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m; + m.insert(values.begin(), values.end()); + ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); +} + +TYPED_TEST_P(ModifiersTest, InsertOrAssign) { +#ifdef UNORDERED_MAP_CXX17 + using std::get; + using K = typename TypeParam::key_type; + using V = typename TypeParam::mapped_type; + K k = hash_internal::Generator()(); + V val = hash_internal::Generator()(); + TypeParam m; + auto p = m.insert_or_assign(k, val); + EXPECT_TRUE(p.second); + EXPECT_EQ(k, get<0>(*p.first)); + EXPECT_EQ(val, get<1>(*p.first)); + V val2 = hash_internal::Generator()(); + p = m.insert_or_assign(k, val2); + EXPECT_FALSE(p.second); + EXPECT_EQ(k, get<0>(*p.first)); + EXPECT_EQ(val2, get<1>(*p.first)); +#endif +} + +TYPED_TEST_P(ModifiersTest, InsertOrAssignHint) { +#ifdef UNORDERED_MAP_CXX17 + using std::get; + using K = typename TypeParam::key_type; + using V = typename TypeParam::mapped_type; + K k = hash_internal::Generator()(); + V val = hash_internal::Generator()(); + TypeParam m; + auto it = m.insert_or_assign(m.end(), k, val); + EXPECT_TRUE(it != m.end()); + EXPECT_EQ(k, get<0>(*it)); + EXPECT_EQ(val, get<1>(*it)); + V val2 = hash_internal::Generator()(); + it = m.insert_or_assign(it, k, val2); + EXPECT_EQ(k, get<0>(*it)); + EXPECT_EQ(val2, get<1>(*it)); +#endif +} + +TYPED_TEST_P(ModifiersTest, Emplace) { + using T = hash_internal::GeneratedType; + using V = typename TypeParam::mapped_type; + T val = hash_internal::Generator()(); + TypeParam m; + // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps + // with test traits/policy. + auto p = m.emplace(val); + EXPECT_TRUE(p.second); + EXPECT_EQ(val, *p.first); + T val2 = {val.first, hash_internal::Generator()()}; + p = m.emplace(val2); + EXPECT_FALSE(p.second); + EXPECT_EQ(val, *p.first); +} + +TYPED_TEST_P(ModifiersTest, EmplaceHint) { + using T = hash_internal::GeneratedType; + using V = typename TypeParam::mapped_type; + T val = hash_internal::Generator()(); + TypeParam m; + // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps + // with test traits/policy. + auto it = m.emplace_hint(m.end(), val); + EXPECT_EQ(val, *it); + T val2 = {val.first, hash_internal::Generator()()}; + it = m.emplace_hint(it, val2); + EXPECT_EQ(val, *it); +} + +TYPED_TEST_P(ModifiersTest, TryEmplace) { +#ifdef UNORDERED_MAP_CXX17 + using T = hash_internal::GeneratedType; + using V = typename TypeParam::mapped_type; + T val = hash_internal::Generator()(); + TypeParam m; + // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps + // with test traits/policy. + auto p = m.try_emplace(val.first, val.second); + EXPECT_TRUE(p.second); + EXPECT_EQ(val, *p.first); + T val2 = {val.first, hash_internal::Generator()()}; + p = m.try_emplace(val2.first, val2.second); + EXPECT_FALSE(p.second); + EXPECT_EQ(val, *p.first); +#endif +} + +TYPED_TEST_P(ModifiersTest, TryEmplaceHint) { +#ifdef UNORDERED_MAP_CXX17 + using T = hash_internal::GeneratedType; + using V = typename TypeParam::mapped_type; + T val = hash_internal::Generator()(); + TypeParam m; + // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps + // with test traits/policy. + auto it = m.try_emplace(m.end(), val.first, val.second); + EXPECT_EQ(val, *it); + T val2 = {val.first, hash_internal::Generator()()}; + it = m.try_emplace(it, val2.first, val2.second); + EXPECT_EQ(val, *it); +#endif +} + +template +using IfNotVoid = typename std::enable_if::value, V>::type; + +// In openmap we chose not to return the iterator from erase because that's +// more expensive. As such we adapt erase to return an iterator here. +struct EraseFirst { + template + auto operator()(Map* m, int) const + -> IfNotVoiderase(m->begin()))> { + return m->erase(m->begin()); + } + template + typename Map::iterator operator()(Map* m, ...) const { + auto it = m->begin(); + m->erase(it++); + return it; + } +}; + +TYPED_TEST_P(ModifiersTest, Erase) { + using T = hash_internal::GeneratedType; + using std::get; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end()); + ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); + auto& first = *m.begin(); + std::vector values2; + for (const auto& val : values) + if (get<0>(val) != get<0>(first)) values2.push_back(val); + auto it = EraseFirst()(&m, 0); + ASSERT_TRUE(it != m.end()); + EXPECT_EQ(1, std::count(values2.begin(), values2.end(), *it)); + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values2.begin(), + values2.end())); +} + +TYPED_TEST_P(ModifiersTest, EraseRange) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end()); + ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); + auto it = m.erase(m.begin(), m.end()); + EXPECT_THAT(items(m), ::testing::UnorderedElementsAre()); + EXPECT_TRUE(it == m.end()); +} + +TYPED_TEST_P(ModifiersTest, EraseKey) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end()); + ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_EQ(1, m.erase(values[0].first)); + EXPECT_EQ(0, std::count(m.begin(), m.end(), values[0])); + EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values.begin() + 1, + values.end())); +} + +TYPED_TEST_P(ModifiersTest, Swap) { + using T = hash_internal::GeneratedType; + std::vector v1; + std::vector v2; + std::generate_n(std::back_inserter(v1), 5, hash_internal::Generator()); + std::generate_n(std::back_inserter(v2), 5, hash_internal::Generator()); + TypeParam m1(v1.begin(), v1.end()); + TypeParam m2(v2.begin(), v2.end()); + EXPECT_THAT(items(m1), ::testing::UnorderedElementsAreArray(v1)); + EXPECT_THAT(items(m2), ::testing::UnorderedElementsAreArray(v2)); + m1.swap(m2); + EXPECT_THAT(items(m1), ::testing::UnorderedElementsAreArray(v2)); + EXPECT_THAT(items(m2), ::testing::UnorderedElementsAreArray(v1)); +} + +// TODO(alkis): Write tests for extract. +// TODO(alkis): Write tests for merge. + +REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, + InsertRange, InsertOrAssign, InsertOrAssignHint, + Emplace, EmplaceHint, TryEmplace, TryEmplaceHint, + Erase, EraseRange, EraseKey, Swap); + +template +struct is_unique_ptr : std::false_type {}; + +template +struct is_unique_ptr> : std::true_type {}; + +template +class UniquePtrModifiersTest : public ::testing::Test { + protected: + UniquePtrModifiersTest() { + static_assert(is_unique_ptr::value, + "UniquePtrModifiersTyest may only be called with a " + "std::unique_ptr value type."); + } +}; + +TYPED_TEST_SUITE_P(UniquePtrModifiersTest); + +// Test that we do not move from rvalue arguments if an insertion does not +// happen. +TYPED_TEST_P(UniquePtrModifiersTest, TryEmplace) { +#ifdef UNORDERED_MAP_CXX17 + using T = hash_internal::GeneratedType; + using V = typename TypeParam::mapped_type; + T val = hash_internal::Generator()(); + TypeParam m; + auto p = m.try_emplace(val.first, std::move(val.second)); + EXPECT_TRUE(p.second); + // A moved from std::unique_ptr is guaranteed to be nullptr. + EXPECT_EQ(val.second, nullptr); + T val2 = {val.first, hash_internal::Generator()()}; + p = m.try_emplace(val2.first, std::move(val2.second)); + EXPECT_FALSE(p.second); + EXPECT_NE(val2.second, nullptr); +#endif +} + +REGISTER_TYPED_TEST_SUITE_P(UniquePtrModifiersTest, TryEmplace); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_constructor_test.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_constructor_test.h new file mode 100644 index 0000000000000000000000000000000000000000..41165b05e97b3e46949215f4fd22a2f1719307e3 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_constructor_test.h @@ -0,0 +1,496 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_ + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/internal/hash_generator_testing.h" +#include "absl/container/internal/hash_policy_testing.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class ConstructorTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(ConstructorTest); + +TYPED_TEST_P(ConstructorTest, NoArgs) { + TypeParam m; + EXPECT_TRUE(m.empty()); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); +} + +TYPED_TEST_P(ConstructorTest, BucketCount) { + TypeParam m(123); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, BucketCountHash) { + using H = typename TypeParam::hasher; + H hasher; + TypeParam m(123, hasher); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, BucketCountHashEqual) { + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + H hasher; + E equal; + TypeParam m(123, hasher, equal); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.key_eq(), equal); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) { + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(123, hasher, equal, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.key_eq(), equal); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); + + const auto& cm = m; + EXPECT_EQ(cm.hash_function(), hasher); + EXPECT_EQ(cm.key_eq(), equal); + EXPECT_EQ(cm.get_allocator(), alloc); + EXPECT_TRUE(cm.empty()); + EXPECT_THAT(keys(cm), ::testing::UnorderedElementsAre()); + EXPECT_GE(cm.bucket_count(), 123); +} + +template +struct is_std_unordered_set : std::false_type {}; + +template +struct is_std_unordered_set> : std::true_type {}; + +#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +using has_cxx14_std_apis = std::true_type; +#else +using has_cxx14_std_apis = std::false_type; +#endif + +template +using expect_cxx14_apis = + absl::disjunction>, + has_cxx14_std_apis>; + +template +void BucketCountAllocTest(std::false_type) {} + +template +void BucketCountAllocTest(std::true_type) { + using A = typename TypeParam::allocator_type; + A alloc(0); + TypeParam m(123, alloc); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { + BucketCountAllocTest(expect_cxx14_apis()); +} + +template +void BucketCountHashAllocTest(std::false_type) {} + +template +void BucketCountHashAllocTest(std::true_type) { + using H = typename TypeParam::hasher; + using A = typename TypeParam::allocator_type; + H hasher; + A alloc(0); + TypeParam m(123, hasher, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { + BucketCountHashAllocTest(expect_cxx14_apis()); +} + +#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +using has_alloc_std_constructors = std::true_type; +#else +using has_alloc_std_constructors = std::false_type; +#endif + +template +using expect_alloc_constructors = + absl::disjunction>, + has_alloc_std_constructors>; + +template +void AllocTest(std::false_type) {} + +template +void AllocTest(std::true_type) { + using A = typename TypeParam::allocator_type; + A alloc(0); + TypeParam m(alloc); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_TRUE(m.empty()); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); +} + +TYPED_TEST_P(ConstructorTest, Alloc) { + AllocTest(expect_alloc_constructors()); +} + +TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + std::vector values; + for (size_t i = 0; i != 10; ++i) + values.push_back(hash_internal::Generator()()); + TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.key_eq(), equal); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +template +void InputIteratorBucketAllocTest(std::false_type) {} + +template +void InputIteratorBucketAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using A = typename TypeParam::allocator_type; + A alloc(0); + std::vector values; + for (size_t i = 0; i != 10; ++i) + values.push_back(hash_internal::Generator()()); + TypeParam m(values.begin(), values.end(), 123, alloc); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { + InputIteratorBucketAllocTest(expect_cxx14_apis()); +} + +template +void InputIteratorBucketHashAllocTest(std::false_type) {} + +template +void InputIteratorBucketHashAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using A = typename TypeParam::allocator_type; + H hasher; + A alloc(0); + std::vector values; + for (size_t i = 0; i != 10; ++i) + values.push_back(hash_internal::Generator()()); + TypeParam m(values.begin(), values.end(), 123, hasher, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { + InputIteratorBucketHashAllocTest(expect_cxx14_apis()); +} + +TYPED_TEST_P(ConstructorTest, CopyConstructor) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(123, hasher, equal, alloc); + for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + TypeParam n(m); + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_EQ(m.get_allocator(), n.get_allocator()); + EXPECT_EQ(m, n); + EXPECT_NE(TypeParam(0, hasher, equal, alloc), n); +} + +template +void CopyConstructorAllocTest(std::false_type) {} + +template +void CopyConstructorAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(123, hasher, equal, alloc); + for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + TypeParam n(m, A(11)); + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_NE(m.get_allocator(), n.get_allocator()); + EXPECT_EQ(m, n); +} + +TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { + CopyConstructorAllocTest(expect_alloc_constructors()); +} + +// TODO(alkis): Test non-propagating allocators on copy constructors. + +TYPED_TEST_P(ConstructorTest, MoveConstructor) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(123, hasher, equal, alloc); + for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + TypeParam t(m); + TypeParam n(std::move(t)); + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_EQ(m.get_allocator(), n.get_allocator()); + EXPECT_EQ(m, n); +} + +template +void MoveConstructorAllocTest(std::false_type) {} + +template +void MoveConstructorAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(123, hasher, equal, alloc); + for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + TypeParam t(m); + TypeParam n(std::move(t), A(1)); + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_NE(m.get_allocator(), n.get_allocator()); + EXPECT_EQ(m, n); +} + +TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { + MoveConstructorAllocTest(expect_alloc_constructors()); +} + +// TODO(alkis): Test non-propagating allocators on move constructors. + +TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + TypeParam m(values, 123, hasher, equal, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.key_eq(), equal); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +template +void InitializerListBucketAllocTest(std::false_type) {} + +template +void InitializerListBucketAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using A = typename TypeParam::allocator_type; + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + A alloc(0); + TypeParam m(values, 123, alloc); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { + InitializerListBucketAllocTest(expect_cxx14_apis()); +} + +template +void InitializerListBucketHashAllocTest(std::false_type) {} + +template +void InitializerListBucketHashAllocTest(std::true_type) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using A = typename TypeParam::allocator_type; + H hasher; + A alloc(0); + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + TypeParam m(values, 123, hasher, alloc); + EXPECT_EQ(m.hash_function(), hasher); + EXPECT_EQ(m.get_allocator(), alloc); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_GE(m.bucket_count(), 123); +} + +TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { + InitializerListBucketHashAllocTest(expect_cxx14_apis()); +} + +TYPED_TEST_P(ConstructorTest, CopyAssignment) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + hash_internal::Generator gen; + TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); + TypeParam n; + n = m; + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_EQ(m, n); +} + +// TODO(alkis): Test [non-]propagating allocators on move/copy assignments +// (it depends on traits). + +TYPED_TEST_P(ConstructorTest, MoveAssignment) { + using T = hash_internal::GeneratedType; + using H = typename TypeParam::hasher; + using E = typename TypeParam::key_equal; + using A = typename TypeParam::allocator_type; + H hasher; + E equal; + A alloc(0); + hash_internal::Generator gen; + TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); + TypeParam t(m); + TypeParam n; + n = std::move(t); + EXPECT_EQ(m.hash_function(), n.hash_function()); + EXPECT_EQ(m.key_eq(), n.key_eq()); + EXPECT_EQ(m, n); +} + +TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + TypeParam m; + m = values; + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); +} + +TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + TypeParam m({gen(), gen(), gen()}); + TypeParam n({gen()}); + n = m; + EXPECT_EQ(m, n); +} + +TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + TypeParam m({gen(), gen(), gen()}); + TypeParam t(m); + TypeParam n({gen()}); + n = std::move(t); + EXPECT_EQ(m, n); +} + +TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + TypeParam m; + m = values; + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); +} + +TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) { + using T = hash_internal::GeneratedType; + hash_internal::Generator gen; + std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; + TypeParam m(values); + m = *&m; // Avoid -Wself-assign. + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); +} + +REGISTER_TYPED_TEST_CASE_P( + ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual, + BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc, + InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, + InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc, + MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc, + InitializerListBucketAlloc, InitializerListBucketHashAlloc, CopyAssignment, + MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting, + MoveAssignmentOverwritesExisting, + AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_lookup_test.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_lookup_test.h new file mode 100644 index 0000000000000000000000000000000000000000..8f2f4b207ef0fff7d8658e9cb27062c75a195127 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_lookup_test.h @@ -0,0 +1,91 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_ + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/internal/hash_generator_testing.h" +#include "absl/container/internal/hash_policy_testing.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class LookupTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(LookupTest); + +TYPED_TEST_P(LookupTest, Count) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m; + for (const auto& v : values) + EXPECT_EQ(0, m.count(v)) << ::testing::PrintToString(v); + m.insert(values.begin(), values.end()); + for (const auto& v : values) + EXPECT_EQ(1, m.count(v)) << ::testing::PrintToString(v); +} + +TYPED_TEST_P(LookupTest, Find) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m; + for (const auto& v : values) + EXPECT_TRUE(m.end() == m.find(v)) << ::testing::PrintToString(v); + m.insert(values.begin(), values.end()); + for (const auto& v : values) { + typename TypeParam::iterator it = m.find(v); + static_assert(std::is_same::value, + ""); + static_assert(std::is_same())>::value, + ""); + EXPECT_TRUE(m.end() != it) << ::testing::PrintToString(v); + EXPECT_EQ(v, *it) << ::testing::PrintToString(v); + } +} + +TYPED_TEST_P(LookupTest, EqualRange) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m; + for (const auto& v : values) { + auto r = m.equal_range(v); + ASSERT_EQ(0, std::distance(r.first, r.second)); + } + m.insert(values.begin(), values.end()); + for (const auto& v : values) { + auto r = m.equal_range(v); + ASSERT_EQ(1, std::distance(r.first, r.second)); + EXPECT_EQ(v, *r.first); + } +} + +REGISTER_TYPED_TEST_CASE_P(LookupTest, Count, Find, EqualRange); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_members_test.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_members_test.h new file mode 100644 index 0000000000000000000000000000000000000000..4c5e104af292412cab62f95b98cfd834b6775fd8 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_members_test.h @@ -0,0 +1,86 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_ + +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class MembersTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(MembersTest); + +template +void UseType() {} + +TYPED_TEST_P(MembersTest, Typedefs) { + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((absl::conjunction< + absl::negation>, + std::is_integral>())); + EXPECT_TRUE((absl::conjunction< + std::is_signed, + std::is_integral>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval()( + std::declval())), + size_t>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval()( + std::declval(), + std::declval())), + bool>())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same::pointer, + typename TypeParam::pointer>())); + EXPECT_TRUE( + (std::is_same::const_pointer, + typename TypeParam::const_pointer>())); +} + +TYPED_TEST_P(MembersTest, SimpleFunctions) { + EXPECT_GT(TypeParam().max_size(), 0); +} + +TYPED_TEST_P(MembersTest, BeginEnd) { + TypeParam t = {typename TypeParam::value_type{}}; + EXPECT_EQ(t.begin(), t.cbegin()); + EXPECT_EQ(t.end(), t.cend()); + EXPECT_NE(t.begin(), t.end()); + EXPECT_NE(t.cbegin(), t.cend()); +} + +REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_modifiers_test.h b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_modifiers_test.h new file mode 100644 index 0000000000000000000000000000000000000000..26be58d99f216db3cb3de7707d3ecaf7a947d02e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/internal/unordered_set_modifiers_test.h @@ -0,0 +1,190 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_ + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/internal/hash_generator_testing.h" +#include "absl/container/internal/hash_policy_testing.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +class ModifiersTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(ModifiersTest); + +TYPED_TEST_P(ModifiersTest, Clear) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end()); + ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); + m.clear(); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); + EXPECT_TRUE(m.empty()); +} + +TYPED_TEST_P(ModifiersTest, Insert) { + using T = hash_internal::GeneratedType; + T val = hash_internal::Generator()(); + TypeParam m; + auto p = m.insert(val); + EXPECT_TRUE(p.second); + EXPECT_EQ(val, *p.first); + p = m.insert(val); + EXPECT_FALSE(p.second); +} + +TYPED_TEST_P(ModifiersTest, InsertHint) { + using T = hash_internal::GeneratedType; + T val = hash_internal::Generator()(); + TypeParam m; + auto it = m.insert(m.end(), val); + EXPECT_TRUE(it != m.end()); + EXPECT_EQ(val, *it); + it = m.insert(it, val); + EXPECT_TRUE(it != m.end()); + EXPECT_EQ(val, *it); +} + +TYPED_TEST_P(ModifiersTest, InsertRange) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m; + m.insert(values.begin(), values.end()); + ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); +} + +TYPED_TEST_P(ModifiersTest, Emplace) { + using T = hash_internal::GeneratedType; + T val = hash_internal::Generator()(); + TypeParam m; + // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps + // with test traits/policy. + auto p = m.emplace(val); + EXPECT_TRUE(p.second); + EXPECT_EQ(val, *p.first); + p = m.emplace(val); + EXPECT_FALSE(p.second); + EXPECT_EQ(val, *p.first); +} + +TYPED_TEST_P(ModifiersTest, EmplaceHint) { + using T = hash_internal::GeneratedType; + T val = hash_internal::Generator()(); + TypeParam m; + // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps + // with test traits/policy. + auto it = m.emplace_hint(m.end(), val); + EXPECT_EQ(val, *it); + it = m.emplace_hint(it, val); + EXPECT_EQ(val, *it); +} + +template +using IfNotVoid = typename std::enable_if::value, V>::type; + +// In openmap we chose not to return the iterator from erase because that's +// more expensive. As such we adapt erase to return an iterator here. +struct EraseFirst { + template + auto operator()(Map* m, int) const + -> IfNotVoiderase(m->begin()))> { + return m->erase(m->begin()); + } + template + typename Map::iterator operator()(Map* m, ...) const { + auto it = m->begin(); + m->erase(it++); + return it; + } +}; + +TYPED_TEST_P(ModifiersTest, Erase) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end()); + ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); + std::vector values2; + for (const auto& val : values) + if (val != *m.begin()) values2.push_back(val); + auto it = EraseFirst()(&m, 0); + ASSERT_TRUE(it != m.end()); + EXPECT_EQ(1, std::count(values2.begin(), values2.end(), *it)); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values2.begin(), + values2.end())); +} + +TYPED_TEST_P(ModifiersTest, EraseRange) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end()); + ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); + auto it = m.erase(m.begin(), m.end()); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); + EXPECT_TRUE(it == m.end()); +} + +TYPED_TEST_P(ModifiersTest, EraseKey) { + using T = hash_internal::GeneratedType; + std::vector values; + std::generate_n(std::back_inserter(values), 10, + hash_internal::Generator()); + TypeParam m(values.begin(), values.end()); + ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); + EXPECT_EQ(1, m.erase(values[0])); + EXPECT_EQ(0, std::count(m.begin(), m.end(), values[0])); + EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values.begin() + 1, + values.end())); +} + +TYPED_TEST_P(ModifiersTest, Swap) { + using T = hash_internal::GeneratedType; + std::vector v1; + std::vector v2; + std::generate_n(std::back_inserter(v1), 5, hash_internal::Generator()); + std::generate_n(std::back_inserter(v2), 5, hash_internal::Generator()); + TypeParam m1(v1.begin(), v1.end()); + TypeParam m2(v2.begin(), v2.end()); + EXPECT_THAT(keys(m1), ::testing::UnorderedElementsAreArray(v1)); + EXPECT_THAT(keys(m2), ::testing::UnorderedElementsAreArray(v2)); + m1.swap(m2); + EXPECT_THAT(keys(m1), ::testing::UnorderedElementsAreArray(v2)); + EXPECT_THAT(keys(m2), ::testing::UnorderedElementsAreArray(v1)); +} + +// TODO(alkis): Write tests for extract. +// TODO(alkis): Write tests for merge. + +REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, + InsertRange, Emplace, EmplaceHint, Erase, EraseRange, + EraseKey, Swap); + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/node_hash_map.h b/libs/or-tools-src-ubuntu/include/absl/container/node_hash_map.h new file mode 100644 index 0000000000000000000000000000000000000000..fccea1841c08f5cf33192d3b073df5bc44baaadd --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/node_hash_map.h @@ -0,0 +1,597 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: node_hash_map.h +// ----------------------------------------------------------------------------- +// +// An `absl::node_hash_map` is an unordered associative container of +// unique keys and associated values designed to be a more efficient replacement +// for `std::unordered_map`. Like `unordered_map`, search, insertion, and +// deletion of map elements can be done as an `O(1)` operation. However, +// `node_hash_map` (and other unordered associative containers known as the +// collection of Abseil "Swiss tables") contain other optimizations that result +// in both memory and computation advantages. +// +// In most cases, your default choice for a hash map should be a map of type +// `flat_hash_map`. However, if you need pointer stability and cannot store +// a `flat_hash_map` with `unique_ptr` elements, a `node_hash_map` may be a +// valid alternative. As well, if you are migrating your code from using +// `std::unordered_map`, a `node_hash_map` provides a more straightforward +// migration, because it guarantees pointer stability. Consider migrating to +// `node_hash_map` and perhaps converting to a more efficient `flat_hash_map` +// upon further review. + +#ifndef ABSL_CONTAINER_NODE_HASH_MAP_H_ +#define ABSL_CONTAINER_NODE_HASH_MAP_H_ + +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/node_hash_policy.h" +#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +template +class NodeHashMapPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::node_hash_map +// ----------------------------------------------------------------------------- +// +// An `absl::node_hash_map` is an unordered associative container which +// has been optimized for both speed and memory footprint in most common use +// cases. Its interface is similar to that of `std::unordered_map` with +// the following notable differences: +// +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the map is provided a compatible heterogeneous +// hashing function and equality operator. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash map. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `node_hash_map` uses the `absl::Hash` hashing framework. +// All fundamental and Abseil types that support the `absl::Hash` framework have +// a compatible equality operator for comparing insertions into `node_hash_map`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// Example: +// +// // Create a node hash map of three strings (that map to strings) +// absl::node_hash_map ducks = +// {{"a", "huey"}, {"b", "dewey"}, {"c", "louie"}}; +// +// // Insert a new element into the node hash map +// ducks.insert({"d", "donald"}}; +// +// // Force a rehash of the node hash map +// ducks.rehash(0); +// +// // Find the element with the key "b" +// std::string search_key = "b"; +// auto result = ducks.find(search_key); +// if (result != ducks.end()) { +// std::cout << "Result: " << result->second << std::endl; +// } +template , + class Eq = absl::container_internal::hash_default_eq, + class Alloc = std::allocator>> +class node_hash_map + : public absl::container_internal::raw_hash_map< + absl::container_internal::NodeHashMapPolicy, Hash, Eq, + Alloc> { + using Base = typename node_hash_map::raw_hash_map; + + public: + // Constructors and Assignment Operators + // + // A node_hash_map supports the same overload set as `std::unordered_map` + // for construction and assignment: + // + // * Default constructor + // + // // No allocation for the table's elements is made. + // absl::node_hash_map map1; + // + // * Initializer List constructor + // + // absl::node_hash_map map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::node_hash_map map3(map2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::node_hash_map map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::node_hash_map map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::node_hash_map map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::node_hash_map map7(v.begin(), v.end()); + node_hash_map() {} + using Base::Base; + + // node_hash_map::begin() + // + // Returns an iterator to the beginning of the `node_hash_map`. + using Base::begin; + + // node_hash_map::cbegin() + // + // Returns a const iterator to the beginning of the `node_hash_map`. + using Base::cbegin; + + // node_hash_map::cend() + // + // Returns a const iterator to the end of the `node_hash_map`. + using Base::cend; + + // node_hash_map::end() + // + // Returns an iterator to the end of the `node_hash_map`. + using Base::end; + + // node_hash_map::capacity() + // + // Returns the number of element slots (assigned, deleted, and empty) + // available within the `node_hash_map`. + // + // NOTE: this member function is particular to `absl::node_hash_map` and is + // not provided in the `std::unordered_map` API. + using Base::capacity; + + // node_hash_map::empty() + // + // Returns whether or not the `node_hash_map` is empty. + using Base::empty; + + // node_hash_map::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `node_hash_map` under current memory constraints. This value can be thought + // of as the largest value of `std::distance(begin(), end())` for a + // `node_hash_map`. + using Base::max_size; + + // node_hash_map::size() + // + // Returns the number of elements currently within the `node_hash_map`. + using Base::size; + + // node_hash_map::clear() + // + // Removes all elements from the `node_hash_map`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // node_hash_map::erase() + // + // Erases elements within the `node_hash_map`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // Erases the element at `position` of the `node_hash_map`, returning + // `void`. + // + // NOTE: this return behavior is different than that of STL containers in + // general and `std::unordered_map` in particular. + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists. + using Base::erase; + + // node_hash_map::insert() + // + // Inserts an element of the specified value into the `node_hash_map`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair insert(const init_type& value): + // + // Inserts a value into the `node_hash_map`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a `bool` denoting whether the insertion took place. + // + // std::pair insert(T&& value): + // std::pair insert(init_type&& value): + // + // Inserts a moveable value into the `node_hash_map`. Returns a `std::pair` + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a `bool` denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const init_type& value): + // iterator insert(const_iterator hint, T&& value): + // iterator insert(const_iterator hint, init_type&& value); + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently, for `node_hash_map` we guarantee the + // first match is inserted. + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently within the initializer list, for + // `node_hash_map` we guarantee the first match is inserted. + using Base::insert; + + // node_hash_map::insert_or_assign() + // + // Inserts an element of the specified value into the `node_hash_map` provided + // that a value with the given key does not already exist, or replaces it with + // the element value if a key for that value already exists, returning an + // iterator pointing to the newly inserted element. If rehashing occurs due to + // the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair insert_or_assign(const init_type& k, T&& obj): + // std::pair insert_or_assign(init_type&& k, T&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `node_hash_map`. + // + // iterator insert_or_assign(const_iterator hint, + // const init_type& k, T&& obj): + // iterator insert_or_assign(const_iterator hint, init_type&& k, T&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `node_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::insert_or_assign; + + // node_hash_map::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `node_hash_map`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace; + + // node_hash_map::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `node_hash_map`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace_hint; + + // node_hash_map::try_emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `node_hash_map`, provided that no element with the given key + // already exists. Unlike `emplace()`, if an element with the given key + // already exists, we guarantee that no element is constructed. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + // Overloads are listed below. + // + // std::pair try_emplace(const key_type& k, Args&&... args): + // std::pair try_emplace(key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `node_hash_map`. + // + // iterator try_emplace(const_iterator hint, + // const init_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, init_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `node_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + // + // All `try_emplace()` overloads make the same guarantees regarding rvalue + // arguments as `std::unordered_map::try_emplace()`, namely that these + // functions will not move from rvalue arguments if insertions do not happen. + using Base::try_emplace; + + // node_hash_map::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the key,value pair of the element at the indicated position and + // returns a node handle owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the key,value pair of the element with a key matching the passed + // key value and returns a node handle owning that extracted data. If the + // `node_hash_map` does not contain an element with a matching key, this + // function returns an empty node handle. + using Base::extract; + + // node_hash_map::merge() + // + // Extracts elements from a given `source` node hash map into this + // `node_hash_map`. If the destination `node_hash_map` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // node_hash_map::swap(node_hash_map& other) + // + // Exchanges the contents of this `node_hash_map` with those of the `other` + // node hash map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `node_hash_map` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the node hash map's hashing and key equivalence + // functions be Swappable, and are exchaged using unqualified calls to + // non-member `swap()`. If the map's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // node_hash_map::rehash(count) + // + // Rehashes the `node_hash_map`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + using Base::rehash; + + // node_hash_map::reserve(count) + // + // Sets the number of slots in the `node_hash_map` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // node_hash_map::at() + // + // Returns a reference to the mapped value of the element with key equivalent + // to the passed key. + using Base::at; + + // node_hash_map::contains() + // + // Determines whether an element with a key comparing equal to the given `key` + // exists within the `node_hash_map`, returning `true` if so or `false` + // otherwise. + using Base::contains; + + // node_hash_map::count(const Key& key) const + // + // Returns the number of elements with a key comparing equal to the given + // `key` within the `node_hash_map`. note that this function will return + // either `1` or `0` since duplicate keys are not allowed within a + // `node_hash_map`. + using Base::count; + + // node_hash_map::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `node_hash_map`. + using Base::equal_range; + + // node_hash_map::find() + // + // Finds an element with the passed `key` within the `node_hash_map`. + using Base::find; + + // node_hash_map::operator[]() + // + // Returns a reference to the value mapped to the passed key within the + // `node_hash_map`, performing an `insert()` if the key does not already + // exist. If an insertion occurs and results in a rehashing of the container, + // all iterators are invalidated. Otherwise iterators are not affected and + // references are not invalidated. Overloads are listed below. + // + // T& operator[](const Key& key): + // + // Inserts an init_type object constructed in-place if the element with the + // given key does not exist. + // + // T& operator[](Key&& key): + // + // Inserts an init_type object constructed in-place provided that an element + // with the given key does not exist. + using Base::operator[]; + + // node_hash_map::bucket_count() + // + // Returns the number of "buckets" within the `node_hash_map`. + using Base::bucket_count; + + // node_hash_map::load_factor() + // + // Returns the current load factor of the `node_hash_map` (the average number + // of slots occupied with a value within the hash map). + using Base::load_factor; + + // node_hash_map::max_load_factor() + // + // Manages the maximum load factor of the `node_hash_map`. Overloads are + // listed below. + // + // float node_hash_map::max_load_factor() + // + // Returns the current maximum load factor of the `node_hash_map`. + // + // void node_hash_map::max_load_factor(float ml) + // + // Sets the maximum load factor of the `node_hash_map` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `node_hash_map` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // node_hash_map::get_allocator() + // + // Returns the allocator function associated with this `node_hash_map`. + using Base::get_allocator; + + // node_hash_map::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `node_hash_map`. + using Base::hash_function; + + // node_hash_map::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; + + ABSL_DEPRECATED("Call `hash_function()` instead.") + typename Base::hasher hash_funct() { return this->hash_function(); } + + ABSL_DEPRECATED("Call `rehash()` instead.") + void resize(typename Base::size_type hint) { this->rehash(hint); } +}; + +// erase_if(node_hash_map<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template +void erase_if(node_hash_map& c, Predicate pred) { + container_internal::EraseIf(pred, &c); +} + +namespace container_internal { + +template +class NodeHashMapPolicy + : public absl::container_internal::node_hash_policy< + std::pair&, NodeHashMapPolicy> { + using value_type = std::pair; + + public: + using key_type = Key; + using mapped_type = Value; + using init_type = std::pair; + + template + static value_type* new_element(Allocator* alloc, Args&&... args) { + using PairAlloc = typename absl::allocator_traits< + Allocator>::template rebind_alloc; + PairAlloc pair_alloc(*alloc); + value_type* res = + absl::allocator_traits::allocate(pair_alloc, 1); + absl::allocator_traits::construct(pair_alloc, res, + std::forward(args)...); + return res; + } + + template + static void delete_element(Allocator* alloc, value_type* pair) { + using PairAlloc = typename absl::allocator_traits< + Allocator>::template rebind_alloc; + PairAlloc pair_alloc(*alloc); + absl::allocator_traits::destroy(pair_alloc, pair); + absl::allocator_traits::deallocate(pair_alloc, pair, 1); + } + + template + static decltype(absl::container_internal::DecomposePair( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposePair(std::forward(f), + std::forward(args)...); + } + + static size_t element_space_used(const value_type*) { + return sizeof(value_type); + } + + static Value& value(value_type* elem) { return elem->second; } + static const Value& value(const value_type* elem) { return elem->second; } +}; +} // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template +struct IsUnorderedContainer< + absl::node_hash_map> : std::true_type {}; + +} // namespace container_algorithm_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_NODE_HASH_MAP_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/container/node_hash_set.h b/libs/or-tools-src-ubuntu/include/absl/container/node_hash_set.h new file mode 100644 index 0000000000000000000000000000000000000000..ad54b6dccb5059e3f475cd6c933f5c9bb33dd5b4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/container/node_hash_set.h @@ -0,0 +1,498 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: node_hash_set.h +// ----------------------------------------------------------------------------- +// +// An `absl::node_hash_set` is an unordered associative container designed to +// be a more efficient replacement for `std::unordered_set`. Like +// `unordered_set`, search, insertion, and deletion of map elements can be done +// as an `O(1)` operation. However, `node_hash_set` (and other unordered +// associative containers known as the collection of Abseil "Swiss tables") +// contain other optimizations that result in both memory and computation +// advantages. +// +// In most cases, your default choice for a hash table should be a map of type +// `flat_hash_map` or a set of type `flat_hash_set`. However, if you need +// pointer stability, a `node_hash_set` should be your preferred choice. As +// well, if you are migrating your code from using `std::unordered_set`, a +// `node_hash_set` should be an easy migration. Consider migrating to +// `node_hash_set` and perhaps converting to a more efficient `flat_hash_set` +// upon further review. + +#ifndef ABSL_CONTAINER_NODE_HASH_SET_H_ +#define ABSL_CONTAINER_NODE_HASH_SET_H_ + +#include + +#include "absl/algorithm/container.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/node_hash_policy.h" +#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +template +struct NodeHashSetPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::node_hash_set +// ----------------------------------------------------------------------------- +// +// An `absl::node_hash_set` is an unordered associative container which +// has been optimized for both speed and memory footprint in most common use +// cases. Its interface is similar to that of `std::unordered_set` with the +// following notable differences: +// +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the map is provided a compatible heterogeneous +// hashing function and equality operator. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash set. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `node_hash_set` uses the `absl::Hash` hashing framework. +// All fundamental and Abseil types that support the `absl::Hash` framework have +// a compatible equality operator for comparing insertions into `node_hash_set`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// Example: +// +// // Create a node hash set of three strings +// absl::node_hash_map ducks = +// {"huey", "dewey", "louie"}; +// +// // Insert a new element into the node hash map +// ducks.insert("donald"}; +// +// // Force a rehash of the node hash map +// ducks.rehash(0); +// +// // See if "dewey" is present +// if (ducks.contains("dewey")) { +// std::cout << "We found dewey!" << std::endl; +// } +template , + class Eq = absl::container_internal::hash_default_eq, + class Alloc = std::allocator> +class node_hash_set + : public absl::container_internal::raw_hash_set< + absl::container_internal::NodeHashSetPolicy, Hash, Eq, Alloc> { + using Base = typename node_hash_set::raw_hash_set; + + public: + // Constructors and Assignment Operators + // + // A node_hash_set supports the same overload set as `std::unordered_map` + // for construction and assignment: + // + // * Default constructor + // + // // No allocation for the table's elements is made. + // absl::node_hash_set set1; + // + // * Initializer List constructor + // + // absl::node_hash_set set2 = + // {{"huey"}, {"dewey"}, {"louie"}}; + // + // * Copy constructor + // + // absl::node_hash_set set3(set2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::node_hash_set set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::node_hash_set set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::node_hash_set set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector v = {"a", "b"}; + // absl::node_hash_set set7(v.begin(), v.end()); + node_hash_set() {} + using Base::Base; + + // node_hash_set::begin() + // + // Returns an iterator to the beginning of the `node_hash_set`. + using Base::begin; + + // node_hash_set::cbegin() + // + // Returns a const iterator to the beginning of the `node_hash_set`. + using Base::cbegin; + + // node_hash_set::cend() + // + // Returns a const iterator to the end of the `node_hash_set`. + using Base::cend; + + // node_hash_set::end() + // + // Returns an iterator to the end of the `node_hash_set`. + using Base::end; + + // node_hash_set::capacity() + // + // Returns the number of element slots (assigned, deleted, and empty) + // available within the `node_hash_set`. + // + // NOTE: this member function is particular to `absl::node_hash_set` and is + // not provided in the `std::unordered_map` API. + using Base::capacity; + + // node_hash_set::empty() + // + // Returns whether or not the `node_hash_set` is empty. + using Base::empty; + + // node_hash_set::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `node_hash_set` under current memory constraints. This value can be thought + // of the largest value of `std::distance(begin(), end())` for a + // `node_hash_set`. + using Base::max_size; + + // node_hash_set::size() + // + // Returns the number of elements currently within the `node_hash_set`. + using Base::size; + + // node_hash_set::clear() + // + // Removes all elements from the `node_hash_set`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // node_hash_set::erase() + // + // Erases elements within the `node_hash_set`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // Erases the element at `position` of the `node_hash_set`, returning + // `void`. + // + // NOTE: this return behavior is different than that of STL containers in + // general and `std::unordered_map` in particular. + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists. + using Base::erase; + + // node_hash_set::insert() + // + // Inserts an element of the specified value into the `node_hash_set`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair insert(const T& value): + // + // Inserts a value into the `node_hash_set`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(T&& value): + // + // Inserts a moveable value into the `node_hash_set`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const T& value): + // iterator insert(const_iterator hint, T&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently, for `node_hash_set` we guarantee the + // first match is inserted. + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently within the initializer list, for + // `node_hash_set` we guarantee the first match is inserted. + using Base::insert; + + // node_hash_set::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `node_hash_set`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace; + + // node_hash_set::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `node_hash_set`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace_hint; + + // node_hash_set::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `node_hash_set` + // does not contain an element with a matching key, this function returns an + // empty node handle. + using Base::extract; + + // node_hash_set::merge() + // + // Extracts elements from a given `source` flat hash map into this + // `node_hash_set`. If the destination `node_hash_set` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // node_hash_set::swap(node_hash_set& other) + // + // Exchanges the contents of this `node_hash_set` with those of the `other` + // flat hash map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `node_hash_set` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the flat hash set's hashing and key equivalence + // functions be Swappable, and are exchaged using unqualified calls to + // non-member `swap()`. If the map's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // node_hash_set::rehash(count) + // + // Rehashes the `node_hash_set`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + // + // NOTE: unlike behavior in `std::unordered_set`, references are also + // invalidated upon a `rehash()`. + using Base::rehash; + + // node_hash_set::reserve(count) + // + // Sets the number of slots in the `node_hash_set` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // node_hash_set::contains() + // + // Determines whether an element comparing equal to the given `key` exists + // within the `node_hash_set`, returning `true` if so or `false` otherwise. + using Base::contains; + + // node_hash_set::count(const Key& key) const + // + // Returns the number of elements comparing equal to the given `key` within + // the `node_hash_set`. note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `node_hash_set`. + using Base::count; + + // node_hash_set::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `node_hash_set`. + using Base::equal_range; + + // node_hash_set::find() + // + // Finds an element with the passed `key` within the `node_hash_set`. + using Base::find; + + // node_hash_set::bucket_count() + // + // Returns the number of "buckets" within the `node_hash_set`. Note that + // because a flat hash map contains all elements within its internal storage, + // this value simply equals the current capacity of the `node_hash_set`. + using Base::bucket_count; + + // node_hash_set::load_factor() + // + // Returns the current load factor of the `node_hash_set` (the average number + // of slots occupied with a value within the hash map). + using Base::load_factor; + + // node_hash_set::max_load_factor() + // + // Manages the maximum load factor of the `node_hash_set`. Overloads are + // listed below. + // + // float node_hash_set::max_load_factor() + // + // Returns the current maximum load factor of the `node_hash_set`. + // + // void node_hash_set::max_load_factor(float ml) + // + // Sets the maximum load factor of the `node_hash_set` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `node_hash_set` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // node_hash_set::get_allocator() + // + // Returns the allocator function associated with this `node_hash_set`. + using Base::get_allocator; + + // node_hash_set::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `node_hash_set`. + using Base::hash_function; + + // node_hash_set::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; + + ABSL_DEPRECATED("Call `hash_function()` instead.") + typename Base::hasher hash_funct() { return this->hash_function(); } + + ABSL_DEPRECATED("Call `rehash()` instead.") + void resize(typename Base::size_type hint) { this->rehash(hint); } +}; + +// erase_if(node_hash_set<>, Pred) +// +// Erases all elements that satisfy the predicate `pred` from the container `c`. +template +void erase_if(node_hash_set& c, Predicate pred) { + container_internal::EraseIf(pred, &c); +} + +namespace container_internal { + +template +struct NodeHashSetPolicy + : absl::container_internal::node_hash_policy> { + using key_type = T; + using init_type = T; + using constant_iterators = std::true_type; + + template + static T* new_element(Allocator* alloc, Args&&... args) { + using ValueAlloc = + typename absl::allocator_traits::template rebind_alloc; + ValueAlloc value_alloc(*alloc); + T* res = absl::allocator_traits::allocate(value_alloc, 1); + absl::allocator_traits::construct(value_alloc, res, + std::forward(args)...); + return res; + } + + template + static void delete_element(Allocator* alloc, T* elem) { + using ValueAlloc = + typename absl::allocator_traits::template rebind_alloc; + ValueAlloc value_alloc(*alloc); + absl::allocator_traits::destroy(value_alloc, elem); + absl::allocator_traits::deallocate(value_alloc, elem, 1); + } + + template + static decltype(absl::container_internal::DecomposeValue( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposeValue( + std::forward(f), std::forward(args)...); + } + + static size_t element_space_used(const T*) { return sizeof(T); } +}; +} // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template +struct IsUnorderedContainer> + : std::true_type {}; + +} // namespace container_algorithm_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_NODE_HASH_SET_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/failure_signal_handler.h b/libs/or-tools-src-ubuntu/include/absl/debugging/failure_signal_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..f5a83962f1fca8bf45f254910ac013afc913e811 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/failure_signal_handler.h @@ -0,0 +1,121 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: failure_signal_handler.h +// ----------------------------------------------------------------------------- +// +// This file configures the Abseil *failure signal handler* to capture and dump +// useful debugging information (such as a stacktrace) upon program failure. +// +// To use the failure signal handler, call `absl::InstallFailureSignalHandler()` +// very early in your program, usually in the first few lines of main(): +// +// int main(int argc, char** argv) { +// // Initialize the symbolizer to get a human-readable stack trace +// absl::InitializeSymbolizer(argv[0]); +// +// absl::FailureSignalHandlerOptions options; +// absl::InstallFailureSignalHandler(options); +// DoSomethingInteresting(); +// return 0; +// } +// +// Any program that raises a fatal signal (such as `SIGSEGV`, `SIGILL`, +// `SIGFPE`, `SIGABRT`, `SIGTERM`, `SIGBUG`, and `SIGTRAP`) will call the +// installed failure signal handler and provide debugging information to stderr. +// +// Note that you should *not* install the Abseil failure signal handler more +// than once. You may, of course, have another (non-Abseil) failure signal +// handler installed (which would be triggered if Abseil's failure signal +// handler sets `call_previous_handler` to `true`). + +#ifndef ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ +#define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// FailureSignalHandlerOptions +// +// Struct for holding `absl::InstallFailureSignalHandler()` configuration +// options. +struct FailureSignalHandlerOptions { + // If true, try to symbolize the stacktrace emitted on failure, provided that + // you have initialized a symbolizer for that purpose. (See symbolize.h for + // more information.) + bool symbolize_stacktrace = true; + + // If true, try to run signal handlers on an alternate stack (if supported on + // the given platform). An alternate stack is useful for program crashes due + // to a stack overflow; by running on a alternate stack, the signal handler + // may run even when normal stack space has been exausted. The downside of + // using an alternate stack is that extra memory for the alternate stack needs + // to be pre-allocated. + bool use_alternate_stack = true; + + // If positive, indicates the number of seconds after which the failure signal + // handler is invoked to abort the program. Setting such an alarm is useful in + // cases where the failure signal handler itself may become hung or + // deadlocked. + int alarm_on_failure_secs = 3; + + // If true, call the previously registered signal handler for the signal that + // was received (if one was registered) after the existing signal handler + // runs. This mechanism can be used to chain signal handlers together. + // + // If false, the signal is raised to the default handler for that signal + // (which normally terminates the program). + // + // IMPORTANT: If true, the chained fatal signal handlers must not try to + // recover from the fatal signal. Instead, they should terminate the program + // via some mechanism, like raising the default handler for the signal, or by + // calling `_exit()`. Note that the failure signal handler may put parts of + // the Abseil library into a state from which they cannot recover. + bool call_previous_handler = false; + + // If non-null, indicates a pointer to a callback function that will be called + // upon failure, with a std::string argument containing failure data. This function + // may be used as a hook to write failure data to a secondary location, such + // as a log file. This function may also be called with null data, as a hint + // to flush any buffered data before the program may be terminated. Consider + // flushing any buffered data in all calls to this function. + // + // Since this function runs within a signal handler, it should be + // async-signal-safe if possible. + // See http://man7.org/linux/man-pages/man7/signal-safety.7.html + void (*writerfn)(const char*) = nullptr; +}; + +// InstallFailureSignalHandler() +// +// Installs a signal handler for the common failure signals `SIGSEGV`, `SIGILL`, +// `SIGFPE`, `SIGABRT`, `SIGTERM`, `SIGBUG`, and `SIGTRAP` (provided they exist +// on the given platform). The failure signal handler dumps program failure data +// useful for debugging in an unspecified format to stderr. This data may +// include the program counter, a stacktrace, and register information on some +// systems; do not rely on an exact format for the output, as it is subject to +// change. +void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options); + +namespace debugging_internal { +const char* FailureSignalToString(int signo); +} // namespace debugging_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/address_is_readable.h b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/address_is_readable.h new file mode 100644 index 0000000000000000000000000000000000000000..4bbaf4d69bd8e91242c8cd0584a3c91477be7791 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/address_is_readable.h @@ -0,0 +1,32 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ +#define ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// Return whether the byte at *addr is readable, without faulting. +// Save and restores errno. +bool AddressIsReadable(const void *addr); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/demangle.h b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/demangle.h new file mode 100644 index 0000000000000000000000000000000000000000..c314d9bc237c0dd129799bc869871c2f997248eb --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/demangle.h @@ -0,0 +1,71 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. + +// An async-signal-safe and thread-safe demangler for Itanium C++ ABI +// (aka G++ V3 ABI). +// +// The demangler is implemented to be used in async signal handlers to +// symbolize stack traces. We cannot use libstdc++'s +// abi::__cxa_demangle() in such signal handlers since it's not async +// signal safe (it uses malloc() internally). +// +// Note that this demangler doesn't support full demangling. More +// specifically, it doesn't print types of function parameters and +// types of template arguments. It just skips them. However, it's +// still very useful to extract basic information such as class, +// function, constructor, destructor, and operator names. +// +// See the implementation note in demangle.cc if you are interested. +// +// Example: +// +// | Mangled Name | The Demangler | abi::__cxa_demangle() +// |---------------|---------------|----------------------- +// | _Z1fv | f() | f() +// | _Z1fi | f() | f(int) +// | _Z3foo3bar | foo() | foo(bar) +// | _Z1fIiEvi | f<>() | void f(int) +// | _ZN1N1fE | N::f | N::f +// | _ZN3Foo3BarEv | Foo::Bar() | Foo::Bar() +// | _Zrm1XS_" | operator%() | operator%(X, X) +// | _ZN3FooC1Ev | Foo::Foo() | Foo::Foo() +// | _Z1fSs | f() | f(std::basic_string, +// | | | std::allocator >) +// +// See the unit test for more examples. +// +// Note: we might want to write demanglers for ABIs other than Itanium +// C++ ABI in the future. +// + +#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ +#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// Demangle `mangled`. On success, return true and write the +// demangled symbol name to `out`. Otherwise, return false. +// `out` is modified even if demangling is unsuccessful. +bool Demangle(const char *mangled, char *out, int out_size); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/elf_mem_image.h b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/elf_mem_image.h new file mode 100644 index 0000000000000000000000000000000000000000..46bfade3503ebcfd4c109c25613011671f6905f2 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/elf_mem_image.h @@ -0,0 +1,134 @@ +/* + * Copyright 2017 The Abseil Authors. + * + * 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 + * + * https://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. + */ + +// Allow dynamic symbol lookup for in-memory Elf images. + +#ifndef ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_ +#define ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_ + +// Including this will define the __GLIBC__ macro if glibc is being +// used. +#include + +#include "absl/base/config.h" + +// Maybe one day we can rewrite this file not to require the elf +// symbol extensions in glibc, but for right now we need them. +#ifdef ABSL_HAVE_ELF_MEM_IMAGE +#error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set +#endif + +#if defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \ + !defined(__asmjs__) && !defined(__wasm__) +#define ABSL_HAVE_ELF_MEM_IMAGE 1 +#endif + +#ifdef ABSL_HAVE_ELF_MEM_IMAGE + +#include // for ElfW + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// An in-memory ELF image (may not exist on disk). +class ElfMemImage { + private: + // Sentinel: there could never be an elf image at &kInvalidBaseSentinel. + static const int kInvalidBaseSentinel; + + public: + // Sentinel: there could never be an elf image at this address. + static constexpr const void *const kInvalidBase = + static_cast(&kInvalidBaseSentinel); + + // Information about a single vdso symbol. + // All pointers are into .dynsym, .dynstr, or .text of the VDSO. + // Do not free() them or modify through them. + struct SymbolInfo { + const char *name; // E.g. "__vdso_getcpu" + const char *version; // E.g. "LINUX_2.6", could be "" + // for unversioned symbol. + const void *address; // Relocated symbol address. + const ElfW(Sym) *symbol; // Symbol in the dynamic symbol table. + }; + + // Supports iteration over all dynamic symbols. + class SymbolIterator { + public: + friend class ElfMemImage; + const SymbolInfo *operator->() const; + const SymbolInfo &operator*() const; + SymbolIterator& operator++(); + bool operator!=(const SymbolIterator &rhs) const; + bool operator==(const SymbolIterator &rhs) const; + private: + SymbolIterator(const void *const image, int index); + void Update(int incr); + SymbolInfo info_; + int index_; + const void *const image_; + }; + + + explicit ElfMemImage(const void *base); + void Init(const void *base); + bool IsPresent() const { return ehdr_ != nullptr; } + const ElfW(Phdr)* GetPhdr(int index) const; + const ElfW(Sym)* GetDynsym(int index) const; + const ElfW(Versym)* GetVersym(int index) const; + const ElfW(Verdef)* GetVerdef(int index) const; + const ElfW(Verdaux)* GetVerdefAux(const ElfW(Verdef) *verdef) const; + const char* GetDynstr(ElfW(Word) offset) const; + const void* GetSymAddr(const ElfW(Sym) *sym) const; + const char* GetVerstr(ElfW(Word) offset) const; + int GetNumSymbols() const; + + SymbolIterator begin() const; + SymbolIterator end() const; + + // Look up versioned dynamic symbol in the image. + // Returns false if image is not present, or doesn't contain given + // symbol/version/type combination. + // If info_out is non-null, additional details are filled in. + bool LookupSymbol(const char *name, const char *version, + int symbol_type, SymbolInfo *info_out) const; + + // Find info about symbol (if any) which overlaps given address. + // Returns true if symbol was found; false if image isn't present + // or doesn't have a symbol overlapping given address. + // If info_out is non-null, additional details are filled in. + bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const; + + private: + const ElfW(Ehdr) *ehdr_; + const ElfW(Sym) *dynsym_; + const ElfW(Versym) *versym_; + const ElfW(Verdef) *verdef_; + const ElfW(Word) *hash_; + const char *dynstr_; + size_t strsize_; + size_t verdefnum_; + ElfW(Addr) link_base_; // Link-time base (p_vaddr of first PT_LOAD). +}; + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HAVE_ELF_MEM_IMAGE + +#endif // ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/examine_stack.h b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/examine_stack.h new file mode 100644 index 0000000000000000000000000000000000000000..393369131f1e540946abaf7a8223d16f8adf8b6d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/examine_stack.h @@ -0,0 +1,42 @@ +// +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ +#define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// Returns the program counter from signal context, or nullptr if +// unknown. `vuc` is a ucontext_t*. We use void* to avoid the use of +// ucontext_t on non-POSIX systems. +void* GetProgramCounter(void* vuc); + +// Uses `writerfn` to dump the program counter, stack trace, and stack +// frame sizes. +void DumpPCAndFrameSizesAndStackTrace( + void* pc, void* const stack[], int frame_sizes[], int depth, + int min_dropped_frames, bool symbolize_stacktrace, + void (*writerfn)(const char*, void*), void* writerfn_arg); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stack_consumption.h b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stack_consumption.h new file mode 100644 index 0000000000000000000000000000000000000000..5e60ec422916f6b4a4f46b0ee64a17eacf533f3d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stack_consumption.h @@ -0,0 +1,49 @@ +// +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. + +// Helper function for measuring stack consumption of signal handlers. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_ +#define ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_ + +#include "absl/base/config.h" + +// The code in this module is not portable. +// Use this feature test macro to detect its availability. +#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION +#error ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION cannot be set directly +#elif !defined(__APPLE__) && !defined(_WIN32) && \ + (defined(__i386__) || defined(__x86_64__) || defined(__ppc__)) +#define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1 + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// Returns the stack consumption in bytes for the code exercised by +// signal_handler. To measure stack consumption, signal_handler is registered +// as a signal handler, so the code that it exercises must be async-signal +// safe. The argument of signal_handler is an implementation detail of signal +// handlers and should ignored by the code for signal_handler. Use global +// variables to pass information between your test code and signal_handler. +int GetSignalHandlerStackConsumption(void (*signal_handler)(int)); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION + +#endif // ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_aarch64-inl.inc b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_aarch64-inl.inc new file mode 100644 index 0000000000000000000000000000000000000000..411ea308e90bf1dfab19d5970141d0c7f82f19db --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_aarch64-inl.inc @@ -0,0 +1,192 @@ +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ + +// Generate stack tracer for aarch64 + +#if defined(__linux__) +#include +#include +#include +#endif + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/debugging/internal/address_is_readable.h" +#include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems +#include "absl/debugging/stacktrace.h" + +static const uintptr_t kUnknownFrameSize = 0; + +#if defined(__linux__) +// Returns the address of the VDSO __kernel_rt_sigreturn function, if present. +static const unsigned char* GetKernelRtSigreturnAddress() { + constexpr uintptr_t kImpossibleAddress = 1; + ABSL_CONST_INIT static std::atomic memoized{kImpossibleAddress}; + uintptr_t address = memoized.load(std::memory_order_relaxed); + if (address != kImpossibleAddress) { + return reinterpret_cast(address); + } + + address = reinterpret_cast(nullptr); + +#ifdef ABSL_HAVE_VDSO_SUPPORT + absl::debugging_internal::VDSOSupport vdso; + if (vdso.IsPresent()) { + absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; + if (!vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.6.39", STT_FUNC, + &symbol_info) || + symbol_info.address == nullptr) { + // Unexpected: VDSO is present, yet the expected symbol is missing + // or null. + assert(false && "VDSO is present, but doesn't have expected symbol"); + } else { + if (reinterpret_cast(symbol_info.address) != + kImpossibleAddress) { + address = reinterpret_cast(symbol_info.address); + } else { + assert(false && "VDSO returned invalid address"); + } + } + } +#endif + + memoized.store(address, std::memory_order_relaxed); + return reinterpret_cast(address); +} +#endif // __linux__ + +// Compute the size of a stack frame in [low..high). We assume that +// low < high. Return size of kUnknownFrameSize. +template +static inline uintptr_t ComputeStackFrameSize(const T* low, + const T* high) { + const char* low_char_ptr = reinterpret_cast(low); + const char* high_char_ptr = reinterpret_cast(high); + return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize; +} + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return null if no stackframe can be found. Perform sanity +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +static void **NextStackFrame(void **old_frame_pointer, const void *uc) { + void **new_frame_pointer = reinterpret_cast(*old_frame_pointer); + bool check_frame_size = true; + +#if defined(__linux__) + if (WITH_CONTEXT && uc != nullptr) { + // Check to see if next frame's return address is __kernel_rt_sigreturn. + if (old_frame_pointer[1] == GetKernelRtSigreturnAddress()) { + const ucontext_t *ucv = static_cast(uc); + // old_frame_pointer[0] is not suitable for unwinding, look at + // ucontext to discover frame pointer before signal. + void **const pre_signal_frame_pointer = + reinterpret_cast(ucv->uc_mcontext.regs[29]); + + // Check that alleged frame pointer is actually readable. This is to + // prevent "double fault" in case we hit the first fault due to e.g. + // stack corruption. + if (!absl::debugging_internal::AddressIsReadable( + pre_signal_frame_pointer)) + return nullptr; + + // Alleged frame pointer is readable, use it for further unwinding. + new_frame_pointer = pre_signal_frame_pointer; + + // Skip frame size check if we return from a signal. We may be using a + // an alternate stack for signals. + check_frame_size = false; + } + } +#endif + + // aarch64 ABI requires stack pointer to be 16-byte-aligned. + if ((reinterpret_cast(new_frame_pointer) & 15) != 0) + return nullptr; + + // Check frame size. In strict mode, we assume frames to be under + // 100,000 bytes. In non-strict mode, we relax the limit to 1MB. + if (check_frame_size) { + const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; + const uintptr_t frame_size = + ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); + if (frame_size == kUnknownFrameSize || frame_size > max_size) + return nullptr; + } + + return new_frame_pointer; +} + +template +static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { +#ifdef __GNUC__ + void **frame_pointer = reinterpret_cast(__builtin_frame_address(0)); +#else +# error reading stack point not yet supported on this platform. +#endif + + skip_count++; // Skip the frame for this function. + int n = 0; + + // The frame pointer points to low address of a frame. The first 64-bit + // word of a frame points to the next frame up the call chain, which normally + // is just after the high address of the current frame. The second word of + // a frame contains return adress of to the caller. To find a pc value + // associated with the current frame, we need to go down a level in the call + // chain. So we remember return the address of the last frame seen. This + // does not work for the first stack frame, which belongs to UnwindImp() but + // we skip the frame for UnwindImp() anyway. + void* prev_return_address = nullptr; + + while (frame_pointer && n < max_depth) { + // The absl::GetStackFrames routine is called when we are in some + // informational context (the failure signal handler for example). + // Use the non-strict unwinding rules to produce a stack trace + // that is as complete as possible (even if it contains a few bogus + // entries in some rare cases). + void **next_frame_pointer = + NextStackFrame(frame_pointer, ucp); + + if (skip_count > 0) { + skip_count--; + } else { + result[n] = prev_return_address; + if (IS_STACK_FRAMES) { + sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer); + } + n++; + } + prev_return_address = frame_pointer[1]; + frame_pointer = next_frame_pointer; + } + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 200; + int j = 0; + for (; frame_pointer != nullptr && j < kMaxUnwind; j++) { + frame_pointer = + NextStackFrame(frame_pointer, ucp); + } + *min_dropped_frames = j; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return true; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_arm-inl.inc b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_arm-inl.inc new file mode 100644 index 0000000000000000000000000000000000000000..fffda968dde6ba139709b1f076d31e2e2d96d0d4 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_arm-inl.inc @@ -0,0 +1,125 @@ +// Copyright 2011 and onwards Google Inc. +// All rights reserved. +// +// Author: Doug Kwan +// This is inspired by Craig Silverstein's PowerPC stacktrace code. +// + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ + +#include + +#include "absl/debugging/stacktrace.h" + +// WARNING: +// This only works if all your code is in either ARM or THUMB mode. With +// interworking, the frame pointer of the caller can either be in r11 (ARM +// mode) or r7 (THUMB mode). A callee only saves the frame pointer of its +// mode in a fixed location on its stack frame. If the caller is a different +// mode, there is no easy way to find the frame pointer. It can either be +// still in the designated register or saved on stack along with other callee +// saved registers. + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return nullptr if no stackframe can be found. Perform sanity +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +static void **NextStackFrame(void **old_sp) { + void **new_sp = (void**) old_sp[-1]; + + // Check that the transition from frame pointer old_sp to frame + // pointer new_sp isn't clearly bogus + if (STRICT_UNWINDING) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (new_sp <= old_sp) return nullptr; + // Assume stack frames larger than 100,000 bytes are bogus. + if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return nullptr; + } else { + // In the non-strict mode, allow discontiguous stack frames. + // (alternate-signal-stacks for example). + if (new_sp == old_sp) return nullptr; + // And allow frames upto about 1MB. + if ((new_sp > old_sp) + && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return nullptr; + } + if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return nullptr; + return new_sp; +} + +// This ensures that absl::GetStackTrace sets up the Link Register properly. +#ifdef __GNUC__ +void StacktraceArmDummyFunction() __attribute__((noinline)); +void StacktraceArmDummyFunction() { __asm__ volatile(""); } +#else +# error StacktraceArmDummyFunction() needs to be ported to this platform. +#endif + +template +static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, + const void * /* ucp */, int *min_dropped_frames) { +#ifdef __GNUC__ + void **sp = reinterpret_cast(__builtin_frame_address(0)); +#else +# error reading stack point not yet supported on this platform. +#endif + + // On ARM, the return address is stored in the link register (r14). + // This is not saved on the stack frame of a leaf function. To + // simplify code that reads return addresses, we call a dummy + // function so that the return address of this function is also + // stored in the stack frame. This works at least for gcc. + StacktraceArmDummyFunction(); + + int n = 0; + while (sp && n < max_depth) { + // The absl::GetStackFrames routine is called when we are in some + // informational context (the failure signal handler for example). + // Use the non-strict unwinding rules to produce a stack trace + // that is as complete as possible (even if it contains a few bogus + // entries in some rare cases). + void **next_sp = NextStackFrame(sp); + + if (skip_count > 0) { + skip_count--; + } else { + result[n] = *sp; + + if (IS_STACK_FRAMES) { + if (next_sp > sp) { + sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp; + } else { + // A frame-size of 0 is used to indicate unknown frame size. + sizes[n] = 0; + } + } + n++; + } + sp = next_sp; + } + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 200; + int j = 0; + for (; sp != nullptr && j < kMaxUnwind; j++) { + sp = NextStackFrame(sp); + } + *min_dropped_frames = j; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return false; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_config.h b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_config.h new file mode 100644 index 0000000000000000000000000000000000000000..d4e8480a8e280f38bd99934ff54ed2bb9f6aaaa1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_config.h @@ -0,0 +1,70 @@ +/* + * Copyright 2017 The Abseil Authors. + * + * 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 + * + * https://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. + + * Defines ABSL_STACKTRACE_INL_HEADER to the *-inl.h containing + * actual unwinder implementation. + * This header is "private" to stacktrace.cc. + * DO NOT include it into any other files. +*/ +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ + +#if defined(ABSL_STACKTRACE_INL_HEADER) +#error ABSL_STACKTRACE_INL_HEADER cannot be directly set + +#elif defined(_WIN32) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_win32-inl.inc" + +#elif defined(__linux__) && !defined(__ANDROID__) + +#if !defined(NO_FRAME_POINTER) +# if defined(__i386__) || defined(__x86_64__) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_x86-inl.inc" +# elif defined(__ppc__) || defined(__PPC__) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_powerpc-inl.inc" +# elif defined(__aarch64__) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_aarch64-inl.inc" +# elif defined(__arm__) +// Note: When using glibc this may require -funwind-tables to function properly. +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_generic-inl.inc" +# else +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_unimplemented-inl.inc" +# endif +#else // defined(NO_FRAME_POINTER) +# if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_generic-inl.inc" +# elif defined(__ppc__) || defined(__PPC__) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_generic-inl.inc" +# else +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_unimplemented-inl.inc" +# endif +#endif // NO_FRAME_POINTER + +#else +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_unimplemented-inl.inc" + +#endif + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_generic-inl.inc b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_generic-inl.inc new file mode 100644 index 0000000000000000000000000000000000000000..ac034c9f5b2efa7e8283a0ef99eb0aab4e0c3af6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_generic-inl.inc @@ -0,0 +1,99 @@ +// Copyright 2000 - 2007 Google Inc. +// All rights reserved. +// +// Author: Sanjay Ghemawat +// +// Portable implementation - just use glibc +// +// Note: The glibc implementation may cause a call to malloc. +// This can cause a deadlock in HeapProfiler. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_ + +#include +#include +#include + +#include "absl/debugging/stacktrace.h" +#include "absl/base/attributes.h" + +// Sometimes, we can try to get a stack trace from within a stack +// trace, because we don't block signals inside this code (which would be too +// expensive: the two extra system calls per stack trace do matter here). +// That can cause a self-deadlock. +// Protect against such reentrant call by failing to get a stack trace. +// +// We use __thread here because the code here is extremely low level -- it is +// called while collecting stack traces from within malloc and mmap, and thus +// can not call anything which might call malloc or mmap itself. +static __thread int recursive = 0; + +// The stack trace function might be invoked very early in the program's +// execution (e.g. from the very first malloc if using tcmalloc). Also, the +// glibc implementation itself will trigger malloc the first time it is called. +// As such, we suppress usage of backtrace during this early stage of execution. +static std::atomic disable_stacktraces(true); // Disabled until healthy. +// Waiting until static initializers run seems to be late enough. +// This file is included into stacktrace.cc so this will only run once. +ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() { + void* unused_stack[1]; + // Force the first backtrace to happen early to get the one-time shared lib + // loading (allocation) out of the way. After the first call it is much safer + // to use backtrace from a signal handler if we crash somewhere later. + backtrace(unused_stack, 1); + disable_stacktraces.store(false, std::memory_order_relaxed); + return 0; +}(); + +template +static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { + if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) { + return 0; + } + ++recursive; + + static_cast(ucp); // Unused. + static const int kStackLength = 64; + void * stack[kStackLength]; + int size; + + size = backtrace(stack, kStackLength); + skip_count++; // we want to skip the current frame as well + int result_count = size - skip_count; + if (result_count < 0) + result_count = 0; + if (result_count > max_depth) + result_count = max_depth; + for (int i = 0; i < result_count; i++) + result[i] = stack[i + skip_count]; + + if (IS_STACK_FRAMES) { + // No implementation for finding out the stack frame sizes yet. + memset(sizes, 0, sizeof(*sizes) * result_count); + } + if (min_dropped_frames != nullptr) { + if (size - skip_count - max_depth > 0) { + *min_dropped_frames = size - skip_count - max_depth; + } else { + *min_dropped_frames = 0; + } + } + + --recursive; + + return result_count; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return true; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_powerpc-inl.inc b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_powerpc-inl.inc new file mode 100644 index 0000000000000000000000000000000000000000..2e7c2f404f20e07a01319d0bd5dbeedcaa6461f9 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_powerpc-inl.inc @@ -0,0 +1,248 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Produce stack trace. I'm guessing (hoping!) the code is much like +// for x86. For apple machines, at least, it seems to be; see +// https://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html +// https://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK +// Linux has similar code: http://patchwork.ozlabs.org/linuxppc/patch?id=8882 + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_ + +#if defined(__linux__) +#include // for PT_NIP. +#include // for ucontext_t +#endif + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/optimization.h" +#include "absl/base/port.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/internal/address_is_readable.h" +#include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems + +// Given a stack pointer, return the saved link register value. +// Note that this is the link register for a callee. +static inline void *StacktracePowerPCGetLR(void **sp) { + // PowerPC has 3 main ABIs, which say where in the stack the + // Link Register is. For DARWIN and AIX (used by apple and + // linux ppc64), it's in sp[2]. For SYSV (used by linux ppc), + // it's in sp[1]. +#if defined(_CALL_AIX) || defined(_CALL_DARWIN) + return *(sp+2); +#elif defined(_CALL_SYSV) + return *(sp+1); +#elif defined(__APPLE__) || defined(__FreeBSD__) || \ + (defined(__linux__) && defined(__PPC64__)) + // This check is in case the compiler doesn't define _CALL_AIX/etc. + return *(sp+2); +#elif defined(__linux) + // This check is in case the compiler doesn't define _CALL_SYSV. + return *(sp+1); +#else +#error Need to specify the PPC ABI for your archiecture. +#endif +} + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return null if no stackframe can be found. Perform sanity +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static void **NextStackFrame(void **old_sp, const void *uc) { + void **new_sp = (void **) *old_sp; + enum { kStackAlignment = 16 }; + + // Check that the transition from frame pointer old_sp to frame + // pointer new_sp isn't clearly bogus + if (STRICT_UNWINDING) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (new_sp <= old_sp) return nullptr; + // Assume stack frames larger than 100,000 bytes are bogus. + if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return nullptr; + } else { + // In the non-strict mode, allow discontiguous stack frames. + // (alternate-signal-stacks for example). + if (new_sp == old_sp) return nullptr; + // And allow frames upto about 1MB. + if ((new_sp > old_sp) + && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return nullptr; + } + if ((uintptr_t)new_sp % kStackAlignment != 0) return nullptr; + +#if defined(__linux__) + enum StackTraceKernelSymbolStatus { + kNotInitialized = 0, kAddressValid, kAddressInvalid }; + + if (IS_WITH_CONTEXT && uc != nullptr) { + static StackTraceKernelSymbolStatus kernel_symbol_status = + kNotInitialized; // Sentinel: not computed yet. + // Initialize with sentinel value: __kernel_rt_sigtramp_rt64 can not + // possibly be there. + static const unsigned char *kernel_sigtramp_rt64_address = nullptr; + if (kernel_symbol_status == kNotInitialized) { + absl::debugging_internal::VDSOSupport vdso; + if (vdso.IsPresent()) { + absl::debugging_internal::VDSOSupport::SymbolInfo + sigtramp_rt64_symbol_info; + if (!vdso.LookupSymbol( + "__kernel_sigtramp_rt64", "LINUX_2.6.15", + absl::debugging_internal::VDSOSupport::kVDSOSymbolType, + &sigtramp_rt64_symbol_info) || + sigtramp_rt64_symbol_info.address == nullptr) { + // Unexpected: VDSO is present, yet the expected symbol is missing + // or null. + assert(false && "VDSO is present, but doesn't have expected symbol"); + kernel_symbol_status = kAddressInvalid; + } else { + kernel_sigtramp_rt64_address = + reinterpret_cast( + sigtramp_rt64_symbol_info.address); + kernel_symbol_status = kAddressValid; + } + } else { + kernel_symbol_status = kAddressInvalid; + } + } + + if (new_sp != nullptr && + kernel_symbol_status == kAddressValid && + StacktracePowerPCGetLR(new_sp) == kernel_sigtramp_rt64_address) { + const ucontext_t* signal_context = + reinterpret_cast(uc); + void **const sp_before_signal = + reinterpret_cast(signal_context->uc_mcontext.gp_regs[PT_R1]); + // Check that alleged sp before signal is nonnull and is reasonably + // aligned. + if (sp_before_signal != nullptr && + ((uintptr_t)sp_before_signal % kStackAlignment) == 0) { + // Check that alleged stack pointer is actually readable. This is to + // prevent a "double fault" in case we hit the first fault due to e.g. + // a stack corruption. + if (absl::debugging_internal::AddressIsReadable(sp_before_signal)) { + // Alleged stack pointer is readable, use it for further unwinding. + new_sp = sp_before_signal; + } + } + } + } +#endif + + return new_sp; +} + +// This ensures that absl::GetStackTrace sets up the Link Register properly. +ABSL_ATTRIBUTE_NOINLINE static void AbslStacktracePowerPCDummyFunction() { + ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); +} + +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { + void **sp; + // Apple macOS uses an old version of gnu as -- both Darwin 7.9.0 (Panther) + // and Darwin 8.8.1 (Tiger) use as 1.38. This means we have to use a + // different asm syntax. I don't know quite the best way to discriminate + // systems using the old as from the new one; I've gone with __APPLE__. +#ifdef __APPLE__ + __asm__ volatile ("mr %0,r1" : "=r" (sp)); +#else + __asm__ volatile ("mr %0,1" : "=r" (sp)); +#endif + + // On PowerPC, the "Link Register" or "Link Record" (LR), is a stack + // entry that holds the return address of the subroutine call (what + // instruction we run after our function finishes). This is the + // same as the stack-pointer of our parent routine, which is what we + // want here. While the compiler will always(?) set up LR for + // subroutine calls, it may not for leaf functions (such as this one). + // This routine forces the compiler (at least gcc) to push it anyway. + AbslStacktracePowerPCDummyFunction(); + + // The LR save area is used by the callee, so the top entry is bogus. + skip_count++; + + int n = 0; + + // Unlike ABIs of X86 and ARM, PowerPC ABIs say that return address (in + // the link register) of a function call is stored in the caller's stack + // frame instead of the callee's. When we look for the return address + // associated with a stack frame, we need to make sure that there is a + // caller frame before it. So we call NextStackFrame before entering the + // loop below and check next_sp instead of sp for loop termination. + // The outermost frame is set up by runtimes and it does not have a + // caller frame, so it is skipped. + + // The absl::GetStackFrames routine is called when we are in some + // informational context (the failure signal handler for example). + // Use the non-strict unwinding rules to produce a stack trace + // that is as complete as possible (even if it contains a few + // bogus entries in some rare cases). + void **next_sp = NextStackFrame(sp, ucp); + + while (next_sp && n < max_depth) { + if (skip_count > 0) { + skip_count--; + } else { + result[n] = StacktracePowerPCGetLR(sp); + if (IS_STACK_FRAMES) { + if (next_sp > sp) { + sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp; + } else { + // A frame-size of 0 is used to indicate unknown frame size. + sizes[n] = 0; + } + } + n++; + } + + sp = next_sp; + next_sp = NextStackFrame(sp, ucp); + } + + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 1000; + int j = 0; + for (; next_sp != nullptr && j < kMaxUnwind; j++) { + next_sp = NextStackFrame(next_sp, ucp); + } + *min_dropped_frames = j; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return true; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_unimplemented-inl.inc b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_unimplemented-inl.inc new file mode 100644 index 0000000000000000000000000000000000000000..5b8fb191b65a33000b1c1d2ce53907b4d6e2f2b1 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_unimplemented-inl.inc @@ -0,0 +1,24 @@ +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_ + +template +static int UnwindImpl(void** /* result */, int* /* sizes */, + int /* max_depth */, int /* skip_count */, + const void* /* ucp */, int *min_dropped_frames) { + if (min_dropped_frames != nullptr) { + *min_dropped_frames = 0; + } + return 0; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return false; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_win32-inl.inc b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_win32-inl.inc new file mode 100644 index 0000000000000000000000000000000000000000..9c2c558044d88c332c5f748e228aec581741abb2 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_win32-inl.inc @@ -0,0 +1,85 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Produces a stack trace for Windows. Normally, one could use +// stacktrace_x86-inl.h or stacktrace_x86_64-inl.h -- and indeed, that +// should work for binaries compiled using MSVC in "debug" mode. +// However, in "release" mode, Windows uses frame-pointer +// optimization, which makes getting a stack trace very difficult. +// +// There are several approaches one can take. One is to use Windows +// intrinsics like StackWalk64. These can work, but have restrictions +// on how successful they can be. Another attempt is to write a +// version of stacktrace_x86-inl.h that has heuristic support for +// dealing with FPO, similar to what WinDbg does (see +// http://www.nynaeve.net/?p=97). There are (non-working) examples of +// these approaches, complete with TODOs, in stacktrace_win32-inl.h#1 +// +// The solution we've ended up doing is to call the undocumented +// windows function RtlCaptureStackBackTrace, which probably doesn't +// work with FPO but at least is fast, and doesn't require a symbol +// server. +// +// This code is inspired by a patch from David Vitek: +// https://code.google.com/p/google-perftools/issues/detail?id=83 + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ + +#include // for GetProcAddress and GetModuleHandle +#include + +typedef USHORT NTAPI RtlCaptureStackBackTrace_Function( + IN ULONG frames_to_skip, + IN ULONG frames_to_capture, + OUT PVOID *backtrace, + OUT PULONG backtrace_hash); + +// Load the function we need at static init time, where we don't have +// to worry about someone else holding the loader's lock. +static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = + (RtlCaptureStackBackTrace_Function*) + GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace"); + +template +static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, + const void*, int* min_dropped_frames) { + int n = 0; + if (!RtlCaptureStackBackTrace_fn) { + // can't find a stacktrace with no function to call + } else { + n = (int)RtlCaptureStackBackTrace_fn(skip_count + 2, max_depth, result, 0); + } + if (IS_STACK_FRAMES) { + // No implementation for finding out the stack frame sizes yet. + memset(sizes, 0, sizeof(*sizes) * n); + } + if (min_dropped_frames != nullptr) { + // Not implemented. + *min_dropped_frames = 0; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return false; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_x86-inl.inc b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_x86-inl.inc new file mode 100644 index 0000000000000000000000000000000000000000..bc320ff75bc533774107e8ee1efc46296c92e29d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/stacktrace_x86-inl.inc @@ -0,0 +1,346 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// Produce stack trace + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_ + +#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__)) +#include // for ucontext_t +#endif + +#if !defined(_WIN32) +#include +#endif + +#include +#include + +#include "absl/base/macros.h" +#include "absl/base/port.h" +#include "absl/debugging/internal/address_is_readable.h" +#include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems +#include "absl/debugging/stacktrace.h" + +#include "absl/base/internal/raw_logging.h" + +using absl::debugging_internal::AddressIsReadable; + +#if defined(__linux__) && defined(__i386__) +// Count "push %reg" instructions in VDSO __kernel_vsyscall(), +// preceeding "syscall" or "sysenter". +// If __kernel_vsyscall uses frame pointer, answer 0. +// +// kMaxBytes tells how many instruction bytes of __kernel_vsyscall +// to analyze before giving up. Up to kMaxBytes+1 bytes of +// instructions could be accessed. +// +// Here are known __kernel_vsyscall instruction sequences: +// +// SYSENTER (linux-2.6.26/arch/x86/vdso/vdso32/sysenter.S). +// Used on Intel. +// 0xffffe400 <__kernel_vsyscall+0>: push %ecx +// 0xffffe401 <__kernel_vsyscall+1>: push %edx +// 0xffffe402 <__kernel_vsyscall+2>: push %ebp +// 0xffffe403 <__kernel_vsyscall+3>: mov %esp,%ebp +// 0xffffe405 <__kernel_vsyscall+5>: sysenter +// +// SYSCALL (see linux-2.6.26/arch/x86/vdso/vdso32/syscall.S). +// Used on AMD. +// 0xffffe400 <__kernel_vsyscall+0>: push %ebp +// 0xffffe401 <__kernel_vsyscall+1>: mov %ecx,%ebp +// 0xffffe403 <__kernel_vsyscall+3>: syscall +// + +// The sequence below isn't actually expected in Google fleet, +// here only for completeness. Remove this comment from OSS release. + +// i386 (see linux-2.6.26/arch/x86/vdso/vdso32/int80.S) +// 0xffffe400 <__kernel_vsyscall+0>: int $0x80 +// 0xffffe401 <__kernel_vsyscall+1>: ret +// +static const int kMaxBytes = 10; + +// We use assert()s instead of DCHECK()s -- this is too low level +// for DCHECK(). + +static int CountPushInstructions(const unsigned char *const addr) { + int result = 0; + for (int i = 0; i < kMaxBytes; ++i) { + if (addr[i] == 0x89) { + // "mov reg,reg" + if (addr[i + 1] == 0xE5) { + // Found "mov %esp,%ebp". + return 0; + } + ++i; // Skip register encoding byte. + } else if (addr[i] == 0x0F && + (addr[i + 1] == 0x34 || addr[i + 1] == 0x05)) { + // Found "sysenter" or "syscall". + return result; + } else if ((addr[i] & 0xF0) == 0x50) { + // Found "push %reg". + ++result; + } else if (addr[i] == 0xCD && addr[i + 1] == 0x80) { + // Found "int $0x80" + assert(result == 0); + return 0; + } else { + // Unexpected instruction. + assert(false && "unexpected instruction in __kernel_vsyscall"); + return 0; + } + } + // Unexpected: didn't find SYSENTER or SYSCALL in + // [__kernel_vsyscall, __kernel_vsyscall + kMaxBytes) interval. + assert(false && "did not find SYSENTER or SYSCALL in __kernel_vsyscall"); + return 0; +} +#endif + +// Assume stack frames larger than 100,000 bytes are bogus. +static const int kMaxFrameBytes = 100000; + +// Returns the stack frame pointer from signal context, 0 if unknown. +// vuc is a ucontext_t *. We use void* to avoid the use +// of ucontext_t on non-POSIX systems. +static uintptr_t GetFP(const void *vuc) { +#if !defined(__linux__) + static_cast(vuc); // Avoid an unused argument compiler warning. +#else + if (vuc != nullptr) { + auto *uc = reinterpret_cast(vuc); +#if defined(__i386__) + const auto bp = uc->uc_mcontext.gregs[REG_EBP]; + const auto sp = uc->uc_mcontext.gregs[REG_ESP]; +#elif defined(__x86_64__) + const auto bp = uc->uc_mcontext.gregs[REG_RBP]; + const auto sp = uc->uc_mcontext.gregs[REG_RSP]; +#else + const uintptr_t bp = 0; + const uintptr_t sp = 0; +#endif + // Sanity-check that the base pointer is valid. It should be as long as + // SHRINK_WRAP_FRAME_POINTER is not set, but it's possible that some code in + // the process is compiled with --copt=-fomit-frame-pointer or + // --copt=-momit-leaf-frame-pointer. + // + // TODO(bcmills): -momit-leaf-frame-pointer is currently the default + // behavior when building with clang. Talk to the C++ toolchain team about + // fixing that. + if (bp >= sp && bp - sp <= kMaxFrameBytes) return bp; + + // If bp isn't a plausible frame pointer, return the stack pointer instead. + // If we're lucky, it points to the start of a stack frame; otherwise, we'll + // get one frame of garbage in the stack trace and fail the sanity check on + // the next iteration. + return sp; + } +#endif + return 0; +} + +// Given a pointer to a stack frame, locate and return the calling +// stackframe, or return null if no stackframe can be found. Perform sanity +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static void **NextStackFrame(void **old_fp, const void *uc) { + void **new_fp = (void **)*old_fp; + +#if defined(__linux__) && defined(__i386__) + if (WITH_CONTEXT && uc != nullptr) { + // How many "push %reg" instructions are there at __kernel_vsyscall? + // This is constant for a given kernel and processor, so compute + // it only once. + static int num_push_instructions = -1; // Sentinel: not computed yet. + // Initialize with sentinel value: __kernel_rt_sigreturn can not possibly + // be there. + static const unsigned char *kernel_rt_sigreturn_address = nullptr; + static const unsigned char *kernel_vsyscall_address = nullptr; + if (num_push_instructions == -1) { +#ifdef ABSL_HAVE_VDSO_SUPPORT + absl::debugging_internal::VDSOSupport vdso; + if (vdso.IsPresent()) { + absl::debugging_internal::VDSOSupport::SymbolInfo + rt_sigreturn_symbol_info; + absl::debugging_internal::VDSOSupport::SymbolInfo vsyscall_symbol_info; + if (!vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.5", STT_FUNC, + &rt_sigreturn_symbol_info) || + !vdso.LookupSymbol("__kernel_vsyscall", "LINUX_2.5", STT_FUNC, + &vsyscall_symbol_info) || + rt_sigreturn_symbol_info.address == nullptr || + vsyscall_symbol_info.address == nullptr) { + // Unexpected: 32-bit VDSO is present, yet one of the expected + // symbols is missing or null. + assert(false && "VDSO is present, but doesn't have expected symbols"); + num_push_instructions = 0; + } else { + kernel_rt_sigreturn_address = + reinterpret_cast( + rt_sigreturn_symbol_info.address); + kernel_vsyscall_address = + reinterpret_cast( + vsyscall_symbol_info.address); + num_push_instructions = + CountPushInstructions(kernel_vsyscall_address); + } + } else { + num_push_instructions = 0; + } +#else // ABSL_HAVE_VDSO_SUPPORT + num_push_instructions = 0; +#endif // ABSL_HAVE_VDSO_SUPPORT + } + if (num_push_instructions != 0 && kernel_rt_sigreturn_address != nullptr && + old_fp[1] == kernel_rt_sigreturn_address) { + const ucontext_t *ucv = static_cast(uc); + // This kernel does not use frame pointer in its VDSO code, + // and so %ebp is not suitable for unwinding. + void **const reg_ebp = + reinterpret_cast(ucv->uc_mcontext.gregs[REG_EBP]); + const unsigned char *const reg_eip = + reinterpret_cast(ucv->uc_mcontext.gregs[REG_EIP]); + if (new_fp == reg_ebp && kernel_vsyscall_address <= reg_eip && + reg_eip - kernel_vsyscall_address < kMaxBytes) { + // We "stepped up" to __kernel_vsyscall, but %ebp is not usable. + // Restore from 'ucv' instead. + void **const reg_esp = + reinterpret_cast(ucv->uc_mcontext.gregs[REG_ESP]); + // Check that alleged %esp is not null and is reasonably aligned. + if (reg_esp && + ((uintptr_t)reg_esp & (sizeof(reg_esp) - 1)) == 0) { + // Check that alleged %esp is actually readable. This is to prevent + // "double fault" in case we hit the first fault due to e.g. stack + // corruption. + void *const reg_esp2 = reg_esp[num_push_instructions - 1]; + if (AddressIsReadable(reg_esp2)) { + // Alleged %esp is readable, use it for further unwinding. + new_fp = reinterpret_cast(reg_esp2); + } + } + } + } + } +#endif + + const uintptr_t old_fp_u = reinterpret_cast(old_fp); + const uintptr_t new_fp_u = reinterpret_cast(new_fp); + + // Check that the transition from frame pointer old_fp to frame + // pointer new_fp isn't clearly bogus. Skip the checks if new_fp + // matches the signal context, so that we don't skip out early when + // using an alternate signal stack. + // + // TODO(bcmills): The GetFP call should be completely unnecessary when + // SHRINK_WRAP_FRAME_POINTER is set (because we should be back in the thread's + // stack by this point), but it is empirically still needed (e.g. when the + // stack includes a call to abort). unw_get_reg returns UNW_EBADREG for some + // frames. Figure out why GetValidFrameAddr and/or libunwind isn't doing what + // it's supposed to. + if (STRICT_UNWINDING && + (!WITH_CONTEXT || uc == nullptr || new_fp_u != GetFP(uc))) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (new_fp_u <= old_fp_u) return nullptr; + if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr; + } else { + if (new_fp == nullptr) return nullptr; // skip AddressIsReadable() below + // In the non-strict mode, allow discontiguous stack frames. + // (alternate-signal-stacks for example). + if (new_fp == old_fp) return nullptr; + } + + if (new_fp_u & (sizeof(void *) - 1)) return nullptr; +#ifdef __i386__ + // On 32-bit machines, the stack pointer can be very close to + // 0xffffffff, so we explicitly check for a pointer into the + // last two pages in the address space + if (new_fp_u >= 0xffffe000) return nullptr; +#endif +#if !defined(_WIN32) + if (!STRICT_UNWINDING) { + // Lax sanity checks cause a crash in 32-bit tcmalloc/crash_reason_test + // on AMD-based machines with VDSO-enabled kernels. + // Make an extra sanity check to insure new_fp is readable. + // Note: NextStackFrame() is only called while the program + // is already on its last leg, so it's ok to be slow here. + + if (!AddressIsReadable(new_fp)) { + return nullptr; + } + } +#endif + return new_fp; +} + +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +ABSL_ATTRIBUTE_NOINLINE +static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { + int n = 0; + void **fp = reinterpret_cast(__builtin_frame_address(0)); + + while (fp && n < max_depth) { + if (*(fp + 1) == reinterpret_cast(0)) { + // In 64-bit code, we often see a frame that + // points to itself and has a return address of 0. + break; + } + void **next_fp = NextStackFrame(fp, ucp); + if (skip_count > 0) { + skip_count--; + } else { + result[n] = *(fp + 1); + if (IS_STACK_FRAMES) { + if (next_fp > fp) { + sizes[n] = (uintptr_t)next_fp - (uintptr_t)fp; + } else { + // A frame-size of 0 is used to indicate unknown frame size. + sizes[n] = 0; + } + } + n++; + } + fp = next_fp; + } + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 1000; + int j = 0; + for (; fp != nullptr && j < kMaxUnwind; j++) { + fp = NextStackFrame(fp, ucp); + } + *min_dropped_frames = j; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { + return true; +} +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/symbolize.h b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/symbolize.h new file mode 100644 index 0000000000000000000000000000000000000000..5d0858b5c7e1bc5c2265daca651077782dbcb281 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/symbolize.h @@ -0,0 +1,128 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains internal parts of the Abseil symbolizer. +// Do not depend on the anything in this file, it may change at anytime. + +#ifndef ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ +#define ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ + +#include +#include + +#include "absl/base/config.h" + +#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE +#error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set +#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \ + !defined(__asmjs__) && !defined(__wasm__) +#define ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE 1 + +#include +#include // For ElfW() macro. +#include +#include + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// Iterates over all sections, invoking callback on each with the section name +// and the section header. +// +// Returns true on success; otherwise returns false in case of errors. +// +// This is not async-signal-safe. +bool ForEachSection(int fd, + const std::function& callback); + +// Gets the section header for the given name, if it exists. Returns true on +// success. Otherwise, returns false. +bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, + ElfW(Shdr) *out); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +struct SymbolDecoratorArgs { + // The program counter we are getting symbolic name for. + const void *pc; + // 0 for main executable, load address for shared libraries. + ptrdiff_t relocation; + // Read-only file descriptor for ELF image covering "pc", + // or -1 if no such ELF image exists in /proc/self/maps. + int fd; + // Output buffer, size. + // Note: the buffer may not be empty -- default symbolizer may have already + // produced some output, and earlier decorators may have adorned it in + // some way. You are free to replace or augment the contents (within the + // symbol_buf_size limit). + char *const symbol_buf; + size_t symbol_buf_size; + // Temporary scratch space, size. + // Use that space in preference to allocating your own stack buffer to + // conserve stack. + char *const tmp_buf; + size_t tmp_buf_size; + // User-provided argument + void* arg; +}; +using SymbolDecorator = void (*)(const SymbolDecoratorArgs *); + +// Installs a function-pointer as a decorator. Returns a value less than zero +// if the system cannot install the decorator. Otherwise, returns a unique +// identifier corresponding to the decorator. This identifier can be used to +// uninstall the decorator - See RemoveSymbolDecorator() below. +int InstallSymbolDecorator(SymbolDecorator decorator, void* arg); + +// Removes a previously installed function-pointer decorator. Parameter "ticket" +// is the return-value from calling InstallSymbolDecorator(). +bool RemoveSymbolDecorator(int ticket); + +// Remove all installed decorators. Returns true if successful, false if +// symbolization is currently in progress. +bool RemoveAllSymbolDecorators(void); + +// Registers an address range to a file mapping. +// +// Preconditions: +// start <= end +// filename != nullptr +// +// Returns true if the file was successfully registered. +bool RegisterFileMappingHint( + const void* start, const void* end, uint64_t offset, const char* filename); + +// Looks up the file mapping registered by RegisterFileMappingHint for an +// address range. If there is one, the file name is stored in *filename and +// *start and *end are modified to reflect the registered mapping. Returns +// whether any hint was found. +bool GetFileMappingHint(const void** start, + const void** end, + uint64_t * offset, + const char** filename); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/internal/vdso_support.h b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/vdso_support.h new file mode 100644 index 0000000000000000000000000000000000000000..6562c6c2350aaa2d40c5341737c9a6a63589ba47 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/internal/vdso_support.h @@ -0,0 +1,158 @@ +// +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// + +// Allow dynamic symbol lookup in the kernel VDSO page. +// +// VDSO stands for "Virtual Dynamic Shared Object" -- a page of +// executable code, which looks like a shared library, but doesn't +// necessarily exist anywhere on disk, and which gets mmap()ed into +// every process by kernels which support VDSO, such as 2.6.x for 32-bit +// executables, and 2.6.24 and above for 64-bit executables. +// +// More details could be found here: +// http://www.trilithium.com/johan/2005/08/linux-gate/ +// +// VDSOSupport -- a class representing kernel VDSO (if present). +// +// Example usage: +// VDSOSupport vdso; +// VDSOSupport::SymbolInfo info; +// typedef (*FN)(unsigned *, void *, void *); +// FN fn = nullptr; +// if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) { +// fn = reinterpret_cast(info.address); +// } + +#ifndef ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ +#define ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ + +#include + +#include "absl/base/attributes.h" +#include "absl/debugging/internal/elf_mem_image.h" + +#ifdef ABSL_HAVE_ELF_MEM_IMAGE + +#ifdef ABSL_HAVE_VDSO_SUPPORT +#error ABSL_HAVE_VDSO_SUPPORT cannot be directly set +#else +#define ABSL_HAVE_VDSO_SUPPORT 1 +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { + +// NOTE: this class may be used from within tcmalloc, and can not +// use any memory allocation routines. +class VDSOSupport { + public: + VDSOSupport(); + + typedef ElfMemImage::SymbolInfo SymbolInfo; + typedef ElfMemImage::SymbolIterator SymbolIterator; + + // On PowerPC64 VDSO symbols can either be of type STT_FUNC or STT_NOTYPE + // depending on how the kernel is built. The kernel is normally built with + // STT_NOTYPE type VDSO symbols. Let's make things simpler first by using a + // compile-time constant. +#ifdef __powerpc64__ + enum { kVDSOSymbolType = STT_NOTYPE }; +#else + enum { kVDSOSymbolType = STT_FUNC }; +#endif + + // Answers whether we have a vdso at all. + bool IsPresent() const { return image_.IsPresent(); } + + // Allow to iterate over all VDSO symbols. + SymbolIterator begin() const { return image_.begin(); } + SymbolIterator end() const { return image_.end(); } + + // Look up versioned dynamic symbol in the kernel VDSO. + // Returns false if VDSO is not present, or doesn't contain given + // symbol/version/type combination. + // If info_out != nullptr, additional details are filled in. + bool LookupSymbol(const char *name, const char *version, + int symbol_type, SymbolInfo *info_out) const; + + // Find info about symbol (if any) which overlaps given address. + // Returns true if symbol was found; false if VDSO isn't present + // or doesn't have a symbol overlapping given address. + // If info_out != nullptr, additional details are filled in. + bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const; + + // Used only for testing. Replace real VDSO base with a mock. + // Returns previous value of vdso_base_. After you are done testing, + // you are expected to call SetBase() with previous value, in order to + // reset state to the way it was. + const void *SetBase(const void *s); + + // Computes vdso_base_ and returns it. Should be called as early as + // possible; before any thread creation, chroot or setuid. + static const void *Init(); + + private: + // image_ represents VDSO ELF image in memory. + // image_.ehdr_ == nullptr implies there is no VDSO. + ElfMemImage image_; + + // Cached value of auxv AT_SYSINFO_EHDR, computed once. + // This is a tri-state: + // kInvalidBase => value hasn't been determined yet. + // 0 => there is no VDSO. + // else => vma of VDSO Elf{32,64}_Ehdr. + // + // When testing with mock VDSO, low bit is set. + // The low bit is always available because vdso_base_ is + // page-aligned. + static std::atomic vdso_base_; + + // NOLINT on 'long' because these routines mimic kernel api. + // The 'cache' parameter may be used by some versions of the kernel, + // and should be nullptr or point to a static buffer containing at + // least two 'long's. + static long InitAndGetCPU(unsigned *cpu, void *cache, // NOLINT 'long'. + void *unused); + static long GetCPUViaSyscall(unsigned *cpu, void *cache, // NOLINT 'long'. + void *unused); + typedef long (*GetCpuFn)(unsigned *cpu, void *cache, // NOLINT 'long'. + void *unused); + + // This function pointer may point to InitAndGetCPU, + // GetCPUViaSyscall, or __vdso_getcpu at different stages of initialization. + ABSL_CONST_INIT static std::atomic getcpu_fn_; + + friend int GetCPU(void); // Needs access to getcpu_fn_. + + VDSOSupport(const VDSOSupport&) = delete; + VDSOSupport& operator=(const VDSOSupport&) = delete; +}; + +// Same as sched_getcpu() on later glibc versions. +// Return current CPU, using (fast) __vdso_getcpu@LINUX_2.6 if present, +// otherwise use syscall(SYS_getcpu,...). +// May return -1 with errno == ENOSYS if the kernel doesn't +// support SYS_getcpu. +int GetCPU(); + +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HAVE_ELF_MEM_IMAGE + +#endif // ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/leak_check.h b/libs/or-tools-src-ubuntu/include/absl/debugging/leak_check.h new file mode 100644 index 0000000000000000000000000000000000000000..7a5a22dd1cafd4bd1c0c99e53f388847b1290cff --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/leak_check.h @@ -0,0 +1,113 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: leak_check.h +// ----------------------------------------------------------------------------- +// +// This file contains functions that affect leak checking behavior within +// targets built with the LeakSanitizer (LSan), a memory leak detector that is +// integrated within the AddressSanitizer (ASan) as an additional component, or +// which can be used standalone. LSan and ASan are included (or can be provided) +// as additional components for most compilers such as Clang, gcc and MSVC. +// Note: this leak checking API is not yet supported in MSVC. +// Leak checking is enabled by default in all ASan builds. +// +// See https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer +// +// ----------------------------------------------------------------------------- +#ifndef ABSL_DEBUGGING_LEAK_CHECK_H_ +#define ABSL_DEBUGGING_LEAK_CHECK_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// HaveLeakSanitizer() +// +// Returns true if a leak-checking sanitizer (either ASan or standalone LSan) is +// currently built into this target. +bool HaveLeakSanitizer(); + +// DoIgnoreLeak() +// +// Implements `IgnoreLeak()` below. This function should usually +// not be called directly; calling `IgnoreLeak()` is preferred. +void DoIgnoreLeak(const void* ptr); + +// IgnoreLeak() +// +// Instruct the leak sanitizer to ignore leak warnings on the object referenced +// by the passed pointer, as well as all heap objects transitively referenced +// by it. The passed object pointer can point to either the beginning of the +// object or anywhere within it. +// +// Example: +// +// static T* obj = IgnoreLeak(new T(...)); +// +// If the passed `ptr` does not point to an actively allocated object at the +// time `IgnoreLeak()` is called, the call is a no-op; if it is actively +// allocated, the object must not get deallocated later. +// +template +T* IgnoreLeak(T* ptr) { + DoIgnoreLeak(ptr); + return ptr; +} + +// LeakCheckDisabler +// +// This helper class indicates that any heap allocations done in the code block +// covered by the scoped object, which should be allocated on the stack, will +// not be reported as leaks. Leak check disabling will occur within the code +// block and any nested function calls within the code block. +// +// Example: +// +// void Foo() { +// LeakCheckDisabler disabler; +// ... code that allocates objects whose leaks should be ignored ... +// } +// +// REQUIRES: Destructor runs in same thread as constructor +class LeakCheckDisabler { + public: + LeakCheckDisabler(); + LeakCheckDisabler(const LeakCheckDisabler&) = delete; + LeakCheckDisabler& operator=(const LeakCheckDisabler&) = delete; + ~LeakCheckDisabler(); +}; + +// RegisterLivePointers() +// +// Registers `ptr[0,size-1]` as pointers to memory that is still actively being +// referenced and for which leak checking should be ignored. This function is +// useful if you store pointers in mapped memory, for memory ranges that we know +// are correct but for which normal analysis would flag as leaked code. +void RegisterLivePointers(const void* ptr, size_t size); + +// UnRegisterLivePointers() +// +// Deregisters the pointers previously marked as active in +// `RegisterLivePointers()`, enabling leak checking of those pointers. +void UnRegisterLivePointers(const void* ptr, size_t size); + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_LEAK_CHECK_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/stacktrace.h b/libs/or-tools-src-ubuntu/include/absl/debugging/stacktrace.h new file mode 100644 index 0000000000000000000000000000000000000000..0ec0ffdabd422e07be93660860202c61c4c60406 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/stacktrace.h @@ -0,0 +1,231 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: stacktrace.h +// ----------------------------------------------------------------------------- +// +// This file contains routines to extract the current stack trace and associated +// stack frames. These functions are thread-safe and async-signal-safe. +// +// Note that stack trace functionality is platform dependent and requires +// additional support from the compiler/build system in most cases. (That is, +// this functionality generally only works on platforms/builds that have been +// specifically configured to support it.) +// +// Note: stack traces in Abseil that do not utilize a symbolizer will result in +// frames consisting of function addresses rather than human-readable function +// names. (See symbolize.h for information on symbolizing these values.) + +#ifndef ABSL_DEBUGGING_STACKTRACE_H_ +#define ABSL_DEBUGGING_STACKTRACE_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// GetStackFrames() +// +// Records program counter values for up to `max_depth` frames, skipping the +// most recent `skip_count` stack frames, stores their corresponding values +// and sizes in `results` and `sizes` buffers, and returns the number of frames +// stored. (Note that the frame generated for the `absl::GetStackFrames()` +// routine itself is also skipped.) +// +// Example: +// +// main() { foo(); } +// foo() { bar(); } +// bar() { +// void* result[10]; +// int sizes[10]; +// int depth = absl::GetStackFrames(result, sizes, 10, 1); +// } +// +// The current stack frame would consist of three function calls: `bar()`, +// `foo()`, and then `main()`; however, since the `GetStackFrames()` call sets +// `skip_count` to `1`, it will skip the frame for `bar()`, the most recently +// invoked function call. It will therefore return 2 and fill `result` with +// program counters within the following functions: +// +// result[0] foo() +// result[1] main() +// +// (Note: in practice, a few more entries after `main()` may be added to account +// for startup processes.) +// +// Corresponding stack frame sizes will also be recorded: +// +// sizes[0] 16 +// sizes[1] 16 +// +// (Stack frame sizes of `16` above are just for illustration purposes.) +// +// Stack frame sizes of 0 or less indicate that those frame sizes couldn't +// be identified. +// +// This routine may return fewer stack frame entries than are +// available. Also note that `result` and `sizes` must both be non-null. +extern int GetStackFrames(void** result, int* sizes, int max_depth, + int skip_count); + +// GetStackFramesWithContext() +// +// Records program counter values obtained from a signal handler. Records +// program counter values for up to `max_depth` frames, skipping the most recent +// `skip_count` stack frames, stores their corresponding values and sizes in +// `results` and `sizes` buffers, and returns the number of frames stored. (Note +// that the frame generated for the `absl::GetStackFramesWithContext()` routine +// itself is also skipped.) +// +// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value +// passed to a signal handler registered via the `sa_sigaction` field of a +// `sigaction` struct. (See +// http://man7.org/linux/man-pages/man2/sigaction.2.html.) The `uc` value may +// help a stack unwinder to provide a better stack trace under certain +// conditions. `uc` may safely be null. +// +// The `min_dropped_frames` output parameter, if non-null, points to the +// location to note any dropped stack frames, if any, due to buffer limitations +// or other reasons. (This value will be set to `0` if no frames were dropped.) +// The number of total stack frames is guaranteed to be >= skip_count + +// max_depth + *min_dropped_frames. +extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth, + int skip_count, const void* uc, + int* min_dropped_frames); + +// GetStackTrace() +// +// Records program counter values for up to `max_depth` frames, skipping the +// most recent `skip_count` stack frames, stores their corresponding values +// in `results`, and returns the number of frames +// stored. Note that this function is similar to `absl::GetStackFrames()` +// except that it returns the stack trace only, and not stack frame sizes. +// +// Example: +// +// main() { foo(); } +// foo() { bar(); } +// bar() { +// void* result[10]; +// int depth = absl::GetStackTrace(result, 10, 1); +// } +// +// This produces: +// +// result[0] foo +// result[1] main +// .... ... +// +// `result` must not be null. +extern int GetStackTrace(void** result, int max_depth, int skip_count); + +// GetStackTraceWithContext() +// +// Records program counter values obtained from a signal handler. Records +// program counter values for up to `max_depth` frames, skipping the most recent +// `skip_count` stack frames, stores their corresponding values in `results`, +// and returns the number of frames stored. (Note that the frame generated for +// the `absl::GetStackFramesWithContext()` routine itself is also skipped.) +// +// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value +// passed to a signal handler registered via the `sa_sigaction` field of a +// `sigaction` struct. (See +// http://man7.org/linux/man-pages/man2/sigaction.2.html.) The `uc` value may +// help a stack unwinder to provide a better stack trace under certain +// conditions. `uc` may safely be null. +// +// The `min_dropped_frames` output parameter, if non-null, points to the +// location to note any dropped stack frames, if any, due to buffer limitations +// or other reasons. (This value will be set to `0` if no frames were dropped.) +// The number of total stack frames is guaranteed to be >= skip_count + +// max_depth + *min_dropped_frames. +extern int GetStackTraceWithContext(void** result, int max_depth, + int skip_count, const void* uc, + int* min_dropped_frames); + +// SetStackUnwinder() +// +// Provides a custom function for unwinding stack frames that will be used in +// place of the default stack unwinder when invoking the static +// GetStack{Frames,Trace}{,WithContext}() functions above. +// +// The arguments passed to the unwinder function will match the +// arguments passed to `absl::GetStackFramesWithContext()` except that sizes +// will be non-null iff the caller is interested in frame sizes. +// +// If unwinder is set to null, we revert to the default stack-tracing behavior. +// +// ***************************************************************************** +// WARNING +// ***************************************************************************** +// +// absl::SetStackUnwinder is not suitable for general purpose use. It is +// provided for custom runtimes. +// Some things to watch out for when calling `absl::SetStackUnwinder()`: +// +// (a) The unwinder may be called from within signal handlers and +// therefore must be async-signal-safe. +// +// (b) Even after a custom stack unwinder has been unregistered, other +// threads may still be in the process of using that unwinder. +// Therefore do not clean up any state that may be needed by an old +// unwinder. +// ***************************************************************************** +extern void SetStackUnwinder(int (*unwinder)(void** pcs, int* sizes, + int max_depth, int skip_count, + const void* uc, + int* min_dropped_frames)); + +// DefaultStackUnwinder() +// +// Records program counter values of up to `max_depth` frames, skipping the most +// recent `skip_count` stack frames, and stores their corresponding values in +// `pcs`. (Note that the frame generated for this call itself is also skipped.) +// This function acts as a generic stack-unwinder; prefer usage of the more +// specific `GetStack{Trace,Frames}{,WithContext}()` functions above. +// +// If you have set your own stack unwinder (with the `SetStackUnwinder()` +// function above, you can still get the default stack unwinder by calling +// `DefaultStackUnwinder()`, which will ignore any previously set stack unwinder +// and use the default one instead. +// +// Because this function is generic, only `pcs` is guaranteed to be non-null +// upon return. It is legal for `sizes`, `uc`, and `min_dropped_frames` to all +// be null when called. +// +// The semantics are the same as the corresponding `GetStack*()` function in the +// case where `absl::SetStackUnwinder()` was never called. Equivalents are: +// +// null sizes | non-nullptr sizes +// |==========================================================| +// null uc | GetStackTrace() | GetStackFrames() | +// non-null uc | GetStackTraceWithContext() | GetStackFramesWithContext() | +// |==========================================================| +extern int DefaultStackUnwinder(void** pcs, int* sizes, int max_depth, + int skip_count, const void* uc, + int* min_dropped_frames); + +namespace debugging_internal { +// Returns true for platforms which are expected to have functioning stack trace +// implementations. Intended to be used for tests which want to exclude +// verification of logic known to be broken because stack traces are not +// working. +extern bool StackTraceWorksForTest(); +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_STACKTRACE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize.h b/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize.h new file mode 100644 index 0000000000000000000000000000000000000000..43d93a86822eb5129a9c995bbda54370a973528e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize.h @@ -0,0 +1,99 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: symbolize.h +// ----------------------------------------------------------------------------- +// +// This file configures the Abseil symbolizer for use in converting instruction +// pointer addresses (program counters) into human-readable names (function +// calls, etc.) within Abseil code. +// +// The symbolizer may be invoked from several sources: +// +// * Implicitly, through the installation of an Abseil failure signal handler. +// (See failure_signal_handler.h for more information.) +// * By calling `Symbolize()` directly on a program counter you obtain through +// `absl::GetStackTrace()` or `absl::GetStackFrames()`. (See stacktrace.h +// for more information. +// * By calling `Symbolize()` directly on a program counter you obtain through +// other means (which would be platform-dependent). +// +// In all of the above cases, the symbolizer must first be initialized before +// any program counter values can be symbolized. If you are installing a failure +// signal handler, initialize the symbolizer before you do so. +// +// Example: +// +// int main(int argc, char** argv) { +// // Initialize the Symbolizer before installing the failure signal handler +// absl::InitializeSymbolizer(argv[0]); +// +// // Now you may install the failure signal handler +// absl::FailureSignalHandlerOptions options; +// absl::InstallFailureSignalHandler(options); +// +// // Start running your main program +// ... +// return 0; +// } +// +#ifndef ABSL_DEBUGGING_SYMBOLIZE_H_ +#define ABSL_DEBUGGING_SYMBOLIZE_H_ + +#include "absl/debugging/internal/symbolize.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// InitializeSymbolizer() +// +// Initializes the program counter symbolizer, given the path of the program +// (typically obtained through `main()`s `argv[0]`). The Abseil symbolizer +// allows you to read program counters (instruction pointer values) using their +// human-readable names within output such as stack traces. +// +// Example: +// +// int main(int argc, char *argv[]) { +// absl::InitializeSymbolizer(argv[0]); +// // Now you can use the symbolizer +// } +void InitializeSymbolizer(const char* argv0); +// +// Symbolize() +// +// Symbolizes a program counter (instruction pointer value) `pc` and, on +// success, writes the name to `out`. The symbol name is demangled, if possible. +// Note that the symbolized name may be truncated and will be NUL-terminated. +// Demangling is supported for symbols generated by GCC 3.x or newer). Returns +// `false` on failure. +// +// Example: +// +// // Print a program counter and its symbol name. +// static void DumpPCAndSymbol(void *pc) { +// char tmp[1024]; +// const char *symbol = "(unknown)"; +// if (absl::Symbolize(pc, tmp, sizeof(tmp))) { +// symbol = tmp; +// } +// absl::PrintF("%p %s\n", pc, symbol); +// } +bool Symbolize(const void *pc, char *out, int out_size); + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_SYMBOLIZE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize_elf.inc b/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize_elf.inc new file mode 100644 index 0000000000000000000000000000000000000000..c371635ffa0a50d6571949a2ce732829b65bb6ec --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize_elf.inc @@ -0,0 +1,1480 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 library provides Symbolize() function that symbolizes program +// counters to their corresponding symbol names on linux platforms. +// This library has a minimal implementation of an ELF symbol table +// reader (i.e. it doesn't depend on libelf, etc.). +// +// The algorithm used in Symbolize() is as follows. +// +// 1. Go through a list of maps in /proc/self/maps and find the map +// containing the program counter. +// +// 2. Open the mapped file and find a regular symbol table inside. +// Iterate over symbols in the symbol table and look for the symbol +// containing the program counter. If such a symbol is found, +// obtain the symbol name, and demangle the symbol if possible. +// If the symbol isn't found in the regular symbol table (binary is +// stripped), try the same thing with a dynamic symbol table. +// +// Note that Symbolize() is originally implemented to be used in +// signal handlers, hence it doesn't use malloc() and other unsafe +// operations. It should be both thread-safe and async-signal-safe. +// +// Implementation note: +// +// We don't use heaps but only use stacks. We want to reduce the +// stack consumption so that the symbolizer can run on small stacks. +// +// Here are some numbers collected with GCC 4.1.0 on x86: +// - sizeof(Elf32_Sym) = 16 +// - sizeof(Elf32_Shdr) = 40 +// - sizeof(Elf64_Sym) = 24 +// - sizeof(Elf64_Shdr) = 64 +// +// This implementation is intended to be async-signal-safe but uses some +// functions which are not guaranteed to be so, such as memchr() and +// memmove(). We assume they are async-signal-safe. + +#include +#include +#include +#include // For ElfW() macro. +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/casts.h" +#include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/low_level_alloc.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" +#include "absl/base/port.h" +#include "absl/debugging/internal/demangle.h" +#include "absl/debugging/internal/vdso_support.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Value of argv[0]. Used by MaybeInitializeObjFile(). +static char *argv0_value = nullptr; + +void InitializeSymbolizer(const char *argv0) { + if (argv0_value != nullptr) { + free(argv0_value); + argv0_value = nullptr; + } + if (argv0 != nullptr && argv0[0] != '\0') { + argv0_value = strdup(argv0); + } +} + +namespace debugging_internal { +namespace { + +// Re-runs fn until it doesn't cause EINTR. +#define NO_INTR(fn) \ + do { \ + } while ((fn) < 0 && errno == EINTR) + +// On Linux, ELF_ST_* are defined in . To make this portable +// we define our own ELF_ST_BIND and ELF_ST_TYPE if not available. +#ifndef ELF_ST_BIND +#define ELF_ST_BIND(info) (((unsigned char)(info)) >> 4) +#endif + +#ifndef ELF_ST_TYPE +#define ELF_ST_TYPE(info) (((unsigned char)(info)) & 0xF) +#endif + +// Some platforms use a special .opd section to store function pointers. +const char kOpdSectionName[] = ".opd"; + +#if (defined(__powerpc__) && !(_CALL_ELF > 1)) || defined(__ia64) +// Use opd section for function descriptors on these platforms, the function +// address is the first word of the descriptor. +enum { kPlatformUsesOPDSections = 1 }; +#else // not PPC or IA64 +enum { kPlatformUsesOPDSections = 0 }; +#endif + +// This works for PowerPC & IA64 only. A function descriptor consist of two +// pointers and the first one is the function's entry. +const size_t kFunctionDescriptorSize = sizeof(void *) * 2; + +const int kMaxDecorators = 10; // Seems like a reasonable upper limit. + +struct InstalledSymbolDecorator { + SymbolDecorator fn; + void *arg; + int ticket; +}; + +int g_num_decorators; +InstalledSymbolDecorator g_decorators[kMaxDecorators]; + +struct FileMappingHint { + const void *start; + const void *end; + uint64_t offset; + const char *filename; +}; + +// Protects g_decorators. +// We are using SpinLock and not a Mutex here, because we may be called +// from inside Mutex::Lock itself, and it prohibits recursive calls. +// This happens in e.g. base/stacktrace_syscall_unittest. +// Moreover, we are using only TryLock(), if the decorator list +// is being modified (is busy), we skip all decorators, and possibly +// loose some info. Sorry, that's the best we could do. +base_internal::SpinLock g_decorators_mu(base_internal::kLinkerInitialized); + +const int kMaxFileMappingHints = 8; +int g_num_file_mapping_hints; +FileMappingHint g_file_mapping_hints[kMaxFileMappingHints]; +// Protects g_file_mapping_hints. +base_internal::SpinLock g_file_mapping_mu(base_internal::kLinkerInitialized); + +// Async-signal-safe function to zero a buffer. +// memset() is not guaranteed to be async-signal-safe. +static void SafeMemZero(void* p, size_t size) { + unsigned char *c = static_cast(p); + while (size--) { + *c++ = 0; + } +} + +struct ObjFile { + ObjFile() + : filename(nullptr), + start_addr(nullptr), + end_addr(nullptr), + offset(0), + fd(-1), + elf_type(-1) { + SafeMemZero(&elf_header, sizeof(elf_header)); + } + + char *filename; + const void *start_addr; + const void *end_addr; + uint64_t offset; + + // The following fields are initialized on the first access to the + // object file. + int fd; + int elf_type; + ElfW(Ehdr) elf_header; +}; + +// Build 4-way associative cache for symbols. Within each cache line, symbols +// are replaced in LRU order. +enum { + ASSOCIATIVITY = 4, +}; +struct SymbolCacheLine { + const void *pc[ASSOCIATIVITY]; + char *name[ASSOCIATIVITY]; + + // age[i] is incremented when a line is accessed. it's reset to zero if the + // i'th entry is read. + uint32_t age[ASSOCIATIVITY]; +}; + +// --------------------------------------------------------------- +// An async-signal-safe arena for LowLevelAlloc +static std::atomic g_sig_safe_arena; + +static base_internal::LowLevelAlloc::Arena *SigSafeArena() { + return g_sig_safe_arena.load(std::memory_order_acquire); +} + +static void InitSigSafeArena() { + if (SigSafeArena() == nullptr) { + base_internal::LowLevelAlloc::Arena *new_arena = + base_internal::LowLevelAlloc::NewArena( + base_internal::LowLevelAlloc::kAsyncSignalSafe); + base_internal::LowLevelAlloc::Arena *old_value = nullptr; + if (!g_sig_safe_arena.compare_exchange_strong(old_value, new_arena, + std::memory_order_release, + std::memory_order_relaxed)) { + // We lost a race to allocate an arena; deallocate. + base_internal::LowLevelAlloc::DeleteArena(new_arena); + } + } +} + +// --------------------------------------------------------------- +// An AddrMap is a vector of ObjFile, using SigSafeArena() for allocation. + +class AddrMap { + public: + AddrMap() : size_(0), allocated_(0), obj_(nullptr) {} + ~AddrMap() { base_internal::LowLevelAlloc::Free(obj_); } + int Size() const { return size_; } + ObjFile *At(int i) { return &obj_[i]; } + ObjFile *Add(); + void Clear(); + + private: + int size_; // count of valid elements (<= allocated_) + int allocated_; // count of allocated elements + ObjFile *obj_; // array of allocated_ elements + AddrMap(const AddrMap &) = delete; + AddrMap &operator=(const AddrMap &) = delete; +}; + +void AddrMap::Clear() { + for (int i = 0; i != size_; i++) { + At(i)->~ObjFile(); + } + size_ = 0; +} + +ObjFile *AddrMap::Add() { + if (size_ == allocated_) { + int new_allocated = allocated_ * 2 + 50; + ObjFile *new_obj_ = + static_cast(base_internal::LowLevelAlloc::AllocWithArena( + new_allocated * sizeof(*new_obj_), SigSafeArena())); + if (obj_) { + memcpy(new_obj_, obj_, allocated_ * sizeof(*new_obj_)); + base_internal::LowLevelAlloc::Free(obj_); + } + obj_ = new_obj_; + allocated_ = new_allocated; + } + return new (&obj_[size_++]) ObjFile; +} + +// --------------------------------------------------------------- + +enum FindSymbolResult { SYMBOL_NOT_FOUND = 1, SYMBOL_TRUNCATED, SYMBOL_FOUND }; + +class Symbolizer { + public: + Symbolizer(); + ~Symbolizer(); + const char *GetSymbol(const void *const pc); + + private: + char *CopyString(const char *s) { + int len = strlen(s); + char *dst = static_cast( + base_internal::LowLevelAlloc::AllocWithArena(len + 1, SigSafeArena())); + ABSL_RAW_CHECK(dst != nullptr, "out of memory"); + memcpy(dst, s, len + 1); + return dst; + } + ObjFile *FindObjFile(const void *const start, + size_t size) ABSL_ATTRIBUTE_NOINLINE; + static bool RegisterObjFile(const char *filename, + const void *const start_addr, + const void *const end_addr, uint64_t offset, + void *arg); + SymbolCacheLine *GetCacheLine(const void *const pc); + const char *FindSymbolInCache(const void *const pc); + const char *InsertSymbolInCache(const void *const pc, const char *name); + void AgeSymbols(SymbolCacheLine *line); + void ClearAddrMap(); + FindSymbolResult GetSymbolFromObjectFile(const ObjFile &obj, + const void *const pc, + const ptrdiff_t relocation, + char *out, int out_size, + char *tmp_buf, int tmp_buf_size); + + enum { + SYMBOL_BUF_SIZE = 3072, + TMP_BUF_SIZE = 1024, + SYMBOL_CACHE_LINES = 128, + }; + + AddrMap addr_map_; + + bool ok_; + bool addr_map_read_; + + char symbol_buf_[SYMBOL_BUF_SIZE]; + + // tmp_buf_ will be used to store arrays of ElfW(Shdr) and ElfW(Sym) + // so we ensure that tmp_buf_ is properly aligned to store either. + alignas(16) char tmp_buf_[TMP_BUF_SIZE]; + static_assert(alignof(ElfW(Shdr)) <= 16, + "alignment of tmp buf too small for Shdr"); + static_assert(alignof(ElfW(Sym)) <= 16, + "alignment of tmp buf too small for Sym"); + + SymbolCacheLine symbol_cache_[SYMBOL_CACHE_LINES]; +}; + +static std::atomic g_cached_symbolizer; + +} // namespace + +static int SymbolizerSize() { +#if defined(__wasm__) || defined(__asmjs__) + int pagesize = getpagesize(); +#else + int pagesize = sysconf(_SC_PAGESIZE); +#endif + return ((sizeof(Symbolizer) - 1) / pagesize + 1) * pagesize; +} + +// Return (and set null) g_cached_symbolized_state if it is not null. +// Otherwise return a new symbolizer. +static Symbolizer *AllocateSymbolizer() { + InitSigSafeArena(); + Symbolizer *symbolizer = + g_cached_symbolizer.exchange(nullptr, std::memory_order_acquire); + if (symbolizer != nullptr) { + return symbolizer; + } + return new (base_internal::LowLevelAlloc::AllocWithArena( + SymbolizerSize(), SigSafeArena())) Symbolizer(); +} + +// Set g_cached_symbolize_state to s if it is null, otherwise +// delete s. +static void FreeSymbolizer(Symbolizer *s) { + Symbolizer *old_cached_symbolizer = nullptr; + if (!g_cached_symbolizer.compare_exchange_strong(old_cached_symbolizer, s, + std::memory_order_release, + std::memory_order_relaxed)) { + s->~Symbolizer(); + base_internal::LowLevelAlloc::Free(s); + } +} + +Symbolizer::Symbolizer() : ok_(true), addr_map_read_(false) { + for (SymbolCacheLine &symbol_cache_line : symbol_cache_) { + for (size_t j = 0; j < ABSL_ARRAYSIZE(symbol_cache_line.name); ++j) { + symbol_cache_line.pc[j] = nullptr; + symbol_cache_line.name[j] = nullptr; + symbol_cache_line.age[j] = 0; + } + } +} + +Symbolizer::~Symbolizer() { + for (SymbolCacheLine &symbol_cache_line : symbol_cache_) { + for (char *s : symbol_cache_line.name) { + base_internal::LowLevelAlloc::Free(s); + } + } + ClearAddrMap(); +} + +// We don't use assert() since it's not guaranteed to be +// async-signal-safe. Instead we define a minimal assertion +// macro. So far, we don't need pretty printing for __FILE__, etc. +#define SAFE_ASSERT(expr) ((expr) ? static_cast(0) : abort()) + +// Read up to "count" bytes from file descriptor "fd" into the buffer +// starting at "buf" while handling short reads and EINTR. On +// success, return the number of bytes read. Otherwise, return -1. +static ssize_t ReadPersistent(int fd, void *buf, size_t count) { + SAFE_ASSERT(fd >= 0); + SAFE_ASSERT(count <= SSIZE_MAX); + char *buf0 = reinterpret_cast(buf); + size_t num_bytes = 0; + while (num_bytes < count) { + ssize_t len; + NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes)); + if (len < 0) { // There was an error other than EINTR. + ABSL_RAW_LOG(WARNING, "read failed: errno=%d", errno); + return -1; + } + if (len == 0) { // Reached EOF. + break; + } + num_bytes += len; + } + SAFE_ASSERT(num_bytes <= count); + return static_cast(num_bytes); +} + +// Read up to "count" bytes from "offset" in the file pointed by file +// descriptor "fd" into the buffer starting at "buf". On success, +// return the number of bytes read. Otherwise, return -1. +static ssize_t ReadFromOffset(const int fd, void *buf, const size_t count, + const off_t offset) { + off_t off = lseek(fd, offset, SEEK_SET); + if (off == (off_t)-1) { + ABSL_RAW_LOG(WARNING, "lseek(%d, %ju, SEEK_SET) failed: errno=%d", fd, + static_cast(offset), errno); + return -1; + } + return ReadPersistent(fd, buf, count); +} + +// Try reading exactly "count" bytes from "offset" bytes in a file +// pointed by "fd" into the buffer starting at "buf" while handling +// short reads and EINTR. On success, return true. Otherwise, return +// false. +static bool ReadFromOffsetExact(const int fd, void *buf, const size_t count, + const off_t offset) { + ssize_t len = ReadFromOffset(fd, buf, count, offset); + return len >= 0 && static_cast(len) == count; +} + +// Returns elf_header.e_type if the file pointed by fd is an ELF binary. +static int FileGetElfType(const int fd) { + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return -1; + } + if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) { + return -1; + } + return elf_header.e_type; +} + +// Read the section headers in the given ELF binary, and if a section +// of the specified type is found, set the output to this section header +// and return true. Otherwise, return false. +// To keep stack consumption low, we would like this function to not get +// inlined. +static ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType( + const int fd, ElfW(Half) sh_num, const off_t sh_offset, ElfW(Word) type, + ElfW(Shdr) * out, char *tmp_buf, int tmp_buf_size) { + ElfW(Shdr) *buf = reinterpret_cast(tmp_buf); + const int buf_entries = tmp_buf_size / sizeof(buf[0]); + const int buf_bytes = buf_entries * sizeof(buf[0]); + + for (int i = 0; i < sh_num;) { + const ssize_t num_bytes_left = (sh_num - i) * sizeof(buf[0]); + const ssize_t num_bytes_to_read = + (buf_bytes > num_bytes_left) ? num_bytes_left : buf_bytes; + const off_t offset = sh_offset + i * sizeof(buf[0]); + const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, offset); + if (len % sizeof(buf[0]) != 0) { + ABSL_RAW_LOG( + WARNING, + "Reading %zd bytes from offset %ju returned %zd which is not a " + "multiple of %zu.", + num_bytes_to_read, static_cast(offset), len, + sizeof(buf[0])); + return false; + } + const ssize_t num_headers_in_buf = len / sizeof(buf[0]); + SAFE_ASSERT(num_headers_in_buf <= buf_entries); + for (int j = 0; j < num_headers_in_buf; ++j) { + if (buf[j].sh_type == type) { + *out = buf[j]; + return true; + } + } + i += num_headers_in_buf; + } + return false; +} + +// There is no particular reason to limit section name to 63 characters, +// but there has (as yet) been no need for anything longer either. +const int kMaxSectionNameLen = 64; + +bool ForEachSection(int fd, + const std::function &callback) { + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return false; + } + + ElfW(Shdr) shstrtab; + off_t shstrtab_offset = + (elf_header.e_shoff + elf_header.e_shentsize * elf_header.e_shstrndx); + if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) { + return false; + } + + for (int i = 0; i < elf_header.e_shnum; ++i) { + ElfW(Shdr) out; + off_t section_header_offset = + (elf_header.e_shoff + elf_header.e_shentsize * i); + if (!ReadFromOffsetExact(fd, &out, sizeof(out), section_header_offset)) { + return false; + } + off_t name_offset = shstrtab.sh_offset + out.sh_name; + char header_name[kMaxSectionNameLen + 1]; + ssize_t n_read = + ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset); + if (n_read == -1) { + return false; + } else if (n_read > kMaxSectionNameLen) { + // Long read? + return false; + } + header_name[n_read] = '\0'; + + std::string name(header_name); + if (!callback(name, out)) { + break; + } + } + return true; +} + +// name_len should include terminating '\0'. +bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, + ElfW(Shdr) * out) { + char header_name[kMaxSectionNameLen]; + if (sizeof(header_name) < name_len) { + ABSL_RAW_LOG(WARNING, + "Section name '%s' is too long (%zu); " + "section will not be found (even if present).", + name, name_len); + // No point in even trying. + return false; + } + + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return false; + } + + ElfW(Shdr) shstrtab; + off_t shstrtab_offset = + (elf_header.e_shoff + elf_header.e_shentsize * elf_header.e_shstrndx); + if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) { + return false; + } + + for (int i = 0; i < elf_header.e_shnum; ++i) { + off_t section_header_offset = + (elf_header.e_shoff + elf_header.e_shentsize * i); + if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) { + return false; + } + off_t name_offset = shstrtab.sh_offset + out->sh_name; + ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset); + if (n_read < 0) { + return false; + } else if (static_cast(n_read) != name_len) { + // Short read -- name could be at end of file. + continue; + } + if (memcmp(header_name, name, name_len) == 0) { + return true; + } + } + return false; +} + +// Compare symbols at in the same address. +// Return true if we should pick symbol1. +static bool ShouldPickFirstSymbol(const ElfW(Sym) & symbol1, + const ElfW(Sym) & symbol2) { + // If one of the symbols is weak and the other is not, pick the one + // this is not a weak symbol. + char bind1 = ELF_ST_BIND(symbol1.st_info); + char bind2 = ELF_ST_BIND(symbol1.st_info); + if (bind1 == STB_WEAK && bind2 != STB_WEAK) return false; + if (bind2 == STB_WEAK && bind1 != STB_WEAK) return true; + + // If one of the symbols has zero size and the other is not, pick the + // one that has non-zero size. + if (symbol1.st_size != 0 && symbol2.st_size == 0) { + return true; + } + if (symbol1.st_size == 0 && symbol2.st_size != 0) { + return false; + } + + // If one of the symbols has no type and the other is not, pick the + // one that has a type. + char type1 = ELF_ST_TYPE(symbol1.st_info); + char type2 = ELF_ST_TYPE(symbol1.st_info); + if (type1 != STT_NOTYPE && type2 == STT_NOTYPE) { + return true; + } + if (type1 == STT_NOTYPE && type2 != STT_NOTYPE) { + return false; + } + + // Pick the first one, if we still cannot decide. + return true; +} + +// Return true if an address is inside a section. +static bool InSection(const void *address, const ElfW(Shdr) * section) { + const char *start = reinterpret_cast(section->sh_addr); + size_t size = static_cast(section->sh_size); + return start <= address && address < (start + size); +} + +static const char *ComputeOffset(const char *base, ptrdiff_t offset) { + // Note: cast to uintptr_t to avoid undefined behavior when base evaluates to + // zero and offset is non-zero. + return reinterpret_cast( + reinterpret_cast(base) + offset); +} + +// Read a symbol table and look for the symbol containing the +// pc. Iterate over symbols in a symbol table and look for the symbol +// containing "pc". If the symbol is found, and its name fits in +// out_size, the name is written into out and SYMBOL_FOUND is returned. +// If the name does not fit, truncated name is written into out, +// and SYMBOL_TRUNCATED is returned. Out is NUL-terminated. +// If the symbol is not found, SYMBOL_NOT_FOUND is returned; +// To keep stack consumption low, we would like this function to not get +// inlined. +static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( + const void *const pc, const int fd, char *out, int out_size, + ptrdiff_t relocation, const ElfW(Shdr) * strtab, const ElfW(Shdr) * symtab, + const ElfW(Shdr) * opd, char *tmp_buf, int tmp_buf_size) { + if (symtab == nullptr) { + return SYMBOL_NOT_FOUND; + } + + // Read multiple symbols at once to save read() calls. + ElfW(Sym) *buf = reinterpret_cast(tmp_buf); + const int buf_entries = tmp_buf_size / sizeof(buf[0]); + + const int num_symbols = symtab->sh_size / symtab->sh_entsize; + + // On platforms using an .opd section (PowerPC & IA64), a function symbol + // has the address of a function descriptor, which contains the real + // starting address. However, we do not always want to use the real + // starting address because we sometimes want to symbolize a function + // pointer into the .opd section, e.g. FindSymbol(&foo,...). + const bool pc_in_opd = + kPlatformUsesOPDSections && opd != nullptr && InSection(pc, opd); + const bool deref_function_descriptor_pointer = + kPlatformUsesOPDSections && opd != nullptr && !pc_in_opd; + + ElfW(Sym) best_match; + SafeMemZero(&best_match, sizeof(best_match)); + bool found_match = false; + for (int i = 0; i < num_symbols;) { + off_t offset = symtab->sh_offset + i * symtab->sh_entsize; + const int num_remaining_symbols = num_symbols - i; + const int entries_in_chunk = std::min(num_remaining_symbols, buf_entries); + const int bytes_in_chunk = entries_in_chunk * sizeof(buf[0]); + const ssize_t len = ReadFromOffset(fd, buf, bytes_in_chunk, offset); + SAFE_ASSERT(len % sizeof(buf[0]) == 0); + const ssize_t num_symbols_in_buf = len / sizeof(buf[0]); + SAFE_ASSERT(num_symbols_in_buf <= entries_in_chunk); + for (int j = 0; j < num_symbols_in_buf; ++j) { + const ElfW(Sym) &symbol = buf[j]; + + // For a DSO, a symbol address is relocated by the loading address. + // We keep the original address for opd redirection below. + const char *const original_start_address = + reinterpret_cast(symbol.st_value); + const char *start_address = + ComputeOffset(original_start_address, relocation); + + if (deref_function_descriptor_pointer && + InSection(original_start_address, opd)) { + // The opd section is mapped into memory. Just dereference + // start_address to get the first double word, which points to the + // function entry. + start_address = *reinterpret_cast(start_address); + } + + // If pc is inside the .opd section, it points to a function descriptor. + const size_t size = pc_in_opd ? kFunctionDescriptorSize : symbol.st_size; + const void *const end_address = ComputeOffset(start_address, size); + if (symbol.st_value != 0 && // Skip null value symbols. + symbol.st_shndx != 0 && // Skip undefined symbols. +#ifdef STT_TLS + ELF_ST_TYPE(symbol.st_info) != STT_TLS && // Skip thread-local data. +#endif // STT_TLS + ((start_address <= pc && pc < end_address) || + (start_address == pc && pc == end_address))) { + if (!found_match || ShouldPickFirstSymbol(symbol, best_match)) { + found_match = true; + best_match = symbol; + } + } + } + i += num_symbols_in_buf; + } + + if (found_match) { + const size_t off = strtab->sh_offset + best_match.st_name; + const ssize_t n_read = ReadFromOffset(fd, out, out_size, off); + if (n_read <= 0) { + // This should never happen. + ABSL_RAW_LOG(WARNING, + "Unable to read from fd %d at offset %zu: n_read = %zd", fd, + off, n_read); + return SYMBOL_NOT_FOUND; + } + ABSL_RAW_CHECK(n_read <= out_size, "ReadFromOffset read too much data."); + + // strtab->sh_offset points into .strtab-like section that contains + // NUL-terminated strings: '\0foo\0barbaz\0...". + // + // sh_offset+st_name points to the start of symbol name, but we don't know + // how long the symbol is, so we try to read as much as we have space for, + // and usually over-read (i.e. there is a NUL somewhere before n_read). + if (memchr(out, '\0', n_read) == nullptr) { + // Either out_size was too small (n_read == out_size and no NUL), or + // we tried to read past the EOF (n_read < out_size) and .strtab is + // corrupt (missing terminating NUL; should never happen for valid ELF). + out[n_read - 1] = '\0'; + return SYMBOL_TRUNCATED; + } + return SYMBOL_FOUND; + } + + return SYMBOL_NOT_FOUND; +} + +// Get the symbol name of "pc" from the file pointed by "fd". Process +// both regular and dynamic symbol tables if necessary. +// See FindSymbol() comment for description of return value. +FindSymbolResult Symbolizer::GetSymbolFromObjectFile( + const ObjFile &obj, const void *const pc, const ptrdiff_t relocation, + char *out, int out_size, char *tmp_buf, int tmp_buf_size) { + ElfW(Shdr) symtab; + ElfW(Shdr) strtab; + ElfW(Shdr) opd; + ElfW(Shdr) *opd_ptr = nullptr; + + // On platforms using an .opd sections for function descriptor, read + // the section header. The .opd section is in data segment and should be + // loaded but we check that it is mapped just to be extra careful. + if (kPlatformUsesOPDSections) { + if (GetSectionHeaderByName(obj.fd, kOpdSectionName, + sizeof(kOpdSectionName) - 1, &opd) && + FindObjFile(reinterpret_cast(opd.sh_addr) + relocation, + opd.sh_size) != nullptr) { + opd_ptr = &opd; + } else { + return SYMBOL_NOT_FOUND; + } + } + + // Consult a regular symbol table, then fall back to the dynamic symbol table. + for (const auto symbol_table_type : {SHT_SYMTAB, SHT_DYNSYM}) { + if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum, + obj.elf_header.e_shoff, symbol_table_type, + &symtab, tmp_buf, tmp_buf_size)) { + continue; + } + if (!ReadFromOffsetExact( + obj.fd, &strtab, sizeof(strtab), + obj.elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { + continue; + } + const FindSymbolResult rc = + FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab, + opd_ptr, tmp_buf, tmp_buf_size); + if (rc != SYMBOL_NOT_FOUND) { + return rc; + } + } + + return SYMBOL_NOT_FOUND; +} + +namespace { +// Thin wrapper around a file descriptor so that the file descriptor +// gets closed for sure. +class FileDescriptor { + public: + explicit FileDescriptor(int fd) : fd_(fd) {} + FileDescriptor(const FileDescriptor &) = delete; + FileDescriptor &operator=(const FileDescriptor &) = delete; + + ~FileDescriptor() { + if (fd_ >= 0) { + NO_INTR(close(fd_)); + } + } + + int get() const { return fd_; } + + private: + const int fd_; +}; + +// Helper class for reading lines from file. +// +// Note: we don't use ProcMapsIterator since the object is big (it has +// a 5k array member) and uses async-unsafe functions such as sscanf() +// and snprintf(). +class LineReader { + public: + explicit LineReader(int fd, char *buf, int buf_len) + : fd_(fd), + buf_len_(buf_len), + buf_(buf), + bol_(buf), + eol_(buf), + eod_(buf) {} + + LineReader(const LineReader &) = delete; + LineReader &operator=(const LineReader &) = delete; + + // Read '\n'-terminated line from file. On success, modify "bol" + // and "eol", then return true. Otherwise, return false. + // + // Note: if the last line doesn't end with '\n', the line will be + // dropped. It's an intentional behavior to make the code simple. + bool ReadLine(const char **bol, const char **eol) { + if (BufferIsEmpty()) { // First time. + const ssize_t num_bytes = ReadPersistent(fd_, buf_, buf_len_); + if (num_bytes <= 0) { // EOF or error. + return false; + } + eod_ = buf_ + num_bytes; + bol_ = buf_; + } else { + bol_ = eol_ + 1; // Advance to the next line in the buffer. + SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_". + if (!HasCompleteLine()) { + const int incomplete_line_length = eod_ - bol_; + // Move the trailing incomplete line to the beginning. + memmove(buf_, bol_, incomplete_line_length); + // Read text from file and append it. + char *const append_pos = buf_ + incomplete_line_length; + const int capacity_left = buf_len_ - incomplete_line_length; + const ssize_t num_bytes = + ReadPersistent(fd_, append_pos, capacity_left); + if (num_bytes <= 0) { // EOF or error. + return false; + } + eod_ = append_pos + num_bytes; + bol_ = buf_; + } + } + eol_ = FindLineFeed(); + if (eol_ == nullptr) { // '\n' not found. Malformed line. + return false; + } + *eol_ = '\0'; // Replace '\n' with '\0'. + + *bol = bol_; + *eol = eol_; + return true; + } + + private: + char *FindLineFeed() const { + return reinterpret_cast(memchr(bol_, '\n', eod_ - bol_)); + } + + bool BufferIsEmpty() const { return buf_ == eod_; } + + bool HasCompleteLine() const { + return !BufferIsEmpty() && FindLineFeed() != nullptr; + } + + const int fd_; + const int buf_len_; + char *const buf_; + char *bol_; + char *eol_; + const char *eod_; // End of data in "buf_". +}; +} // namespace + +// Place the hex number read from "start" into "*hex". The pointer to +// the first non-hex character or "end" is returned. +static const char *GetHex(const char *start, const char *end, + uint64_t *const value) { + uint64_t hex = 0; + const char *p; + for (p = start; p < end; ++p) { + int ch = *p; + if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f')) { + hex = (hex << 4) | (ch < 'A' ? ch - '0' : (ch & 0xF) + 9); + } else { // Encountered the first non-hex character. + break; + } + } + SAFE_ASSERT(p <= end); + *value = hex; + return p; +} + +static const char *GetHex(const char *start, const char *end, + const void **const addr) { + uint64_t hex = 0; + const char *p = GetHex(start, end, &hex); + *addr = reinterpret_cast(hex); + return p; +} + +// Normally we are only interested in "r?x" maps. +// On the PowerPC, function pointers point to descriptors in the .opd +// section. The descriptors themselves are not executable code, so +// we need to relax the check below to "r??". +static bool ShouldUseMapping(const char *const flags) { + return flags[0] == 'r' && (kPlatformUsesOPDSections || flags[2] == 'x'); +} + +// Read /proc/self/maps and run "callback" for each mmapped file found. If +// "callback" returns false, stop scanning and return true. Else continue +// scanning /proc/self/maps. Return true if no parse error is found. +static ABSL_ATTRIBUTE_NOINLINE bool ReadAddrMap( + bool (*callback)(const char *filename, const void *const start_addr, + const void *const end_addr, uint64_t offset, void *arg), + void *arg, void *tmp_buf, int tmp_buf_size) { + // Use /proc/self/task//maps instead of /proc/self/maps. The latter + // requires kernel to stop all threads, and is significantly slower when there + // are 1000s of threads. + char maps_path[80]; + snprintf(maps_path, sizeof(maps_path), "/proc/self/task/%d/maps", getpid()); + + int maps_fd; + NO_INTR(maps_fd = open(maps_path, O_RDONLY)); + FileDescriptor wrapped_maps_fd(maps_fd); + if (wrapped_maps_fd.get() < 0) { + ABSL_RAW_LOG(WARNING, "%s: errno=%d", maps_path, errno); + return false; + } + + // Iterate over maps and look for the map containing the pc. Then + // look into the symbol tables inside. + LineReader reader(wrapped_maps_fd.get(), static_cast(tmp_buf), + tmp_buf_size); + while (true) { + const char *cursor; + const char *eol; + if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. + break; + } + + const char *line = cursor; + const void *start_address; + // Start parsing line in /proc/self/maps. Here is an example: + // + // 08048000-0804c000 r-xp 00000000 08:01 2142121 /bin/cat + // + // We want start address (08048000), end address (0804c000), flags + // (r-xp) and file name (/bin/cat). + + // Read start address. + cursor = GetHex(cursor, eol, &start_address); + if (cursor == eol || *cursor != '-') { + ABSL_RAW_LOG(WARNING, "Corrupt /proc/self/maps line: %s", line); + return false; + } + ++cursor; // Skip '-'. + + // Read end address. + const void *end_address; + cursor = GetHex(cursor, eol, &end_address); + if (cursor == eol || *cursor != ' ') { + ABSL_RAW_LOG(WARNING, "Corrupt /proc/self/maps line: %s", line); + return false; + } + ++cursor; // Skip ' '. + + // Read flags. Skip flags until we encounter a space or eol. + const char *const flags_start = cursor; + while (cursor < eol && *cursor != ' ') { + ++cursor; + } + // We expect at least four letters for flags (ex. "r-xp"). + if (cursor == eol || cursor < flags_start + 4) { + ABSL_RAW_LOG(WARNING, "Corrupt /proc/self/maps: %s", line); + return false; + } + + // Check flags. + if (!ShouldUseMapping(flags_start)) { + continue; // We skip this map. + } + ++cursor; // Skip ' '. + + // Read file offset. + uint64_t offset; + cursor = GetHex(cursor, eol, &offset); + ++cursor; // Skip ' '. + + // Skip to file name. "cursor" now points to dev. We need to skip at least + // two spaces for dev and inode. + int num_spaces = 0; + while (cursor < eol) { + if (*cursor == ' ') { + ++num_spaces; + } else if (num_spaces >= 2) { + // The first non-space character after skipping two spaces + // is the beginning of the file name. + break; + } + ++cursor; + } + + // Check whether this entry corresponds to our hint table for the true + // filename. + bool hinted = + GetFileMappingHint(&start_address, &end_address, &offset, &cursor); + if (!hinted && (cursor == eol || cursor[0] == '[')) { + // not an object file, typically [vdso] or [vsyscall] + continue; + } + if (!callback(cursor, start_address, end_address, offset, arg)) break; + } + return true; +} + +// Find the objfile mapped in address region containing [addr, addr + len). +ObjFile *Symbolizer::FindObjFile(const void *const addr, size_t len) { + for (int i = 0; i < 2; ++i) { + if (!ok_) return nullptr; + + // Read /proc/self/maps if necessary + if (!addr_map_read_) { + addr_map_read_ = true; + if (!ReadAddrMap(RegisterObjFile, this, tmp_buf_, TMP_BUF_SIZE)) { + ok_ = false; + return nullptr; + } + } + + int lo = 0; + int hi = addr_map_.Size(); + while (lo < hi) { + int mid = (lo + hi) / 2; + if (addr < addr_map_.At(mid)->end_addr) { + hi = mid; + } else { + lo = mid + 1; + } + } + if (lo != addr_map_.Size()) { + ObjFile *obj = addr_map_.At(lo); + SAFE_ASSERT(obj->end_addr > addr); + if (addr >= obj->start_addr && + reinterpret_cast(addr) + len <= obj->end_addr) + return obj; + } + + // The address mapping may have changed since it was last read. Retry. + ClearAddrMap(); + } + return nullptr; +} + +void Symbolizer::ClearAddrMap() { + for (int i = 0; i != addr_map_.Size(); i++) { + ObjFile *o = addr_map_.At(i); + base_internal::LowLevelAlloc::Free(o->filename); + if (o->fd >= 0) { + NO_INTR(close(o->fd)); + } + } + addr_map_.Clear(); + addr_map_read_ = false; +} + +// Callback for ReadAddrMap to register objfiles in an in-memory table. +bool Symbolizer::RegisterObjFile(const char *filename, + const void *const start_addr, + const void *const end_addr, uint64_t offset, + void *arg) { + Symbolizer *impl = static_cast(arg); + + // Files are supposed to be added in the increasing address order. Make + // sure that's the case. + int addr_map_size = impl->addr_map_.Size(); + if (addr_map_size != 0) { + ObjFile *old = impl->addr_map_.At(addr_map_size - 1); + if (old->end_addr > end_addr) { + ABSL_RAW_LOG(ERROR, + "Unsorted addr map entry: 0x%" PRIxPTR ": %s <-> 0x%" PRIxPTR + ": %s", + reinterpret_cast(end_addr), filename, + reinterpret_cast(old->end_addr), old->filename); + return true; + } else if (old->end_addr == end_addr) { + // The same entry appears twice. This sometimes happens for [vdso]. + if (old->start_addr != start_addr || + strcmp(old->filename, filename) != 0) { + ABSL_RAW_LOG(ERROR, + "Duplicate addr 0x%" PRIxPTR ": %s <-> 0x%" PRIxPTR ": %s", + reinterpret_cast(end_addr), filename, + reinterpret_cast(old->end_addr), old->filename); + } + return true; + } + } + ObjFile *obj = impl->addr_map_.Add(); + obj->filename = impl->CopyString(filename); + obj->start_addr = start_addr; + obj->end_addr = end_addr; + obj->offset = offset; + obj->elf_type = -1; // filled on demand + obj->fd = -1; // opened on demand + return true; +} + +// This function wraps the Demangle function to provide an interface +// where the input symbol is demangled in-place. +// To keep stack consumption low, we would like this function to not +// get inlined. +static ABSL_ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size, + char *tmp_buf, + int tmp_buf_size) { + if (Demangle(out, tmp_buf, tmp_buf_size)) { + // Demangling succeeded. Copy to out if the space allows. + int len = strlen(tmp_buf); + if (len + 1 <= out_size) { // +1 for '\0'. + SAFE_ASSERT(len < tmp_buf_size); + memmove(out, tmp_buf, len + 1); + } + } +} + +SymbolCacheLine *Symbolizer::GetCacheLine(const void *const pc) { + uintptr_t pc0 = reinterpret_cast(pc); + pc0 >>= 3; // drop the low 3 bits + + // Shuffle bits. + pc0 ^= (pc0 >> 6) ^ (pc0 >> 12) ^ (pc0 >> 18); + return &symbol_cache_[pc0 % SYMBOL_CACHE_LINES]; +} + +void Symbolizer::AgeSymbols(SymbolCacheLine *line) { + for (uint32_t &age : line->age) { + ++age; + } +} + +const char *Symbolizer::FindSymbolInCache(const void *const pc) { + if (pc == nullptr) return nullptr; + + SymbolCacheLine *line = GetCacheLine(pc); + for (size_t i = 0; i < ABSL_ARRAYSIZE(line->pc); ++i) { + if (line->pc[i] == pc) { + AgeSymbols(line); + line->age[i] = 0; + return line->name[i]; + } + } + return nullptr; +} + +const char *Symbolizer::InsertSymbolInCache(const void *const pc, + const char *name) { + SAFE_ASSERT(pc != nullptr); + + SymbolCacheLine *line = GetCacheLine(pc); + uint32_t max_age = 0; + int oldest_index = -1; + for (size_t i = 0; i < ABSL_ARRAYSIZE(line->pc); ++i) { + if (line->pc[i] == nullptr) { + AgeSymbols(line); + line->pc[i] = pc; + line->name[i] = CopyString(name); + line->age[i] = 0; + return line->name[i]; + } + if (line->age[i] >= max_age) { + max_age = line->age[i]; + oldest_index = i; + } + } + + AgeSymbols(line); + ABSL_RAW_CHECK(oldest_index >= 0, "Corrupt cache"); + base_internal::LowLevelAlloc::Free(line->name[oldest_index]); + line->pc[oldest_index] = pc; + line->name[oldest_index] = CopyString(name); + line->age[oldest_index] = 0; + return line->name[oldest_index]; +} + +static void MaybeOpenFdFromSelfExe(ObjFile *obj) { + if (memcmp(obj->start_addr, ELFMAG, SELFMAG) != 0) { + return; + } + int fd = open("/proc/self/exe", O_RDONLY); + if (fd == -1) { + return; + } + // Verify that contents of /proc/self/exe matches in-memory image of + // the binary. This can fail if the "deleted" binary is in fact not + // the main executable, or for binaries that have the first PT_LOAD + // segment smaller than 4K. We do it in four steps so that the + // buffer is smaller and we don't consume too much stack space. + const char *mem = reinterpret_cast(obj->start_addr); + for (int i = 0; i < 4; ++i) { + char buf[1024]; + ssize_t n = read(fd, buf, sizeof(buf)); + if (n != sizeof(buf) || memcmp(buf, mem, sizeof(buf)) != 0) { + close(fd); + return; + } + mem += sizeof(buf); + } + obj->fd = fd; +} + +static bool MaybeInitializeObjFile(ObjFile *obj) { + if (obj->fd < 0) { + obj->fd = open(obj->filename, O_RDONLY); + + if (obj->fd < 0) { + // Getting /proc/self/exe here means that we were hinted. + if (strcmp(obj->filename, "/proc/self/exe") == 0) { + // /proc/self/exe may be inaccessible (due to setuid, etc.), so try + // accessing the binary via argv0. + if (argv0_value != nullptr) { + obj->fd = open(argv0_value, O_RDONLY); + } + } else { + MaybeOpenFdFromSelfExe(obj); + } + } + + if (obj->fd < 0) { + ABSL_RAW_LOG(WARNING, "%s: open failed: errno=%d", obj->filename, errno); + return false; + } + obj->elf_type = FileGetElfType(obj->fd); + if (obj->elf_type < 0) { + ABSL_RAW_LOG(WARNING, "%s: wrong elf type: %d", obj->filename, + obj->elf_type); + return false; + } + + if (!ReadFromOffsetExact(obj->fd, &obj->elf_header, sizeof(obj->elf_header), + 0)) { + ABSL_RAW_LOG(WARNING, "%s: failed to read elf header", obj->filename); + return false; + } + } + return true; +} + +// The implementation of our symbolization routine. If it +// successfully finds the symbol containing "pc" and obtains the +// symbol name, returns pointer to that symbol. Otherwise, returns nullptr. +// If any symbol decorators have been installed via InstallSymbolDecorator(), +// they are called here as well. +// To keep stack consumption low, we would like this function to not +// get inlined. +const char *Symbolizer::GetSymbol(const void *const pc) { + const char *entry = FindSymbolInCache(pc); + if (entry != nullptr) { + return entry; + } + symbol_buf_[0] = '\0'; + + ObjFile *const obj = FindObjFile(pc, 1); + ptrdiff_t relocation = 0; + int fd = -1; + if (obj != nullptr) { + if (MaybeInitializeObjFile(obj)) { + if (obj->elf_type == ET_DYN && + reinterpret_cast(obj->start_addr) >= obj->offset) { + // This object was relocated. + // + // For obj->offset > 0, adjust the relocation since a mapping at offset + // X in the file will have a start address of [true relocation]+X. + relocation = reinterpret_cast(obj->start_addr) - obj->offset; + } + + fd = obj->fd; + } + if (GetSymbolFromObjectFile(*obj, pc, relocation, symbol_buf_, + sizeof(symbol_buf_), tmp_buf_, + sizeof(tmp_buf_)) == SYMBOL_FOUND) { + // Only try to demangle the symbol name if it fit into symbol_buf_. + DemangleInplace(symbol_buf_, sizeof(symbol_buf_), tmp_buf_, + sizeof(tmp_buf_)); + } + } else { +#if ABSL_HAVE_VDSO_SUPPORT + VDSOSupport vdso; + if (vdso.IsPresent()) { + VDSOSupport::SymbolInfo symbol_info; + if (vdso.LookupSymbolByAddress(pc, &symbol_info)) { + // All VDSO symbols are known to be short. + size_t len = strlen(symbol_info.name); + ABSL_RAW_CHECK(len + 1 < sizeof(symbol_buf_), + "VDSO symbol unexpectedly long"); + memcpy(symbol_buf_, symbol_info.name, len + 1); + } + } +#endif + } + + if (g_decorators_mu.TryLock()) { + if (g_num_decorators > 0) { + SymbolDecoratorArgs decorator_args = { + pc, relocation, fd, symbol_buf_, sizeof(symbol_buf_), + tmp_buf_, sizeof(tmp_buf_), nullptr}; + for (int i = 0; i < g_num_decorators; ++i) { + decorator_args.arg = g_decorators[i].arg; + g_decorators[i].fn(&decorator_args); + } + } + g_decorators_mu.Unlock(); + } + if (symbol_buf_[0] == '\0') { + return nullptr; + } + symbol_buf_[sizeof(symbol_buf_) - 1] = '\0'; // Paranoia. + return InsertSymbolInCache(pc, symbol_buf_); +} + +bool RemoveAllSymbolDecorators(void) { + if (!g_decorators_mu.TryLock()) { + // Someone else is using decorators. Get out. + return false; + } + g_num_decorators = 0; + g_decorators_mu.Unlock(); + return true; +} + +bool RemoveSymbolDecorator(int ticket) { + if (!g_decorators_mu.TryLock()) { + // Someone else is using decorators. Get out. + return false; + } + for (int i = 0; i < g_num_decorators; ++i) { + if (g_decorators[i].ticket == ticket) { + while (i < g_num_decorators - 1) { + g_decorators[i] = g_decorators[i + 1]; + ++i; + } + g_num_decorators = i; + break; + } + } + g_decorators_mu.Unlock(); + return true; // Decorator is known to be removed. +} + +int InstallSymbolDecorator(SymbolDecorator decorator, void *arg) { + static int ticket = 0; + + if (!g_decorators_mu.TryLock()) { + // Someone else is using decorators. Get out. + return false; + } + int ret = ticket; + if (g_num_decorators >= kMaxDecorators) { + ret = -1; + } else { + g_decorators[g_num_decorators] = {decorator, arg, ticket++}; + ++g_num_decorators; + } + g_decorators_mu.Unlock(); + return ret; +} + +bool RegisterFileMappingHint(const void *start, const void *end, uint64_t offset, + const char *filename) { + SAFE_ASSERT(start <= end); + SAFE_ASSERT(filename != nullptr); + + InitSigSafeArena(); + + if (!g_file_mapping_mu.TryLock()) { + return false; + } + + bool ret = true; + if (g_num_file_mapping_hints >= kMaxFileMappingHints) { + ret = false; + } else { + // TODO(ckennelly): Move this into a std::string copy routine. + int len = strlen(filename); + char *dst = static_cast( + base_internal::LowLevelAlloc::AllocWithArena(len + 1, SigSafeArena())); + ABSL_RAW_CHECK(dst != nullptr, "out of memory"); + memcpy(dst, filename, len + 1); + + auto &hint = g_file_mapping_hints[g_num_file_mapping_hints++]; + hint.start = start; + hint.end = end; + hint.offset = offset; + hint.filename = dst; + } + + g_file_mapping_mu.Unlock(); + return ret; +} + +bool GetFileMappingHint(const void **start, const void **end, uint64_t *offset, + const char **filename) { + if (!g_file_mapping_mu.TryLock()) { + return false; + } + bool found = false; + for (int i = 0; i < g_num_file_mapping_hints; i++) { + if (g_file_mapping_hints[i].start <= *start && + *end <= g_file_mapping_hints[i].end) { + // We assume that the start_address for the mapping is the base + // address of the ELF section, but when [start_address,end_address) is + // not strictly equal to [hint.start, hint.end), that assumption is + // invalid. + // + // This uses the hint's start address (even though hint.start is not + // necessarily equal to start_address) to ensure the correct + // relocation is computed later. + *start = g_file_mapping_hints[i].start; + *end = g_file_mapping_hints[i].end; + *offset = g_file_mapping_hints[i].offset; + *filename = g_file_mapping_hints[i].filename; + found = true; + break; + } + } + g_file_mapping_mu.Unlock(); + return found; +} + +} // namespace debugging_internal + +bool Symbolize(const void *pc, char *out, int out_size) { + // Symbolization is very slow under tsan. + ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN(); + SAFE_ASSERT(out_size >= 0); + debugging_internal::Symbolizer *s = debugging_internal::AllocateSymbolizer(); + const char *name = s->GetSymbol(pc); + bool ok = false; + if (name != nullptr && out_size > 0) { + strncpy(out, name, out_size); + ok = true; + if (out[out_size - 1] != '\0') { + // strncpy() does not '\0' terminate when it truncates. Do so, with + // trailing ellipsis. + static constexpr char kEllipsis[] = "..."; + int ellipsis_size = + std::min(implicit_cast(strlen(kEllipsis)), out_size - 1); + memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size - 1] = '\0'; + } + } + debugging_internal::FreeSymbolizer(s); + ANNOTATE_IGNORE_READS_AND_WRITES_END(); + return ok; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize_unimplemented.inc b/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize_unimplemented.inc new file mode 100644 index 0000000000000000000000000000000000000000..db24456b0afaf974ce063638f1cfb5fce42a93e2 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize_unimplemented.inc @@ -0,0 +1,40 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace debugging_internal { + +int InstallSymbolDecorator(SymbolDecorator, void*) { return -1; } +bool RemoveSymbolDecorator(int) { return false; } +bool RemoveAllSymbolDecorators(void) { return false; } +bool RegisterFileMappingHint(const void *, const void *, uint64_t, const char *) { + return false; +} +bool GetFileMappingHint(const void **, const void **, uint64_t *, const char **) { + return false; +} + +} // namespace debugging_internal + +void InitializeSymbolizer(const char*) {} +bool Symbolize(const void *, char *, int) { return false; } + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize_win32.inc b/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize_win32.inc new file mode 100644 index 0000000000000000000000000000000000000000..c3df46f606c2042cccba4b9f59ed0f762142aa64 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/debugging/symbolize_win32.inc @@ -0,0 +1,81 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. + +// See "Retrieving Symbol Information by Address": +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx + +#include + +// MSVC header dbghelp.h has a warning for an ignored typedef. +#pragma warning(push) +#pragma warning(disable:4091) +#include +#pragma warning(pop) + +#pragma comment(lib, "dbghelp.lib") + +#include +#include + +#include "absl/base/internal/raw_logging.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +static HANDLE process = NULL; + +void InitializeSymbolizer(const char*) { + if (process != nullptr) { + return; + } + process = GetCurrentProcess(); + + // Symbols are not loaded until a reference is made requiring the + // symbols be loaded. This is the fastest, most efficient way to use + // the symbol handler. + SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME); + if (!SymInitialize(process, nullptr, true)) { + // GetLastError() returns a Win32 DWORD, but we assign to + // unsigned long long to simplify the ABSL_RAW_LOG case below. The uniform + // initialization guarantees this is not a narrowing conversion. + const unsigned long long error{GetLastError()}; // NOLINT(runtime/int) + ABSL_RAW_LOG(FATAL, "SymInitialize() failed: %llu", error); + } +} + +bool Symbolize(const void* pc, char* out, int out_size) { + if (out_size <= 0) { + return false; + } + alignas(SYMBOL_INFO) char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + SYMBOL_INFO* symbol = reinterpret_cast(buf); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + if (!SymFromAddr(process, reinterpret_cast(pc), nullptr, symbol)) { + return false; + } + strncpy(out, symbol->Name, out_size); + if (out[out_size - 1] != '\0') { + // strncpy() does not '\0' terminate when it truncates. + static constexpr char kEllipsis[] = "..."; + int ellipsis_size = + std::min(sizeof(kEllipsis) - 1, out_size - 1); + memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size - 1] = '\0'; + } + return true; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/config.h b/libs/or-tools-src-ubuntu/include/absl/flags/config.h new file mode 100644 index 0000000000000000000000000000000000000000..001f8feaf637067ace2ef9a638279230e57cd28d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/config.h @@ -0,0 +1,67 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FLAGS_CONFIG_H_ +#define ABSL_FLAGS_CONFIG_H_ + +// Determine if we should strip string literals from the Flag objects. +// By default we strip string literals on mobile platforms. +#if !defined(ABSL_FLAGS_STRIP_NAMES) + +#if defined(__ANDROID__) +#define ABSL_FLAGS_STRIP_NAMES 1 + +#elif defined(__APPLE__) +#include +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#define ABSL_FLAGS_STRIP_NAMES 1 +#elif defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED +#define ABSL_FLAGS_STRIP_NAMES 1 +#endif // TARGET_OS_* +#endif + +#endif // !defined(ABSL_FLAGS_STRIP_NAMES) + +#if !defined(ABSL_FLAGS_STRIP_NAMES) +// If ABSL_FLAGS_STRIP_NAMES wasn't set on the command line or above, +// the default is not to strip. +#define ABSL_FLAGS_STRIP_NAMES 0 +#endif + +#if !defined(ABSL_FLAGS_STRIP_HELP) +// By default, if we strip names, we also strip help. +#define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES +#endif + +// ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD macro is used for using atomics with +// double words, e.g. absl::Duration. +// For reasons in bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878, modern +// versions of GCC do not support cmpxchg16b instruction in standard atomics. +#ifdef ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD +#error "ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD should not be defined." +#elif defined(__clang__) && defined(__x86_64__) && \ + defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) +#define ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD 1 +#endif + +// ABSL_FLAGS_INTERNAL_HAS_RTTI macro is used for selecting if we can use RTTI +// for flag type identification. +#ifdef ABSL_FLAGS_INTERNAL_HAS_RTTI +#error ABSL_FLAGS_INTERNAL_HAS_RTTI cannot be directly set +#elif !defined(__GNUC__) || defined(__GXX_RTTI) +#define ABSL_FLAGS_INTERNAL_HAS_RTTI 1 +#endif // !defined(__GNUC__) || defined(__GXX_RTTI) + +#endif // ABSL_FLAGS_CONFIG_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/declare.h b/libs/or-tools-src-ubuntu/include/absl/flags/declare.h new file mode 100644 index 0000000000000000000000000000000000000000..0f8cc6a599721ca0ead2add5335f5ae60a273cf2 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/declare.h @@ -0,0 +1,66 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: declare.h +// ----------------------------------------------------------------------------- +// +// This file defines the ABSL_DECLARE_FLAG macro, allowing you to declare an +// `absl::Flag` for use within a translation unit. You should place this +// declaration within the header file associated with the .cc file that defines +// and owns the `Flag`. + +#ifndef ABSL_FLAGS_DECLARE_H_ +#define ABSL_FLAGS_DECLARE_H_ + +#include "absl/base/config.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// absl::Flag represents a flag of type 'T' created by ABSL_FLAG. +template +class Flag; + +} // namespace flags_internal + +// Flag +// +// Forward declaration of the `absl::Flag` type for use in defining the macro. +#if defined(_MSC_VER) && !defined(__clang__) +template +class Flag; +#else +template +using Flag = flags_internal::Flag; +#endif + +ABSL_NAMESPACE_END +} // namespace absl + +// ABSL_DECLARE_FLAG() +// +// This macro is a convenience for declaring use of an `absl::Flag` within a +// translation unit. This macro should be used within a header file to +// declare usage of the flag within any .cc file including that header file. +// +// The ABSL_DECLARE_FLAG(type, name) macro expands to: +// +// extern absl::Flag FLAGS_name; +#define ABSL_DECLARE_FLAG(type, name) extern ::absl::Flag FLAGS_##name + +#endif // ABSL_FLAGS_DECLARE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/flag.h b/libs/or-tools-src-ubuntu/include/absl/flags/flag.h new file mode 100644 index 0000000000000000000000000000000000000000..cff02c1fcb6179eb44f25a54f73a850a017206ef --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/flag.h @@ -0,0 +1,379 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: flag.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `absl::Flag` type for holding command-line +// flag data, and abstractions to create, get and set such flag data. +// +// It is important to note that this type is **unspecified** (an implementation +// detail) and you do not construct or manipulate actual `absl::Flag` +// instances. Instead, you define and declare flags using the +// `ABSL_FLAG()` and `ABSL_DECLARE_FLAG()` macros, and get and set flag values +// using the `absl::GetFlag()` and `absl::SetFlag()` functions. + +#ifndef ABSL_FLAGS_FLAG_H_ +#define ABSL_FLAGS_FLAG_H_ + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/casts.h" +#include "absl/base/config.h" +#include "absl/flags/config.h" +#include "absl/flags/declare.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/flag.h" +#include "absl/flags/internal/registry.h" +#include "absl/flags/marshalling.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Flag +// +// An `absl::Flag` holds a command-line flag value, providing a runtime +// parameter to a binary. Such flags should be defined in the global namespace +// and (preferably) in the module containing the binary's `main()` function. +// +// You should not construct and cannot use the `absl::Flag` type directly; +// instead, you should declare flags using the `ABSL_DECLARE_FLAG()` macro +// within a header file, and define your flag using `ABSL_FLAG()` within your +// header's associated `.cc` file. Such flags will be named `FLAGS_name`. +// +// Example: +// +// .h file +// +// // Declares usage of a flag named "FLAGS_count" +// ABSL_DECLARE_FLAG(int, count); +// +// .cc file +// +// // Defines a flag named "FLAGS_count" with a default `int` value of 0. +// ABSL_FLAG(int, count, 0, "Count of items to process"); +// +// No public methods of `absl::Flag` are part of the Abseil Flags API. +#if !defined(_MSC_VER) || defined(__clang__) +template +using Flag = flags_internal::Flag; +#else +// MSVC debug builds do not implement initialization with constexpr constructors +// correctly. To work around this we add a level of indirection, so that the +// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias +// to that class) and dynamically allocates an instance when necessary. We also +// forward all calls to internal::Flag methods via trampoline methods. In this +// setup the `absl::Flag` class does not have constructor and virtual methods, +// all the data members are public and thus MSVC is able to initialize it at +// link time. To deal with multiple threads accessing the flag for the first +// time concurrently we use an atomic boolean indicating if flag object is +// initialized. We also employ the double-checked locking pattern where the +// second level of protection is a global Mutex, so if two threads attempt to +// construct the flag concurrently only one wins. +// This solution is based on a recomendation here: +// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454 + +namespace flags_internal { +absl::Mutex* GetGlobalConstructionGuard(); +} // namespace flags_internal + +template +class Flag { + public: + // No constructor and destructor to ensure this is an aggregate type. + // Visual Studio 2015 still requires the constructor for class to be + // constexpr initializable. +#if _MSC_VER <= 1900 + constexpr Flag(const char* name, const char* filename, + const flags_internal::HelpGenFunc help_gen, + const flags_internal::FlagDfltGenFunc default_value_gen) + : name_(name), + filename_(filename), + help_gen_(help_gen), + default_value_gen_(default_value_gen), + inited_(false), + impl_(nullptr) {} +#endif + + flags_internal::Flag* GetImpl() const { + if (!inited_.load(std::memory_order_acquire)) { + absl::MutexLock l(flags_internal::GetGlobalConstructionGuard()); + + if (inited_.load(std::memory_order_acquire)) { + return impl_; + } + + impl_ = + new flags_internal::Flag(name_, filename_, + {flags_internal::FlagHelpMsg(help_gen_), + flags_internal::FlagHelpKind::kGenFunc}, + default_value_gen_); + inited_.store(true, std::memory_order_release); + } + + return impl_; + } + + // Public methods of `absl::Flag` are NOT part of the Abseil Flags API. + // See https://abseil.io/docs/cpp/guides/flags + bool IsRetired() const { return GetImpl()->IsRetired(); } + bool IsAbseilFlag() const { return GetImpl()->IsAbseilFlag(); } + absl::string_view Name() const { return GetImpl()->Name(); } + std::string Help() const { return GetImpl()->Help(); } + bool IsModified() const { return GetImpl()->IsModified(); } + bool IsSpecifiedOnCommandLine() const { + return GetImpl()->IsSpecifiedOnCommandLine(); + } + absl::string_view Typename() const { return GetImpl()->Typename(); } + std::string Filename() const { return GetImpl()->Filename(); } + std::string DefaultValue() const { return GetImpl()->DefaultValue(); } + std::string CurrentValue() const { return GetImpl()->CurrentValue(); } + template + inline bool IsOfType() const { + return GetImpl()->template IsOfType(); + } + T Get() const { return GetImpl()->Get(); } + bool AtomicGet(T* v) const { return GetImpl()->AtomicGet(v); } + void Set(const T& v) { GetImpl()->Set(v); } + void SetCallback(const flags_internal::FlagCallbackFunc mutation_callback) { + GetImpl()->SetCallback(mutation_callback); + } + void InvokeCallback() { GetImpl()->InvokeCallback(); } + + // The data members are logically private, but they need to be public for + // this to be an aggregate type. + const char* name_; + const char* filename_; + const flags_internal::HelpGenFunc help_gen_; + const flags_internal::FlagDfltGenFunc default_value_gen_; + + mutable std::atomic inited_; + mutable flags_internal::Flag* impl_; +}; +#endif + +// GetFlag() +// +// Returns the value (of type `T`) of an `absl::Flag` instance, by value. Do +// not construct an `absl::Flag` directly and call `absl::GetFlag()`; +// instead, refer to flag's constructed variable name (e.g. `FLAGS_name`). +// Because this function returns by value and not by reference, it is +// thread-safe, but note that the operation may be expensive; as a result, avoid +// `absl::GetFlag()` within any tight loops. +// +// Example: +// +// // FLAGS_count is a Flag of type `int` +// int my_count = absl::GetFlag(FLAGS_count); +// +// // FLAGS_firstname is a Flag of type `std::string` +// std::string first_name = absl::GetFlag(FLAGS_firstname); +template +ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag& flag) { + return flag.Get(); +} + +// SetFlag() +// +// Sets the value of an `absl::Flag` to the value `v`. Do not construct an +// `absl::Flag` directly and call `absl::SetFlag()`; instead, use the +// flag's variable name (e.g. `FLAGS_name`). This function is +// thread-safe, but is potentially expensive. Avoid setting flags in general, +// but especially within performance-critical code. +template +void SetFlag(absl::Flag* flag, const T& v) { + flag->Set(v); +} + +// Overload of `SetFlag()` to allow callers to pass in a value that is +// convertible to `T`. E.g., use this overload to pass a "const char*" when `T` +// is `std::string`. +template +void SetFlag(absl::Flag* flag, const V& v) { + T value(v); + flag->Set(value); +} + +ABSL_NAMESPACE_END +} // namespace absl + + +// ABSL_FLAG() +// +// This macro defines an `absl::Flag` instance of a specified type `T`: +// +// ABSL_FLAG(T, name, default_value, help); +// +// where: +// +// * `T` is a supported flag type (see the list of types in `marshalling.h`), +// * `name` designates the name of the flag (as a global variable +// `FLAGS_name`), +// * `default_value` is an expression holding the default value for this flag +// (which must be implicitly convertible to `T`), +// * `help` is the help text, which can also be an expression. +// +// This macro expands to a flag named 'FLAGS_name' of type 'T': +// +// absl::Flag FLAGS_name = ...; +// +// Note that all such instances are created as global variables. +// +// For `ABSL_FLAG()` values that you wish to expose to other translation units, +// it is recommended to define those flags within the `.cc` file associated with +// the header where the flag is declared. +// +// Note: do not construct objects of type `absl::Flag` directly. Only use the +// `ABSL_FLAG()` macro for such construction. +#define ABSL_FLAG(Type, name, default_value, help) \ + ABSL_FLAG_IMPL(Type, name, default_value, help) + +// ABSL_FLAG().OnUpdate() +// +// Defines a flag of type `T` with a callback attached: +// +// ABSL_FLAG(T, name, default_value, help).OnUpdate(callback); +// +// After any setting of the flag value, the callback will be called at least +// once. A rapid sequence of changes may be merged together into the same +// callback. No concurrent calls to the callback will be made for the same +// flag. Callbacks are allowed to read the current value of the flag but must +// not mutate that flag. +// +// The update mechanism guarantees "eventual consistency"; if the callback +// derives an auxiliary data structure from the flag value, it is guaranteed +// that eventually the flag value and the derived data structure will be +// consistent. +// +// Note: ABSL_FLAG.OnUpdate() does not have a public definition. Hence, this +// comment serves as its API documentation. + + +// ----------------------------------------------------------------------------- +// Implementation details below this section +// ----------------------------------------------------------------------------- + +// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_NAMES + +#if ABSL_FLAGS_STRIP_NAMES +#define ABSL_FLAG_IMPL_FLAGNAME(txt) "" +#define ABSL_FLAG_IMPL_FILENAME() "" +#if !defined(_MSC_VER) || defined(__clang__) +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar(&flag) +#else +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar(flag.GetImpl()) +#endif +#else +#define ABSL_FLAG_IMPL_FLAGNAME(txt) txt +#define ABSL_FLAG_IMPL_FILENAME() __FILE__ +#if !defined(_MSC_VER) || defined(__clang__) +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar(&flag) +#else +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar(flag.GetImpl()) +#endif +#endif + +// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP + +#if ABSL_FLAGS_STRIP_HELP +#define ABSL_FLAG_IMPL_FLAGHELP(txt) absl::flags_internal::kStrippedFlagHelp +#else +#define ABSL_FLAG_IMPL_FLAGHELP(txt) txt +#endif + +// AbslFlagHelpGenFor##name is used to encapsulate both immediate (method Const) +// and lazy (method NonConst) evaluation of help message expression. We choose +// between the two via the call to HelpArg in absl::Flag instantiation below. +// If help message expression is constexpr evaluable compiler will optimize +// away this whole struct. +#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ + struct AbslFlagHelpGenFor##name { \ + template \ + static constexpr const char* Const() { \ + return absl::flags_internal::HelpConstexprWrap( \ + ABSL_FLAG_IMPL_FLAGHELP(txt)); \ + } \ + static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \ + } + +#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + static void* AbslFlagsInitFlag##name() { \ + return absl::flags_internal::MakeFromDefaultValue(default_value); \ + } + +// ABSL_FLAG_IMPL +// +// Note: Name of registrar object is not arbitrary. It is used to "grab" +// global name for FLAGS_no symbol, thus preventing the possibility +// of defining two flags with names foo and nofoo. +#if !defined(_MSC_VER) || defined(__clang__) +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + namespace absl /* block flags in namespaces */ {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ + ABSL_CONST_INIT absl::Flag FLAGS_##name{ \ + ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ + absl::flags_internal::HelpArg(0), \ + &AbslFlagsInitFlag##name}; \ + extern bool FLAGS_no##name; \ + bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) +#else +// MSVC version uses aggregate initialization. We also do not try to +// optimize away help wrapper. +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + namespace absl /* block flags in namespaces */ {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ + ABSL_CONST_INIT absl::Flag FLAGS_##name{ \ + ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ + &AbslFlagHelpGenFor##name::NonConst, &AbslFlagsInitFlag##name}; \ + extern bool FLAGS_no##name; \ + bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) +#endif + +// ABSL_RETIRED_FLAG +// +// Designates the flag (which is usually pre-existing) as "retired." A retired +// flag is a flag that is now unused by the program, but may still be passed on +// the command line, usually by production scripts. A retired flag is ignored +// and code can't access it at runtime. +// +// This macro registers a retired flag with given name and type, with a name +// identical to the name of the original flag you are retiring. The retired +// flag's type can change over time, so that you can retire code to support a +// custom flag type. +// +// This macro has the same signature as `ABSL_FLAG`. To retire a flag, simply +// replace an `ABSL_FLAG` definition with `ABSL_RETIRED_FLAG`, leaving the +// arguments unchanged (unless of course you actually want to retire the flag +// type at this time as well). +// +// `default_value` is only used as a double check on the type. `explanation` is +// unused. +// TODO(rogeeff): Return an anonymous struct instead of bool, and place it into +// the unnamed namespace. +#define ABSL_RETIRED_FLAG(type, flagname, default_value, explanation) \ + ABSL_ATTRIBUTE_UNUSED static const bool ignored_##flagname = \ + ([] { return type(default_value); }, \ + absl::flags_internal::RetiredFlag(#flagname)) + +#endif // ABSL_FLAGS_FLAG_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/internal/commandlineflag.h b/libs/or-tools-src-ubuntu/include/absl/flags/internal/commandlineflag.h new file mode 100644 index 0000000000000000000000000000000000000000..6363c6615b120ddfdc817c3ddf57812c5bfb50a6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/internal/commandlineflag.h @@ -0,0 +1,213 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ +#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ + +#include +#include + +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/flags/config.h" +#include "absl/flags/marshalling.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// An alias for flag static type id. Values of type identify the flag value type +// simialarly to typeid(T), but without relying on RTTI being available. In most +// cases this id is enough to uniquely identify the flag's value type. In a few +// cases we'll have to resort to using actual RTTI implementation if it is +// available. +using FlagStaticTypeId = void* (*)(); + +// Address of this function template is used in current implementation as a flag +// static type id. +template +void* FlagStaticTypeIdGen() { +#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) + return const_cast(&typeid(T)); +#else + return nullptr; +#endif +} + +// Options that control SetCommandLineOptionWithMode. +enum FlagSettingMode { + // update the flag's value unconditionally (can call this multiple times). + SET_FLAGS_VALUE, + // update the flag's value, but *only if* it has not yet been updated + // with SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef". + SET_FLAG_IF_DEFAULT, + // set the flag's default value to this. If the flag has not been updated + // yet (via SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef") + // change the flag's current value to the new default value as well. + SET_FLAGS_DEFAULT +}; + +// Options that control SetFromString: Source of a value. +enum ValueSource { + // Flag is being set by value specified on a command line. + kCommandLine, + // Flag is being set by value specified in the code. + kProgrammaticChange, +}; + +// Handle to FlagState objects. Specific flag state objects will restore state +// of a flag produced this flag state from method CommandLineFlag::SaveState(). +class FlagStateInterface { + public: + virtual ~FlagStateInterface() {} + + // Restores the flag originated this object to the saved state. + virtual void Restore() const = 0; +}; + +// Holds all information for a flag. +class CommandLineFlag { + public: + constexpr CommandLineFlag() = default; + + // Not copyable/assignable. + CommandLineFlag(const CommandLineFlag&) = delete; + CommandLineFlag& operator=(const CommandLineFlag&) = delete; + + // Non-polymorphic access methods. + + // Return true iff flag has type T. + template + inline bool IsOfType() const { + return TypeId() == &flags_internal::FlagStaticTypeIdGen; + } + + // Attempts to retrieve the flag value. Returns value on success, + // absl::nullopt otherwise. + template + absl::optional Get() const { + if (IsRetired() || !IsOfType()) { + return absl::nullopt; + } + + // Implementation notes: + // + // We are wrapping a union around the value of `T` to serve three purposes: + // + // 1. `U.value` has correct size and alignment for a value of type `T` + // 2. The `U.value` constructor is not invoked since U's constructor does + // not do it explicitly. + // 3. The `U.value` destructor is invoked since U's destructor does it + // explicitly. This makes `U` a kind of RAII wrapper around non default + // constructible value of T, which is destructed when we leave the + // scope. We do need to destroy U.value, which is constructed by + // CommandLineFlag::Read even though we left it in a moved-from state + // after std::move. + // + // All of this serves to avoid requiring `T` being default constructible. + union U { + T value; + U() {} + ~U() { value.~T(); } + }; + U u; + + Read(&u.value); + return std::move(u.value); + } + + // Polymorphic access methods + + // Returns name of this flag. + virtual absl::string_view Name() const = 0; + // Returns name of the file where this flag is defined. + virtual std::string Filename() const = 0; + // Returns name of the flag's value type for some built-in types or empty + // std::string. + virtual absl::string_view Typename() const = 0; + // Returns help message associated with this flag. + virtual std::string Help() const = 0; + // Returns true iff this object corresponds to retired flag. + virtual bool IsRetired() const { return false; } + // Returns true iff this is a handle to an Abseil Flag. + virtual bool IsAbseilFlag() const { return true; } + // Returns id of the flag's value type. + virtual FlagStaticTypeId TypeId() const = 0; + virtual bool IsModified() const = 0; + virtual bool IsSpecifiedOnCommandLine() const = 0; + virtual std::string DefaultValue() const = 0; + virtual std::string CurrentValue() const = 0; + + // Interfaces to operate on validators. + virtual bool ValidateInputValue(absl::string_view value) const = 0; + + // Interface to save flag to some persistent state. Returns current flag state + // or nullptr if flag does not support saving and restoring a state. + virtual std::unique_ptr SaveState() = 0; + + // Sets the value of the flag based on specified std::string `value`. If the flag + // was successfully set to new value, it returns true. Otherwise, sets `error` + // to indicate the error, leaves the flag unchanged, and returns false. There + // are three ways to set the flag's value: + // * Update the current flag value + // * Update the flag's default value + // * Update the current flag value if it was never set before + // The mode is selected based on `set_mode` parameter. + virtual bool SetFromString(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string* error) = 0; + + // Checks that flags default value can be converted to std::string and back to the + // flag's value type. + virtual void CheckDefaultValueParsingRoundtrip() const = 0; + + protected: + ~CommandLineFlag() = default; + + private: + // Copy-construct a new value of the flag's type in a memory referenced by + // the dst based on the current flag's value. + virtual void Read(void* dst) const = 0; +}; + +// This macro is the "source of truth" for the list of supported flag built-in +// types. +#define ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \ + A(bool) \ + A(short) \ + A(unsigned short) \ + A(int) \ + A(unsigned int) \ + A(long) \ + A(unsigned long) \ + A(long long) \ + A(unsigned long long) \ + A(double) \ + A(float) \ + A(std::string) \ + A(std::vector) + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/internal/flag.h b/libs/or-tools-src-ubuntu/include/absl/flags/internal/flag.h new file mode 100644 index 0000000000000000000000000000000000000000..35a148cf6602e23c47f6c56fbb04e07037607863 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/internal/flag.h @@ -0,0 +1,671 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FLAGS_INTERNAL_FLAG_H_ +#define ABSL_FLAGS_INTERNAL_FLAG_H_ + +#include + +#include +#include +#include +#include +#include + +#include "absl/base/call_once.h" +#include "absl/base/config.h" +#include "absl/base/thread_annotations.h" +#include "absl/flags/config.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/registry.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +template +class Flag; + +/////////////////////////////////////////////////////////////////////////////// +// Flag value type operations, eg., parsing, copying, etc. are provided +// by function specific to that type with a signature matching FlagOpFn. + +enum class FlagOp { + kDelete, + kClone, + kCopy, + kCopyConstruct, + kSizeof, + kStaticTypeId, + kParse, + kUnparse, +}; +using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*); + +// Flag value specific operations routine. +template +void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { + switch (op) { + case FlagOp::kDelete: + delete static_cast(v1); + return nullptr; + case FlagOp::kClone: + return new T(*static_cast(v1)); + case FlagOp::kCopy: + *static_cast(v2) = *static_cast(v1); + return nullptr; + case FlagOp::kCopyConstruct: + new (v2) T(*static_cast(v1)); + return nullptr; + case FlagOp::kSizeof: + return reinterpret_cast(sizeof(T)); + case FlagOp::kStaticTypeId: + return reinterpret_cast(&FlagStaticTypeIdGen); + case FlagOp::kParse: { + // Initialize the temporary instance of type T based on current value in + // destination (which is going to be flag's default value). + T temp(*static_cast(v2)); + if (!absl::ParseFlag(*static_cast(v1), &temp, + static_cast(v3))) { + return nullptr; + } + *static_cast(v2) = std::move(temp); + return v2; + } + case FlagOp::kUnparse: + *static_cast(v2) = + absl::UnparseFlag(*static_cast(v1)); + return nullptr; + default: + return nullptr; + } +} + +// Deletes memory interpreting obj as flag value type pointer. +inline void Delete(FlagOpFn op, const void* obj) { + op(FlagOp::kDelete, obj, nullptr, nullptr); +} +// Makes a copy of flag value pointed by obj. +inline void* Clone(FlagOpFn op, const void* obj) { + return op(FlagOp::kClone, obj, nullptr, nullptr); +} +// Copies src to dst interpreting as flag value type pointers. +inline void Copy(FlagOpFn op, const void* src, void* dst) { + op(FlagOp::kCopy, src, dst, nullptr); +} +// Construct a copy of flag value in a location pointed by dst +// based on src - pointer to the flag's value. +inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) { + op(FlagOp::kCopyConstruct, src, dst, nullptr); +} +// Returns true if parsing of input text is successfull. +inline bool Parse(FlagOpFn op, absl::string_view text, void* dst, + std::string* error) { + return op(FlagOp::kParse, &text, dst, error) != nullptr; +} +// Returns string representing supplied value. +inline std::string Unparse(FlagOpFn op, const void* val) { + std::string result; + op(FlagOp::kUnparse, val, &result, nullptr); + return result; +} +// Returns size of flag value type. +inline size_t Sizeof(FlagOpFn op) { + // This sequence of casts reverses the sequence from + // `flags_internal::FlagOps()` + return static_cast(reinterpret_cast( + op(FlagOp::kSizeof, nullptr, nullptr, nullptr))); +} +// Returns static type id coresponding to the value type. +inline FlagStaticTypeId StaticTypeId(FlagOpFn op) { + return reinterpret_cast( + op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Persistent state of the flag data. + +template +class FlagState : public flags_internal::FlagStateInterface { + public: + FlagState(Flag* flag, T&& cur, bool modified, bool on_command_line, + int64_t counter) + : flag_(flag), + cur_value_(std::move(cur)), + modified_(modified), + on_command_line_(on_command_line), + counter_(counter) {} + + ~FlagState() override = default; + + private: + friend class Flag; + + // Restores the flag to the saved state. + void Restore() const override; + + // Flag and saved flag data. + Flag* flag_; + T cur_value_; + bool modified_; + bool on_command_line_; + int64_t counter_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Flag help auxiliary structs. + +// This is help argument for absl::Flag encapsulating the string literal pointer +// or pointer to function generating it as well as enum descriminating two +// cases. +using HelpGenFunc = std::string (*)(); + +union FlagHelpMsg { + constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {} + constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {} + + const char* literal; + HelpGenFunc gen_func; +}; + +enum class FlagHelpKind : uint8_t { kLiteral = 0, kGenFunc = 1 }; + +struct FlagHelpArg { + FlagHelpMsg source; + FlagHelpKind kind; +}; + +extern const char kStrippedFlagHelp[]; + +// HelpConstexprWrap is used by struct AbslFlagHelpGenFor##name generated by +// ABSL_FLAG macro. It is only used to silence the compiler in the case where +// help message expression is not constexpr and does not have type const char*. +// If help message expression is indeed constexpr const char* HelpConstexprWrap +// is just a trivial identity function. +template +const char* HelpConstexprWrap(const T&) { + return nullptr; +} +constexpr const char* HelpConstexprWrap(const char* p) { return p; } +constexpr const char* HelpConstexprWrap(char* p) { return p; } + +// These two HelpArg overloads allows us to select at compile time one of two +// way to pass Help argument to absl::Flag. We'll be passing +// AbslFlagHelpGenFor##name as T and integer 0 as a single argument to prefer +// first overload if possible. If T::Const is evaluatable on constexpr +// context (see non template int parameter below) we'll choose first overload. +// In this case the help message expression is immediately evaluated and is used +// to construct the absl::Flag. No additionl code is generated by ABSL_FLAG. +// Otherwise SFINAE kicks in and first overload is dropped from the +// consideration, in which case the second overload will be used. The second +// overload does not attempt to evaluate the help message expression +// immediately and instead delays the evaluation by returing the function +// pointer (&T::NonConst) genering the help message when necessary. This is +// evaluatable in constexpr context, but the cost is an extra function being +// generated in the ABSL_FLAG code. +template +constexpr FlagHelpArg HelpArg(int) { + return {FlagHelpMsg(T::Const()), FlagHelpKind::kLiteral}; +} + +template +constexpr FlagHelpArg HelpArg(char) { + return {FlagHelpMsg(&T::NonConst), FlagHelpKind::kGenFunc}; +} + +/////////////////////////////////////////////////////////////////////////////// +// Flag default value auxiliary structs. + +// Signature for the function generating the initial flag value (usually +// based on default value supplied in flag's definition) +using FlagDfltGenFunc = void* (*)(); + +union FlagDefaultSrc { + constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg) + : gen_func(gen_func_arg) {} + + void* dynamic_value; + FlagDfltGenFunc gen_func; +}; + +enum class FlagDefaultKind : uint8_t { kDynamicValue = 0, kGenFunc = 1 }; + +/////////////////////////////////////////////////////////////////////////////// +// Flag current value auxiliary structs. + +// The minimum atomic size we believe to generate lock free code, i.e. all +// trivially copyable types not bigger this size generate lock free code. +static constexpr int kMinLockFreeAtomicSize = 8; + +// The same as kMinLockFreeAtomicSize but maximum atomic size. As double words +// might use two registers, we want to dispatch the logic for them. +#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) +static constexpr int kMaxLockFreeAtomicSize = 16; +#else +static constexpr int kMaxLockFreeAtomicSize = 8; +#endif + +// We can use atomic in cases when it fits in the register, trivially copyable +// in order to make memcpy operations. +template +struct IsAtomicFlagTypeTrait { + static constexpr bool value = + (sizeof(T) <= kMaxLockFreeAtomicSize && + type_traits_internal::is_trivially_copyable::value); +}; + +// Clang does not always produce cmpxchg16b instruction when alignment of a 16 +// bytes type is not 16. +struct alignas(16) FlagsInternalTwoWordsType { + int64_t first; + int64_t second; +}; + +constexpr bool operator==(const FlagsInternalTwoWordsType& that, + const FlagsInternalTwoWordsType& other) { + return that.first == other.first && that.second == other.second; +} +constexpr bool operator!=(const FlagsInternalTwoWordsType& that, + const FlagsInternalTwoWordsType& other) { + return !(that == other); +} + +constexpr int64_t SmallAtomicInit() { return 0xababababababababll; } + +template +struct BestAtomicType { + using type = int64_t; + static constexpr int64_t AtomicInit() { return SmallAtomicInit(); } +}; + +template +struct BestAtomicType< + T, typename std::enable_if<(kMinLockFreeAtomicSize < sizeof(T) && + sizeof(T) <= kMaxLockFreeAtomicSize), + void>::type> { + using type = FlagsInternalTwoWordsType; + static constexpr FlagsInternalTwoWordsType AtomicInit() { + return {SmallAtomicInit(), SmallAtomicInit()}; + } +}; + +struct FlagValue { + // Heap allocated value. + void* dynamic = nullptr; + // For some types, a copy of the current value is kept in an atomically + // accessible field. + union Atomics { + // Using small atomic for small types. + std::atomic small_atomic; + template ::type> + int64_t load() const { + return small_atomic.load(std::memory_order_acquire); + } + +#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) + // Using big atomics for big types. + std::atomic big_atomic; + template ::type> + FlagsInternalTwoWordsType load() const { + return big_atomic.load(std::memory_order_acquire); + } + constexpr Atomics() + : big_atomic{FlagsInternalTwoWordsType{SmallAtomicInit(), + SmallAtomicInit()}} {} +#else + constexpr Atomics() : small_atomic{SmallAtomicInit()} {} +#endif + }; + Atomics atomics{}; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Flag callback auxiliary structs. + +// Signature for the mutation callback used by watched Flags +// The callback is noexcept. +// TODO(rogeeff): add noexcept after C++17 support is added. +using FlagCallbackFunc = void (*)(); + +struct FlagCallback { + FlagCallbackFunc func; + absl::Mutex guard; // Guard for concurrent callback invocations. +}; + +/////////////////////////////////////////////////////////////////////////////// +// Flag implementation, which does not depend on flag value type. +// The class encapsulates the Flag's data and access to it. + +struct DynValueDeleter { + explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {} + void operator()(void* ptr) const { + if (op != nullptr) Delete(op, ptr); + } + + FlagOpFn op; +}; + +class FlagImpl { + public: + constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op, + FlagHelpArg help, FlagDfltGenFunc default_value_gen) + : name_(name), + filename_(filename), + op_(op), + help_(help.source), + help_source_kind_(static_cast(help.kind)), + def_kind_(static_cast(FlagDefaultKind::kGenFunc)), + modified_(false), + on_command_line_(false), + counter_(0), + callback_(nullptr), + default_value_(default_value_gen), + data_guard_{} {} + + // Constant access methods + absl::string_view Name() const; + std::string Filename() const; + std::string Help() const; + bool IsModified() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + void Read(void* dst) const ABSL_LOCKS_EXCLUDED(*DataGuard()); + + template ::value, int>::type = 0> + void Get(T* dst) const { + AssertValidType(&flags_internal::FlagStaticTypeIdGen); + Read(dst); + } + // Overload for `GetFlag()` for types that support lock-free reads. + template ::value, + int>::type = 0> + void Get(T* dst) const { + // For flags of types which can be accessed "atomically" we want to avoid + // slowing down flag value access due to type validation. That's why + // this validation is hidden behind !NDEBUG +#ifndef NDEBUG + AssertValidType(&flags_internal::FlagStaticTypeIdGen); +#endif + using U = flags_internal::BestAtomicType; + typename U::type r = value_.atomics.template load(); + if (r != U::AtomicInit()) { + std::memcpy(static_cast(dst), &r, sizeof(T)); + } else { + Read(dst); + } + } + template + void Set(const T& src) { + AssertValidType(&flags_internal::FlagStaticTypeIdGen); + Write(&src); + } + + // Mutating access methods + void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool SetFromString(absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string* err) + ABSL_LOCKS_EXCLUDED(*DataGuard()); + // If possible, updates copy of the Flag's value that is stored in an + // atomic word. + void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + + // Interfaces to operate on callbacks. + void SetCallback(const FlagCallbackFunc mutation_callback) + ABSL_LOCKS_EXCLUDED(*DataGuard()); + void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + + // Interfaces to save/restore mutable flag data + template + std::unique_ptr SaveState(Flag* flag) const + ABSL_LOCKS_EXCLUDED(*DataGuard()) { + T&& cur_value = flag->Get(); + absl::MutexLock l(DataGuard()); + + return absl::make_unique>( + flag, std::move(cur_value), modified_, on_command_line_, counter_); + } + bool RestoreState(const void* value, bool modified, bool on_command_line, + int64_t counter) ABSL_LOCKS_EXCLUDED(*DataGuard()); + + // Value validation interfaces. + void CheckDefaultValueParsingRoundtrip() const + ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool ValidateInputValue(absl::string_view value) const + ABSL_LOCKS_EXCLUDED(*DataGuard()); + + private: + // Ensures that `data_guard_` is initialized and returns it. + absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED((absl::Mutex*)&data_guard_); + // Returns heap allocated value of type T initialized with default value. + std::unique_ptr MakeInitValue() const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + // Flag initialization called via absl::call_once. + void Init(); + // Attempts to parse supplied `value` std::string. If parsing is successful, + // returns new value. Otherwise returns nullptr. + std::unique_ptr TryParse(absl::string_view value, + std::string* err) const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + // Stores the flag value based on the pointer to the source. + void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + + FlagHelpKind HelpSourceKind() const { + return static_cast(help_source_kind_); + } + FlagDefaultKind DefaultKind() const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()) { + return static_cast(def_kind_); + } + // Used in read/write operations to validate source/target has correct type. + // For example if flag is declared as absl::Flag FLAGS_foo, a call to + // absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed + // int. To do that we pass the "assumed" type id (which is deduced from type + // int) as an argument `op`, which is in turn is validated against the type id + // stored in flag object by flag definition statement. + void AssertValidType(FlagStaticTypeId type_id) const; + + // Immutable flag's state. + + // Flags name passed to ABSL_FLAG as second arg. + const char* const name_; + // The file name where ABSL_FLAG resides. + const char* const filename_; + // Type-specific operations "vtable". + const FlagOpFn op_; + // Help message literal or function to generate it. + const FlagHelpMsg help_; + // Indicates if help message was supplied as literal or generator func. + const uint8_t help_source_kind_ : 1; + + // ------------------------------------------------------------------------ + // The bytes containing the const bitfields must not be shared with bytes + // containing the mutable bitfields. + // ------------------------------------------------------------------------ + + // Unique tag for absl::call_once call to initialize this flag. + // + // The placement of this variable between the immutable and mutable bitfields + // is important as prevents them from occupying the same byte. If you remove + // this variable, make sure to maintain this property. + absl::once_flag init_control_; + + // Mutable flag's state (guarded by `data_guard_`). + + // If def_kind_ == kDynamicValue, default_value_ holds a dynamically allocated + // value. + uint8_t def_kind_ : 1 ABSL_GUARDED_BY(*DataGuard()); + // Has this flag's value been modified? + bool modified_ : 1 ABSL_GUARDED_BY(*DataGuard()); + // Has this flag been specified on command line. + bool on_command_line_ : 1 ABSL_GUARDED_BY(*DataGuard()); + + // Mutation counter + int64_t counter_ ABSL_GUARDED_BY(*DataGuard()); + // Optional flag's callback and absl::Mutex to guard the invocations. + FlagCallback* callback_ ABSL_GUARDED_BY(*DataGuard()); + // Either a pointer to the function generating the default value based on the + // value specified in ABSL_FLAG or pointer to the dynamically set default + // value via SetCommandLineOptionWithMode. def_kind_ is used to distinguish + // these two cases. + FlagDefaultSrc default_value_ ABSL_GUARDED_BY(*DataGuard()); + // Current Flag Value + FlagValue value_; + + // This is reserved space for an absl::Mutex to guard flag data. It will be + // initialized in FlagImpl::Init via placement new. + // We can't use "absl::Mutex data_guard_", since this class is not literal. + // We do not want to use "absl::Mutex* data_guard_", since this would require + // heap allocation during initialization, which is both slows program startup + // and can fail. Using reserved space + placement new allows us to avoid both + // problems. + alignas(absl::Mutex) mutable char data_guard_[sizeof(absl::Mutex)]; +}; + +/////////////////////////////////////////////////////////////////////////////// +// The Flag object parameterized by the flag's value type. This class implements +// flag reflection handle interface. + +template +class Flag final : public flags_internal::CommandLineFlag { + public: + constexpr Flag(const char* name, const char* filename, const FlagHelpArg help, + const FlagDfltGenFunc default_value_gen) + : impl_(name, filename, &FlagOps, help, default_value_gen) {} + + T Get() const { + // See implementation notes in CommandLineFlag::Get(). + union U { + T value; + U() {} + ~U() { value.~T(); } + }; + U u; + + impl_.Get(&u.value); + return std::move(u.value); + } + void Set(const T& v) { impl_.Set(v); } + void SetCallback(const FlagCallbackFunc mutation_callback) { + impl_.SetCallback(mutation_callback); + } + + // CommandLineFlag interface + absl::string_view Name() const override { return impl_.Name(); } + std::string Filename() const override { return impl_.Filename(); } + absl::string_view Typename() const override { return ""; } + std::string Help() const override { return impl_.Help(); } + bool IsModified() const override { return impl_.IsModified(); } + bool IsSpecifiedOnCommandLine() const override { + return impl_.IsSpecifiedOnCommandLine(); + } + std::string DefaultValue() const override { return impl_.DefaultValue(); } + std::string CurrentValue() const override { return impl_.CurrentValue(); } + bool ValidateInputValue(absl::string_view value) const override { + return impl_.ValidateInputValue(value); + } + + // Interfaces to save and restore flags to/from persistent state. + // Returns current flag state or nullptr if flag does not support + // saving and restoring a state. + std::unique_ptr SaveState() override { + return impl_.SaveState(this); + } + + // Restores the flag state to the supplied state object. If there is + // nothing to restore returns false. Otherwise returns true. + bool RestoreState(const FlagState& flag_state) { + return impl_.RestoreState(&flag_state.cur_value_, flag_state.modified_, + flag_state.on_command_line_, flag_state.counter_); + } + bool SetFromString(absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string* error) override { + return impl_.SetFromString(value, set_mode, source, error); + } + void CheckDefaultValueParsingRoundtrip() const override { + impl_.CheckDefaultValueParsingRoundtrip(); + } + + private: + friend class FlagState; + + void Read(void* dst) const override { impl_.Read(dst); } + FlagStaticTypeId TypeId() const override { return &FlagStaticTypeIdGen; } + + // Flag's data + FlagImpl impl_; +}; + +template +inline void FlagState::Restore() const { + if (flag_->RestoreState(*this)) { + ABSL_INTERNAL_LOG(INFO, + absl::StrCat("Restore saved value of ", flag_->Name(), + " to: ", flag_->CurrentValue())); + } +} + +// This class facilitates Flag object registration and tail expression-based +// flag definition, for example: +// ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher); +template +class FlagRegistrar { + public: + explicit FlagRegistrar(Flag* flag) : flag_(flag) { + if (do_register) flags_internal::RegisterCommandLineFlag(flag_); + } + + FlagRegistrar& OnUpdate(FlagCallbackFunc cb) && { + flag_->SetCallback(cb); + return *this; + } + + // Make the registrar "die" gracefully as a bool on a line where registration + // happens. Registrar objects are intended to live only as temporary. + operator bool() const { return true; } // NOLINT + + private: + Flag* flag_; // Flag being registered (not owned). +}; + +// This struct and corresponding overload to MakeDefaultValue are used to +// facilitate usage of {} as default value in ABSL_FLAG macro. +struct EmptyBraces {}; + +template +T* MakeFromDefaultValue(T t) { + return new T(std::move(t)); +} + +template +T* MakeFromDefaultValue(EmptyBraces) { + return new T; +} + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_FLAG_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/internal/parse.h b/libs/or-tools-src-ubuntu/include/absl/flags/internal/parse.h new file mode 100644 index 0000000000000000000000000000000000000000..03e8a07bf3abc0e913588ebd6723b4a865132e02 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/internal/parse.h @@ -0,0 +1,51 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FLAGS_INTERNAL_PARSE_H_ +#define ABSL_FLAGS_INTERNAL_PARSE_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/flags/declare.h" + +ABSL_DECLARE_FLAG(std::vector, flagfile); +ABSL_DECLARE_FLAG(std::vector, fromenv); +ABSL_DECLARE_FLAG(std::vector, tryfromenv); +ABSL_DECLARE_FLAG(std::vector, undefok); + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs }; +enum class UsageFlagsAction { kHandleUsage, kIgnoreUsage }; +enum class OnUndefinedFlag { + kIgnoreUndefined, + kReportUndefined, + kAbortIfUndefined +}; + +std::vector ParseCommandLineImpl(int argc, char* argv[], + ArgvListAction arg_list_act, + UsageFlagsAction usage_flag_act, + OnUndefinedFlag on_undef_flag); + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_PARSE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/internal/path_util.h b/libs/or-tools-src-ubuntu/include/absl/flags/internal/path_util.h new file mode 100644 index 0000000000000000000000000000000000000000..365c830522665f28882fb790abb9408324c41f37 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/internal/path_util.h @@ -0,0 +1,63 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ +#define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ + +#include "absl/base/config.h" +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// A portable interface that returns the basename of the filename passed as an +// argument. It is similar to basename(3) +// . +// For example: +// flags_internal::Basename("a/b/prog/file.cc") +// returns "file.cc" +// flags_internal::Basename("file.cc") +// returns "file.cc" +inline absl::string_view Basename(absl::string_view filename) { + auto last_slash_pos = filename.find_last_of("/\\"); + + return last_slash_pos == absl::string_view::npos + ? filename + : filename.substr(last_slash_pos + 1); +} + +// A portable interface that returns the directory name of the filename +// passed as an argument, including the trailing slash. +// Returns the empty string if a slash is not found in the input file name. +// For example: +// flags_internal::Package("a/b/prog/file.cc") +// returns "a/b/prog/" +// flags_internal::Package("file.cc") +// returns "" +inline absl::string_view Package(absl::string_view filename) { + auto last_slash_pos = filename.find_last_of("/\\"); + + return last_slash_pos == absl::string_view::npos + ? absl::string_view() + : filename.substr(0, last_slash_pos + 1); +} + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/internal/program_name.h b/libs/or-tools-src-ubuntu/include/absl/flags/internal/program_name.h new file mode 100644 index 0000000000000000000000000000000000000000..b99b94fe18ab18ecff9d84032e6d11bc59f9e99a --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/internal/program_name.h @@ -0,0 +1,50 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_ +#define ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_ + +#include + +#include "absl/base/config.h" +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Program name + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// Returns program invocation name or "UNKNOWN" if `SetProgramInvocationName()` +// is never called. At the moment this is always set to argv[0] as part of +// library initialization. +std::string ProgramInvocationName(); + +// Returns base name for program invocation name. For example, if +// ProgramInvocationName() == "a/b/mybinary" +// then +// ShortProgramInvocationName() == "mybinary" +std::string ShortProgramInvocationName(); + +// Sets program invocation name to a new value. Should only be called once +// during program initialization, before any threads are spawned. +void SetProgramInvocationName(absl::string_view prog_name_str); + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/internal/registry.h b/libs/or-tools-src-ubuntu/include/absl/flags/internal/registry.h new file mode 100644 index 0000000000000000000000000000000000000000..69ff889fb1007c29639e5481e2676bfd6ddfce7d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/internal/registry.h @@ -0,0 +1,124 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FLAGS_INTERNAL_REGISTRY_H_ +#define ABSL_FLAGS_INTERNAL_REGISTRY_H_ + +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Global flags registry API. + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +CommandLineFlag* FindCommandLineFlag(absl::string_view name); +CommandLineFlag* FindRetiredFlag(absl::string_view name); + +// Executes specified visitor for each non-retired flag in the registry. +// Requires the caller hold the registry lock. +void ForEachFlagUnlocked(std::function visitor); +// Executes specified visitor for each non-retired flag in the registry. While +// callback are executed, the registry is locked and can't be changed. +void ForEachFlag(std::function visitor); + +//----------------------------------------------------------------------------- + +bool RegisterCommandLineFlag(CommandLineFlag*); + +//----------------------------------------------------------------------------- +// Retired registrations: +// +// Retired flag registrations are treated specially. A 'retired' flag is +// provided only for compatibility with automated invocations that still +// name it. A 'retired' flag: +// - is not bound to a C++ FLAGS_ reference. +// - has a type and a value, but that value is intentionally inaccessible. +// - does not appear in --help messages. +// - is fully supported by _all_ flag parsing routines. +// - consumes args normally, and complains about type mismatches in its +// argument. +// - emits a complaint but does not die (e.g. LOG(ERROR)) if it is +// accessed by name through the flags API for parsing or otherwise. +// +// The registrations for a flag happen in an unspecified order as the +// initializers for the namespace-scope objects of a program are run. +// Any number of weak registrations for a flag can weakly define the flag. +// One non-weak registration will upgrade the flag from weak to non-weak. +// Further weak registrations of a non-weak flag are ignored. +// +// This mechanism is designed to support moving dead flags into a +// 'graveyard' library. An example migration: +// +// 0: Remove references to this FLAGS_flagname in the C++ codebase. +// 1: Register as 'retired' in old_lib. +// 2: Make old_lib depend on graveyard. +// 3: Add a redundant 'retired' registration to graveyard. +// 4: Remove the old_lib 'retired' registration. +// 5: Eventually delete the graveyard registration entirely. +// + +// Retire flag with name "name" and type indicated by ops. +bool Retire(const char* name, FlagStaticTypeId type_id); + +// Registered a retired flag with name 'flag_name' and type 'T'. +template +inline bool RetiredFlag(const char* flag_name) { + return flags_internal::Retire(flag_name, &FlagStaticTypeIdGen); +} + +// If the flag is retired, returns true and indicates in |*type_is_bool| +// whether the type of the retired flag is a bool. +// Only to be called by code that needs to explicitly ignore retired flags. +bool IsRetiredFlag(absl::string_view name, bool* type_is_bool); + +//----------------------------------------------------------------------------- +// Saves the states (value, default value, whether the user has set +// the flag, registered validators, etc) of all flags, and restores +// them when the FlagSaver is destroyed. +// +// This class is thread-safe. However, its destructor writes to +// exactly the set of flags that have changed value during its +// lifetime, so concurrent _direct_ access to those flags +// (i.e. FLAGS_foo instead of {Get,Set}CommandLineOption()) is unsafe. + +class FlagSaver { + public: + FlagSaver(); + ~FlagSaver(); + + FlagSaver(const FlagSaver&) = delete; + void operator=(const FlagSaver&) = delete; + + // Prevents saver from restoring the saved state of flags. + void Ignore(); + + private: + class FlagSaverImpl* impl_; // we use pimpl here to keep API steady +}; + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_REGISTRY_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/internal/type_erased.h b/libs/or-tools-src-ubuntu/include/absl/flags/internal/type_erased.h new file mode 100644 index 0000000000000000000000000000000000000000..188429c77129ed826fb6584ba920577beab5b5ba --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/internal/type_erased.h @@ -0,0 +1,90 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ +#define ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ + +#include + +#include "absl/base/config.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/registry.h" +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Registry interfaces operating on type erased handles. + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// If a flag named "name" exists, store its current value in *OUTPUT +// and return true. Else return false without changing *OUTPUT. +// Thread-safe. +bool GetCommandLineOption(absl::string_view name, std::string* value); + +// Set the value of the flag named "name" to value. If successful, +// returns true. If not successful (e.g., the flag was not found or +// the value is not a valid value), returns false. +// Thread-safe. +bool SetCommandLineOption(absl::string_view name, absl::string_view value); + +bool SetCommandLineOptionWithMode(absl::string_view name, + absl::string_view value, + FlagSettingMode set_mode); + +//----------------------------------------------------------------------------- + +// Returns true iff all of the following conditions are true: +// (a) "name" names a registered flag +// (b) "value" can be parsed succesfully according to the type of the flag +// (c) parsed value passes any validator associated with the flag +bool IsValidFlagValue(absl::string_view name, absl::string_view value); + +//----------------------------------------------------------------------------- + +// Returns true iff a flag named "name" was specified on the command line +// (either directly, or via one of --flagfile or --fromenv or --tryfromenv). +// +// Any non-command-line modification of the flag does not affect the +// result of this function. So for example, if a flag was passed on +// the command line but then reset via SET_FLAGS_DEFAULT, this +// function will still return true. +bool SpecifiedOnCommandLine(absl::string_view name); + +//----------------------------------------------------------------------------- + +// If a flag with specified "name" exists and has type T, store +// its current value in *dst and return true. Else return false +// without touching *dst. T must obey all of the requirements for +// types passed to DEFINE_FLAG. +template +inline bool GetByName(absl::string_view name, T* dst) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + if (!flag) return false; + + if (auto val = flag->Get()) { + *dst = *val; + return true; + } + + return false; +} + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/internal/usage.h b/libs/or-tools-src-ubuntu/include/absl/flags/internal/usage.h new file mode 100644 index 0000000000000000000000000000000000000000..6b080fd1eeec5a0271b9c06760e55a0943df6d6e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/internal/usage.h @@ -0,0 +1,81 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FLAGS_INTERNAL_USAGE_H_ +#define ABSL_FLAGS_INTERNAL_USAGE_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/flags/declare.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Usage reporting interfaces + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// The format to report the help messages in. +enum class HelpFormat { + kHumanReadable, +}; + +// Outputs the help message describing specific flag. +void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, + HelpFormat format = HelpFormat::kHumanReadable); + +// Produces the help messages for all flags matching the filter. A flag matches +// the filter if it is defined in a file with a filename which includes +// filter string as a substring. You can use '/' and '.' to restrict the +// matching to a specific file names. For example: +// FlagsHelp(out, "/path/to/file."); +// restricts help to only flags which resides in files named like: +// .../path/to/file. +// for any extension 'ext'. If the filter is empty this function produces help +// messages for all flags. +void FlagsHelp(std::ostream& out, absl::string_view filter, + HelpFormat format, absl::string_view program_usage_message); + +// -------------------------------------------------------------------- + +// If any of the 'usage' related command line flags (listed on the bottom of +// this file) has been set this routine produces corresponding help message in +// the specified output stream and returns: +// 0 - if "version" or "only_check_flags" flags were set and handled. +// 1 - if some other 'usage' related flag was set and handled. +// -1 - if no usage flags were set on a commmand line. +// Non negative return values are expected to be used as an exit code for a +// binary. +int HandleUsageFlags(std::ostream& out, + absl::string_view program_usage_message); + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +ABSL_DECLARE_FLAG(bool, help); +ABSL_DECLARE_FLAG(bool, helpfull); +ABSL_DECLARE_FLAG(bool, helpshort); +ABSL_DECLARE_FLAG(bool, helppackage); +ABSL_DECLARE_FLAG(bool, version); +ABSL_DECLARE_FLAG(bool, only_check_args); +ABSL_DECLARE_FLAG(std::string, helpon); +ABSL_DECLARE_FLAG(std::string, helpmatch); + +#endif // ABSL_FLAGS_INTERNAL_USAGE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/marshalling.h b/libs/or-tools-src-ubuntu/include/absl/flags/marshalling.h new file mode 100644 index 0000000000000000000000000000000000000000..0b5033547e97841f2408afed87678162c7c5dc4e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/marshalling.h @@ -0,0 +1,264 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: marshalling.h +// ----------------------------------------------------------------------------- +// +// This header file defines the API for extending Abseil flag support to +// custom types, and defines the set of overloads for fundamental types. +// +// Out of the box, the Abseil flags library supports the following types: +// +// * `bool` +// * `int16_t` +// * `uint16_t` +// * `int32_t` +// * `uint32_t` +// * `int64_t` +// * `uint64_t` +// * `float` +// * `double` +// * `std::string` +// * `std::vector` +// * `absl::LogSeverity` (provided natively for layering reasons) +// +// Note that support for integral types is implemented using overloads for +// variable-width fundamental types (`short`, `int`, `long`, etc.). However, +// you should prefer the fixed-width integral types (`int32_t`, `uint64_t`, +// etc.) we've noted above within flag definitions. +// +// In addition, several Abseil libraries provide their own custom support for +// Abseil flags. Documentation for these formats is provided in the type's +// `AbslParseFlag()` definition. +// +// The Abseil time library provides the following support for civil time values: +// +// * `absl::CivilSecond` +// * `absl::CivilMinute` +// * `absl::CivilHour` +// * `absl::CivilDay` +// * `absl::CivilMonth` +// * `absl::CivilYear` +// +// and also provides support for the following absolute time values: +// +// * `absl::Duration` +// * `absl::Time` +// +// Additional support for Abseil types will be noted here as it is added. +// +// You can also provide your own custom flags by adding overloads for +// `AbslParseFlag()` and `AbslUnparseFlag()` to your type definitions. (See +// below.) +// +// ----------------------------------------------------------------------------- +// Adding Type Support for Abseil Flags +// ----------------------------------------------------------------------------- +// +// To add support for your user-defined type, add overloads of `AbslParseFlag()` +// and `AbslUnparseFlag()` as free (non-member) functions to your type. If `T` +// is a class type, these functions can be friend function definitions. These +// overloads must be added to the same namespace where the type is defined, so +// that they can be discovered by Argument-Dependent Lookup (ADL). +// +// Example: +// +// namespace foo { +// +// enum OutputMode { kPlainText, kHtml }; +// +// // AbslParseFlag converts from a string to OutputMode. +// // Must be in same namespace as OutputMode. +// +// // Parses an OutputMode from the command line flag value `text. Returns +// // `true` and sets `*mode` on success; returns `false` and sets `*error` +// // on failure. +// bool AbslParseFlag(absl::string_view text, +// OutputMode* mode, +// std::string* error) { +// if (text == "plaintext") { +// *mode = kPlainText; +// return true; +// } +// if (text == "html") { +// *mode = kHtml; +// return true; +// } +// *error = "unknown value for enumeration"; +// return false; +// } +// +// // AbslUnparseFlag converts from an OutputMode to a string. +// // Must be in same namespace as OutputMode. +// +// // Returns a textual flag value corresponding to the OutputMode `mode`. +// std::string AbslUnparseFlag(OutputMode mode) { +// switch (mode) { +// case kPlainText: return "plaintext"; +// case kHtml: return "html"; +// } +// return absl::StrCat(mode); +// } +// +// Notice that neither `AbslParseFlag()` nor `AbslUnparseFlag()` are class +// members, but free functions. `AbslParseFlag/AbslUnparseFlag()` overloads +// for a type should only be declared in the same file and namespace as said +// type. The proper `AbslParseFlag/AbslUnparseFlag()` implementations for a +// given type will be discovered via Argument-Dependent Lookup (ADL). +// +// `AbslParseFlag()` may need, in turn, to parse simpler constituent types +// using `absl::ParseFlag()`. For example, a custom struct `MyFlagType` +// consisting of a `std::pair` would add an `AbslParseFlag()` +// overload for its `MyFlagType` like so: +// +// Example: +// +// namespace my_flag_type { +// +// struct MyFlagType { +// std::pair my_flag_data; +// }; +// +// bool AbslParseFlag(absl::string_view text, MyFlagType* flag, +// std::string* err); +// +// std::string AbslUnparseFlag(const MyFlagType&); +// +// // Within the implementation, `AbslParseFlag()` will, in turn invoke +// // `absl::ParseFlag()` on its constituent `int` and `std::string` types +// // (which have built-in Abseil flag support. +// +// bool AbslParseFlag(absl::string_view text, MyFlagType* flag, +// std::string* err) { +// std::pair tokens = +// absl::StrSplit(text, ','); +// if (!absl::ParseFlag(tokens.first, &flag->my_flag_data.first, err)) +// return false; +// if (!absl::ParseFlag(tokens.second, &flag->my_flag_data.second, err)) +// return false; +// return true; +// } +// +// // Similarly, for unparsing, we can simply invoke `absl::UnparseFlag()` on +// // the constituent types. +// std::string AbslUnparseFlag(const MyFlagType& flag) { +// return absl::StrCat(absl::UnparseFlag(flag.my_flag_data.first), +// ",", +// absl::UnparseFlag(flag.my_flag_data.second)); +// } +#ifndef ABSL_FLAGS_MARSHALLING_H_ +#define ABSL_FLAGS_MARSHALLING_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// Overloads of `AbslParseFlag()` and `AbslUnparseFlag()` for fundamental types. +bool AbslParseFlag(absl::string_view, bool*, std::string*); +bool AbslParseFlag(absl::string_view, short*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, unsigned short*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, int*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, unsigned int*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, long*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, unsigned long*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, long long*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, unsigned long long*, // NOLINT + std::string*); +bool AbslParseFlag(absl::string_view, float*, std::string*); +bool AbslParseFlag(absl::string_view, double*, std::string*); +bool AbslParseFlag(absl::string_view, std::string*, std::string*); +bool AbslParseFlag(absl::string_view, std::vector*, std::string*); + +template +bool InvokeParseFlag(absl::string_view input, T* dst, std::string* err) { + // Comment on next line provides a good compiler error message if T + // does not have AbslParseFlag(absl::string_view, T*, std::string*). + return AbslParseFlag(input, dst, err); // Is T missing AbslParseFlag? +} + +// Strings and std:: containers do not have the same overload resolution +// considerations as fundamental types. Naming these 'AbslUnparseFlag' means we +// can avoid the need for additional specializations of Unparse (below). +std::string AbslUnparseFlag(absl::string_view v); +std::string AbslUnparseFlag(const std::vector&); + +template +std::string Unparse(const T& v) { + // Comment on next line provides a good compiler error message if T does not + // have UnparseFlag. + return AbslUnparseFlag(v); // Is T missing AbslUnparseFlag? +} + +// Overloads for builtin types. +std::string Unparse(bool v); +std::string Unparse(short v); // NOLINT +std::string Unparse(unsigned short v); // NOLINT +std::string Unparse(int v); // NOLINT +std::string Unparse(unsigned int v); // NOLINT +std::string Unparse(long v); // NOLINT +std::string Unparse(unsigned long v); // NOLINT +std::string Unparse(long long v); // NOLINT +std::string Unparse(unsigned long long v); // NOLINT +std::string Unparse(float v); +std::string Unparse(double v); + +} // namespace flags_internal + +// ParseFlag() +// +// Parses a string value into a flag value of type `T`. Do not add overloads of +// this function for your type directly; instead, add an `AbslParseFlag()` +// free function as documented above. +// +// Some implementations of `AbslParseFlag()` for types which consist of other, +// constituent types which already have Abseil flag support, may need to call +// `absl::ParseFlag()` on those consituent string values. (See above.) +template +inline bool ParseFlag(absl::string_view input, T* dst, std::string* error) { + return flags_internal::InvokeParseFlag(input, dst, error); +} + +// UnparseFlag() +// +// Unparses a flag value of type `T` into a string value. Do not add overloads +// of this function for your type directly; instead, add an `AbslUnparseFlag()` +// free function as documented above. +// +// Some implementations of `AbslUnparseFlag()` for types which consist of other, +// constituent types which already have Abseil flag support, may want to call +// `absl::UnparseFlag()` on those constituent types. (See above.) +template +inline std::string UnparseFlag(const T& v) { + return flags_internal::Unparse(v); +} + +// Overloads for `absl::LogSeverity` can't (easily) appear alongside that type's +// definition because it is layered below flags. See proper documentation in +// base/log_severity.h. +enum class LogSeverity : int; +bool AbslParseFlag(absl::string_view, absl::LogSeverity*, std::string*); +std::string AbslUnparseFlag(absl::LogSeverity); + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_MARSHALLING_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/parse.h b/libs/or-tools-src-ubuntu/include/absl/flags/parse.h new file mode 100644 index 0000000000000000000000000000000000000000..f37b0602e662875fc9a953b3b60fdd5d9b0aad25 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/parse.h @@ -0,0 +1,61 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: parse.h +// ----------------------------------------------------------------------------- +// +// This file defines the main parsing function for Abseil flags: +// `absl::ParseCommandLine()`. + +#ifndef ABSL_FLAGS_PARSE_H_ +#define ABSL_FLAGS_PARSE_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/flags/internal/parse.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// ParseCommandLine() +// +// Parses the set of command-line arguments passed in the `argc` (argument +// count) and `argv[]` (argument vector) parameters from `main()`, assigning +// values to any defined Abseil flags. (Any arguments passed after the +// flag-terminating delimiter (`--`) are treated as positional arguments and +// ignored.) +// +// Any command-line flags (and arguments to those flags) are parsed into Abseil +// Flag values, if those flags are defined. Any undefined flags will either +// return an error, or be ignored if that flag is designated using `undefok` to +// indicate "undefined is OK." +// +// Any command-line positional arguments not part of any command-line flag (or +// arguments to a flag) are returned in a vector, with the program invocation +// name at position 0 of that vector. (Note that this includes positional +// arguments after the flag-terminating delimiter `--`.) +// +// After all flags and flag arguments are parsed, this function looks for any +// built-in usage flags (e.g. `--help`), and if any were specified, it reports +// help messages and then exits the program. +std::vector ParseCommandLine(int argc, char* argv[]); + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_PARSE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/usage.h b/libs/or-tools-src-ubuntu/include/absl/flags/usage.h new file mode 100644 index 0000000000000000000000000000000000000000..ad12ab7ad902a835731bbee9cae4110522d3982c --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/usage.h @@ -0,0 +1,43 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FLAGS_USAGE_H_ +#define ABSL_FLAGS_USAGE_H_ + +#include "absl/base/config.h" +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Usage reporting interfaces + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Sets the "usage" message to be used by help reporting routines. +// For example: +// absl::SetProgramUsageMessage( +// absl::StrCat("This program does nothing. Sample usage:\n", argv[0], +// " ")); +// Do not include commandline flags in the usage: we do that for you! +// Note: Calling SetProgramUsageMessage twice will trigger a call to std::exit. +void SetProgramUsageMessage(absl::string_view new_usage_message); + +// Returns the usage message set by SetProgramUsageMessage(). +absl::string_view ProgramUsageMessage(); + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_USAGE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/flags/usage_config.h b/libs/or-tools-src-ubuntu/include/absl/flags/usage_config.h new file mode 100644 index 0000000000000000000000000000000000000000..0ed7e1b47c0c5d48dfa95f5e922728c6bd45c851 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/flags/usage_config.h @@ -0,0 +1,134 @@ +// +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: usage_config.h +// ----------------------------------------------------------------------------- +// +// This file defines the main usage reporting configuration interfaces and +// documents Abseil's supported built-in usage flags. If these flags are found +// when parsing a command-line, Abseil will exit the program and display +// appropriate help messages. +#ifndef ABSL_FLAGS_USAGE_CONFIG_H_ +#define ABSL_FLAGS_USAGE_CONFIG_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/strings/string_view.h" + +// ----------------------------------------------------------------------------- +// Built-in Usage Flags +// ----------------------------------------------------------------------------- +// +// Abseil supports the following built-in usage flags. When passed, these flags +// exit the program and : +// +// * --help +// Shows help on important flags for this binary +// * --helpfull +// Shows help on all flags +// * --helpshort +// Shows help on only the main module for this program +// * --helppackage +// Shows help on all modules in the main package +// * --version +// Shows the version and build info for this binary and exits +// * --only_check_args +// Exits after checking all flags +// * --helpon +// Shows help on the modules named by this flag value +// * --helpmatch +// Shows help on modules whose name contains the specified substring + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace flags_internal { +using FlagKindFilter = std::function; +} // namespace flags_internal + +// FlagsUsageConfig +// +// This structure contains the collection of callbacks for changing the behavior +// of the usage reporting routines in Abseil Flags. +struct FlagsUsageConfig { + // Returns true if flags defined in the given source code file should be + // reported with --helpshort flag. For example, if the file + // "path/to/my/code.cc" defines the flag "--my_flag", and + // contains_helpshort_flags("path/to/my/code.cc") returns true, invoking the + // program with --helpshort will include information about --my_flag in the + // program output. + flags_internal::FlagKindFilter contains_helpshort_flags; + + // Returns true if flags defined in the filename should be reported with + // --help flag. For example, if the file + // "path/to/my/code.cc" defines the flag "--my_flag", and + // contains_help_flags("path/to/my/code.cc") returns true, invoking the + // program with --help will include information about --my_flag in the + // program output. + flags_internal::FlagKindFilter contains_help_flags; + + // Returns true if flags defined in the filename should be reported with + // --helppackage flag. For example, if the file + // "path/to/my/code.cc" defines the flag "--my_flag", and + // contains_helppackage_flags("path/to/my/code.cc") returns true, invoking the + // program with --helppackage will include information about --my_flag in the + // program output. + flags_internal::FlagKindFilter contains_helppackage_flags; + + // Generates std::string containing program version. This is the std::string reported + // when user specifies --version in a command line. + std::function version_string; + + // Normalizes the filename specific to the build system/filesystem used. This + // routine is used when we report the information about the flag definition + // location. For instance, if your build resides at some location you do not + // want to expose in the usage output, you can trim it to show only relevant + // part. + // For example: + // normalize_filename("/my_company/some_long_path/src/project/file.cc") + // might produce + // "project/file.cc". + std::function normalize_filename; +}; + +// SetFlagsUsageConfig() +// +// Sets the usage reporting configuration callbacks. If any of the callbacks are +// not set in usage_config instance, then the default value of the callback is +// used. +void SetFlagsUsageConfig(FlagsUsageConfig usage_config); + +namespace flags_internal { + +FlagsUsageConfig GetUsageConfig(); + +void ReportUsageError(absl::string_view msg, bool is_fatal); + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +extern "C" { + +// Additional report of fatal usage error message before we std::exit. Error is +// fatal if is_fatal argument to ReportUsageError is true. +void AbslInternalReportFatalUsageError(absl::string_view); + +} // extern "C" + +#endif // ABSL_FLAGS_USAGE_CONFIG_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/functional/bind_front.h b/libs/or-tools-src-ubuntu/include/absl/functional/bind_front.h new file mode 100644 index 0000000000000000000000000000000000000000..5b47970e35779139436dc406cad130b7cb37a767 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/functional/bind_front.h @@ -0,0 +1,184 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: bind_front.h +// ----------------------------------------------------------------------------- +// +// `absl::bind_front()` returns a functor by binding a number of arguments to +// the front of a provided (usually more generic) functor. Unlike `std::bind`, +// it does not require the use of argument placeholders. The simpler syntax of +// `absl::bind_front()` allows you to avoid known misuses with `std::bind()`. +// +// `absl::bind_front()` is meant as a drop-in replacement for C++20's upcoming +// `std::bind_front()`, which similarly resolves these issues with +// `std::bind()`. Both `bind_front()` alternatives, unlike `std::bind()`, allow +// partial function application. (See +// https://en.wikipedia.org/wiki/Partial_application). + +#ifndef ABSL_FUNCTIONAL_BIND_FRONT_H_ +#define ABSL_FUNCTIONAL_BIND_FRONT_H_ + +#include "absl/functional/internal/front_binder.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// bind_front() +// +// Binds the first N arguments of an invocable object and stores them by value. +// +// Like `std::bind()`, `absl::bind_front()` is implicitly convertible to +// `std::function`. In particular, it may be used as a simpler replacement for +// `std::bind()` in most cases, as it does not require placeholders to be +// specified. More importantly, it provides more reliable correctness guarantees +// than `std::bind()`; while `std::bind()` will silently ignore passing more +// parameters than expected, for example, `absl::bind_front()` will report such +// mis-uses as errors. +// +// absl::bind_front(a...) can be seen as storing the results of +// std::make_tuple(a...). +// +// Example: Binding a free function. +// +// int Minus(int a, int b) { return a - b; } +// +// assert(absl::bind_front(Minus)(3, 2) == 3 - 2); +// assert(absl::bind_front(Minus, 3)(2) == 3 - 2); +// assert(absl::bind_front(Minus, 3, 2)() == 3 - 2); +// +// Example: Binding a member function. +// +// struct Math { +// int Double(int a) const { return 2 * a; } +// }; +// +// Math math; +// +// assert(absl::bind_front(&Math::Double)(&math, 3) == 2 * 3); +// // Stores a pointer to math inside the functor. +// assert(absl::bind_front(&Math::Double, &math)(3) == 2 * 3); +// // Stores a copy of math inside the functor. +// assert(absl::bind_front(&Math::Double, math)(3) == 2 * 3); +// // Stores std::unique_ptr inside the functor. +// assert(absl::bind_front(&Math::Double, +// std::unique_ptr(new Math))(3) == 2 * 3); +// +// Example: Using `absl::bind_front()`, instead of `std::bind()`, with +// `std::function`. +// +// class FileReader { +// public: +// void ReadFileAsync(const std::string& filename, std::string* content, +// const std::function& done) { +// // Calls Executor::Schedule(std::function). +// Executor::DefaultExecutor()->Schedule( +// absl::bind_front(&FileReader::BlockingRead, this, +// filename, content, done)); +// } +// +// private: +// void BlockingRead(const std::string& filename, std::string* content, +// const std::function& done) { +// CHECK_OK(file::GetContents(filename, content, {})); +// done(); +// } +// }; +// +// `absl::bind_front()` stores bound arguments explicitly using the type passed +// rather than implicitly based on the type accepted by its functor. +// +// Example: Binding arguments explicitly. +// +// void LogStringView(absl::string_view sv) { +// LOG(INFO) << sv; +// } +// +// Executor* e = Executor::DefaultExecutor(); +// std::string s = "hello"; +// absl::string_view sv = s; +// +// // absl::bind_front(LogStringView, arg) makes a copy of arg and stores it. +// e->Schedule(absl::bind_front(LogStringView, sv)); // ERROR: dangling +// // string_view. +// +// e->Schedule(absl::bind_front(LogStringView, s)); // OK: stores a copy of +// // s. +// +// To store some of the arguments passed to `absl::bind_front()` by reference, +// use std::ref()` and `std::cref()`. +// +// Example: Storing some of the bound arguments by reference. +// +// class Service { +// public: +// void Serve(const Request& req, std::function* done) { +// // The request protocol buffer won't be deleted until done is called. +// // It's safe to store a reference to it inside the functor. +// Executor::DefaultExecutor()->Schedule( +// absl::bind_front(&Service::BlockingServe, this, std::cref(req), +// done)); +// } +// +// private: +// void BlockingServe(const Request& req, std::function* done); +// }; +// +// Example: Storing bound arguments by reference. +// +// void Print(const std::string& a, const std::string& b) { +// std::cerr << a << b; +// } +// +// std::string hi = "Hello, "; +// std::vector names = {"Chuk", "Gek"}; +// // Doesn't copy hi. +// for_each(names.begin(), names.end(), +// absl::bind_front(Print, std::ref(hi))); +// +// // DO NOT DO THIS: the functor may outlive "hi", resulting in +// // dangling references. +// foo->DoInFuture(absl::bind_front(Print, std::ref(hi), "Guest")); // BAD! +// auto f = absl::bind_front(Print, std::ref(hi), "Guest"); // BAD! +// +// Example: Storing reference-like types. +// +// void Print(absl::string_view a, const std::string& b) { +// std::cerr << a << b; +// } +// +// std::string hi = "Hello, "; +// // Copies "hi". +// absl::bind_front(Print, hi)("Chuk"); +// +// // Compile error: std::reference_wrapper is not implicitly +// // convertible to string_view. +// // absl::bind_front(Print, std::cref(hi))("Chuk"); +// +// // Doesn't copy "hi". +// absl::bind_front(Print, absl::string_view(hi))("Chuk"); +// +template +constexpr functional_internal::bind_front_t bind_front( + F&& func, BoundArgs&&... args) { + return functional_internal::bind_front_t( + absl::in_place, absl::forward(func), + absl::forward(args)...); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_BIND_FRONT_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/functional/function_ref.h b/libs/or-tools-src-ubuntu/include/absl/functional/function_ref.h new file mode 100644 index 0000000000000000000000000000000000000000..370acc55b041347bb54db71b929f4c385492f08a --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/functional/function_ref.h @@ -0,0 +1,139 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: function_ref.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `absl::FunctionRef` type for holding a +// non-owning reference to an object of any invocable type. This function +// reference is typically most useful as a type-erased argument type for +// accepting function types that neither take ownership nor copy the type; using +// the reference type in this case avoids a copy and an allocation. Best +// practices of other non-owning reference-like objects (such as +// `absl::string_view`) apply here. +// +// An `absl::FunctionRef` is similar in usage to a `std::function` but has the +// following differences: +// +// * It doesn't own the underlying object. +// * It doesn't have a null or empty state. +// * It never performs deep copies or allocations. +// * It's much faster and cheaper to construct. +// * It's trivially copyable and destructable. +// +// Generally, `absl::FunctionRef` should not be used as a return value, data +// member, or to initialize a `std::function`. Such usages will often lead to +// problematic lifetime issues. Once you convert something to an +// `absl::FunctionRef` you cannot make a deep copy later. +// +// This class is suitable for use wherever a "const std::function<>&" +// would be used without making a copy. ForEach functions and other versions of +// the visitor pattern are a good example of when this class should be used. +// +// This class is trivial to copy and should be passed by value. +#ifndef ABSL_FUNCTIONAL_FUNCTION_REF_H_ +#define ABSL_FUNCTIONAL_FUNCTION_REF_H_ + +#include +#include +#include + +#include "absl/functional/internal/function_ref.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// FunctionRef +// +// Dummy class declaration to allow the partial specialization based on function +// types below. +template +class FunctionRef; + +// FunctionRef +// +// An `absl::FunctionRef` is a lightweight wrapper to any invokable object with +// a compatible signature. Generally, an `absl::FunctionRef` should only be used +// as an argument type and should be preferred as an argument over a const +// reference to a `std::function`. +// +// Example: +// +// // The following function takes a function callback by const reference +// bool Visitor(const std::function& callback); +// +// // Assuming that the function is not stored or otherwise copied, it can be +// // replaced by an `absl::FunctionRef`: +// bool Visitor(absl::FunctionRef +// callback); +// +// Note: the assignment operator within an `absl::FunctionRef` is intentionally +// deleted to prevent misuse; because the `absl::FunctionRef` does not own the +// underlying type, assignment likely indicates misuse. +template +class FunctionRef { + private: + // Used to disable constructors for objects that are not compatible with the + // signature of this FunctionRef. + template > + using EnableIfCompatible = + typename std::enable_if::value || + std::is_convertible::value>::type; + + public: + // Constructs a FunctionRef from any invokable type. + template > + FunctionRef(const F& f) // NOLINT(runtime/explicit) + : invoker_(&absl::functional_internal::InvokeObject) { + absl::functional_internal::AssertNonNull(f); + ptr_.obj = &f; + } + + // Overload for function pointers. This eliminates a level of indirection that + // would happen if the above overload was used (it lets us store the pointer + // instead of a pointer to a pointer). + // + // This overload is also used for references to functions, since references to + // functions can decay to function pointers implicitly. + template < + typename F, typename = EnableIfCompatible, + absl::functional_internal::EnableIf::value> = 0> + FunctionRef(F* f) // NOLINT(runtime/explicit) + : invoker_(&absl::functional_internal::InvokeFunction) { + assert(f != nullptr); + ptr_.fun = reinterpret_cast(f); + } + + // To help prevent subtle lifetime bugs, FunctionRef is not assignable. + // Typically, it should only be used as an argument type. + FunctionRef& operator=(const FunctionRef& rhs) = delete; + + // Call the underlying object. + R operator()(Args... args) const { + return invoker_(ptr_, std::forward(args)...); + } + + private: + absl::functional_internal::VoidPtr ptr_; + absl::functional_internal::Invoker invoker_; +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_FUNCTION_REF_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/functional/internal/front_binder.h b/libs/or-tools-src-ubuntu/include/absl/functional/internal/front_binder.h new file mode 100644 index 0000000000000000000000000000000000000000..a4d95da44a7de0b5269594058597dc4b550e22b6 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/functional/internal/front_binder.h @@ -0,0 +1,95 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. + +// Implementation details for `absl::bind_front()`. + +#ifndef ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ +#define ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ + +#include +#include +#include + +#include "absl/base/internal/invoke.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace functional_internal { + +// Invoke the method, expanding the tuple of bound arguments. +template +R Apply(Tuple&& bound, absl::index_sequence, Args&&... free) { + return base_internal::Invoke( + absl::forward(bound).template get()..., + absl::forward(free)...); +} + +template +class FrontBinder { + using BoundArgsT = absl::container_internal::CompressedTuple; + using Idx = absl::make_index_sequence; + + BoundArgsT bound_args_; + + public: + template + constexpr explicit FrontBinder(absl::in_place_t, Ts&&... ts) + : bound_args_(absl::forward(ts)...) {} + + template > + R operator()(FreeArgs&&... free_args) & { + return functional_internal::Apply(bound_args_, Idx(), + absl::forward(free_args)...); + } + + template > + R operator()(FreeArgs&&... free_args) const& { + return functional_internal::Apply(bound_args_, Idx(), + absl::forward(free_args)...); + } + + template > + R operator()(FreeArgs&&... free_args) && { + // This overload is called when *this is an rvalue. If some of the bound + // arguments are stored by value or rvalue reference, we move them. + return functional_internal::Apply(absl::move(bound_args_), Idx(), + absl::forward(free_args)...); + } + + template > + R operator()(FreeArgs&&... free_args) const&& { + // This overload is called when *this is an rvalue. If some of the bound + // arguments are stored by value or rvalue reference, we move them. + return functional_internal::Apply(absl::move(bound_args_), Idx(), + absl::forward(free_args)...); + } +}; + +template +using bind_front_t = FrontBinder, absl::decay_t...>; + +} // namespace functional_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/functional/internal/function_ref.h b/libs/or-tools-src-ubuntu/include/absl/functional/internal/function_ref.h new file mode 100644 index 0000000000000000000000000000000000000000..d1575054eaf7a779fd589c17127c301ab7159d99 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/functional/internal/function_ref.h @@ -0,0 +1,106 @@ +// Copyright 2019 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ +#define ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ + +#include +#include +#include + +#include "absl/base/internal/invoke.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace functional_internal { + +// Like a void* that can handle function pointers as well. The standard does not +// allow function pointers to round-trip through void*, but void(*)() is fine. +// +// Note: It's important that this class remains trivial and is the same size as +// a pointer, since this allows the compiler to perform tail-call optimizations +// when the underlying function is a callable object with a matching signature. +union VoidPtr { + const void* obj; + void (*fun)(); +}; + +// Chooses the best type for passing T as an argument. +// Attempt to be close to SystemV AMD64 ABI. Objects with trivial copy ctor are +// passed by value. +template +constexpr bool PassByValue() { + return !std::is_lvalue_reference::value && + absl::is_trivially_copy_constructible::value && + absl::is_trivially_copy_assignable< + typename std::remove_cv::type>::value && + std::is_trivially_destructible::value && + sizeof(T) <= 2 * sizeof(void*); +} + +template +struct ForwardT : std::conditional(), T, T&&> {}; + +// An Invoker takes a pointer to the type-erased invokable object, followed by +// the arguments that the invokable object expects. +// +// Note: The order of arguments here is an optimization, since member functions +// have an implicit "this" pointer as their first argument, putting VoidPtr +// first allows the compiler to perform tail-call optimization in many cases. +template +using Invoker = R (*)(VoidPtr, typename ForwardT::type...); + +// +// InvokeObject and InvokeFunction provide static "Invoke" functions that can be +// used as Invokers for objects or functions respectively. +// +// static_cast handles the case the return type is void. +template +R InvokeObject(VoidPtr ptr, typename ForwardT::type... args) { + auto o = static_cast(ptr.obj); + return static_cast( + absl::base_internal::Invoke(*o, std::forward(args)...)); +} + +template +R InvokeFunction(VoidPtr ptr, typename ForwardT::type... args) { + auto f = reinterpret_cast(ptr.fun); + return static_cast( + absl::base_internal::Invoke(f, std::forward(args)...)); +} + +template +void AssertNonNull(const std::function& f) { + assert(f != nullptr); + (void)f; +} + +template +void AssertNonNull(const F&) {} + +template +void AssertNonNull(F C::*f) { + assert(f != nullptr); + (void)f; +} + +template +using EnableIf = typename ::std::enable_if::type; + +} // namespace functional_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/hash/hash.h b/libs/or-tools-src-ubuntu/include/absl/hash/hash.h new file mode 100644 index 0000000000000000000000000000000000000000..23a65ea8687eb0d1f181267c2babe5a7ab730f8b --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/hash/hash.h @@ -0,0 +1,324 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: hash.h +// ----------------------------------------------------------------------------- +// +// This header file defines the Abseil `hash` library and the Abseil hashing +// framework. This framework consists of the following: +// +// * The `absl::Hash` functor, which is used to invoke the hasher within the +// Abseil hashing framework. `absl::Hash` supports most basic types and +// a number of Abseil types out of the box. +// * `AbslHashValue`, an extension point that allows you to extend types to +// support Abseil hashing without requiring you to define a hashing +// algorithm. +// * `HashState`, a type-erased class which implements the manipulation of the +// hash state (H) itself, contains member functions `combine()` and +// `combine_contiguous()`, which you can use to contribute to an existing +// hash state when hashing your types. +// +// Unlike `std::hash` or other hashing frameworks, the Abseil hashing framework +// provides most of its utility by abstracting away the hash algorithm (and its +// implementation) entirely. Instead, a type invokes the Abseil hashing +// framework by simply combining its state with the state of known, hashable +// types. Hashing of that combined state is separately done by `absl::Hash`. +// +// One should assume that a hash algorithm is chosen randomly at the start of +// each process. E.g., absl::Hash()(9) in one process and +// absl::Hash()(9) in another process are likely to differ. +// +// Example: +// +// // Suppose we have a class `Circle` for which we want to add hashing: +// class Circle { +// public: +// ... +// private: +// std::pair center_; +// int radius_; +// }; +// +// // To add hashing support to `Circle`, we simply need to add a free +// // (non-member) function `AbslHashValue()`, and return the combined hash +// // state of the existing hash state and the class state. You can add such a +// // free function using a friend declaration within the body of the class: +// class Circle { +// public: +// ... +// template +// friend H AbslHashValue(H h, const Circle& c) { +// return H::combine(std::move(h), c.center_, c.radius_); +// } +// ... +// }; +// +// For more information, see Adding Type Support to `absl::Hash` below. +// +#ifndef ABSL_HASH_HASH_H_ +#define ABSL_HASH_HASH_H_ + +#include "absl/hash/internal/hash.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// ----------------------------------------------------------------------------- +// `absl::Hash` +// ----------------------------------------------------------------------------- +// +// `absl::Hash` is a convenient general-purpose hash functor for any type `T` +// satisfying any of the following conditions (in order): +// +// * T is an arithmetic or pointer type +// * T defines an overload for `AbslHashValue(H, const T&)` for an arbitrary +// hash state `H`. +// - T defines a specialization of `HASH_NAMESPACE::hash` +// - T defines a specialization of `std::hash` +// +// `absl::Hash` intrinsically supports the following types: +// +// * All integral types (including bool) +// * All enum types +// * All floating-point types (although hashing them is discouraged) +// * All pointer types, including nullptr_t +// * std::pair, if T1 and T2 are hashable +// * std::tuple, if all the Ts... are hashable +// * std::unique_ptr and std::shared_ptr +// * All string-like types including: +// * std::string +// * std::string_view (as well as any instance of std::basic_string that +// uses char and std::char_traits) +// * All the standard sequence containers (provided the elements are hashable) +// * All the standard ordered associative containers (provided the elements are +// hashable) +// * absl types such as the following: +// * absl::string_view +// * absl::InlinedVector +// * absl::FixedArray +// * absl::uint128 +// * absl::Time, absl::Duration, and absl::TimeZone +// +// Note: the list above is not meant to be exhaustive. Additional type support +// may be added, in which case the above list will be updated. +// +// ----------------------------------------------------------------------------- +// absl::Hash Invocation Evaluation +// ----------------------------------------------------------------------------- +// +// When invoked, `absl::Hash` searches for supplied hash functions in the +// following order: +// +// * Natively supported types out of the box (see above) +// * Types for which an `AbslHashValue()` overload is provided (such as +// user-defined types). See "Adding Type Support to `absl::Hash`" below. +// * Types which define a `HASH_NAMESPACE::hash` specialization (aka +// `__gnu_cxx::hash` for gcc/Clang or `stdext::hash` for MSVC) +// * Types which define a `std::hash` specialization +// +// The fallback to legacy hash functions exists mainly for backwards +// compatibility. If you have a choice, prefer defining an `AbslHashValue` +// overload instead of specializing any legacy hash functors. +// +// ----------------------------------------------------------------------------- +// The Hash State Concept, and using `HashState` for Type Erasure +// ----------------------------------------------------------------------------- +// +// The `absl::Hash` framework relies on the Concept of a "hash state." Such a +// hash state is used in several places: +// +// * Within existing implementations of `absl::Hash` to store the hashed +// state of an object. Note that it is up to the implementation how it stores +// such state. A hash table, for example, may mix the state to produce an +// integer value; a testing framework may simply hold a vector of that state. +// * Within implementations of `AbslHashValue()` used to extend user-defined +// types. (See "Adding Type Support to absl::Hash" below.) +// * Inside a `HashState`, providing type erasure for the concept of a hash +// state, which you can use to extend the `absl::Hash` framework for types +// that are otherwise difficult to extend using `AbslHashValue()`. (See the +// `HashState` class below.) +// +// The "hash state" concept contains two member functions for mixing hash state: +// +// * `H::combine(state, values...)` +// +// Combines an arbitrary number of values into a hash state, returning the +// updated state. Note that the existing hash state is move-only and must be +// passed by value. +// +// Each of the value types T must be hashable by H. +// +// NOTE: +// +// state = H::combine(std::move(state), value1, value2, value3); +// +// must be guaranteed to produce the same hash expansion as +// +// state = H::combine(std::move(state), value1); +// state = H::combine(std::move(state), value2); +// state = H::combine(std::move(state), value3); +// +// * `H::combine_contiguous(state, data, size)` +// +// Combines a contiguous array of `size` elements into a hash state, +// returning the updated state. Note that the existing hash state is +// move-only and must be passed by value. +// +// NOTE: +// +// state = H::combine_contiguous(std::move(state), data, size); +// +// need NOT be guaranteed to produce the same hash expansion as a loop +// (it may perform internal optimizations). If you need this guarantee, use a +// loop instead. +// +// ----------------------------------------------------------------------------- +// Adding Type Support to `absl::Hash` +// ----------------------------------------------------------------------------- +// +// To add support for your user-defined type, add a proper `AbslHashValue()` +// overload as a free (non-member) function. The overload will take an +// existing hash state and should combine that state with state from the type. +// +// Example: +// +// template +// H AbslHashValue(H state, const MyType& v) { +// return H::combine(std::move(state), v.field1, ..., v.fieldN); +// } +// +// where `(field1, ..., fieldN)` are the members you would use on your +// `operator==` to define equality. +// +// Notice that `AbslHashValue` is not a class member, but an ordinary function. +// An `AbslHashValue` overload for a type should only be declared in the same +// file and namespace as said type. The proper `AbslHashValue` implementation +// for a given type will be discovered via ADL. +// +// Note: unlike `std::hash', `absl::Hash` should never be specialized. It must +// only be extended by adding `AbslHashValue()` overloads. +// +template +using Hash = absl::hash_internal::Hash; + +// HashState +// +// A type erased version of the hash state concept, for use in user-defined +// `AbslHashValue` implementations that can't use templates (such as PImpl +// classes, virtual functions, etc.). The type erasure adds overhead so it +// should be avoided unless necessary. +// +// Note: This wrapper will only erase calls to: +// combine_contiguous(H, const unsigned char*, size_t) +// +// All other calls will be handled internally and will not invoke overloads +// provided by the wrapped class. +// +// Users of this class should still define a template `AbslHashValue` function, +// but can use `absl::HashState::Create(&state)` to erase the type of the hash +// state and dispatch to their private hashing logic. +// +// This state can be used like any other hash state. In particular, you can call +// `HashState::combine()` and `HashState::combine_contiguous()` on it. +// +// Example: +// +// class Interface { +// public: +// template +// friend H AbslHashValue(H state, const Interface& value) { +// state = H::combine(std::move(state), std::type_index(typeid(*this))); +// value.HashValue(absl::HashState::Create(&state)); +// return state; +// } +// private: +// virtual void HashValue(absl::HashState state) const = 0; +// }; +// +// class Impl : Interface { +// private: +// void HashValue(absl::HashState state) const override { +// absl::HashState::combine(std::move(state), v1_, v2_); +// } +// int v1_; +// std::string v2_; +// }; +class HashState : public hash_internal::HashStateBase { + public: + // HashState::Create() + // + // Create a new `HashState` instance that wraps `state`. All calls to + // `combine()` and `combine_contiguous()` on the new instance will be + // redirected to the original `state` object. The `state` object must outlive + // the `HashState` instance. + template + static HashState Create(T* state) { + HashState s; + s.Init(state); + return s; + } + + HashState(const HashState&) = delete; + HashState& operator=(const HashState&) = delete; + HashState(HashState&&) = default; + HashState& operator=(HashState&&) = default; + + // HashState::combine() + // + // Combines an arbitrary number of values into a hash state, returning the + // updated state. + using HashState::HashStateBase::combine; + + // HashState::combine_contiguous() + // + // Combines a contiguous array of `size` elements into a hash state, returning + // the updated state. + static HashState combine_contiguous(HashState hash_state, + const unsigned char* first, size_t size) { + hash_state.combine_contiguous_(hash_state.state_, first, size); + return hash_state; + } + using HashState::HashStateBase::combine_contiguous; + + private: + HashState() = default; + + template + static void CombineContiguousImpl(void* p, const unsigned char* first, + size_t size) { + T& state = *static_cast(p); + state = T::combine_contiguous(std::move(state), first, size); + } + + template + void Init(T* state) { + state_ = state; + combine_contiguous_ = &CombineContiguousImpl; + } + + // Do not erase an already erased state. + void Init(HashState* state) { + state_ = state->state_; + combine_contiguous_ = state->combine_contiguous_; + } + + void* state_; + void (*combine_contiguous_)(void*, const unsigned char*, size_t); +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HASH_HASH_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/hash/hash_testing.h b/libs/or-tools-src-ubuntu/include/absl/hash/hash_testing.h new file mode 100644 index 0000000000000000000000000000000000000000..1e1c5741491eaf7c3334e10f590625e839514e1e --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/hash/hash_testing.h @@ -0,0 +1,378 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_HASH_HASH_TESTING_H_ +#define ABSL_HASH_HASH_TESTING_H_ + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/hash/internal/spy_hash_state.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/str_cat.h" +#include "absl/types/variant.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Run the absl::Hash algorithm over all the elements passed in and verify that +// their hash expansion is congruent with their `==` operator. +// +// It is used in conjunction with EXPECT_TRUE. Failures will output information +// on what requirement failed and on which objects. +// +// Users should pass a collection of types as either an initializer list or a +// container of cases. +// +// EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( +// {v1, v2, ..., vN})); +// +// std::vector cases; +// // Fill cases... +// EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases)); +// +// Users can pass a variety of types for testing heterogeneous lookup with +// `std::make_tuple`: +// +// EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( +// std::make_tuple(v1, v2, ..., vN))); +// +// +// Ideally, the values passed should provide enough coverage of the `==` +// operator and the AbslHashValue implementations. +// For dynamically sized types, the empty state should usually be included in +// the values. +// +// The function accepts an optional comparator function, in case that `==` is +// not enough for the values provided. +// +// Usage: +// +// EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( +// std::make_tuple(v1, v2, ..., vN), MyCustomEq{})); +// +// It checks the following requirements: +// 1. The expansion for a value is deterministic. +// 2. For any two objects `a` and `b` in the sequence, if `a == b` evaluates +// to true, then their hash expansion must be equal. +// 3. If `a == b` evaluates to false their hash expansion must be unequal. +// 4. If `a == b` evaluates to false neither hash expansion can be a +// suffix of the other. +// 5. AbslHashValue overloads should not be called by the user. They are only +// meant to be called by the framework. Users should call H::combine() and +// H::combine_contiguous(). +// 6. No moved-from instance of the hash state is used in the implementation +// of AbslHashValue. +// +// The values do not have to have the same type. This can be useful for +// equivalent types that support heterogeneous lookup. +// +// A possible reason for breaking (2) is combining state in the hash expansion +// that was not used in `==`. +// For example: +// +// struct Bad2 { +// int a, b; +// template +// friend H AbslHashValue(H state, Bad2 x) { +// // Uses a and b. +// return H::combine(std::move(state), x.a, x.b); +// } +// friend bool operator==(Bad2 x, Bad2 y) { +// // Only uses a. +// return x.a == y.a; +// } +// }; +// +// As for (3), breaking this usually means that there is state being passed to +// the `==` operator that is not used in the hash expansion. +// For example: +// +// struct Bad3 { +// int a, b; +// template +// friend H AbslHashValue(H state, Bad3 x) { +// // Only uses a. +// return H::combine(std::move(state), x.a); +// } +// friend bool operator==(Bad3 x, Bad3 y) { +// // Uses a and b. +// return x.a == y.a && x.b == y.b; +// } +// }; +// +// Finally, a common way to break 4 is by combining dynamic ranges without +// combining the size of the range. +// For example: +// +// struct Bad4 { +// int *p, size; +// template +// friend H AbslHashValue(H state, Bad4 x) { +// return H::combine_contiguous(std::move(state), x.p, x.p + x.size); +// } +// friend bool operator==(Bad4 x, Bad4 y) { +// // Compare two ranges for equality. C++14 code can instead use std::equal. +// return absl::equal(x.p, x.p + x.size, y.p, y.p + y.size); +// } +// }; +// +// An easy solution to this is to combine the size after combining the range, +// like so: +// template +// friend H AbslHashValue(H state, Bad4 x) { +// return H::combine( +// H::combine_contiguous(std::move(state), x.p, x.p + x.size), x.size); +// } +// +template +ABSL_MUST_USE_RESULT testing::AssertionResult +VerifyTypeImplementsAbslHashCorrectly(const Container& values); + +template +ABSL_MUST_USE_RESULT testing::AssertionResult +VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals); + +template +ABSL_MUST_USE_RESULT testing::AssertionResult +VerifyTypeImplementsAbslHashCorrectly(std::initializer_list values); + +template +ABSL_MUST_USE_RESULT testing::AssertionResult +VerifyTypeImplementsAbslHashCorrectly(std::initializer_list values, + Eq equals); + +namespace hash_internal { + +struct PrintVisitor { + size_t index; + template + std::string operator()(const T* value) const { + return absl::StrCat("#", index, "(", testing::PrintToString(*value), ")"); + } +}; + +template +struct EqVisitor { + Eq eq; + template + bool operator()(const T* t, const U* u) const { + return eq(*t, *u); + } +}; + +struct ExpandVisitor { + template + SpyHashState operator()(const T* value) const { + return SpyHashState::combine(SpyHashState(), *value); + } +}; + +template +ABSL_MUST_USE_RESULT testing::AssertionResult +VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) { + using V = typename Container::value_type; + + struct Info { + const V& value; + size_t index; + std::string ToString() const { + return absl::visit(PrintVisitor{index}, value); + } + SpyHashState expand() const { return absl::visit(ExpandVisitor{}, value); } + }; + + using EqClass = std::vector; + std::vector classes; + + // Gather the values in equivalence classes. + size_t i = 0; + for (const auto& value : values) { + EqClass* c = nullptr; + for (auto& eqclass : classes) { + if (absl::visit(EqVisitor{equals}, value, eqclass[0].value)) { + c = &eqclass; + break; + } + } + if (c == nullptr) { + classes.emplace_back(); + c = &classes.back(); + } + c->push_back({value, i}); + ++i; + + // Verify potential errors captured by SpyHashState. + if (auto error = c->back().expand().error()) { + return testing::AssertionFailure() << *error; + } + } + + if (classes.size() < 2) { + return testing::AssertionFailure() + << "At least two equivalence classes are expected."; + } + + // We assume that equality is correctly implemented. + // Now we verify that AbslHashValue is also correctly implemented. + + for (const auto& c : classes) { + // All elements of the equivalence class must have the same hash + // expansion. + const SpyHashState expected = c[0].expand(); + for (const Info& v : c) { + if (v.expand() != v.expand()) { + return testing::AssertionFailure() + << "Hash expansion for " << v.ToString() + << " is non-deterministic."; + } + if (v.expand() != expected) { + return testing::AssertionFailure() + << "Values " << c[0].ToString() << " and " << v.ToString() + << " evaluate as equal but have an unequal hash expansion."; + } + } + + // Elements from other classes must have different hash expansion. + for (const auto& c2 : classes) { + if (&c == &c2) continue; + const SpyHashState c2_hash = c2[0].expand(); + switch (SpyHashState::Compare(expected, c2_hash)) { + case SpyHashState::CompareResult::kEqual: + return testing::AssertionFailure() + << "Values " << c[0].ToString() << " and " << c2[0].ToString() + << " evaluate as unequal but have an equal hash expansion."; + case SpyHashState::CompareResult::kBSuffixA: + return testing::AssertionFailure() + << "Hash expansion of " << c2[0].ToString() + << " is a suffix of the hash expansion of " << c[0].ToString() + << "."; + case SpyHashState::CompareResult::kASuffixB: + return testing::AssertionFailure() + << "Hash expansion of " << c[0].ToString() + << " is a suffix of the hash expansion of " << c2[0].ToString() + << "."; + case SpyHashState::CompareResult::kUnequal: + break; + } + } + } + return testing::AssertionSuccess(); +} + +template +struct TypeSet { + template ...>::value> + struct Insert { + using type = TypeSet; + }; + template + struct Insert { + using type = TypeSet; + }; + + template (tuple)...}; + } + + static Out Do(const std::tuple& values) { + return DoImpl(values, absl::index_sequence_for()); + } +}; + +template <> +struct ContainerAsVector> { + static std::vector> Do(std::tuple<>) { return {}; } +}; + +struct DefaultEquals { + template + bool operator()(const T& t, const U& u) const { + return t == u; + } +}; + +} // namespace hash_internal + +template +ABSL_MUST_USE_RESULT testing::AssertionResult +VerifyTypeImplementsAbslHashCorrectly(const Container& values) { + return hash_internal::VerifyTypeImplementsAbslHashCorrectly( + hash_internal::ContainerAsVector::Do(values), + hash_internal::DefaultEquals{}); +} + +template +ABSL_MUST_USE_RESULT testing::AssertionResult +VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) { + return hash_internal::VerifyTypeImplementsAbslHashCorrectly( + hash_internal::ContainerAsVector::Do(values), equals); +} + +template +ABSL_MUST_USE_RESULT testing::AssertionResult +VerifyTypeImplementsAbslHashCorrectly(std::initializer_list values) { + return hash_internal::VerifyTypeImplementsAbslHashCorrectly( + hash_internal::ContainerAsVector>::Do(values), + hash_internal::DefaultEquals{}); +} + +template +ABSL_MUST_USE_RESULT testing::AssertionResult +VerifyTypeImplementsAbslHashCorrectly(std::initializer_list values, + Eq equals) { + return hash_internal::VerifyTypeImplementsAbslHashCorrectly( + hash_internal::ContainerAsVector>::Do(values), + equals); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HASH_HASH_TESTING_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/hash/internal/city.h b/libs/or-tools-src-ubuntu/include/absl/hash/internal/city.h new file mode 100644 index 0000000000000000000000000000000000000000..161c7748ec89b36c66e16336b0ca1d58230b6ebc --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/hash/internal/city.h @@ -0,0 +1,96 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// https://code.google.com/p/cityhash/ +// +// This file provides a few functions for hashing strings. All of them are +// high-quality functions in the sense that they pass standard tests such +// as Austin Appleby's SMHasher. They are also fast. +// +// For 64-bit x86 code, on short strings, we don't know of anything faster than +// CityHash64 that is of comparable quality. We believe our nearest competitor +// is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash +// tables and most other hashing (excluding cryptography). +// +// For 32-bit x86 code, we don't know of anything faster than CityHash32 that +// is of comparable quality. We believe our nearest competitor is Murmur3A. +// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.) +// +// Functions in the CityHash family are not suitable for cryptography. +// +// Please see CityHash's README file for more details on our performance +// measurements and so on. +// +// WARNING: This code has been only lightly tested on big-endian platforms! +// It is known to work well on little-endian platforms that have a small penalty +// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. +// It should work on all 32-bit and 64-bit platforms that allow unaligned reads; +// bug reports are welcome. +// +// By the way, for some hash functions, given strings a and b, the hash +// of a+b is easily derived from the hashes of a and b. This property +// doesn't hold for any hash functions in this file. + +#ifndef ABSL_HASH_INTERNAL_CITY_H_ +#define ABSL_HASH_INTERNAL_CITY_H_ + +#include +#include // for size_t. + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace hash_internal { + +typedef std::pair uint128; + +inline uint64_t Uint128Low64(const uint128 &x) { return x.first; } +inline uint64_t Uint128High64(const uint128 &x) { return x.second; } + +// Hash function for a byte array. +uint64_t CityHash64(const char *s, size_t len); + +// Hash function for a byte array. For convenience, a 64-bit seed is also +// hashed into the result. +uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed); + +// Hash function for a byte array. For convenience, two seeds are also +// hashed into the result. +uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t seed0, + uint64_t seed1); + +// Hash function for a byte array. Most useful in 32-bit binaries. +uint32_t CityHash32(const char *s, size_t len); + +// Hash 128 input bits down to 64 bits of output. +// This is intended to be a reasonably good hash function. +inline uint64_t Hash128to64(const uint128 &x) { + // Murmur-inspired hashing. + const uint64_t kMul = 0x9ddfea08eb382d69ULL; + uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; + a ^= (a >> 47); + uint64_t b = (Uint128High64(x) ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + return b; +} + +} // namespace hash_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HASH_INTERNAL_CITY_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/hash/internal/hash.h b/libs/or-tools-src-ubuntu/include/absl/hash/internal/hash.h new file mode 100644 index 0000000000000000000000000000000000000000..ae7a60cd55ada4aa03322d8245d3470e0c5eeddb --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/hash/internal/hash.h @@ -0,0 +1,988 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: hash.h +// ----------------------------------------------------------------------------- +// +#ifndef ABSL_HASH_INTERNAL_HASH_H_ +#define ABSL_HASH_INTERNAL_HASH_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/internal/endian.h" +#include "absl/base/port.h" +#include "absl/container/fixed_array.h" +#include "absl/meta/type_traits.h" +#include "absl/numeric/int128.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "absl/types/variant.h" +#include "absl/utility/utility.h" +#include "absl/hash/internal/city.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace hash_internal { + +class PiecewiseCombiner; + +// Internal detail: Large buffers are hashed in smaller chunks. This function +// returns the size of these chunks. +constexpr size_t PiecewiseChunkSize() { return 1024; } + +// HashStateBase +// +// A hash state object represents an intermediate state in the computation +// of an unspecified hash algorithm. `HashStateBase` provides a CRTP style +// base class for hash state implementations. Developers adding type support +// for `absl::Hash` should not rely on any parts of the state object other than +// the following member functions: +// +// * HashStateBase::combine() +// * HashStateBase::combine_contiguous() +// +// A derived hash state class of type `H` must provide a static member function +// with a signature similar to the following: +// +// `static H combine_contiguous(H state, const unsigned char*, size_t)`. +// +// `HashStateBase` will provide a complete implementation for a hash state +// object in terms of this method. +// +// Example: +// +// // Use CRTP to define your derived class. +// struct MyHashState : HashStateBase { +// static H combine_contiguous(H state, const unsigned char*, size_t); +// using MyHashState::HashStateBase::combine; +// using MyHashState::HashStateBase::combine_contiguous; +// }; +template +class HashStateBase { + public: + // HashStateBase::combine() + // + // Combines an arbitrary number of values into a hash state, returning the + // updated state. + // + // Each of the value types `T` must be separately hashable by the Abseil + // hashing framework. + // + // NOTE: + // + // state = H::combine(std::move(state), value1, value2, value3); + // + // is guaranteed to produce the same hash expansion as: + // + // state = H::combine(std::move(state), value1); + // state = H::combine(std::move(state), value2); + // state = H::combine(std::move(state), value3); + template + static H combine(H state, const T& value, const Ts&... values); + static H combine(H state) { return state; } + + // HashStateBase::combine_contiguous() + // + // Combines a contiguous array of `size` elements into a hash state, returning + // the updated state. + // + // NOTE: + // + // state = H::combine_contiguous(std::move(state), data, size); + // + // is NOT guaranteed to produce the same hash expansion as a for-loop (it may + // perform internal optimizations). If you need this guarantee, use the + // for-loop instead. + template + static H combine_contiguous(H state, const T* data, size_t size); + + private: + friend class PiecewiseCombiner; +}; + +// is_uniquely_represented +// +// `is_uniquely_represented` is a trait class that indicates whether `T` +// is uniquely represented. +// +// A type is "uniquely represented" if two equal values of that type are +// guaranteed to have the same bytes in their underlying storage. In other +// words, if `a == b`, then `memcmp(&a, &b, sizeof(T))` is guaranteed to be +// zero. This property cannot be detected automatically, so this trait is false +// by default, but can be specialized by types that wish to assert that they are +// uniquely represented. This makes them eligible for certain optimizations. +// +// If you have any doubt whatsoever, do not specialize this template. +// The default is completely safe, and merely disables some optimizations +// that will not matter for most types. Specializing this template, +// on the other hand, can be very hazardous. +// +// To be uniquely represented, a type must not have multiple ways of +// representing the same value; for example, float and double are not +// uniquely represented, because they have distinct representations for +// +0 and -0. Furthermore, the type's byte representation must consist +// solely of user-controlled data, with no padding bits and no compiler- +// controlled data such as vptrs or sanitizer metadata. This is usually +// very difficult to guarantee, because in most cases the compiler can +// insert data and padding bits at its own discretion. +// +// If you specialize this template for a type `T`, you must do so in the file +// that defines that type (or in this file). If you define that specialization +// anywhere else, `is_uniquely_represented` could have different meanings +// in different places. +// +// The Enable parameter is meaningless; it is provided as a convenience, +// to support certain SFINAE techniques when defining specializations. +template +struct is_uniquely_represented : std::false_type {}; + +// is_uniquely_represented +// +// unsigned char is a synonym for "byte", so it is guaranteed to be +// uniquely represented. +template <> +struct is_uniquely_represented : std::true_type {}; + +// is_uniquely_represented for non-standard integral types +// +// Integral types other than bool should be uniquely represented on any +// platform that this will plausibly be ported to. +template +struct is_uniquely_represented< + Integral, typename std::enable_if::value>::type> + : std::true_type {}; + +// is_uniquely_represented +// +// +template <> +struct is_uniquely_represented : std::false_type {}; + +// hash_bytes() +// +// Convenience function that combines `hash_state` with the byte representation +// of `value`. +template +H hash_bytes(H hash_state, const T& value) { + const unsigned char* start = reinterpret_cast(&value); + return H::combine_contiguous(std::move(hash_state), start, sizeof(value)); +} + +// PiecewiseCombiner +// +// PiecewiseCombiner is an internal-only helper class for hashing a piecewise +// buffer of `char` or `unsigned char` as though it were contiguous. This class +// provides two methods: +// +// H add_buffer(state, data, size) +// H finalize(state) +// +// `add_buffer` can be called zero or more times, followed by a single call to +// `finalize`. This will produce the same hash expansion as concatenating each +// buffer piece into a single contiguous buffer, and passing this to +// `H::combine_contiguous`. +// +// Example usage: +// PiecewiseCombiner combiner; +// for (const auto& piece : pieces) { +// state = combiner.add_buffer(std::move(state), piece.data, piece.size); +// } +// return combiner.finalize(std::move(state)); +class PiecewiseCombiner { + public: + PiecewiseCombiner() : position_(0) {} + PiecewiseCombiner(const PiecewiseCombiner&) = delete; + PiecewiseCombiner& operator=(const PiecewiseCombiner&) = delete; + + // PiecewiseCombiner::add_buffer() + // + // Appends the given range of bytes to the sequence to be hashed, which may + // modify the provided hash state. + template + H add_buffer(H state, const unsigned char* data, size_t size); + template + H add_buffer(H state, const char* data, size_t size) { + return add_buffer(std::move(state), + reinterpret_cast(data), size); + } + + // PiecewiseCombiner::finalize() + // + // Finishes combining the hash sequence, which may may modify the provided + // hash state. + // + // Once finalize() is called, add_buffer() may no longer be called. The + // resulting hash state will be the same as if the pieces passed to + // add_buffer() were concatenated into a single flat buffer, and then provided + // to H::combine_contiguous(). + template + H finalize(H state); + + private: + unsigned char buf_[PiecewiseChunkSize()]; + size_t position_; +}; + +// ----------------------------------------------------------------------------- +// AbslHashValue for Basic Types +// ----------------------------------------------------------------------------- + +// Note: Default `AbslHashValue` implementations live in `hash_internal`. This +// allows us to block lexical scope lookup when doing an unqualified call to +// `AbslHashValue` below. User-defined implementations of `AbslHashValue` can +// only be found via ADL. + +// AbslHashValue() for hashing bool values +// +// We use SFINAE to ensure that this overload only accepts bool, not types that +// are convertible to bool. +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, B value) { + return H::combine(std::move(hash_state), + static_cast(value ? 1 : 0)); +} + +// AbslHashValue() for hashing enum values +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, Enum e) { + // In practice, we could almost certainly just invoke hash_bytes directly, + // but it's possible that a sanitizer might one day want to + // store data in the unused bits of an enum. To avoid that risk, we + // convert to the underlying type before hashing. Hopefully this will get + // optimized away; if not, we can reopen discussion with c-toolchain-team. + return H::combine(std::move(hash_state), + static_cast::type>(e)); +} +// AbslHashValue() for hashing floating-point values +template +typename std::enable_if::value || + std::is_same::value, + H>::type +AbslHashValue(H hash_state, Float value) { + return hash_internal::hash_bytes(std::move(hash_state), + value == 0 ? 0 : value); +} + +// Long double has the property that it might have extra unused bytes in it. +// For example, in x86 sizeof(long double)==16 but it only really uses 80-bits +// of it. This means we can't use hash_bytes on a long double and have to +// convert it to something else first. +template +typename std::enable_if::value, H>::type +AbslHashValue(H hash_state, LongDouble value) { + const int category = std::fpclassify(value); + switch (category) { + case FP_INFINITE: + // Add the sign bit to differentiate between +Inf and -Inf + hash_state = H::combine(std::move(hash_state), std::signbit(value)); + break; + + case FP_NAN: + case FP_ZERO: + default: + // Category is enough for these. + break; + + case FP_NORMAL: + case FP_SUBNORMAL: + // We can't convert `value` directly to double because this would have + // undefined behavior if the value is out of range. + // std::frexp gives us a value in the range (-1, -.5] or [.5, 1) that is + // guaranteed to be in range for `double`. The truncation is + // implementation defined, but that works as long as it is deterministic. + int exp; + auto mantissa = static_cast(std::frexp(value, &exp)); + hash_state = H::combine(std::move(hash_state), mantissa, exp); + } + + return H::combine(std::move(hash_state), category); +} + +// AbslHashValue() for hashing pointers +template +H AbslHashValue(H hash_state, T* ptr) { + auto v = reinterpret_cast(ptr); + // Due to alignment, pointers tend to have low bits as zero, and the next few + // bits follow a pattern since they are also multiples of some base value. + // Mixing the pointer twice helps prevent stuck low bits for certain alignment + // values. + return H::combine(std::move(hash_state), v, v); +} + +// AbslHashValue() for hashing nullptr_t +template +H AbslHashValue(H hash_state, std::nullptr_t) { + return H::combine(std::move(hash_state), static_cast(nullptr)); +} + +// ----------------------------------------------------------------------------- +// AbslHashValue for Composite Types +// ----------------------------------------------------------------------------- + +// is_hashable() +// +// Trait class which returns true if T is hashable by the absl::Hash framework. +// Used for the AbslHashValue implementations for composite types below. +template +struct is_hashable; + +// AbslHashValue() for hashing pairs +template +typename std::enable_if::value && is_hashable::value, + H>::type +AbslHashValue(H hash_state, const std::pair& p) { + return H::combine(std::move(hash_state), p.first, p.second); +} + +// hash_tuple() +// +// Helper function for hashing a tuple. The third argument should +// be an index_sequence running from 0 to tuple_size - 1. +template +H hash_tuple(H hash_state, const Tuple& t, absl::index_sequence) { + return H::combine(std::move(hash_state), std::get(t)...); +} + +// AbslHashValue for hashing tuples +template +#if defined(_MSC_VER) +// This SFINAE gets MSVC confused under some conditions. Let's just disable it +// for now. +H +#else // _MSC_VER +typename std::enable_if...>::value, H>::type +#endif // _MSC_VER +AbslHashValue(H hash_state, const std::tuple& t) { + return hash_internal::hash_tuple(std::move(hash_state), t, + absl::make_index_sequence()); +} + +// ----------------------------------------------------------------------------- +// AbslHashValue for Pointers +// ----------------------------------------------------------------------------- + +// AbslHashValue for hashing unique_ptr +template +H AbslHashValue(H hash_state, const std::unique_ptr& ptr) { + return H::combine(std::move(hash_state), ptr.get()); +} + +// AbslHashValue for hashing shared_ptr +template +H AbslHashValue(H hash_state, const std::shared_ptr& ptr) { + return H::combine(std::move(hash_state), ptr.get()); +} + +// ----------------------------------------------------------------------------- +// AbslHashValue for String-Like Types +// ----------------------------------------------------------------------------- + +// AbslHashValue for hashing strings +// +// All the string-like types supported here provide the same hash expansion for +// the same character sequence. These types are: +// +// - `std::string` (and std::basic_string, A> for +// any allocator A) +// - `absl::string_view` and `std::string_view` +// +// For simplicity, we currently support only `char` strings. This support may +// be broadened, if necessary, but with some caution - this overload would +// misbehave in cases where the traits' `eq()` member isn't equivalent to `==` +// on the underlying character type. +template +H AbslHashValue(H hash_state, absl::string_view str) { + return H::combine( + H::combine_contiguous(std::move(hash_state), str.data(), str.size()), + str.size()); +} + +// Support std::wstring, std::u16string and std::u32string. +template ::value || + std::is_same::value || + std::is_same::value>> +H AbslHashValue( + H hash_state, + const std::basic_string, Alloc>& str) { + return H::combine( + H::combine_contiguous(std::move(hash_state), str.data(), str.size()), + str.size()); +} + +// ----------------------------------------------------------------------------- +// AbslHashValue for Sequence Containers +// ----------------------------------------------------------------------------- + +// AbslHashValue for hashing std::array +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, const std::array& array) { + return H::combine_contiguous(std::move(hash_state), array.data(), + array.size()); +} + +// AbslHashValue for hashing std::deque +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, const std::deque& deque) { + // TODO(gromer): investigate a more efficient implementation taking + // advantage of the chunk structure. + for (const auto& t : deque) { + hash_state = H::combine(std::move(hash_state), t); + } + return H::combine(std::move(hash_state), deque.size()); +} + +// AbslHashValue for hashing std::forward_list +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, const std::forward_list& list) { + size_t size = 0; + for (const T& t : list) { + hash_state = H::combine(std::move(hash_state), t); + ++size; + } + return H::combine(std::move(hash_state), size); +} + +// AbslHashValue for hashing std::list +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, const std::list& list) { + for (const auto& t : list) { + hash_state = H::combine(std::move(hash_state), t); + } + return H::combine(std::move(hash_state), list.size()); +} + +// AbslHashValue for hashing std::vector +// +// Do not use this for vector. It does not have a .data(), and a fallback +// for std::hash<> is most likely faster. +template +typename std::enable_if::value && !std::is_same::value, + H>::type +AbslHashValue(H hash_state, const std::vector& vector) { + return H::combine(H::combine_contiguous(std::move(hash_state), vector.data(), + vector.size()), + vector.size()); +} + +// ----------------------------------------------------------------------------- +// AbslHashValue for Ordered Associative Containers +// ----------------------------------------------------------------------------- + +// AbslHashValue for hashing std::map +template +typename std::enable_if::value && is_hashable::value, + H>::type +AbslHashValue(H hash_state, const std::map& map) { + for (const auto& t : map) { + hash_state = H::combine(std::move(hash_state), t); + } + return H::combine(std::move(hash_state), map.size()); +} + +// AbslHashValue for hashing std::multimap +template +typename std::enable_if::value && is_hashable::value, + H>::type +AbslHashValue(H hash_state, + const std::multimap& map) { + for (const auto& t : map) { + hash_state = H::combine(std::move(hash_state), t); + } + return H::combine(std::move(hash_state), map.size()); +} + +// AbslHashValue for hashing std::set +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, const std::set& set) { + for (const auto& t : set) { + hash_state = H::combine(std::move(hash_state), t); + } + return H::combine(std::move(hash_state), set.size()); +} + +// AbslHashValue for hashing std::multiset +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, const std::multiset& set) { + for (const auto& t : set) { + hash_state = H::combine(std::move(hash_state), t); + } + return H::combine(std::move(hash_state), set.size()); +} + +// ----------------------------------------------------------------------------- +// AbslHashValue for Wrapper Types +// ----------------------------------------------------------------------------- + +// AbslHashValue for hashing absl::optional +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, const absl::optional& opt) { + if (opt) hash_state = H::combine(std::move(hash_state), *opt); + return H::combine(std::move(hash_state), opt.has_value()); +} + +// VariantVisitor +template +struct VariantVisitor { + H&& hash_state; + template + H operator()(const T& t) const { + return H::combine(std::move(hash_state), t); + } +}; + +// AbslHashValue for hashing absl::variant +template +typename std::enable_if...>::value, H>::type +AbslHashValue(H hash_state, const absl::variant& v) { + if (!v.valueless_by_exception()) { + hash_state = absl::visit(VariantVisitor{std::move(hash_state)}, v); + } + return H::combine(std::move(hash_state), v.index()); +} + +// ----------------------------------------------------------------------------- +// AbslHashValue for Other Types +// ----------------------------------------------------------------------------- + +// AbslHashValue for hashing std::bitset is not defined, for the same reason as +// for vector (see std::vector above): It does not expose the raw bytes, +// and a fallback to std::hash<> is most likely faster. + +// ----------------------------------------------------------------------------- + +// hash_range_or_bytes() +// +// Mixes all values in the range [data, data+size) into the hash state. +// This overload accepts only uniquely-represented types, and hashes them by +// hashing the entire range of bytes. +template +typename std::enable_if::value, H>::type +hash_range_or_bytes(H hash_state, const T* data, size_t size) { + const auto* bytes = reinterpret_cast(data); + return H::combine_contiguous(std::move(hash_state), bytes, sizeof(T) * size); +} + +// hash_range_or_bytes() +template +typename std::enable_if::value, H>::type +hash_range_or_bytes(H hash_state, const T* data, size_t size) { + for (const auto end = data + size; data < end; ++data) { + hash_state = H::combine(std::move(hash_state), *data); + } + return hash_state; +} + +#if defined(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE) && \ + ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ +#define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 1 +#else +#define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 0 +#endif + +// HashSelect +// +// Type trait to select the appropriate hash implementation to use. +// HashSelect::type will give the proper hash implementation, to be invoked +// as: +// HashSelect::type::Invoke(state, value) +// Also, HashSelect::type::value is a boolean equal to `true` if there is a +// valid `Invoke` function. Types that are not hashable will have a ::value of +// `false`. +struct HashSelect { + private: + struct State : HashStateBase { + static State combine_contiguous(State hash_state, const unsigned char*, + size_t); + using State::HashStateBase::combine_contiguous; + }; + + struct UniquelyRepresentedProbe { + template + static auto Invoke(H state, const T& value) + -> absl::enable_if_t::value, H> { + return hash_internal::hash_bytes(std::move(state), value); + } + }; + + struct HashValueProbe { + template + static auto Invoke(H state, const T& value) -> absl::enable_if_t< + std::is_same::value, + H> { + return AbslHashValue(std::move(state), value); + } + }; + + struct LegacyHashProbe { +#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ + template + static auto Invoke(H state, const T& value) -> absl::enable_if_t< + std::is_convertible< + decltype(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash()(value)), + size_t>::value, + H> { + return hash_internal::hash_bytes( + std::move(state), + ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash{}(value)); + } +#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ + }; + + struct StdHashProbe { + template + static auto Invoke(H state, const T& value) + -> absl::enable_if_t::value, H> { + return hash_internal::hash_bytes(std::move(state), std::hash{}(value)); + } + }; + + template + struct Probe : Hash { + private: + template (), std::declval()))> + static std::true_type Test(int); + template + static std::false_type Test(char); + + public: + static constexpr bool value = decltype(Test(0))::value; + }; + + public: + // Probe each implementation in order. + // disjunction provides short circuiting wrt instantiation. + template + using Apply = absl::disjunction< // + Probe, // + Probe, // + Probe, // + Probe, // + std::false_type>; +}; + +template +struct is_hashable + : std::integral_constant::value> {}; + +// CityHashState +class ABSL_DLL CityHashState + : public HashStateBase { + // absl::uint128 is not an alias or a thin wrapper around the intrinsic. + // We use the intrinsic when available to improve performance. +#ifdef ABSL_HAVE_INTRINSIC_INT128 + using uint128 = __uint128_t; +#else // ABSL_HAVE_INTRINSIC_INT128 + using uint128 = absl::uint128; +#endif // ABSL_HAVE_INTRINSIC_INT128 + + static constexpr uint64_t kMul = + sizeof(size_t) == 4 ? uint64_t{0xcc9e2d51} + : uint64_t{0x9ddfea08eb382d69}; + + template + using IntegralFastPath = + conjunction, is_uniquely_represented>; + + public: + // Move only + CityHashState(CityHashState&&) = default; + CityHashState& operator=(CityHashState&&) = default; + + // CityHashState::combine_contiguous() + // + // Fundamental base case for hash recursion: mixes the given range of bytes + // into the hash state. + static CityHashState combine_contiguous(CityHashState hash_state, + const unsigned char* first, + size_t size) { + return CityHashState( + CombineContiguousImpl(hash_state.state_, first, size, + std::integral_constant{})); + } + using CityHashState::HashStateBase::combine_contiguous; + + // CityHashState::hash() + // + // For performance reasons in non-opt mode, we specialize this for + // integral types. + // Otherwise we would be instantiating and calling dozens of functions for + // something that is just one multiplication and a couple xor's. + // The result should be the same as running the whole algorithm, but faster. + template ::value, int> = 0> + static size_t hash(T value) { + return static_cast(Mix(Seed(), static_cast(value))); + } + + // Overload of CityHashState::hash() + template ::value, int> = 0> + static size_t hash(const T& value) { + return static_cast(combine(CityHashState{}, value).state_); + } + + private: + // Invoked only once for a given argument; that plus the fact that this is + // move-only ensures that there is only one non-moved-from object. + CityHashState() : state_(Seed()) {} + + // Workaround for MSVC bug. + // We make the type copyable to fix the calling convention, even though we + // never actually copy it. Keep it private to not affect the public API of the + // type. + CityHashState(const CityHashState&) = default; + + explicit CityHashState(uint64_t state) : state_(state) {} + + // Implementation of the base case for combine_contiguous where we actually + // mix the bytes into the state. + // Dispatch to different implementations of the combine_contiguous depending + // on the value of `sizeof(size_t)`. + static uint64_t CombineContiguousImpl(uint64_t state, + const unsigned char* first, size_t len, + std::integral_constant + /* sizeof_size_t */); + static uint64_t CombineContiguousImpl(uint64_t state, + const unsigned char* first, size_t len, + std::integral_constant + /* sizeof_size_t*/); + + // Slow dispatch path for calls to CombineContiguousImpl with a size argument + // larger than PiecewiseChunkSize(). Has the same effect as calling + // CombineContiguousImpl() repeatedly with the chunk stride size. + static uint64_t CombineLargeContiguousImpl32(uint64_t state, + const unsigned char* first, + size_t len); + static uint64_t CombineLargeContiguousImpl64(uint64_t state, + const unsigned char* first, + size_t len); + + // Reads 9 to 16 bytes from p. + // The first 8 bytes are in .first, the rest (zero padded) bytes are in + // .second. + static std::pair Read9To16(const unsigned char* p, + size_t len) { + uint64_t high = little_endian::Load64(p + len - 8); + return {little_endian::Load64(p), high >> (128 - len * 8)}; + } + + // Reads 4 to 8 bytes from p. Zero pads to fill uint64_t. + static uint64_t Read4To8(const unsigned char* p, size_t len) { + return (static_cast(little_endian::Load32(p + len - 4)) + << (len - 4) * 8) | + little_endian::Load32(p); + } + + // Reads 1 to 3 bytes from p. Zero pads to fill uint32_t. + static uint32_t Read1To3(const unsigned char* p, size_t len) { + return static_cast((p[0]) | // + (p[len / 2] << (len / 2 * 8)) | // + (p[len - 1] << ((len - 1) * 8))); + } + + ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Mix(uint64_t state, uint64_t v) { + using MultType = + absl::conditional_t; + // We do the addition in 64-bit space to make sure the 128-bit + // multiplication is fast. If we were to do it as MultType the compiler has + // to assume that the high word is non-zero and needs to perform 2 + // multiplications instead of one. + MultType m = state + v; + m *= kMul; + return static_cast(m ^ (m >> (sizeof(m) * 8 / 2))); + } + + // Seed() + // + // A non-deterministic seed. + // + // The current purpose of this seed is to generate non-deterministic results + // and prevent having users depend on the particular hash values. + // It is not meant as a security feature right now, but it leaves the door + // open to upgrade it to a true per-process random seed. A true random seed + // costs more and we don't need to pay for that right now. + // + // On platforms with ASLR, we take advantage of it to make a per-process + // random value. + // See https://en.wikipedia.org/wiki/Address_space_layout_randomization + // + // On other platforms this is still going to be non-deterministic but most + // probably per-build and not per-process. + ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Seed() { + return static_cast(reinterpret_cast(kSeed)); + } + static const void* const kSeed; + + uint64_t state_; +}; + +// CityHashState::CombineContiguousImpl() +inline uint64_t CityHashState::CombineContiguousImpl( + uint64_t state, const unsigned char* first, size_t len, + std::integral_constant /* sizeof_size_t */) { + // For large values we use CityHash, for small ones we just use a + // multiplicative hash. + uint64_t v; + if (len > 8) { + if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) { + return CombineLargeContiguousImpl32(state, first, len); + } + v = absl::hash_internal::CityHash32(reinterpret_cast(first), len); + } else if (len >= 4) { + v = Read4To8(first, len); + } else if (len > 0) { + v = Read1To3(first, len); + } else { + // Empty ranges have no effect. + return state; + } + return Mix(state, v); +} + +// Overload of CityHashState::CombineContiguousImpl() +inline uint64_t CityHashState::CombineContiguousImpl( + uint64_t state, const unsigned char* first, size_t len, + std::integral_constant /* sizeof_size_t */) { + // For large values we use CityHash, for small ones we just use a + // multiplicative hash. + uint64_t v; + if (len > 16) { + if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) { + return CombineLargeContiguousImpl64(state, first, len); + } + v = absl::hash_internal::CityHash64(reinterpret_cast(first), len); + } else if (len > 8) { + auto p = Read9To16(first, len); + state = Mix(state, p.first); + v = p.second; + } else if (len >= 4) { + v = Read4To8(first, len); + } else if (len > 0) { + v = Read1To3(first, len); + } else { + // Empty ranges have no effect. + return state; + } + return Mix(state, v); +} + +struct AggregateBarrier {}; + +// HashImpl + +// Add a private base class to make sure this type is not an aggregate. +// Aggregates can be aggregate initialized even if the default constructor is +// deleted. +struct PoisonedHash : private AggregateBarrier { + PoisonedHash() = delete; + PoisonedHash(const PoisonedHash&) = delete; + PoisonedHash& operator=(const PoisonedHash&) = delete; +}; + +template +struct HashImpl { + size_t operator()(const T& value) const { return CityHashState::hash(value); } +}; + +template +struct Hash + : absl::conditional_t::value, HashImpl, PoisonedHash> {}; + +template +template +H HashStateBase::combine(H state, const T& value, const Ts&... values) { + return H::combine(hash_internal::HashSelect::template Apply::Invoke( + std::move(state), value), + values...); +} + +// HashStateBase::combine_contiguous() +template +template +H HashStateBase::combine_contiguous(H state, const T* data, size_t size) { + return hash_internal::hash_range_or_bytes(std::move(state), data, size); +} + +// HashStateBase::PiecewiseCombiner::add_buffer() +template +H PiecewiseCombiner::add_buffer(H state, const unsigned char* data, + size_t size) { + if (position_ + size < PiecewiseChunkSize()) { + // This partial chunk does not fill our existing buffer + memcpy(buf_ + position_, data, size); + position_ += size; + return state; + } + + // Complete the buffer and hash it + const size_t bytes_needed = PiecewiseChunkSize() - position_; + memcpy(buf_ + position_, data, bytes_needed); + state = H::combine_contiguous(std::move(state), buf_, PiecewiseChunkSize()); + data += bytes_needed; + size -= bytes_needed; + + // Hash whatever chunks we can without copying + while (size >= PiecewiseChunkSize()) { + state = H::combine_contiguous(std::move(state), data, PiecewiseChunkSize()); + data += PiecewiseChunkSize(); + size -= PiecewiseChunkSize(); + } + // Fill the buffer with the remainder + memcpy(buf_, data, size); + position_ = size; + return state; +} + +// HashStateBase::PiecewiseCombiner::finalize() +template +H PiecewiseCombiner::finalize(H state) { + // Hash the remainder left in the buffer, which may be empty + return H::combine_contiguous(std::move(state), buf_, position_); +} + +} // namespace hash_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HASH_INTERNAL_HASH_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/hash/internal/spy_hash_state.h b/libs/or-tools-src-ubuntu/include/absl/hash/internal/spy_hash_state.h new file mode 100644 index 0000000000000000000000000000000000000000..c0831208118026d7aac4e3a8610beb364dc3aab7 --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/hash/internal/spy_hash_state.h @@ -0,0 +1,231 @@ +// Copyright 2018 The Abseil Authors. +// +// 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 +// +// https://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 ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_ +#define ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_ + +#include +#include +#include + +#include "absl/hash/hash.h" +#include "absl/strings/match.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace hash_internal { + +// SpyHashState is an implementation of the HashState API that simply +// accumulates all input bytes in an internal buffer. This makes it useful +// for testing AbslHashValue overloads (so long as they are templated on the +// HashState parameter), since it can report the exact hash representation +// that the AbslHashValue overload produces. +// +// Sample usage: +// EXPECT_EQ(SpyHashState::combine(SpyHashState(), foo), +// SpyHashState::combine(SpyHashState(), bar)); +template +class SpyHashStateImpl : public HashStateBase> { + public: + SpyHashStateImpl() : error_(std::make_shared>()) { + static_assert(std::is_void::value, ""); + } + + // Move-only + SpyHashStateImpl(const SpyHashStateImpl&) = delete; + SpyHashStateImpl& operator=(const SpyHashStateImpl&) = delete; + + SpyHashStateImpl(SpyHashStateImpl&& other) noexcept { + *this = std::move(other); + } + + SpyHashStateImpl& operator=(SpyHashStateImpl&& other) noexcept { + hash_representation_ = std::move(other.hash_representation_); + error_ = other.error_; + moved_from_ = other.moved_from_; + other.moved_from_ = true; + return *this; + } + + template + SpyHashStateImpl(SpyHashStateImpl&& other) { // NOLINT + hash_representation_ = std::move(other.hash_representation_); + error_ = other.error_; + moved_from_ = other.moved_from_; + other.moved_from_ = true; + } + + template + static SpyHashStateImpl combine(SpyHashStateImpl s, const A& a, + const Args&... args) { + // Pass an instance of SpyHashStateImpl when trying to combine `A`. This + // allows us to test that the user only uses this instance for combine calls + // and does not call AbslHashValue directly. + // See AbslHashValue implementation at the bottom. + s = SpyHashStateImpl::HashStateBase::combine(std::move(s), a); + return SpyHashStateImpl::combine(std::move(s), args...); + } + static SpyHashStateImpl combine(SpyHashStateImpl s) { + if (direct_absl_hash_value_error_) { + *s.error_ = "AbslHashValue should not be invoked directly."; + } else if (s.moved_from_) { + *s.error_ = "Used moved-from instance of the hash state object."; + } + return s; + } + + static void SetDirectAbslHashValueError() { + direct_absl_hash_value_error_ = true; + } + + // Two SpyHashStateImpl objects are equal if they hold equal hash + // representations. + friend bool operator==(const SpyHashStateImpl& lhs, + const SpyHashStateImpl& rhs) { + return lhs.hash_representation_ == rhs.hash_representation_; + } + + friend bool operator!=(const SpyHashStateImpl& lhs, + const SpyHashStateImpl& rhs) { + return !(lhs == rhs); + } + + enum class CompareResult { + kEqual, + kASuffixB, + kBSuffixA, + kUnequal, + }; + + static CompareResult Compare(const SpyHashStateImpl& a, + const SpyHashStateImpl& b) { + const std::string a_flat = absl::StrJoin(a.hash_representation_, ""); + const std::string b_flat = absl::StrJoin(b.hash_representation_, ""); + if (a_flat == b_flat) return CompareResult::kEqual; + if (absl::EndsWith(a_flat, b_flat)) return CompareResult::kBSuffixA; + if (absl::EndsWith(b_flat, a_flat)) return CompareResult::kASuffixB; + return CompareResult::kUnequal; + } + + // operator<< prints the hash representation as a hex and ASCII dump, to + // facilitate debugging. + friend std::ostream& operator<<(std::ostream& out, + const SpyHashStateImpl& hash_state) { + out << "[\n"; + for (auto& s : hash_state.hash_representation_) { + size_t offset = 0; + for (char c : s) { + if (offset % 16 == 0) { + out << absl::StreamFormat("\n0x%04x: ", offset); + } + if (offset % 2 == 0) { + out << " "; + } + out << absl::StreamFormat("%02x", c); + ++offset; + } + out << "\n"; + } + return out << "]"; + } + + // The base case of the combine recursion, which writes raw bytes into the + // internal buffer. + static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state, + const unsigned char* begin, + size_t size) { + const size_t large_chunk_stride = PiecewiseChunkSize(); + if (size > large_chunk_stride) { + // Combining a large contiguous buffer must have the same effect as + // doing it piecewise by the stride length, followed by the (possibly + // empty) remainder. + while (size >= large_chunk_stride) { + hash_state = SpyHashStateImpl::combine_contiguous( + std::move(hash_state), begin, large_chunk_stride); + begin += large_chunk_stride; + size -= large_chunk_stride; + } + } + + hash_state.hash_representation_.emplace_back( + reinterpret_cast(begin), size); + return hash_state; + } + + using SpyHashStateImpl::HashStateBase::combine_contiguous; + + absl::optional error() const { + if (moved_from_) { + return "Returned a moved-from instance of the hash state object."; + } + return *error_; + } + + private: + template + friend class SpyHashStateImpl; + + // This is true if SpyHashStateImpl has been passed to a call of + // AbslHashValue with the wrong type. This detects that the user called + // AbslHashValue directly (because the hash state type does not match). + static bool direct_absl_hash_value_error_; + + std::vector hash_representation_; + // This is a shared_ptr because we want all instances of the particular + // SpyHashState run to share the field. This way we can set the error for + // use-after-move and all the copies will see it. + std::shared_ptr> error_; + bool moved_from_ = false; +}; + +template +bool SpyHashStateImpl::direct_absl_hash_value_error_; + +template +struct OdrUse { + constexpr OdrUse() {} + bool& b = B; +}; + +template +struct RunOnStartup { + static bool run; + static constexpr OdrUse kOdrUse{}; +}; + +template +bool RunOnStartup::run = (f(), true); + +template < + typename T, typename U, + // Only trigger for when (T != U), + typename = absl::enable_if_t::value>, + // This statement works in two ways: + // - First, it instantiates RunOnStartup and forces the initialization of + // `run`, which set the global variable. + // - Second, it triggers a SFINAE error disabling the overload to prevent + // compile time errors. If we didn't disable the overload we would get + // ambiguous overload errors, which we don't want. + int = RunOnStartup::SetDirectAbslHashValueError>::run> +void AbslHashValue(SpyHashStateImpl, const U&); + +using SpyHashState = SpyHashStateImpl; + +} // namespace hash_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_ diff --git a/libs/or-tools-src-ubuntu/include/absl/memory/memory.h b/libs/or-tools-src-ubuntu/include/absl/memory/memory.h new file mode 100644 index 0000000000000000000000000000000000000000..513f7103a00c1470cca56c1e57f35a0c7988cc4d --- /dev/null +++ b/libs/or-tools-src-ubuntu/include/absl/memory/memory.h @@ -0,0 +1,695 @@ +// Copyright 2017 The Abseil Authors. +// +// 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 +// +// https://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. +// +// ----------------------------------------------------------------------------- +// File: memory.h +// ----------------------------------------------------------------------------- +// +// This header file contains utility functions for managing the creation and +// conversion of smart pointers. This file is an extension to the C++ +// standard library header file. + +#ifndef ABSL_MEMORY_MEMORY_H_ +#define ABSL_MEMORY_MEMORY_H_ + +#include +#include +#include +#include +#include +#include + +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// ----------------------------------------------------------------------------- +// Function Template: WrapUnique() +// ----------------------------------------------------------------------------- +// +// Adopts ownership from a raw pointer and transfers it to the returned +// `std::unique_ptr`, whose type is deduced. Because of this deduction, *do not* +// specify the template type `T` when calling `WrapUnique`. +// +// Example: +// X* NewX(int, int); +// auto x = WrapUnique(NewX(1, 2)); // 'x' is std::unique_ptr. +// +// Do not call WrapUnique with an explicit type, as in +// `WrapUnique(NewX(1, 2))`. The purpose of WrapUnique is to automatically +// deduce the pointer type. If you wish to make the type explicit, just use +// `std::unique_ptr` directly. +// +// auto x = std::unique_ptr(NewX(1, 2)); +// - or - +// std::unique_ptr x(NewX(1, 2)); +// +// While `absl::WrapUnique` is useful for capturing the output of a raw +// pointer factory, prefer 'absl::make_unique(args...)' over +// 'absl::WrapUnique(new T(args...))'. +// +// auto x = WrapUnique(new X(1, 2)); // works, but nonideal. +// auto x = make_unique(1, 2); // safer, standard, avoids raw 'new'. +// +// Note that `absl::WrapUnique(p)` is valid only if `delete p` is a valid +// expression. In particular, `absl::WrapUnique()` cannot wrap pointers to +// arrays, functions or void, and it must not be used to capture pointers +// obtained from array-new expressions (even though that would compile!). +template +std::unique_ptr WrapUnique(T* ptr) { + static_assert(!std::is_array::value, "array types are unsupported"); + static_assert(std::is_object::value, "non-object types are unsupported"); + return std::unique_ptr(ptr); +} + +namespace memory_internal { + +// Traits to select proper overload and return type for `absl::make_unique<>`. +template +struct MakeUniqueResult { + using scalar = std::unique_ptr; +}; +template +struct MakeUniqueResult { + using array = std::unique_ptr; +}; +template +struct MakeUniqueResult { + using invalid = void; +}; + +} // namespace memory_internal + +// gcc 4.8 has __cplusplus at 201301 but the libstdc++ shipped with it doesn't +// define make_unique. Other supported compilers either just define __cplusplus +// as 201103 but have make_unique (msvc), or have make_unique whenever +// __cplusplus > 201103 (clang). +#if (__cplusplus > 201103L || defined(_MSC_VER)) && \ + !(defined(__GLIBCXX__) && !defined(__cpp_lib_make_unique)) +using std::make_unique; +#else +// ----------------------------------------------------------------------------- +// Function Template: make_unique() +// ----------------------------------------------------------------------------- +// +// Creates a `std::unique_ptr<>`, while avoiding issues creating temporaries +// during the construction process. `absl::make_unique<>` also avoids redundant +// type declarations, by avoiding the need to explicitly use the `new` operator. +// +// This implementation of `absl::make_unique<>` is designed for C++11 code and +// will be replaced in C++14 by the equivalent `std::make_unique<>` abstraction. +// `absl::make_unique<>` is designed to be 100% compatible with +// `std::make_unique<>` so that the eventual migration will involve a simple +// rename operation. +// +// For more background on why `std::unique_ptr(new T(a,b))` is problematic, +// see Herb Sutter's explanation on +// (Exception-Safe Function Calls)[https://herbsutter.com/gotw/_102/]. +// (In general, reviewers should treat `new T(a,b)` with scrutiny.) +// +// Example usage: +// +// auto p = make_unique(args...); // 'p' is a std::unique_ptr +// auto pa = make_unique(5); // 'pa' is a std::unique_ptr +// +// Three overloads of `absl::make_unique` are required: +// +// - For non-array T: +// +// Allocates a T with `new T(std::forward args...)`, +// forwarding all `args` to T's constructor. +// Returns a `std::unique_ptr` owning that object. +// +// - For an array of unknown bounds T[]: +// +// `absl::make_unique<>` will allocate an array T of type U[] with +// `new U[n]()` and return a `std::unique_ptr` owning that array. +// +// Note that 'U[n]()' is different from 'U[n]', and elements will be +// value-initialized. Note as well that `std::unique_ptr` will perform its +// own destruction of the array elements upon leaving scope, even though +// the array [] does not have a default destructor. +// +// NOTE: an array of unknown bounds T[] may still be (and often will be) +// initialized to have a size, and will still use this overload. E.g: +// +// auto my_array = absl::make_unique(10); +// +// - For an array of known bounds T[N]: +// +// `absl::make_unique<>` is deleted (like with `std::make_unique<>`) as +// this overload is not useful. +// +// NOTE: an array of known bounds T[N] is not considered a useful +// construction, and may cause undefined behavior in templates. E.g: +// +// auto my_array = absl::make_unique(); +// +// In those cases, of course, you can still use the overload above and +// simply initialize it to its desired size: +// +// auto my_array = absl::make_unique(10); + +// `absl::make_unique` overload for non-array types. +template +typename memory_internal::MakeUniqueResult::scalar make_unique( + Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +// `absl::make_unique` overload for an array T[] of unknown bounds. +// The array allocation needs to use the `new T[size]` form and cannot take +// element constructor arguments. The `std::unique_ptr` will manage destructing +// these array elements. +template +typename memory_internal::MakeUniqueResult::array make_unique(size_t n) { + return std::unique_ptr(new typename absl::remove_extent_t[n]()); +} + +// `absl::make_unique` overload for an array T[N] of known bounds. +// This construction will be rejected. +template +typename memory_internal::MakeUniqueResult::invalid make_unique( + Args&&... /* args */) = delete; +#endif + +// ----------------------------------------------------------------------------- +// Function Template: RawPtr() +// ----------------------------------------------------------------------------- +// +// Extracts the raw pointer from a pointer-like value `ptr`. `absl::RawPtr` is +// useful within templates that need to handle a complement of raw pointers, +// `std::nullptr_t`, and smart pointers. +template +auto RawPtr(T&& ptr) -> decltype(std::addressof(*ptr)) { + // ptr is a forwarding reference to support Ts with non-const operators. + return (ptr != nullptr) ? std::addressof(*ptr) : nullptr; +} +inline std::nullptr_t RawPtr(std::nullptr_t) { return nullptr; } + +// ----------------------------------------------------------------------------- +// Function Template: ShareUniquePtr() +// ----------------------------------------------------------------------------- +// +// Adopts a `std::unique_ptr` rvalue and returns a `std::shared_ptr` of deduced +// type. Ownership (if any) of the held value is transferred to the returned +// shared pointer. +// +// Example: +// +// auto up = absl::make_unique(10); +// auto sp = absl::ShareUniquePtr(std::move(up)); // shared_ptr +// CHECK_EQ(*sp, 10); +// CHECK(up == nullptr); +// +// Note that this conversion is correct even when T is an array type, and more +// generally it works for *any* deleter of the `unique_ptr` (single-object +// deleter, array deleter, or any custom deleter), since the deleter is adopted +// by the shared pointer as well. The deleter is copied (unless it is a +// reference). +// +// Implements the resolution of [LWG 2415](http://wg21.link/lwg2415), by which a +// null shared pointer does not attempt to call the deleter. +template +std::shared_ptr ShareUniquePtr(std::unique_ptr&& ptr) { + return ptr ? std::shared_ptr(std::move(ptr)) : std::shared_ptr(); +} + +// ----------------------------------------------------------------------------- +// Function Template: WeakenPtr() +// ----------------------------------------------------------------------------- +// +// Creates a weak pointer associated with a given shared pointer. The returned +// value is a `std::weak_ptr` of deduced type. +// +// Example: +// +// auto sp = std::make_shared(10); +// auto wp = absl::WeakenPtr(sp); +// CHECK_EQ(sp.get(), wp.lock().get()); +// sp.reset(); +// CHECK(wp.lock() == nullptr); +// +template +std::weak_ptr WeakenPtr(const std::shared_ptr& ptr) { + return std::weak_ptr(ptr); +} + +namespace memory_internal { + +// ExtractOr::type evaluates to E if possible. Otherwise, D. +template