diff --git a/examples/cpp/model_util.cc b/examples/cpp/model_util.cc index cdc7279f2d..6d324cf366 100644 --- a/examples/cpp/model_util.cc +++ b/examples/cpp/model_util.cc @@ -366,13 +366,13 @@ int Run() { &sequence_variables, &interval_variables); LOG(INFO) << "Primary integer variables = " - << DebugStringVector(primary_integer_variables, ", "); + << JoinDebugStringPtr(primary_integer_variables, ", "); LOG(INFO) << "Secondary integer variables = " - << DebugStringVector(secondary_integer_variables, ", "); + << JoinDebugStringPtr(secondary_integer_variables, ", "); LOG(INFO) << "Sequence variables = " - << DebugStringVector(sequence_variables, ", "); + << JoinDebugStringPtr(sequence_variables, ", "); LOG(INFO) << "Interval variables = " - << DebugStringVector(interval_variables, ", "); + << JoinDebugStringPtr(interval_variables, ", "); } } diff --git a/examples/cpp/tsp.cc b/examples/cpp/tsp.cc index 3eefd489d1..91730c9e33 100644 --- a/examples/cpp/tsp.cc +++ b/examples/cpp/tsp.cc @@ -39,6 +39,7 @@ using operations_research::ACMRandom; using operations_research::StrCat; using operations_research::scoped_ptr; + 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, diff --git a/examples/python/golomb8.py b/examples/python/golomb8.py index f14f06c98a..881bd5c39c 100644 --- a/examples/python/golomb8.py +++ b/examples/python/golomb8.py @@ -32,7 +32,7 @@ FLAGS = gflags.FLAGS # We disable the following warning because it is a false positive on constraints # like: solver.Add(x == 0) -# pylint: disable-msg=C6403 +# pylint: disable=g-explicit-bool-comparison def main(unused_argv): diff --git a/examples/python/linear_assignment_api.py b/examples/python/linear_assignment_api.py index 8ef7e3afc6..349c51e55c 100644 --- a/examples/python/linear_assignment_api.py +++ b/examples/python/linear_assignment_api.py @@ -24,12 +24,8 @@ from google.apputils import app from graph import pywrapgraph -def RunAssignmentOn4x4Matrix(forward_graph): +def RunAssignmentOn4x4Matrix(): """Test linear sum assignment on a 4x4 matrix. - - Arguments: - forward_graph: if true uses a ForwardStarGraph or a StarGraph to model - the assignment problem. """ num_sources = 4 num_targets = 4 @@ -39,28 +35,28 @@ def RunAssignmentOn4x4Matrix(forward_graph): [45, 110, 95, 115]] expected_cost = cost[0][3] + cost[1][2] + cost[2][1] + cost[3][0] - if forward_graph: - graph = pywrapgraph.ForwardStarGraph(num_sources + num_targets, - num_sources * num_targets) - assignment = pywrapgraph.ForwardEbertLinearSumAssignment(graph, num_sources) - else: - graph = pywrapgraph.StarGraph(num_sources + num_targets, - num_sources * num_targets) - assignment = pywrapgraph.EbertLinearSumAssignment(graph, num_sources) - + assignment = pywrapgraph.LinearSumAssignment() for source in range (0, num_sources): for target in range(0, num_targets): - arc = graph.AddArc(source, num_sources + target) - assignment.SetArcCost(arc, cost[source][target]) + assignment.AddArcWithCost(source, target, cost[source][target]) - assignment.ComputeAssignment() - total_cost = assignment.GetCost() - print 'total cost', total_cost, '/', expected_cost + solve_status = assignment.Solve() + if solve_status == assignment.OPTIMAL: + print 'Successful solve.' + print 'Total cost', assignment.OptimalCost(), '/', expected_cost + for i in range(0, assignment.NumNodes()): + print 'Left node %d assigned to right node %d with cost %d.' % ( + i, + assignment.RightMate(i), + assignment.AssignmentCost(i)) + elif solve_status == assignment.INFEASIBLE: + print 'No perfect matching exists.' + elif solve_status == assignment.POSSIBLE_OVERFLOW: + print 'Some input costs are too large and may cause an integer overflow.' def main(unused_argv): - RunAssignmentOn4x4Matrix(True) - RunAssignmentOn4x4Matrix(False) + RunAssignmentOn4x4Matrix() if __name__ == '__main__': app.run() diff --git a/examples/python/sendmore.py b/examples/python/sendmore.py index 040609f576..4afef52c8b 100644 --- a/examples/python/sendmore.py +++ b/examples/python/sendmore.py @@ -47,7 +47,7 @@ def main(unused_argv): solver.Add(1000*s + 100*e + 10*n + d + 1000*m + 100*o + 10*r + e == 10000*m + 1000*o + 100*n + 10*e + y) - # pylint: disable-msg=C6403 + # pylint: disable=g-explicit-bool-comparison solver.Add(s != 0) solver.Add(m != 0) diff --git a/examples/python/tsp.py b/examples/python/tsp.py index 71ebcdb8e3..344f73546d 100644 --- a/examples/python/tsp.py +++ b/examples/python/tsp.py @@ -24,6 +24,8 @@ (forbidden arcs). """ + + import random from google.apputils import app @@ -33,11 +35,11 @@ from constraint_solver import pywraprouting FLAGS = gflags.FLAGS gflags.DEFINE_integer('tsp_size', 10, - 'Size of Traveling Salesman Problem instance.') + 'Size of Traveling Salesman Problem instance.') gflags.DEFINE_boolean('tsp_use_random_matrix', True, - 'Use random cost matrix.') + 'Use random cost matrix.') gflags.DEFINE_integer('tsp_random_forbidden_connections', 0, - 'Number of random forbidden connections.') + 'Number of random forbidden connections.') gflags.DEFINE_integer('tsp_random_seed', 0, 'Random seed.') diff --git a/makefiles/Makefile.third_party.win b/makefiles/Makefile.third_party.win index 7658f397b2..387f346b7f 100644 --- a/makefiles/Makefile.third_party.win +++ b/makefiles/Makefile.third_party.win @@ -9,8 +9,8 @@ ZLIB_ARCHIVE_TAG = 128 SWIG_TAG = 2.0.9 # Build extra dependencies (GLPK, SCIP) from archive only if the archive is present. -# The archive should be glpk-4.49.tar.gz -GLPK_TAG = 4.49 +# The archive should be glpk-4.52.tar.gz +GLPK_TAG = 4.52 # The archive should be scipoptsuite-3.0.1.tgz SCIP_TAG = 3.0.1 SOPLEX_TAG = 1.7.1 diff --git a/src/base/callback_types.h b/src/base/callback_types.h deleted file mode 100755 index 099052aab3..0000000000 --- a/src/base/callback_types.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2010-2013 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. - -#ifndef OR_TOOLS_BASE_CALLBACK_TYPES_H_ -#define OR_TOOLS_BASE_CALLBACK_TYPES_H_ - -// This file is defined for compatibility reasons. - -#endif // OR_TOOLS_BASE_CALLBACK_TYPES_H_ diff --git a/src/base/int_type.h b/src/base/int_type.h index 99b8295048..045daeff55 100755 --- a/src/base/int_type.h +++ b/src/base/int_type.h @@ -90,7 +90,7 @@ // as key to hashable containers such as hash_map and hash_set. // // We suggest using the IntTypeIndexedContainer wrapper around STL -// vector (see int-type-indexed-vector.h) if an IntType is intended +// vector (see int_type_indexed_vector.h) if an IntType is intended // to be used as an index into these containers. These wrappers are // indexed in a type-safe manner using IntTypes to ensure type-safety. // diff --git a/src/base/int_type_indexed_vector.h b/src/base/int_type_indexed_vector.h index fa7d50c789..baf0b0f87f 100755 --- a/src/base/int_type_indexed_vector.h +++ b/src/base/int_type_indexed_vector.h @@ -13,7 +13,7 @@ // This file provides the ITIVector container that wraps around the STL vector. // The wrapper restrict indexing to a pre-specified type-safe integer type or -// IntType (see base/int-type.h). It prevents accidental indexing +// IntType (see base/int_type.h). It prevents accidental indexing // by different "logical" integer-like types (e.g. another IntType) or native // integer types. The wrapper is useful as C++ and the standard template // library allows the user to mix "logical" integral indices that might have a @@ -26,7 +26,7 @@ // // where IntTypeName is the desired name for the "logical" integer-like type // and the ValueType is a supported native integer type such as int or -// uint64 (see base/int-type.h for details). +// uint64 (see base/int_type.h for details). // // The wrapper exposes all public methods of STL vector and behaves mostly as // pass-through. The only method modified to ensure type-safety is the operator diff --git a/src/base/random.h b/src/base/random.h index 92dca0c32b..c8b13030b4 100644 --- a/src/base/random.h +++ b/src/base/random.h @@ -92,6 +92,8 @@ class MTRandom : public ACMRandom { } }; +typedef ACMRandom RandomBase; + } // namespace operations_research #endif // OR_TOOLS_BASE_RANDOM_H_ diff --git a/src/base/scoped_ptr.h b/src/base/scoped_ptr.h index 89e2d7cb8c..7ed2d135a1 100644 --- a/src/base/scoped_ptr.h +++ b/src/base/scoped_ptr.h @@ -14,9 +14,9 @@ #ifndef OR_TOOLS_BASE_SCOPED_PTR_H_ #define OR_TOOLS_BASE_SCOPED_PTR_H_ -// This is an implementation designed to match the anticipated future -// TR2 implementation of the scoped_ptr class, and its closely-related -// brethren, scoped_ptr_malloc, and make_scoped_ptr. +// This is an implementation designed to match the anticipated future TR2 +// implementation of the scoped_ptr class, and its closely-related brethren, +// scoped_ptr_malloc, and make_scoped_ptr. #include #include @@ -31,9 +31,7 @@ namespace operations_research { template class scoped_ptr; template class scoped_ptr_malloc; - -template -scoped_ptr make_scoped_ptr(C * param); +template scoped_ptr make_scoped_ptr(C * param); // A scoped_ptr is like a T*, except that the destructor of scoped_ptr // automatically deletes the pointer it holds (if any). diff --git a/src/base/unique_ptr.h b/src/base/unique_ptr.h index 3168bc4b90..bd354323b5 100644 --- a/src/base/unique_ptr.h +++ b/src/base/unique_ptr.h @@ -194,5 +194,4 @@ class unique_ptr { } // namespace std #endif - -#endif // OR_TOOLS_BASE_UNIQUE_PTR_H_ +#endif // OR_TOOLS_BASE_UNIQUE_PTR_H_ diff --git a/src/constraint_solver/alldiff_cst.cc b/src/constraint_solver/alldiff_cst.cc index a81aeae05d..1227be3c9f 100644 --- a/src/constraint_solver/alldiff_cst.cc +++ b/src/constraint_solver/alldiff_cst.cc @@ -35,7 +35,7 @@ class BaseAllDifferent : public Constraint { ~BaseAllDifferent() {} string DebugStringInternal(const string& name) const { return StringPrintf("%s(%s)", name.c_str(), - DebugStringVector(vars_, ", ").c_str()); + JoinDebugStringPtr(vars_, ", ").c_str()); } protected: @@ -493,8 +493,9 @@ class SortConstraint : public Constraint { } virtual string DebugString() const { - return StringPrintf("Sort(%s, %s)", DebugStringVector(ovars_, ", ").c_str(), - DebugStringVector(svars_, ", ").c_str()); + return StringPrintf("Sort(%s, %s)", + JoinDebugStringPtr(ovars_, ", ").c_str(), + JoinDebugStringPtr(svars_, ", ").c_str()); } private: @@ -570,7 +571,8 @@ class AllDifferentExcept : public Constraint { virtual string DebugString() const { return StringPrintf("AllDifferentExcept([%s], %" GG_LL_FORMAT "d", - DebugStringVector(vars_, ", ").c_str(), escape_value_); + JoinDebugStringPtr(vars_, ", ").c_str(), + escape_value_); } virtual void Accept(ModelVisitor* const visitor) const { @@ -662,8 +664,8 @@ class NullIntersectArrayExcept : public Constraint { virtual string DebugString() const { return StringPrintf( "NullIntersectArray([%s], [%s], escape = %" GG_LL_FORMAT "d", - DebugStringVector(first_vars_, ", ").c_str(), - DebugStringVector(second_vars_, ", ").c_str(), escape_value_); + JoinDebugStringPtr(first_vars_, ", ").c_str(), + JoinDebugStringPtr(second_vars_, ", ").c_str(), escape_value_); } virtual void Accept(ModelVisitor* const visitor) const { diff --git a/src/constraint_solver/constraint_solver.h b/src/constraint_solver/constraint_solver.h index 0c34049543..6bba55cf6d 100644 --- a/src/constraint_solver/constraint_solver.h +++ b/src/constraint_solver/constraint_solver.h @@ -62,7 +62,7 @@ #include "base/hash.h" #include "base/hash.h" #include -#include +#include "base/unique_ptr.h" #include #include #include @@ -80,7 +80,6 @@ #include "base/map_util.h" #include "base/hash.h" #include "base/random.h" -#include "base/unique_ptr.h" #include "util/tuple_set.h" class Closure; @@ -4965,9 +4964,9 @@ class DisjunctiveConstraint : public Constraint { virtual SequenceVar* MakeSequenceVar() = 0; // Add a transition time between intervals. It forces the distance between - // interval a and interval b that follows it to be at lest - // transit_evaluator->Run(a, b). This evaluator must always returns a positive - // or null value. + // the end of interval a and start of interval b that follows it to be at + // least transit_evaluator->Run(a, b). This evaluator must always returns + // a positive or null value. // This method takes ownership of the evaluator. virtual void SetTransitionTime(Solver::IndexEvaluator2* transit_evaluator) { transition_time_.reset(transit_evaluator); diff --git a/src/constraint_solver/constraint_solveri.h b/src/constraint_solver/constraint_solveri.h index 4da81b8f2b..fea87b6c37 100644 --- a/src/constraint_solver/constraint_solveri.h +++ b/src/constraint_solver/constraint_solveri.h @@ -57,7 +57,7 @@ #include #include -#include "base/callback_types.h" +#include "base/callback.h" #include "base/commandlineflags.h" #include "base/integral_types.h" #include "base/logging.h" diff --git a/src/constraint_solver/constraints.cc b/src/constraint_solver/constraints.cc index 1207ce539f..27485e7e09 100644 --- a/src/constraint_solver/constraints.cc +++ b/src/constraint_solver/constraints.cc @@ -13,7 +13,7 @@ #include #include -#include +#include "base/unique_ptr.h" #include #include @@ -230,7 +230,7 @@ class MapDomain : public Constraint { } virtual string DebugString() const { return StringPrintf("MapDomain(%s, [%s])", var_->DebugString().c_str(), - DebugStringVector(actives_, ", ").c_str()); + JoinDebugStringPtr(actives_, ", ").c_str()); } void Accept(ModelVisitor* const visitor) const { @@ -482,7 +482,8 @@ void NoCycle::ComputeSupports() { } string NoCycle::DebugString() const { - return StringPrintf("NoCycle(%s)", DebugStringVector(nexts_, ", ").c_str()); + return StringPrintf("NoCycle(%s)", + JoinDebugStringPtr(nexts_, ", ").c_str()); } // ----- Circuit constraint ----- @@ -540,7 +541,7 @@ class Circuit : public Constraint { } virtual string DebugString() const { - return StringPrintf("Circuit(%s)", DebugStringVector(nexts_, " ").c_str()); + return StringPrintf("Circuit(%s)", JoinDebugStringPtr(nexts_, " ").c_str()); } void Accept(ModelVisitor* const visitor) const { @@ -709,7 +710,7 @@ class SubCircuit : public Constraint { virtual string DebugString() const { return StringPrintf( - "SubCircuit(%s)", DebugStringVector(nexts_, " ").c_str()); + "SubCircuit(%s)", JoinDebugStringPtr(nexts_, " ").c_str()); } void Accept(ModelVisitor* const visitor) const { @@ -1250,12 +1251,14 @@ void ResultCallback2SlackPathCumul::NextBound(int index) { IntVar* const cumul_next = cumuls_[next]; IntVar* const slack = slacks_[index]; const int64 transit = transits_evaluator_->Run(index, next); - cumul_next->SetMin(cumul->Min() + transit + slack->Min()); + const int64 cumul_next_minus_transit_min = CapSub(cumul_next->Min(), transit); + const int64 cumul_next_minus_transit_max = CapSub(cumul_next->Max(), transit); + cumul_next->SetMin(CapAdd(CapAdd(cumul->Min(), transit), slack->Min())); cumul_next->SetMax(CapAdd(CapAdd(cumul->Max(), transit), slack->Max())); - cumul->SetMin(CapSub(cumul_next->Min(), CapAdd(transit, slack->Max()))); - cumul->SetMax(CapSub(cumul_next->Max(), CapAdd(transit, slack->Min()))); - slack->SetMin(CapSub(cumul_next->Min(), CapAdd(transit, cumul->Max()))); - slack->SetMax(CapSub(cumul_next->Max(), CapAdd(transit, cumul->Min()))); + cumul->SetMin(CapSub(cumul_next_minus_transit_min, slack->Max())); + cumul->SetMax(CapSub(cumul_next_minus_transit_max, slack->Min())); + slack->SetMin(CapSub(cumul_next_minus_transit_min, cumul->Max())); + slack->SetMax(CapSub(cumul_next_minus_transit_max, cumul->Min())); if (prevs_[next] < 0) { prevs_.SetValue(solver(), next, index); } @@ -1266,8 +1269,9 @@ bool ResultCallback2SlackPathCumul::AcceptLink(int i, int j) const { const IntVar* const cumul_j = cumuls_[j]; const IntVar* const slack = slacks_[i]; const int64 transit = transits_evaluator_->Run(i, j); - return transit + slack->Min() <= CapSub(cumul_j->Max(), cumul_i->Min()) && - CapSub(cumul_j->Min(), cumul_i->Max()) <= slack->Max() + transit; + return + CapAdd(transit, slack->Min()) <= CapSub(cumul_j->Max(), cumul_i->Min()) && + CapSub(cumul_j->Min(), cumul_i->Max()) <= CapAdd(slack->Max(), transit); } } // namespace diff --git a/src/constraint_solver/count_cst.cc b/src/constraint_solver/count_cst.cc index 1d84b665e2..421b925fc2 100644 --- a/src/constraint_solver/count_cst.cc +++ b/src/constraint_solver/count_cst.cc @@ -87,9 +87,9 @@ class Distribute : public Constraint { void CardMax(int cindex); virtual string DebugString() const { return StringPrintf("Distribute(vars = [%s], values = [%s], cards = [%s])", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), IntVectorToString(values_, ", ").c_str(), - DebugStringVector(cards_, ", ").c_str()); + JoinDebugStringPtr(cards_, ", ").c_str()); } virtual void Accept(ModelVisitor* const visitor) const { @@ -306,8 +306,8 @@ FastDistribute::FastDistribute(Solver* const s, const std::vector& vars string FastDistribute::DebugString() const { return StringPrintf("FastDistribute(vars = [%s], cards = [%s])", - DebugStringVector(vars_, ", ").c_str(), - DebugStringVector(cards_, ", ").c_str()); + JoinDebugStringPtr(vars_, ", ").c_str(), + JoinDebugStringPtr(cards_, ", ").c_str()); } void FastDistribute::Post() { @@ -507,7 +507,7 @@ BoundedDistribute::BoundedDistribute(Solver* const s, string BoundedDistribute::DebugString() const { return StringPrintf( "BoundedDistribute([%s], values = [%s], card_min = [%s], card_max = [%s]", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), IntVectorToString(values_, ", ").c_str(), IntVectorToString(card_min_, ", ").c_str(), IntVectorToString(card_max_, ", ").c_str()); @@ -706,7 +706,7 @@ BoundedFastDistribute::BoundedFastDistribute(Solver* const s, string BoundedFastDistribute::DebugString() const { return StringPrintf( "BoundedFastDistribute([%s], card_min = [%s], card_max = [%s]", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), IntVectorToString(card_min_, ", ").c_str(), IntVectorToString(card_max_, ", ").c_str()); } diff --git a/src/constraint_solver/default_search.cc b/src/constraint_solver/default_search.cc index e4b1261101..0df6536c37 100644 --- a/src/constraint_solver/default_search.cc +++ b/src/constraint_solver/default_search.cc @@ -1118,7 +1118,7 @@ class DefaultIntegerSearch : public DecisionBuilder { out.append(parameters_.decision_builder->DebugString()); out.append(", "); } - out.append(DebugStringVector(vars_, ", ")); + out.append(JoinDebugStringPtr(vars_, ", ")); out.append(")"); return out; } diff --git a/src/constraint_solver/deviation.cc b/src/constraint_solver/deviation.cc index 4688f2253a..40ec431c31 100644 --- a/src/constraint_solver/deviation.cc +++ b/src/constraint_solver/deviation.cc @@ -73,7 +73,7 @@ class Deviation : public Constraint { virtual string DebugString() const { return StringPrintf("Deviation([%s], deviation_var = %s, sum = %lld)", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), deviation_var_->DebugString().c_str(), total_sum_); } diff --git a/src/constraint_solver/diffn.cc b/src/constraint_solver/diffn.cc index c093822679..7e7909395d 100644 --- a/src/constraint_solver/diffn.cc +++ b/src/constraint_solver/diffn.cc @@ -110,10 +110,10 @@ class Diffn : public Constraint { virtual string DebugString() const { return StringPrintf("Diffn(x = [%s], y = [%s], dx = [%s], dy = [%s]))", - DebugStringVector(x_, ", ").c_str(), - DebugStringVector(y_, ", ").c_str(), - DebugStringVector(dx_, ", ").c_str(), - DebugStringVector(dy_, ", ").c_str()); + JoinDebugStringPtr(x_, ", ").c_str(), + JoinDebugStringPtr(y_, ", ").c_str(), + JoinDebugStringPtr(dx_, ", ").c_str(), + JoinDebugStringPtr(dy_, ", ").c_str()); } virtual void Accept(ModelVisitor* const visitor) const { diff --git a/src/constraint_solver/element.cc b/src/constraint_solver/element.cc index 932d290e1d..8b3cb20dc2 100644 --- a/src/constraint_solver/element.cc +++ b/src/constraint_solver/element.cc @@ -1103,7 +1103,7 @@ string IntExprArrayElementCt::DebugString() const { target_var_->DebugString().c_str()); } else { return StringPrintf("IntExprArrayElement([%s], %s) == %s", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), index_->DebugString().c_str(), target_var_->DebugString().c_str()); } @@ -1159,7 +1159,7 @@ class IntExprArrayElementCstCt : public Constraint { virtual string DebugString() const { return StringPrintf("IntExprArrayElement([%s], %s) == %" GG_LL_FORMAT "d", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), index_->DebugString().c_str(), target_); } @@ -1254,7 +1254,7 @@ class IntExprIndexOfCt : public Constraint { virtual string DebugString() const { return StringPrintf("IntExprIndexOf([%s], %s) == %" GG_LL_FORMAT "d", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), index_->DebugString().c_str(), target_); } @@ -1321,7 +1321,8 @@ IntExpr* Solver::MakeElement(const std::vector& vars, IntVar* const ind size > 10 ? StringPrintf("ElementVar(var array of size %d, %s)", size, index->DebugString().c_str()) - : StringPrintf("ElementVar([%s], %s)", NameVector(vars, ", ").c_str(), + : StringPrintf("ElementVar([%s], %s)", + JoinNamePtr(vars, ", ").c_str(), index->name().c_str()); IntVar* const element_var = MakeIntVar(emin, emax, vname); AddConstraint( @@ -1394,7 +1395,8 @@ IntExpr* Solver::MakeIndexExpression(const std::vector& vars, int64 val return cache->Var(); } else { const string name = StringPrintf("Index(%s, %" GG_LL_FORMAT "d)", - NameVector(vars, ", ").c_str(), value); + JoinNamePtr(vars, ", ").c_str(), + value); IntVar* const index = MakeIntVar(0, vars.size() - 1, name); AddConstraint(MakeIndexOfConstraint(vars, index, value)); model_cache_->InsertVarArrayConstantExpression( diff --git a/src/constraint_solver/expr_array.cc b/src/constraint_solver/expr_array.cc index d84c99e1e1..35b064ecf8 100644 --- a/src/constraint_solver/expr_array.cc +++ b/src/constraint_solver/expr_array.cc @@ -56,7 +56,7 @@ class TreeArrayConstraint : public CastConstraint { string DebugStringInternal(const string& name) const { return StringPrintf("%s(%s) == %s", name.c_str(), - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), target_var_->DebugString().c_str()); } @@ -901,7 +901,8 @@ class ArrayBoolAndEq : public CastConstraint { } string DebugString() const { - return StringPrintf("And(%s) == %s", DebugStringVector(vars_, ", ").c_str(), + return StringPrintf("And(%s) == %s", + JoinDebugStringPtr(vars_, ", ").c_str(), target_var_->DebugString().c_str()); } @@ -1030,7 +1031,8 @@ class ArrayBoolOrEq : public CastConstraint { } string DebugString() const { - return StringPrintf("Or(%s) == %s", DebugStringVector(vars_, ", ").c_str(), + return StringPrintf("Or(%s) == %s", + JoinDebugStringPtr(vars_, ", ").c_str(), target_var_->DebugString().c_str()); } @@ -1081,7 +1083,7 @@ class BaseSumBooleanConstraint : public Constraint { protected: string DebugStringInternal(const string& name) const { return StringPrintf("%s(%s)", name.c_str(), - DebugStringVector(vars_, ", ").c_str()); + JoinDebugStringPtr(vars_, ", ").c_str()); } const std::vector vars_; @@ -1597,7 +1599,7 @@ class BooleanScalProdLessConstant : public Constraint { virtual string DebugString() const { return StringPrintf("BooleanScalProd([%s], [%s]) <= %" GG_LL_FORMAT "d)", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), IntVectorToString(coefs_, ", ").c_str(), upper_bound_); } @@ -1714,7 +1716,7 @@ class PositiveBooleanScalProdEqVar : public CastConstraint { virtual string DebugString() const { return StringPrintf("PositiveBooleanScal([%s], [%s]) == %s", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), IntVectorToString(coefs_, ", ").c_str(), target_var_->DebugString().c_str()); } @@ -1827,7 +1829,7 @@ class PositiveBooleanScalProd : public BaseIntExpr { virtual string DebugString() const { return StringPrintf("PositiveBooleanScalProd([%s], [%s])", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), IntVectorToString(coefs_, ", ").c_str()); } @@ -1956,7 +1958,7 @@ class PositiveBooleanScalProdEqCst : public Constraint { virtual string DebugString() const { return StringPrintf("PositiveBooleanScalProd([%s], [%s]) == %" GG_LL_FORMAT "d", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), IntVectorToString(coefs_, ", ").c_str(), constant_); } @@ -2577,7 +2579,7 @@ IntExpr* MakeSumArrayAux(Solver* const solver, const std::vector& vars, return solver->MakeSum(cache, constant); } else { const string name = - StringPrintf("Sum([%s])", NameVector(vars, ", ").c_str()); + StringPrintf("Sum([%s])", JoinNamePtr(vars, ", ").c_str()); IntVar* const sum_var = solver->MakeIntVar(new_min, new_max, name); if (AreAllBooleans(vars)) { solver->AddConstraint( @@ -2769,14 +2771,15 @@ IntExpr* Solver::MakeSum(const std::vector& vars) { const bool all_booleans = AreAllBooleans(vars); if (all_booleans) { const string name = - StringPrintf("BooleanSum([%s])", NameVector(vars, ", ").c_str()); + StringPrintf("BooleanSum([%s])", + JoinNamePtr(vars, ", ").c_str()); sum_var = MakeIntVar(new_min, new_max, name); AddConstraint(RevAlloc(new SumBooleanEqualToVar(this, vars, sum_var))); } else if (new_min != kint64min && new_max != kint64max) { sum_var = MakeSumFct(this, vars)->Var(); } else { const string name = - StringPrintf("Sum([%s])", NameVector(vars, ", ").c_str()); + StringPrintf("Sum([%s])", JoinNamePtr(vars, ", ").c_str()); sum_var = MakeIntVar(new_min, new_max, name); AddConstraint(RevAlloc(new SafeSumConstraint(this, vars, sum_var))); } diff --git a/src/constraint_solver/nogoods.cc b/src/constraint_solver/nogoods.cc index 4b5bf7597a..a88b0233ef 100644 --- a/src/constraint_solver/nogoods.cc +++ b/src/constraint_solver/nogoods.cc @@ -147,7 +147,7 @@ bool NoGood::Apply(Solver* const solver) { } string NoGood::DebugString() const { - return StringPrintf("(%s)", DebugStringVector(terms_, " && ").c_str()); + return StringPrintf("(%s)", JoinDebugStringPtr(terms_, " && ").c_str()); } namespace { diff --git a/src/constraint_solver/resource.cc b/src/constraint_solver/resource.cc index c8076faaef..6828eb64eb 100644 --- a/src/constraint_solver/resource.cc +++ b/src/constraint_solver/resource.cc @@ -719,8 +719,8 @@ class RankedPropagator : public Constraint { IntVar* const slack = RankedSlack(i); const int64 transition_time = RankedTransitionTime(i, i + 1); next_interval->SetStartRange( - CapAdd(interval->EndMin(), slack->Min() + transition_time), - CapAdd(interval->EndMax(), slack->Max() + transition_time)); + CapAdd(interval->StartMin(), slack->Min() + transition_time), + CapAdd(interval->StartMax(), slack->Max() + transition_time)); } // Propagates on ranked last from right to left. for (int i = last_position; i > last_sentinel; --i) { @@ -728,7 +728,7 @@ class RankedPropagator : public Constraint { IntervalVar* const next_interval = RankedInterval(i); IntVar* const slack = RankedSlack(i - 1); const int64 transition_time = RankedTransitionTime(i - 1, i); - interval->SetEndRange( + interval->SetStartRange( CapSub(next_interval->StartMin(), slack->Max() + transition_time), CapSub(next_interval->StartMax(), slack->Min() + transition_time)); } @@ -746,53 +746,50 @@ class RankedPropagator : public Constraint { return; } // Propagates to the middle part. + // This assumes triangular inequality in the transition times. for (int i = first_sentinel; i <= last_sentinel; ++i) { IntervalVar* const interval = RankedInterval(i); IntVar* const slack = RankedSlack(i); if (interval->MayBePerformed()) { + const bool performed = interval->MustBePerformed(); if (first_interval != nullptr) { const int64 transition_time = RankedTransitionTime(first_sentinel - 1, i); - interval->SetStartRange(CapAdd(first_interval->EndMin(), + interval->SetStartRange(CapAdd(first_interval->StartMin(), first_slack->Min() + transition_time), - CapAdd(first_interval->EndMax(), + CapAdd(first_interval->StartMax(), first_slack->Max() + transition_time)); - } - if (last_interval != nullptr) { - const int64 transition_time = - RankedTransitionTime(i, last_sentinel + 1); - interval->SetEndRange( - CapSub(last_interval->StartMin(), slack->Max() + transition_time), - CapSub(last_interval->StartMax(), - slack->Min() + transition_time)); - } - if (interval->MustBePerformed()) { - if (last_interval != nullptr) { - const int64 transition_time = - RankedTransitionTime(i, last_sentinel + 1); - last_interval->SetStartRange( - CapAdd(interval->EndMin(), slack->Min() + transition_time), - CapAdd(interval->EndMax(), slack->Max() + transition_time)); - } - if (first_interval != nullptr) { - const int64 transition_time = - RankedTransitionTime(first_sentinel - 1, i); - first_interval->SetEndRange( + if (performed) { + first_interval->SetStartRange( CapSub(interval->StartMin(), first_slack->Max() + transition_time), CapSub(interval->StartMax(), first_slack->Min() + transition_time)); } } + if (last_interval != nullptr) { + const int64 transition_time = + RankedTransitionTime(i, last_sentinel + 1); + interval->SetStartRange( + CapSub(last_interval->StartMin(), slack->Max() + transition_time), + CapSub(last_interval->StartMax(), + slack->Min() + transition_time)); + if (performed) { + last_interval->SetStartRange( + CapAdd(interval->StartMin(), slack->Min() + transition_time), + CapAdd(interval->StartMax(), slack->Max() + transition_time)); + } + } } } + // TODO(user): cache transition on ranked intervals in a vector. // Propagates on ranked first from right to left. for (int i = std::min(first_sentinel - 2, last_position - 1); i >= 0; --i) { IntervalVar* const interval = RankedInterval(i); IntervalVar* const next_interval = RankedInterval(i + 1); IntVar* const slack = RankedSlack(i); const int64 transition_time = RankedTransitionTime(i, i + 1); - interval->SetEndRange( + interval->SetStartRange( CapSub(next_interval->StartMin(), slack->Max() + transition_time), CapSub(next_interval->StartMax(), slack->Min() + transition_time)); } @@ -803,8 +800,8 @@ class RankedPropagator : public Constraint { IntVar* const slack = RankedSlack(i); const int64 transition_time = RankedTransitionTime(i, i + 1); next_interval->SetStartRange( - CapAdd(interval->EndMin(), slack->Min() + transition_time), - CapAdd(interval->EndMax(), slack->Max() + transition_time)); + CapAdd(interval->StartMin(), slack->Min() + transition_time), + CapAdd(interval->StartMax(), slack->Max() + transition_time)); } // TODO(user) : Propagate on slacks. } @@ -819,7 +816,7 @@ class RankedPropagator : public Constraint { return slacks_[index]; } - int64 RankedTransitionTime(int before, int after) { + int64 RankedTransitionTime(int before, int after) const { const int before_index = partial_sequence_[before]; const int after_index = partial_sequence_[after]; @@ -832,8 +829,8 @@ class RankedPropagator : public Constraint { return StringPrintf( "RankedPropagator([%s], nexts = [%s], intervals = [%s])", partial_sequence_.DebugString().c_str(), - DebugStringVector(nexts_, ", ").c_str(), - DebugStringVector(intervals_, ", ").c_str()); + JoinDebugStringPtr(nexts_, ", ").c_str(), + JoinDebugStringPtr(intervals_, ", ").c_str()); } void Accept(ModelVisitor* const visitor) const { @@ -852,6 +849,7 @@ class RankedPropagator : public Constraint { // A class that stores several propagators for the sequence constraint, and // calls them until a fixpoint is reached. + class FullDisjunctiveConstraint : public DisjunctiveConstraint { public: FullDisjunctiveConstraint(Solver* const s, @@ -911,23 +909,18 @@ class FullDisjunctiveConstraint : public DisjunctiveConstraint { virtual string DebugString() const { return StringPrintf("FullDisjunctiveConstraint([%s])", - DebugStringVector(intervals_, ",").c_str()); - } - - int64 MinDistance(int64 activity_plus_one, int64 next_activity_plus_one) { - if (activity_plus_one == 0) { - return 0; - } else { - const int64 transition_time = - transition_time_.get() != nullptr - ? transition_time_->Run(activity_plus_one - 1, - next_activity_plus_one - 1) - : 0; - return intervals_[activity_plus_one - 1]->DurationMin() + transition_time; - } + JoinDebugStringPtr(intervals_, ",").c_str()); } private: + int64 MinDistance(int64 activity_plus_one, int64 next_activity_plus_one) { + return (transition_time_.get() == nullptr || activity_plus_one == 0 || + next_activity_plus_one > intervals_.size()) + ? 0 + : transition_time_->Run(activity_plus_one - 1, + next_activity_plus_one - 1); + } + void BuildNextModelIfNeeded() { if (!nexts_.empty()) { return; @@ -971,20 +964,32 @@ class FullDisjunctiveConstraint : public DisjunctiveConstraint { for (int64 i = 0; i < num_intervals; ++i) { IntervalVar* const var = intervals_[i]; - time_slacks_[i + 1] = s->MakeIntVar( - 0, horizon, StringPrintf("time_slacks(%" GG_LL_FORMAT "d)", i + 1)); - // TODO(user): Check SafeStartExpr(); - time_cumuls_[i + 1] = - var->MayBePerformed() ? var->SafeStartExpr(var->StartMin())->Var() - : s->MakeIntConst(horizon); + if (var->MayBePerformed()) { + const int64 duration_min = var->DurationMin(); + time_slacks_[i + 1] = s->MakeIntVar( + duration_min, horizon, + StringPrintf("time_slacks(%" GG_LL_FORMAT "d)", i + 1)); + // TODO(user): Check SafeStartExpr(); + time_cumuls_[i + 1] = var->MayBePerformed() + ? var->SafeStartExpr(var->StartMin())->Var() + : s->MakeIntConst(horizon); + if (var->DurationMax() != duration_min) { + s->AddConstraint(s->MakeGreaterOrEqual( + time_slacks_[i + 1], var->SafeDurationExpr(duration_min))); + } + } else { + time_slacks_[i + 1] = s->MakeIntVar( + 0, horizon, StringPrintf("time_slacks(%" GG_LL_FORMAT "d)", i + 1)); + time_cumuls_[i + 1] = s->MakeIntConst(horizon); + } } - time_cumuls_[num_nodes] = s->MakeIntVar(0, horizon, ct_name + "_ect"); + // TODO(user): Find a better UB for the last time cumul. + time_cumuls_[num_nodes] = s->MakeIntVar(0, 2 * horizon, ct_name + "_ect"); s->AddConstraint(s->MakePathCumul( nexts_, actives_, time_cumuls_, time_slacks_, NewPermanentCallback(this, &FullDisjunctiveConstraint::MinDistance))); - std::vector short_slacks(time_slacks_.begin() + 1, - time_slacks_.end()); + std::vector short_slacks(time_slacks_.begin() + 1, time_slacks_.end()); s->AddConstraint(s->RevAlloc(new RankedPropagator( s, nexts_, intervals_, short_slacks, transition_time_.get()))); } @@ -1887,8 +1892,8 @@ DisjunctiveConstraint* Solver::MakeDisjunctiveConstraint( } Constraint* Solver::MakeCumulative(const std::vector& intervals, - const std::vector& demands, - int64 capacity, const string& name) { + const std::vector& demands, int64 capacity, + const string& name) { CHECK_EQ(intervals.size(), demands.size()); for (int i = 0; i < intervals.size(); ++i) { CHECK_GE(demands[i], 0); @@ -1901,8 +1906,8 @@ Constraint* Solver::MakeCumulative(const std::vector& intervals, } Constraint* Solver::MakeCumulative(const std::vector& intervals, - const std::vector& demands, - int64 capacity, const string& name) { + const std::vector& demands, int64 capacity, + const string& name) { return MakeCumulative(intervals, ToInt64Vector(demands), capacity, name); } diff --git a/src/constraint_solver/routing.cc b/src/constraint_solver/routing.cc index 66331b1307..a3e9482342 100644 --- a/src/constraint_solver/routing.cc +++ b/src/constraint_solver/routing.cc @@ -2091,6 +2091,11 @@ void RoutingModel::CloseModel() { cost_ = solver_->MakeSum(cost_elements)->Var(); cost_->set_name("Cost"); + // Keep this out of SetupSearch as this contains static search objects. + // This will allow calling SetupSearch multiple times with different search + // parameters. + CreateNeighborhoodOperators(); + CreateFirstSolutionDecisionBuilders(); SetupSearch(); } @@ -3428,6 +3433,8 @@ const char* RoutingModel::RoutingStrategyName(RoutingStrategy strategy) { return "Savings"; case ROUTING_SWEEP: return "Sweep"; + default: + return nullptr; } return nullptr; } @@ -4013,101 +4020,129 @@ LocalSearchOperator* RoutingModel::CreateInsertionOperator() { } } -#define CP_ROUTING_PUSH_BACK_OPERATOR(operator_type) \ - if (homogeneous_costs_) { \ - operators.push_back(solver_->MakeOperator(nexts_, operator_type)); \ - } else { \ - operators.push_back( \ - solver_->MakeOperator(nexts_, vehicle_vars_, operator_type)); \ +#define CP_ROUTING_ADD_OPERATOR(operator_type, cp_operator_type) \ + if (homogeneous_costs_) { \ + local_search_operators_[operator_type] = \ + solver_->MakeOperator(nexts_, cp_operator_type); \ + } else { \ + local_search_operators_[operator_type] = \ + solver_->MakeOperator(nexts_, vehicle_vars_, cp_operator_type); \ } -#define CP_ROUTING_PUSH_BACK_CALLBACK_OPERATOR(operator_type) \ - if (homogeneous_costs_) { \ - operators.push_back( \ - solver_->MakeOperator(nexts_, BuildCostCallback(), operator_type)); \ - } else { \ - operators.push_back(solver_->MakeOperator( \ - nexts_, vehicle_vars_, BuildCostCallback(), operator_type)); \ +#define CP_ROUTING_ADD_CALLBACK_OPERATOR(operator_type, cp_operator_type) \ + if (homogeneous_costs_) { \ + local_search_operators_[operator_type] = \ + solver_->MakeOperator(nexts_, BuildCostCallback(), cp_operator_type); \ + } else { \ + local_search_operators_[operator_type] = \ + solver_->MakeOperator( \ + nexts_, vehicle_vars_, BuildCostCallback(), cp_operator_type); \ } -LocalSearchOperator* RoutingModel::CreateNeighborhoodOperators() { +void RoutingModel::CreateNeighborhoodOperators() { + local_search_operators_.clear(); + local_search_operators_.resize(ROUTING_LOCAL_SEARCH_OPERATOR_COUNTER, + nullptr); + CP_ROUTING_ADD_OPERATOR(ROUTING_RELOCATE, Solver::RELOCATE); + std::vector empty; + local_search_operators_[ROUTING_PAIR_RELOCATE] = MakePairRelocate( + solver_.get(), nexts_, homogeneous_costs_ ? empty : vehicle_vars_, + pickup_delivery_pairs_); + local_search_operators_[ROUTING_RELOCATE_NEIGHBORS] = MakeRelocateNeighbors( + solver_.get(), nexts_, homogeneous_costs_ ? empty : vehicle_vars_, + NewPermanentCallback(this, &RoutingModel::GetHomogeneousCost)); + CP_ROUTING_ADD_OPERATOR(ROUTING_EXCHANGE, Solver::EXCHANGE); + CP_ROUTING_ADD_OPERATOR(ROUTING_CROSS, Solver::CROSS); + CP_ROUTING_ADD_OPERATOR(ROUTING_TWO_OPT, Solver::TWOOPT); + CP_ROUTING_ADD_OPERATOR(ROUTING_OR_OPT, Solver::OROPT); + CP_ROUTING_ADD_CALLBACK_OPERATOR(ROUTING_LKH, Solver::LK); + local_search_operators_[ROUTING_MAKE_ACTIVE] = CreateInsertionOperator(); + CP_ROUTING_ADD_OPERATOR(ROUTING_MAKE_INACTIVE, Solver::MAKEINACTIVE); + CP_ROUTING_ADD_OPERATOR(ROUTING_MAKE_CHAIN_INACTIVE, + Solver::MAKECHAININACTIVE); + CP_ROUTING_ADD_OPERATOR(ROUTING_SWAP_ACTIVE, Solver::SWAPACTIVE); + CP_ROUTING_ADD_OPERATOR(ROUTING_EXTENDED_SWAP_ACTIVE, + Solver::EXTENDEDSWAPACTIVE); + CP_ROUTING_ADD_CALLBACK_OPERATOR(ROUTING_TSP_OPT, Solver::TSPOPT); + CP_ROUTING_ADD_CALLBACK_OPERATOR(ROUTING_TSP_LNS, Solver::TSPLNS); + CP_ROUTING_ADD_OPERATOR(ROUTING_PATH_LNS, Solver::PATHLNS); + CP_ROUTING_ADD_OPERATOR(ROUTING_FULL_PATH_LNS, Solver::FULLPATHLNS); + CP_ROUTING_ADD_OPERATOR(ROUTING_INACTIVE_LNS, Solver::UNACTIVELNS); +} + +#undef CP_ROUTING_ADD_CALLBACK_OPERATOR +#undef CP_ROUTING_ADD_OPERATOR + +LocalSearchOperator* RoutingModel::GetNeighborhoodOperators() const { std::vector operators = extra_operators_; if (pickup_delivery_pairs_.size() > 0) { - std::vector empty; - operators.push_back(MakePairRelocate( - solver_.get(), nexts_, homogeneous_costs_ ? empty : vehicle_vars_, - pickup_delivery_pairs_)); + operators.push_back(local_search_operators_[ROUTING_PAIR_RELOCATE]); } if (vehicles_ > 1) { if (!FLAGS_routing_no_relocate) { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::RELOCATE); + operators.push_back(local_search_operators_[ROUTING_RELOCATE]); } if (!FLAGS_routing_no_exchange) { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::EXCHANGE); + operators.push_back(local_search_operators_[ROUTING_EXCHANGE]); } if (!FLAGS_routing_no_cross) { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::CROSS); + operators.push_back(local_search_operators_[ROUTING_CROSS]); } } if (pickup_delivery_pairs_.size() > 0 || !FLAGS_routing_no_relocate_neighbors) { - std::vector empty; - operators.push_back(MakeRelocateNeighbors( - solver_.get(), nexts_, homogeneous_costs_ ? empty : vehicle_vars_, - NewPermanentCallback(this, &RoutingModel::GetHomogeneousCost))); + operators.push_back(local_search_operators_[ROUTING_RELOCATE_NEIGHBORS]); } if (!FLAGS_routing_no_lkh && !FLAGS_routing_tabu_search && !FLAGS_routing_simulated_annealing) { - CP_ROUTING_PUSH_BACK_CALLBACK_OPERATOR(Solver::LK); + operators.push_back(local_search_operators_[ROUTING_LKH]); } if (!FLAGS_routing_no_2opt) { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::TWOOPT); + operators.push_back(local_search_operators_[ROUTING_TWO_OPT]); } if (!FLAGS_routing_no_oropt) { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::OROPT); + operators.push_back(local_search_operators_[ROUTING_OR_OPT]); } if (!FLAGS_routing_no_make_active && disjunctions_.size() != 0) { if (!FLAGS_routing_use_chain_make_inactive) { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::MAKEINACTIVE); + operators.push_back(local_search_operators_[ROUTING_MAKE_INACTIVE]); } else { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::MAKECHAININACTIVE); + operators.push_back(local_search_operators_[ROUTING_MAKE_CHAIN_INACTIVE]); } // TODO(user): On cases where we have a mix of node pairs and // individual nodes, only pairs are going to be made active. In practice // such cases should not appear, but we might want to be robust to them // anyway. - operators.push_back(CreateInsertionOperator()); + operators.push_back(local_search_operators_[ROUTING_MAKE_ACTIVE]); if (!FLAGS_routing_use_extended_swap_active) { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::SWAPACTIVE); + operators.push_back(local_search_operators_[ROUTING_SWAP_ACTIVE]); } else { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::EXTENDEDSWAPACTIVE); + operators.push_back( + local_search_operators_[ROUTING_EXTENDED_SWAP_ACTIVE]); } } // TODO(user): move the following operators to a second local search loop. if (!FLAGS_routing_no_tsp && !FLAGS_routing_tabu_search && !FLAGS_routing_simulated_annealing) { - CP_ROUTING_PUSH_BACK_CALLBACK_OPERATOR(Solver::TSPOPT); + operators.push_back(local_search_operators_[ROUTING_TSP_OPT]); } if (!FLAGS_routing_no_tsplns && !FLAGS_routing_tabu_search && !FLAGS_routing_simulated_annealing) { - CP_ROUTING_PUSH_BACK_CALLBACK_OPERATOR(Solver::TSPLNS); + operators.push_back(local_search_operators_[ROUTING_TSP_LNS]); } if (!FLAGS_routing_no_fullpathlns) { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::FULLPATHLNS); + operators.push_back(local_search_operators_[ROUTING_FULL_PATH_LNS]); } if (!FLAGS_routing_no_lns) { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::PATHLNS); + operators.push_back(local_search_operators_[ROUTING_PATH_LNS]); if (disjunctions_.size() != 0) { - CP_ROUTING_PUSH_BACK_OPERATOR(Solver::UNACTIVELNS); + operators.push_back(local_search_operators_[ROUTING_INACTIVE_LNS]); } } return solver_->ConcatenateOperators(operators); } -#undef CP_ROUTING_PUSH_BACK_CALLBACK_OPERATOR -#undef CP_ROUTING_PUSH_BACK_OPERATOR - const std::vector& RoutingModel::GetOrCreateLocalSearchFilters() { // Note on objective injection from one filter to another. @@ -4210,113 +4245,117 @@ DecisionBuilder* RoutingModel::CreateSolutionFinalizer() { return solver_->Compose(decision_builders); } -DecisionBuilder* RoutingModel::CreateFirstSolutionDecisionBuilder() { - DecisionBuilder* finalize_solution = CreateSolutionFinalizer(); - DecisionBuilder* first_solution = finalize_solution; - const RoutingStrategy first_solution_strategy = - GetSelectedFirstSolutionStrategy(); - VLOG(1) << "Using first solution strategy: " - << RoutingStrategyName(first_solution_strategy); - switch (first_solution_strategy) { - case ROUTING_GLOBAL_CHEAPEST_ARC: - first_solution = solver_->MakePhase( +void RoutingModel::CreateFirstSolutionDecisionBuilders() { + first_solution_decision_builders_.resize( + ROUTING_FIRST_SOLUTION_STRATEGY_COUNTER); + DecisionBuilder* const finalize_solution = CreateSolutionFinalizer(); + // Default heuristic + first_solution_decision_builders_[ROUTING_DEFAULT_STRATEGY] = + finalize_solution; + // Global cheapest addition heuristic. + first_solution_decision_builders_[ROUTING_GLOBAL_CHEAPEST_ARC] = + solver_->MakePhase( nexts_, NewPermanentCallback(this, &RoutingModel::GetFirstSolutionCost), Solver::CHOOSE_STATIC_GLOBAL_BEST); - break; - case ROUTING_LOCAL_CHEAPEST_ARC: - first_solution = solver_->MakePhase( + // Cheapest addition heuristic. + first_solution_decision_builders_[ROUTING_LOCAL_CHEAPEST_ARC] = + solver_->MakePhase( nexts_, Solver::CHOOSE_FIRST_UNBOUND, NewPermanentCallback(this, &RoutingModel::GetFirstSolutionCost)); - break; - case ROUTING_PATH_CHEAPEST_ARC: - first_solution = solver_->MakePhase( + // Path-based cheapest addition heuristic. + first_solution_decision_builders_[ROUTING_PATH_CHEAPEST_ARC] = + solver_->MakePhase( nexts_, Solver::CHOOSE_PATH, NewPermanentCallback(this, &RoutingModel::GetFirstSolutionCost)); - if (vehicles() == 1) { - DecisionBuilder* fast_one_path_builder = - solver_->RevAlloc(new FastOnePathBuilder( - this, NewPermanentCallback( - this, &RoutingModel::GetFirstSolutionCost))); - first_solution = solver_->Try(fast_one_path_builder, first_solution); - } - break; - case ROUTING_PATH_MOST_CONSTRAINED_ARC: - // TODO(user): implement the variable selector CHOOSE_SMALLEST_PATH - // and use it here. - first_solution = solver_->MakePhase( - nexts_, Solver::CHOOSE_PATH, - NewPermanentCallback( - this, &RoutingModel::ArcIsMoreConstrainedThanArc)); - break; - case ROUTING_EVALUATOR_STRATEGY: - CHECK(first_solution_evaluator_ != nullptr); - first_solution = solver_->MakePhase( - nexts_, Solver::CHOOSE_PATH, - NewPermanentCallback(first_solution_evaluator_.get(), - &Solver::IndexEvaluator2::Run)); - break; - case ROUTING_DEFAULT_STRATEGY: - break; - case ROUTING_ALL_UNPERFORMED: - first_solution = solver_->RevAlloc(new AllUnperformed(this)); - break; - case ROUTING_BEST_INSERTION: { - SearchLimit* const ls_limit = solver_->MakeLimit( - time_limit_ms_, kint64max, kint64max, kint64max, true); - DecisionBuilder* const finalize = solver_->MakeSolveOnce( - finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit()); - LocalSearchPhaseParameters* const insertion_parameters = - solver_->MakeLocalSearchPhaseParameters( - CreateInsertionOperator(), finalize, ls_limit, - GetOrCreateLocalSearchFilters()); - std::vector monitors; - monitors.push_back(GetOrCreateLimit()); - std::vector decision_vars = nexts_; - if (!homogeneous_costs_) { - decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(), - vehicle_vars_.end()); - } - first_solution = solver_->MakeNestedOptimize( + if (vehicles() == 1) { + DecisionBuilder* fast_one_path_builder = + solver_->RevAlloc(new FastOnePathBuilder( + this, NewPermanentCallback( + this, &RoutingModel::GetFirstSolutionCost))); + first_solution_decision_builders_[ROUTING_PATH_CHEAPEST_ARC] = solver_->Try( + fast_one_path_builder, + first_solution_decision_builders_[ROUTING_PATH_CHEAPEST_ARC]); + } + // Path-based most constrained arc addition heuristic. + // TODO(user): implement the variable selector CHOOSE_SMALLEST_PATH and use + // it here. + first_solution_decision_builders_[ROUTING_PATH_MOST_CONSTRAINED_ARC] = + solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, + NewPermanentCallback( + this, &RoutingModel::ArcIsMoreConstrainedThanArc)); + // Evaluator-based path heuristic. + if (first_solution_evaluator_ != nullptr) { + first_solution_decision_builders_[ROUTING_EVALUATOR_STRATEGY] = + solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, + NewPermanentCallback(first_solution_evaluator_.get(), + &Solver::IndexEvaluator2::Run)); + } else { + first_solution_decision_builders_[ROUTING_EVALUATOR_STRATEGY] = nullptr; + } + // All unperformed heuristic. + first_solution_decision_builders_[ROUTING_ALL_UNPERFORMED] = + solver_->RevAlloc(new AllUnperformed(this)); + // Best insertion heuristic. + SearchLimit* const ls_limit = solver_->MakeLimit( + time_limit_ms_, kint64max, kint64max, kint64max, true); + DecisionBuilder* const finalize = solver_->MakeSolveOnce( + finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit()); + LocalSearchPhaseParameters* const insertion_parameters = + solver_->MakeLocalSearchPhaseParameters( + CreateInsertionOperator(), finalize, ls_limit, + GetOrCreateLocalSearchFilters()); + std::vector monitors; + monitors.push_back(GetOrCreateLimit()); + std::vector decision_vars = nexts_; + if (!homogeneous_costs_) { + decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(), + vehicle_vars_.end()); + } + first_solution_decision_builders_[ROUTING_BEST_INSERTION] = + solver_->MakeNestedOptimize( solver_->MakeLocalSearchPhase( decision_vars, solver_->RevAlloc(new AllUnperformed(this)), insertion_parameters), GetOrCreateAssignment(), false, FLAGS_routing_optimization_step, monitors); - first_solution = solver_->Compose(first_solution, finalize); - break; - } - case ROUTING_SAVINGS: { - first_solution = solver_->RevAlloc(new SavingsBuilder(this, true)); - DecisionBuilder* savings_builder = - solver_->RevAlloc(new SavingsBuilder(this, false)); - first_solution = solver_->Try(savings_builder, first_solution); - break; - } -#ifndef SWIG - case ROUTING_SWEEP: { - first_solution = solver_->RevAlloc(new SweepBuilder(this, true)); - DecisionBuilder* sweep_builder = - solver_->RevAlloc(new SweepBuilder(this, false)); - first_solution = solver_->Try(sweep_builder, first_solution); - break; - } -#endif - default: - LOG(WARNING) << "Unknown argument for routing_first_solution, " - "using default"; - } + first_solution_decision_builders_[ROUTING_BEST_INSERTION] = + solver_->Compose( + first_solution_decision_builders_[ROUTING_BEST_INSERTION], + finalize); + first_solution_decision_builders_[ROUTING_SAVINGS] = + solver_->RevAlloc(new SavingsBuilder(this, true)); + DecisionBuilder* savings_builder = + solver_->RevAlloc(new SavingsBuilder(this, false)); + first_solution_decision_builders_[ROUTING_SAVINGS] = solver_->Try( + savings_builder, first_solution_decision_builders_[ROUTING_SAVINGS]); + first_solution_decision_builders_[ROUTING_SWEEP] = + solver_->RevAlloc(new SweepBuilder(this, true)); + DecisionBuilder* sweep_builder = + solver_->RevAlloc(new SweepBuilder(this, false)); + first_solution_decision_builders_[ROUTING_SWEEP] = solver_->Try( + sweep_builder, first_solution_decision_builders_[ROUTING_SWEEP]); if (FLAGS_routing_use_first_solution_dive) { - DecisionBuilder* apply = + DecisionBuilder* const apply = solver_->MakeApplyBranchSelector(NewPermanentCallback(&LeftDive)); - first_solution = solver_->Compose(apply, first_solution); + for (int i = 0; i < first_solution_decision_builders_.size(); ++i) { + first_solution_decision_builders_[i] = + solver_->Compose(apply, first_solution_decision_builders_[i]); + } } - return first_solution; +} + +DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder() const { + const RoutingStrategy first_solution_strategy = + GetSelectedFirstSolutionStrategy(); + VLOG(1) << "Using first solution strategy: " + << RoutingStrategyName(first_solution_strategy); + return first_solution_decision_builders_[first_solution_strategy]; } LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters() { return solver_->MakeLocalSearchPhaseParameters( - CreateNeighborhoodOperators(), + GetNeighborhoodOperators(), solver_->MakeSolveOnce(CreateSolutionFinalizer(), GetOrCreateLargeNeighborhoodSearchLimit()), GetOrCreateLocalSearchLimit(), GetOrCreateLocalSearchFilters()); @@ -4324,7 +4363,7 @@ LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters() { DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder() { const int size = Size(); - DecisionBuilder* first_solution = CreateFirstSolutionDecisionBuilder(); + DecisionBuilder* first_solution = GetFirstSolutionDecisionBuilder(); LocalSearchPhaseParameters* parameters = CreateLocalSearchParameters(); if (homogeneous_costs_) { return solver_->MakeLocalSearchPhase(nexts_, first_solution, parameters); @@ -4343,7 +4382,7 @@ DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder() { void RoutingModel::SetupDecisionBuilders() { if (FLAGS_routing_dfs) { - solve_db_ = CreateFirstSolutionDecisionBuilder(); + solve_db_ = GetFirstSolutionDecisionBuilder(); } else { solve_db_ = CreateLocalSearchDecisionBuilder(); } diff --git a/src/constraint_solver/routing.h b/src/constraint_solver/routing.h index 44f7f09b41..2b9ce0fb64 100644 --- a/src/constraint_solver/routing.h +++ b/src/constraint_solver/routing.h @@ -40,7 +40,7 @@ // * "next(i)" variables representing the immediate successor of the node // corresponding to i; use IndexToNode() to get the node corresponding to // a "next" variable value; note that node indices are strongly typed -// integers (cf. base/int-type.h); +// integers (cf. base/int_type.h); // * "vehicle(i)" variables representing the vehicle route to which the // node corresponding to i belongs; // * "active(i)" boolean variables, true if the node corresponding to i is @@ -158,7 +158,7 @@ #include #include -#include "base/callback_types.h" +#include "base/callback.h" #include "base/commandlineflags.h" #include "base/integral_types.h" #include "base/macros.h" @@ -283,6 +283,8 @@ struct RoutingSearchParameters { class RoutingModel { public: // First solution strategies, used as starting point of local search. + // TODO(user): Remove this when the corresponding enum is available in + // the routing search parameters protobuf. enum RoutingStrategy { // Select the first node with an unbound successor and connect it to the // first available node. @@ -323,6 +325,7 @@ class RoutingModel { // Operational Research Quarterly (1970-1977), // Vol. 23, No. 3 (Sep., 1972), pp. 333-344 ROUTING_SWEEP, + ROUTING_FIRST_SOLUTION_STRATEGY_COUNTER }; // Metaheuristics used to guide the search. Apart greedy descent, they will @@ -897,6 +900,31 @@ class RoutingModel { RoutingMetaheuristic* metaheuristic); private: + // Local search move operator usable in routing. + // TODO(user): Remove this when the corresponding enum is available in + // the routing search parameters protobuf. + enum RoutingLocalSearchOperator { + ROUTING_RELOCATE = 0, + ROUTING_PAIR_RELOCATE, + ROUTING_RELOCATE_NEIGHBORS, + ROUTING_EXCHANGE, + ROUTING_CROSS, + ROUTING_TWO_OPT, + ROUTING_OR_OPT, + ROUTING_LKH, + ROUTING_TSP_OPT, + ROUTING_TSP_LNS, + ROUTING_PATH_LNS, + ROUTING_FULL_PATH_LNS, + ROUTING_INACTIVE_LNS, + ROUTING_MAKE_ACTIVE, + ROUTING_MAKE_INACTIVE, + ROUTING_MAKE_CHAIN_INACTIVE, + ROUTING_SWAP_ACTIVE, + ROUTING_EXTENDED_SWAP_ACTIVE, + ROUTING_LOCAL_SEARCH_OPERATOR_COUNTER + }; + // Structure storing node disjunction information (nodes and penalty when // unperformed). struct Disjunction { @@ -980,10 +1008,12 @@ class RoutingModel { SearchLimit* GetOrCreateLocalSearchLimit(); SearchLimit* GetOrCreateLargeNeighborhoodSearchLimit(); LocalSearchOperator* CreateInsertionOperator(); - LocalSearchOperator* CreateNeighborhoodOperators(); + void CreateNeighborhoodOperators(); + LocalSearchOperator* GetNeighborhoodOperators() const; const std::vector& GetOrCreateLocalSearchFilters(); DecisionBuilder* CreateSolutionFinalizer(); - DecisionBuilder* CreateFirstSolutionDecisionBuilder(); + void CreateFirstSolutionDecisionBuilders(); + DecisionBuilder* GetFirstSolutionDecisionBuilder() const; LocalSearchPhaseParameters* CreateLocalSearchParameters(); DecisionBuilder* CreateLocalSearchDecisionBuilder(); void SetupDecisionBuilders(); @@ -1037,8 +1067,10 @@ class RoutingModel { Status status_; // Search data + std::vector first_solution_decision_builders_; RoutingStrategy first_solution_strategy_; scoped_ptr first_solution_evaluator_; + std::vector local_search_operators_; RoutingMetaheuristic metaheuristic_; std::vector monitors_; SolutionCollector* collect_assignments_; diff --git a/src/constraint_solver/sched_constraints.cc b/src/constraint_solver/sched_constraints.cc index 8418d2342f..205a5a7c89 100644 --- a/src/constraint_solver/sched_constraints.cc +++ b/src/constraint_solver/sched_constraints.cc @@ -65,7 +65,7 @@ class TreeArrayConstraint : public Constraint { string DebugStringInternal(const string& name) const { return StringPrintf("Cover(%s) == %s", - DebugStringVector(vars_, ", ").c_str(), + JoinDebugStringPtr(vars_, ", ").c_str(), target_var_->DebugString().c_str()); } diff --git a/src/constraint_solver/sched_search.cc b/src/constraint_solver/sched_search.cc index da00d0607d..80fb7b87ec 100644 --- a/src/constraint_solver/sched_search.cc +++ b/src/constraint_solver/sched_search.cc @@ -64,7 +64,7 @@ string SequenceVar::DebugString() const { "d, duration = %" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d, not ranked = %d, ranked = %d, nexts = [%s])", name().c_str(), hmin, hmax, dmin, dmax, not_ranked, - ranked, DebugStringVector(nexts_, ", ").c_str()); + ranked, JoinDebugStringPtr(nexts_, ", ").c_str()); } void SequenceVar::Accept(ModelVisitor* const visitor) const { diff --git a/src/constraint_solver/search.cc b/src/constraint_solver/search.cc index 5921804138..a9cef4959e 100644 --- a/src/constraint_solver/search.cc +++ b/src/constraint_solver/search.cc @@ -451,7 +451,7 @@ Decision* ComposeDecisionBuilder::Next(Solver* const s) { string ComposeDecisionBuilder::DebugString() const { return StringPrintf("ComposeDecisionBuilder(%s)", - DebugStringVector(builders_, ", ").c_str()); + JoinDebugStringPtr(builders_, ", ").c_str()); } } // namespace @@ -565,8 +565,7 @@ Decision* TryDecisionBuilder::Next(Solver* const solver) { string TryDecisionBuilder::DebugString() const { return StringPrintf( - "TryDecisionBuilder(%s)", - DebugStringArray(builders_.data(), builders_.size(), ", ").c_str()); + "TryDecisionBuilder(%s)", JoinDebugStringPtr(builders_, ", ").c_str()); } void TryDecisionBuilder::AdvanceToNextBuilder(Solver* const solver) { @@ -635,7 +634,7 @@ class VariableSelector : public BaseObject { virtual ~VariableSelector() {} virtual IntVar* Select(Solver* const s, int64* id) = 0; string VarDebugString() const { - return StringPrintf("(%s)", DebugStringVector(vars_, ", ").c_str()); + return StringPrintf("(%s)", JoinDebugStringPtr(vars_, ", ").c_str()); } void Accept(ModelVisitor* const visitor) const { visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension); @@ -1434,7 +1433,7 @@ class BaseEvaluatorSelector : public BaseVariableAssignmentSelector { string DebugStringInternal(const string& name) const { return StringPrintf("%s(%s)", name.c_str(), - DebugStringVector(vars_, ", ").c_str()); + JoinDebugStringPtr(vars_, ", ").c_str()); } const std::vector vars_; diff --git a/src/constraint_solver/table.cc b/src/constraint_solver/table.cc index 9a35ece8cc..da30fe30fd 100644 --- a/src/constraint_solver/table.cc +++ b/src/constraint_solver/table.cc @@ -637,8 +637,8 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint { } } } else { // Domain is sparse. - // Let's not collect all values below the first supported - // value as this can easily and more rapidly be taken care + // Let's not collect all values below the first supported + // value as this can easily and more rapidly be taken care // of by a SetRange() call. new_min = kint64max; // escape value. IntVarIterator* const it = iterators_[var_index]; @@ -1284,7 +1284,8 @@ class TransitionConstraint : public Constraint { return StringPrintf( "TransitionConstraint([%s], %d transitions, initial = %" GG_LL_FORMAT "d, final = [%s])", - DebugStringVector(vars_, ", ").c_str(), transition_table_.NumTuples(), + JoinDebugStringPtr(vars_, ", ").c_str(), + transition_table_.NumTuples(), initial_state_, IntVectorToString(final_states_, ", ").c_str()); } diff --git a/src/linear_solver/linear_solver.cc b/src/linear_solver/linear_solver.cc index 7e1c59e7f5..bf8f98ecb9 100644 --- a/src/linear_solver/linear_solver.cc +++ b/src/linear_solver/linear_solver.cc @@ -21,14 +21,6 @@ #include #include #include -#if defined(_MSC_VER) -#define isnan(x) _isnan(x) -static inline double round(double val) { - return floor(val + 0.5); -} -#elif defined(__APPLE__) -using std::isnan; -#endif #include "base/commandlineflags.h" #include "base/integral_types.h" @@ -46,6 +38,7 @@ using std::isnan; #include "linear_solver/model_exporter.h" #include "util/fp_utils.h" + DEFINE_bool(verify_solution, false, "Systematically verify the solution when calling Solve()" ", and change the return value of Solve() to ABNORMAL if" diff --git a/src/linear_solver/linear_solver.h b/src/linear_solver/linear_solver.h index 14ba9d2785..7a09b312b2 100644 --- a/src/linear_solver/linear_solver.h +++ b/src/linear_solver/linear_solver.h @@ -594,10 +594,20 @@ class MPSolver { DISALLOW_COPY_AND_ASSIGN(MPSolver); }; +#if !defined(SWIG) // The data structure used to store the coefficients of the contraints and of // the objective. Also define a type to facilitate iteration over them with: // for (CoeffEntry entry : coefficients_) { ... } -typedef hash_map CoeffMap; +class CoeffMap : public hash_map { + public: + explicit CoeffMap(int num_buckets) + #if !defined(_MSC_VER) // Visual C++ doesn't support this constructor + : hash_map(num_buckets) + #endif // _MSC_VER + { } +}; +#endif // SWIG + typedef std::pair CoeffEntry; // A class to express a linear objective. diff --git a/src/linear_solver/model_exporter.cc b/src/linear_solver/model_exporter.cc index 5d6517f541..9cb894dd2a 100644 --- a/src/linear_solver/model_exporter.cc +++ b/src/linear_solver/model_exporter.cc @@ -23,15 +23,7 @@ #include "base/map_util.h" #include "linear_solver/linear_solver.h" #include "linear_solver/linear_solver.pb.h" - -#if defined(_MSC_VER) -#define isnan(x) _isnan(x) -static inline double round(double val) { - return floor(val + 0.5); -} -#elif defined(__APPLE__) -using std::isnan; -#endif +#include "util/fp_utils.h" namespace operations_research { diff --git a/src/util/fp_utils.h b/src/util/fp_utils.h index c4f321fc7a..e28177cab2 100644 --- a/src/util/fp_utils.h +++ b/src/util/fp_utils.h @@ -37,6 +37,13 @@ #include "base/logging.h" +#if defined(_MSC_VER) +static inline double isnan(x) { return _isnan(x); } +static inline double round(double val) { return floor(val + 0.5); } +#elif defined(__APPLE__) +using std::isnan; +#endif + namespace operations_research { // The following macro does not change "var", but forces gcc to consider it @@ -48,8 +55,7 @@ namespace operations_research { #define TOUCH(var) #endif -#if defined(__i386__) || defined(__x86_64__) -#if defined(__linux__) +#if (defined(__i386__) || defined(__x86_64__)) && defined(__linux__) inline fpu_control_t GetFPPrecision() { fpu_control_t status; _FPU_GETCW(status); @@ -67,8 +73,7 @@ inline void SetFPPrecision(fpu_control_t precision) { _FPU_SETCW(status); DCHECK_EQ(precision, GetFPPrecision()); } -#endif // defined(__linux__) -#endif // defined(__i386__) || defined(__x86_64__) +#endif // (defined(__i386__) || defined(__x86_64__)) && defined(__linux__) #undef TOUCH diff --git a/src/util/saturated_arithmetic.cc b/src/util/saturated_arithmetic.cc index a115bce24e..a968c03a1e 100644 --- a/src/util/saturated_arithmetic.cc +++ b/src/util/saturated_arithmetic.cc @@ -61,4 +61,5 @@ uint64 UnsignedCapProd(uint64 left, uint64 right) { } return left * right; } + } // namespace operations_research diff --git a/src/util/string_array.h b/src/util/string_array.h index 1e5ed424ff..b4eea7bbac 100644 --- a/src/util/string_array.h +++ b/src/util/string_array.h @@ -24,78 +24,46 @@ using std::string; namespace operations_research { // ---------- Pretty Print Helpers ---------- -// Creates a string from an array of object pointers supporting the -// DebugString() method, and a separator. +// See the straightforward (and unique) usage of this macro below. +#define RETURN_STRINGIFIED_VECTOR(vector, separator, method)\ + string out;\ + for (int i = 0; i < vector.size(); ++i) {\ + if (i > 0) out += separator;\ + out += vector[i] method;\ + }\ + return out + +// Converts a vector into a string by calling the given method (or simply +// getting the given string member), on all elements, and concatenating +// the obtained strings with the given separator. + +// Join v[i].DebugString(). template -string DebugStringArray(T* const* array, int size, const string& separator) { - string out; - for (int i = 0; i < size; ++i) { - if (i > 0) { - out.append(separator); - } - out.append(array[i]->DebugString()); - } - return out; +string JoinDebugString(const std::vector& v, const string& separator) { + RETURN_STRINGIFIED_VECTOR(v, separator, .DebugString()); } -// TODO(user): rename DebugStringVector to PtrVectorDebugString and -// DebugStringRefVector to VectorDebugString - -// Creates a string from an vector of object pointers supporting the -// DebugString() method, and a separator. +// Join v[i]->DebugString(). template -string DebugStringVector(const std::vector& array, const string& separator) { - return DebugStringArray(array.data(), array.size(), separator); +string JoinDebugStringPtr(const std::vector& v, const string& separator) { + RETURN_STRINGIFIED_VECTOR(v, separator, ->DebugString()); } -// Creates a string from an vector of objects supporting the -// DebugString() method, and a separator. +// Join v[i]->name(). template -string DebugStringRefVector(const std::vector& array, const string& separator) { - string out; - for (int i = 0; i < array.size(); ++i) { - if (i > 0) { - out.append(separator); - } - out.append(array[i].DebugString()); - } - return out; +string JoinNamePtr(const std::vector& v, const string& separator) { + RETURN_STRINGIFIED_VECTOR(v, separator, ->name()); } -// Creates a string from an array of objects supporting the -// name() method, and a separator. +// Join v[i]->name. template -string NameArray(T* const* array, int size, const string& separator) { - string out; - for (int i = 0; i < size; ++i) { - if (i > 0) { - out.append(separator); - } - out.append(array[i]->name()); - } - return out; +string JoinNameFieldPtr(const std::vector& v, const string& separator) { + RETURN_STRINGIFIED_VECTOR(v, separator, ->name); } -// Creates a string from an vector of objects supporting the -// name() method, and a separator. -template -string NameVector(const std::vector& array, const string& separator) { - return NameArray(array.data(), array.size(), separator); -} +#undef RETURN_STRINGIFIED_VECTOR -// Creates a string from vector of objects having a string field "name" (i.e. -// "object.name" is a string), and a separator. -template -string NameFieldVector(const std::vector& array, const string& separator) { - string out; - for (int i = 0; i < array.size(); ++i) { - if (i > 0) { - out.append(separator); - } - out.append(array[i]->name); - } - return out; -} +// TODO(user): use strings::Join instead of the methods below. // Creates a string from an array of int64, and a separator. inline string Int64ArrayToString(const int64* const array, int size,