diff --git a/examples/cpp/BUILD.bazel b/examples/cpp/BUILD.bazel index a9478fd08e..8585a27ad4 100644 --- a/examples/cpp/BUILD.bazel +++ b/examples/cpp/BUILD.bazel @@ -434,7 +434,6 @@ cc_binary( ], ) - cc_binary( name = "qap_sat", srcs = ["qap_sat.cc"], diff --git a/examples/cpp/qap_sat.cc b/examples/cpp/qap_sat.cc index a70541d13c..5d470b77aa 100644 --- a/examples/cpp/qap_sat.cc +++ b/examples/cpp/qap_sat.cc @@ -11,8 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include +#include #include #include "absl/flags/flag.h" @@ -30,40 +30,31 @@ ABSL_FLAG(std::string, params, "", "Specific params to use with CP-SAT."); namespace operations_research { namespace sat { -namespace { -// place_vars[f][l] contains the binary variable that decides whether to put -// factory f in location l. -std::vector> CreatePlaceVars(int n, - CpModelBuilder& cp_model) { - const BoolVar false_literal = cp_model.FalseVar(); +void SolveQap() { + LOG(INFO) << "Reading QAP problem from '" << absl::GetFlag(FLAGS_input) + << "'."; + QapProblem qap = ReadQapProblemOrDie(absl::GetFlag(FLAGS_input)); + const int n = qap.weights.size(); + + CpModelBuilder cp_model; + + // Create placement variables. + // place_vars[f][l] contains the binary variable that decides whether to put + // factory f in location l. std::vector> place_vars; place_vars.resize(n); for (int f = 0; f < n; ++f) { - place_vars[f].assign(n, false_literal); - } - - for (int f = 0; f < n; ++f) { + place_vars[f].resize(n); for (int l = 0; l < n; ++l) { place_vars[f][l] = cp_model.NewBoolVar().WithName(absl::StrCat("place_", f, "_", l)); } } - return place_vars; -} - -void CreatePlacementConstraints( - const std::vector>& place_vars, - CpModelBuilder& cp_model) { - const int n = place_vars.size(); // Place each factory exactly once. for (int f = 0; f < n; ++f) { - std::vector tmp; - for (int l = 0; l < n; ++l) { - tmp.push_back(place_vars[f][l]); - } - cp_model.AddExactlyOne(tmp); + cp_model.AddExactlyOne(place_vars[f]); } // Occupy each location exactly once. @@ -74,13 +65,9 @@ void CreatePlacementConstraints( } cp_model.AddExactlyOne(tmp); } -} - -void CreateObjective(const QapProblem& qap, - const std::vector>& place_vars, - CpModelBuilder& cp_model) { - const int n = place_vars.size(); + // Create objective. + absl::flat_hash_map, BoolVar> cache; LinearExpr objective; for (int f1 = 0; f1 < n; ++f1) { for (int f2 = 0; f2 < n; ++f2) { @@ -91,9 +78,19 @@ void CreateObjective(const QapProblem& qap, if (l1 == l2) continue; if (qap.distances[l1][l2] == 0) continue; - BoolVar product = cp_model.NewBoolVar(); - cp_model.AddMultiplicationEquality(product, place_vars[f1][l1], - place_vars[f2][l2]); + const std::tuple key = + f1 < f2 ? std::make_tuple(f1, f2, l1, l2) + : std::make_tuple(f2, f1, l2, l1); + BoolVar product; + const auto it = cache.find(key); + if (it == cache.end()) { + product = cp_model.NewBoolVar(); + cp_model.AddMultiplicationEquality(product, place_vars[f1][l1], + place_vars[f2][l2]); + cache[key] = product; + } else { + product = it->second; + } objective += product * qap.weights[f1][f2] * qap.distances[l1][l2]; } @@ -108,20 +105,6 @@ void CreateObjective(const QapProblem& qap, } cp_model.Minimize(objective); -} - -} // namespace - -void SolveQap() { - LOG(INFO) << "Reading QAP problem from '" << absl::GetFlag(FLAGS_input) - << "'."; - QapProblem qap = ReadQapProblemOrDie(absl::GetFlag(FLAGS_input)); - const int n = qap.weights.size(); - - CpModelBuilder cp_model; - std::vector> place_vars = CreatePlaceVars(n, cp_model); - CreatePlacementConstraints(place_vars, cp_model); - CreateObjective(qap, place_vars, cp_model); // Setup parameters. SatParameters parameters; diff --git a/ortools/util/qap_reader.cc b/ortools/util/qap_reader.cc index 3a6bf2d03f..cc1e4eabc7 100644 --- a/ortools/util/qap_reader.cc +++ b/ortools/util/qap_reader.cc @@ -30,7 +30,8 @@ QapProblem ReadQapProblemOrDie(const std::string& filepath) { int n = 0; int k = 0; - for (const std::string& line : FileLines(filepath)) { + for (const std::string& line : + FileLines(filepath, FileLineIterator::REMOVE_LINEFEED)) { const std::vector tokens = absl::StrSplit(line, ' ', absl::SkipEmpty()); if (tokens.empty()) continue; @@ -49,7 +50,7 @@ QapProblem ReadQapProblemOrDie(const std::string& filepath) { const int i = (k - 1) / n; const int j = (k - 1) % n; int64_t v = 0.0; - CHECK(absl::SimpleAtoi(token, &v)); + CHECK(absl::SimpleAtoi(token, &v)) << "'" << token << "'"; qap_problem.weights[i][j] = v; ++k; }