diff --git a/examples/cpp/costas_array.cc b/examples/cpp/costas_array.cc index b6f047e67e..5457d5bedd 100644 --- a/examples/cpp/costas_array.cc +++ b/examples/cpp/costas_array.cc @@ -312,8 +312,11 @@ void CostasSoft(const int dim) { // The first solution that the local optimization is based on Evaluator evaluator(matrix); DecisionBuilder* const first_solution = solver.MakePhase( - matrix, NewPermanentCallback(&evaluator, &Evaluator::VarEvaluator), - NewPermanentCallback(&evaluator, &Evaluator::ValueEvaluator)); + matrix, + [&evaluator](int64 index) { return evaluator.VarEvaluator(index); }, + [&evaluator](int64 var, int64 value) { + return evaluator.ValueEvaluator(var, value); + }); SearchLimit* const search_time_limit = solver.MakeLimit(FLAGS_timelimit, kint64max, kint64max, kint64max); diff --git a/examples/cpp/cvrptw.cc b/examples/cpp/cvrptw.cc index 0cd8b96814..75674e8b00 100644 --- a/examples/cpp/cvrptw.cc +++ b/examples/cpp/cvrptw.cc @@ -21,7 +21,8 @@ // distances are computed using the Manhattan distance. Distances are assumed // to be in meters and times in seconds. -#include "base/unique_ptr.h" +#include +#include #include #include "base/callback.h" @@ -49,9 +50,13 @@ 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"); const char* kTime = "Time"; const char* kCapacity = "Capacity"; +const int kMaxNodesPerGroup = 10; +const int64 kSameVehicleCost = 1000; // Random seed generator. int32 GetSeed() { @@ -177,6 +182,30 @@ void DisplayPlan(const RoutingModel& routing, const Assignment& plan) { plan_output += "Dropped orders:" + dropped + "\n"; } + if (FLAGS_vrp_use_same_vehicle_costs) { + int group_size = 0; + int64 same_vehicle_cost = 0; + std::set visited; + const RoutingModel::NodeIndex kFirstNodeAfterDepot(1); + for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; + order < routing.nodes(); ++order) { + ++group_size; + visited.insert( + plan.Value(routing.VehicleVar(routing.NodeToIndex(order)))); + if (group_size == kMaxNodesPerGroup) { + if (visited.size() > 1) { + same_vehicle_cost += (visited.size() - 1) * kSameVehicleCost; + } + group_size = 0; + visited.clear(); + } + } + if (visited.size() > 1) { + same_vehicle_cost += (visited.size() - 1) * kSameVehicleCost; + } + LOG(INFO) << "Same vehicle costs: " << same_vehicle_cost; + } + // Display actual output for each vehicle. const RoutingDimension& capacity_dimension = routing.GetDimensionOrDie(kCapacity); @@ -197,6 +226,7 @@ void DisplayPlan(const RoutingModel& routing, const Assignment& plan) { if (routing.IsEnd(order)) break; order = plan.Value(routing.NextVar(order)); } + plan_output += "\n"; } } LOG(INFO) << plan_output; @@ -258,7 +288,7 @@ int main(int argc, char** argv) { } // Adding penalty costs to allow skipping orders. - const int64 kPenalty = 100000; + const int64 kPenalty = 10000000; const RoutingModel::NodeIndex kFirstNodeAfterDepot(1); for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; order < routing.nodes(); ++order) { @@ -266,6 +296,22 @@ int main(int argc, char** argv) { routing.AddDisjunction(orders, kPenalty); } + // Adding same vehicle constraint costs for consecutive nodes. + if (FLAGS_vrp_use_same_vehicle_costs) { + std::vector group; + for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot; + order < routing.nodes(); ++order) { + group.push_back(order); + 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). const Assignment* solution = routing.Solve(); if (solution != NULL) { diff --git a/examples/cpp/cvrptw_with_refueling.cc b/examples/cpp/cvrptw_with_refueling.cc index 9746e827b5..5b7f2c9885 100644 --- a/examples/cpp/cvrptw_with_refueling.cc +++ b/examples/cpp/cvrptw_with_refueling.cc @@ -19,7 +19,7 @@ // must visit certain nodes (refueling nodes) before the quantity of fuel // reaches zero. Fuel consumption is proportional to the distance traveled. -#include "base/unique_ptr.h" +#include #include #include "base/callback.h" diff --git a/examples/cpp/cvrptw_with_resources.cc b/examples/cpp/cvrptw_with_resources.cc index 588d57dda1..8c3e4cb4fe 100644 --- a/examples/cpp/cvrptw_with_resources.cc +++ b/examples/cpp/cvrptw_with_resources.cc @@ -21,7 +21,7 @@ // empty routes; fix this when we have an API on the cumulative constraints // with variable demands. -#include "base/unique_ptr.h" +#include #include #include "base/callback.h" diff --git a/examples/cpp/cvrptw_with_stop_times_and_resources.cc b/examples/cpp/cvrptw_with_stop_times_and_resources.cc index 24a46490d5..b0b6fa8c27 100644 --- a/examples/cpp/cvrptw_with_stop_times_and_resources.cc +++ b/examples/cpp/cvrptw_with_stop_times_and_resources.cc @@ -19,7 +19,7 @@ // limits the number of vehicles which can simultaneously leave or enter a node // to one. -#include "base/unique_ptr.h" +#include #include #include "base/callback.h" @@ -312,13 +312,11 @@ int main(int argc, char** argv) { // 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( - NewPermanentCallback(&locations, - &LocationContainer::SameLocationFromIndex, - order), - routing.NextVar(order))->Var(); - solver->AddConstraint(solver->MakeNonEquality(interval->PerformedExpr(), - 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. diff --git a/examples/cpp/model_util.cc b/examples/cpp/model_util.cc index 32aa44e367..143acca803 100644 --- a/examples/cpp/model_util.cc +++ b/examples/cpp/model_util.cc @@ -12,7 +12,7 @@ // limitations under the License. -#include "base/unique_ptr.h" +#include #include "base/commandlineflags.h" #include "base/commandlineflags.h" diff --git a/examples/cpp/multidim_knapsack.cc b/examples/cpp/multidim_knapsack.cc index 2a62b84b65..04fb263566 100644 --- a/examples/cpp/multidim_knapsack.cc +++ b/examples/cpp/multidim_knapsack.cc @@ -252,14 +252,14 @@ class MultiDimKnapsackData { int problem_type_; // -1 = undefined, 0 = original, 1 = new format }; -int64 EvaluateItem(MultiDimKnapsackData* const data, int64 var, int64 val) { +int64 EvaluateItem(const MultiDimKnapsackData& data, int64 var, int64 val) { if (val == 0) { return 0LL; } - const int profit = data->profit(var); + const int profit = data.profit(var); int max_weight = 0; - for (int i = 0; i < data->dims(); ++i) { - const int weight = data->weight(i, var); + for (int i = 0; i < data.dims(); ++i) { + const int weight = data.weight(i, var); if (weight > max_weight) { max_weight = weight; } @@ -297,8 +297,9 @@ void SolveKnapsack(MultiDimKnapsackData* const data) { monitors.push_back(search_log); } DecisionBuilder* const db = - solver.MakePhase(assign, NewPermanentCallback(&EvaluateItem, data), - Solver::CHOOSE_STATIC_GLOBAL_BEST); + solver.MakePhase(assign, [data](int64 var, int64 value) { + return EvaluateItem(*data, var, value); + }, Solver::CHOOSE_STATIC_GLOBAL_BEST); if (FLAGS_time_limit_in_ms != 0) { LOG(INFO) << "adding time limit of " << FLAGS_time_limit_in_ms << " ms"; SearchLimit* const limit = solver.MakeLimit( diff --git a/examples/cpp/network_routing.cc b/examples/cpp/network_routing.cc index 4080a08010..714b2f8755 100644 --- a/examples/cpp/network_routing.cc +++ b/examples/cpp/network_routing.cc @@ -675,13 +675,13 @@ class NetworkRoutingSolver { static const int kOneThousand = 1000; - int64 EvaluateMarginalCost(std::vector* path_costs, int64 var, + int64 EvaluateMarginalCost(const std::vector& path_costs, int64 var, int64 val) { int64 best_cost = 0; const int64 traffic = demands_array_[var].traffic; const OnePath& path = all_paths_[var][val]; for (const int arc : path) { - const int64 current_percent = (*path_costs)[arc]->Min(); + const int64 current_percent = path_costs[arc]->Min(); const int64 current_capacity = arc_capacity_[arc]; const int64 expected_percent = current_percent + traffic * kOneThousand / current_capacity; @@ -706,8 +706,7 @@ class NetworkRoutingSolver { virtual ~ApplyMaxDiscrepancy() {} virtual Decision* Next(Solver* const solver) { - solver->SetBranchSelector( - NewPermanentCallback(&NetworkRoutingSolver::MaxDiscrepancy1)); + solver->SetBranchSelector([solver]() { return MaxDiscrepancy1(solver); }); return NULL; } @@ -802,10 +801,11 @@ class NetworkRoutingSolver { } // DecisionBuilder. - DecisionBuilder* const db = solver.MakePhase( - decision_vars, Solver::CHOOSE_RANDOM, - NewPermanentCallback(this, &NetworkRoutingSolver::EvaluateMarginalCost, - &usage_costs)); + DecisionBuilder* const db = + solver.MakePhase(decision_vars, Solver::CHOOSE_RANDOM, + [this, &usage_costs](int64 var, int64 value) { + return EvaluateMarginalCost(usage_costs, var, value); + }); // Limits. if (time_limit != 0 || fail_limit != 0) { @@ -832,10 +832,11 @@ class NetworkRoutingSolver { actual_usage_costs)); SearchLimit* const lns_limit = solver.MakeLimit(kint64max, kint64max, FLAGS_lns_limit, kint64max); - DecisionBuilder* const inner_db = solver.MakePhase( - decision_vars, Solver::CHOOSE_RANDOM, - NewPermanentCallback(this, &NetworkRoutingSolver::EvaluateMarginalCost, - &usage_costs)); + DecisionBuilder* const inner_db = + solver.MakePhase(decision_vars, Solver::CHOOSE_RANDOM, + [this, &usage_costs](int64 var, int64 value) { + return EvaluateMarginalCost(usage_costs, var, value); + }); DecisionBuilder* const apply = solver.RevAlloc(new ApplyMaxDiscrepancy); DecisionBuilder* const max_discrepency_db = solver.Compose(apply, inner_db); diff --git a/examples/cpp/parse_dimacs_assignment.h b/examples/cpp/parse_dimacs_assignment.h index c453e50459..c5a151e5ca 100644 --- a/examples/cpp/parse_dimacs_assignment.h +++ b/examples/cpp/parse_dimacs_assignment.h @@ -22,7 +22,7 @@ #include #include #include -#include "base/unique_ptr.h" +#include #include #include "base/callback.h" diff --git a/examples/cpp/strawberry_fields_with_column_generation.cc b/examples/cpp/strawberry_fields_with_column_generation.cc index 3bed4cc0ce..43f95f4625 100644 --- a/examples/cpp/strawberry_fields_with_column_generation.cc +++ b/examples/cpp/strawberry_fields_with_column_generation.cc @@ -55,7 +55,7 @@ #include #include // strlen #include -#include "base/unique_ptr.h" +#include #include #include #include diff --git a/examples/cpp/tsp.cc b/examples/cpp/tsp.cc index 7fd0ba93c3..33244b2966 100644 --- a/examples/cpp/tsp.cc +++ b/examples/cpp/tsp.cc @@ -25,7 +25,7 @@ // Optionally one can randomly forbid a set of random connections between nodes // (forbidden arcs). -#include "base/unique_ptr.h" +#include #include "base/callback.h" #include "base/commandlineflags.h" diff --git a/examples/csharp/TaskScheduling.cs b/examples/csharp/TaskScheduling.cs index 98d9c4d039..0ff7c25ba4 100644 --- a/examples/csharp/TaskScheduling.cs +++ b/examples/csharp/TaskScheduling.cs @@ -34,6 +34,14 @@ class Task { } } +class Prefix : VoidToString +{ + public override string Run() + { + return "[TaskScheduling] "; + } +} + class TaskScheduling { public static List myJobList = new List(); public static Dictionary> tasksToEquipment = @@ -176,8 +184,9 @@ class TaskScheduling { DecisionBuilder main_phase = solver.Compose(sequence_phase, objective_phase); const int kLogFrequency = 1000000; + VoidToString prefix = new Prefix(); SearchMonitor search_log = - solver.MakeSearchLog(kLogFrequency, objective_monitor); + solver.MakeSearchLog(kLogFrequency, objective_monitor, prefix); SolutionCollector collector = solver.MakeLastSolutionCollector(); collector.Add(all_seq.ToArray()); diff --git a/makefiles/Makefile.csharp.mk b/makefiles/Makefile.csharp.mk index 22ac85ea10..152992f2ea 100644 --- a/makefiles/Makefile.csharp.mk +++ b/makefiles/Makefile.csharp.mk @@ -151,6 +151,7 @@ $(GEN_DIR)/constraint_solver/constraint_solver_csharp_wrap.cc: \ $(SRC_DIR)/constraint_solver/csharp/constraint_solver.swig \ $(SRC_DIR)/base/base.swig \ $(SRC_DIR)/util/csharp/data.swig \ + $(SRC_DIR)/util/csharp/functions.swig \ $(SRC_DIR)/constraint_solver/constraint_solver.h $(SWIG_BINARY) $(SWIG_INC) -I$(INC_DIR) -c++ -csharp -o $(GEN_DIR)$Sconstraint_solver$Sconstraint_solver_csharp_wrap.cc -module operations_research_constraint_solver -namespace $(CLR_DLL_NAME).ConstraintSolver -dllimport "$(CLR_DLL_NAME).$(DYNAMIC_SWIG_LIB_SUFFIX)" -outdir $(GEN_DIR)$Scom$Sgoogle$Sortools$Sconstraintsolver $(SRC_DIR)$Sconstraint_solver$Scsharp$Srouting.swig $(SED) -i -e 's/CSharp_new_Solver/CSharp_new_CpSolver/g' $(GEN_DIR)/com/google/ortools/constraintsolver/*cs $(GEN_DIR)/constraint_solver/constraint_solver_csharp_wrap.* diff --git a/src/algorithms/dynamic_permutation.h b/src/algorithms/dynamic_permutation.h index d3ebdd04f2..1228c23a89 100644 --- a/src/algorithms/dynamic_permutation.h +++ b/src/algorithms/dynamic_permutation.h @@ -14,7 +14,7 @@ #ifndef OR_TOOLS_ALGORITHMS_DYNAMIC_PERMUTATION_H_ #define OR_TOOLS_ALGORITHMS_DYNAMIC_PERMUTATION_H_ -#include "base/unique_ptr.h" +#include #include // TODO(user): remove when no longer used. #include diff --git a/src/algorithms/find_graph_symmetries.h b/src/algorithms/find_graph_symmetries.h index 527652ecac..402cccc6d6 100644 --- a/src/algorithms/find_graph_symmetries.h +++ b/src/algorithms/find_graph_symmetries.h @@ -16,7 +16,7 @@ #ifndef OR_TOOLS_ALGORITHMS_FIND_GRAPH_SYMMETRIES_H_ #define OR_TOOLS_ALGORITHMS_FIND_GRAPH_SYMMETRIES_H_ -#include "base/unique_ptr.h" +#include #include #include "algorithms/dynamic_partition.h" diff --git a/src/algorithms/knapsack_solver.h b/src/algorithms/knapsack_solver.h index 3ec71b081f..931b1fbb97 100644 --- a/src/algorithms/knapsack_solver.h +++ b/src/algorithms/knapsack_solver.h @@ -58,7 +58,7 @@ #define OR_TOOLS_ALGORITHMS_KNAPSACK_SOLVER_H_ #include -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/base/base.swig b/src/base/base.swig index d609b3f395..8da0e5957b 100644 --- a/src/base/base.swig +++ b/src/base/base.swig @@ -81,7 +81,7 @@ class std::string; if (PyString_AsStringAndSize($input, &buf, &len) == -1) return NULL; $1 = std::string(buf, len); - } +} %typemap(in) const std::string & (std::string temp) { char * buf; @@ -90,30 +90,30 @@ class std::string; return NULL; temp = std::string(buf, len); $1 = &temp; - } +} %typemap(out) std::string { $result = PyString_FromStringAndSize($1.data(), $1.size()); - } +} %typemap(out) const std::string & { $result = PyString_FromStringAndSize($1->data(), $1->size()); - } +} %typemap(in, numinputs = 0) std::string * OUTPUT (std::string temp) { temp = std::string(); $1 = &temp; - } +} %typemap(argout, fragment = "t_output_helper") std::string * OUTPUT { $result = t_output_helper( $result, PyString_FromStringAndSize($1->data(), $1->length())); - } +} %typemap(varout) std::string { $result = PyString_FromStringAndSize($1.data(), $1.size()); - } +} %apply const std::string & {std::string &}; %apply const std::string & {std::string *}; diff --git a/src/base/file.cc b/src/base/file.cc index d9ecd4a36c..4c1cf207c9 100644 --- a/src/base/file.cc +++ b/src/base/file.cc @@ -22,11 +22,11 @@ #endif #include +#include #include #include "base/file.h" #include "base/logging.h" -#include "base/unique_ptr.h" #include "base/join.h" namespace operations_research { diff --git a/src/base/filelinereader.cc b/src/base/filelinereader.cc index 6a12d0076e..e503a2e350 100644 --- a/src/base/filelinereader.cc +++ b/src/base/filelinereader.cc @@ -14,11 +14,11 @@ #include "base/filelinereader.h" #include +#include #include #include "base/file.h" #include "base/logging.h" -#include "base/unique_ptr.h" namespace operations_research { FileLineReader::FileLineReader(const char* const filename) diff --git a/src/base/filelinereader.h b/src/base/filelinereader.h index 626ff17abb..76a6bb26c5 100644 --- a/src/base/filelinereader.h +++ b/src/base/filelinereader.h @@ -16,12 +16,12 @@ #include #include +#include #include #include "base/callback.h" #include "base/integral_types.h" #include "base/file.h" -#include "base/unique_ptr.h" namespace operations_research { // The FileLineReader class will read a text file specified by diff --git a/src/base/recordio.cc b/src/base/recordio.cc index 6b50cf35f4..a34ea3b0d0 100644 --- a/src/base/recordio.cc +++ b/src/base/recordio.cc @@ -12,10 +12,10 @@ // limitations under the License. #include +#include #include #include "base/logging.h" #include "base/recordio.h" -#include "base/unique_ptr.h" namespace operations_research { const int RecordWriter::kMagicNumber = 0x3ed7230a; diff --git a/src/base/recordio.h b/src/base/recordio.h index 6045c7abf1..89a66904f9 100644 --- a/src/base/recordio.h +++ b/src/base/recordio.h @@ -14,9 +14,9 @@ #ifndef OR_TOOLS_BASE_RECORDIO_H_ #define OR_TOOLS_BASE_RECORDIO_H_ +#include #include #include "base/file.h" -#include "base/unique_ptr.h" // This file defines some IO interfaces to compatible with Google // IO specifications. diff --git a/src/base/unique_ptr.h b/src/base/unique_ptr.h deleted file mode 100644 index 3271bfb3d2..0000000000 --- a/src/base/unique_ptr.h +++ /dev/null @@ -1,208 +0,0 @@ -// 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. - -#ifndef OR_TOOLS_BASE_UNIQUE_PTR_H_ -#define OR_TOOLS_BASE_UNIQUE_PTR_H_ - -#include - -#if defined(__APPLE__) && defined(__GLIBCXX__) -#include -#include "base/macros.h" -namespace std { -// A unique_ptr is like a T*, except that the destructor of unique_ptr -// automatically deletes the pointer it holds (if any). -// That is, unique_ptr owns the T object that it points to. -// Like a T*, a unique_ptr may hold either NULL or a pointer to a T object. -// -// The size of a unique_ptr is small: -// sizeof(unique_ptr) == sizeof(C*) -template -class unique_ptr { - public: - // The element type - typedef C element_type; - - // Constructor. Defaults to intializing with NULL. - // There is no way to create an uninitialized unique_ptr. - // The input parameter must be allocated with new. - explicit unique_ptr(C* p = NULL) : ptr_(p) {} - - // Destructor. If there is a C object, delete it. - // We don't need to test ptr_ == NULL because C++ does that for us. - ~unique_ptr() { - enum { - type_must_be_complete = sizeof(C) - }; - delete ptr_; - } - - // Reset. Deletes the current owned object, if any. - // Then takes ownership of a new object, if given. - // this->reset(this->get()) works. - void reset(C* p = NULL) { - if (p != ptr_) { - enum { - type_must_be_complete = sizeof(C) - }; - delete ptr_; - ptr_ = p; - } - } - - // Accessors to get the owned object. - // operator* and operator-> will assert() if there is no current object. - C& operator*() const { - assert(ptr_ != NULL); - return *ptr_; - } - C* operator->() const { - assert(ptr_ != NULL); - return ptr_; - } - C* get() const { return ptr_; } - - // Comparison operators. - // These return whether two unique_ptr refer to the same object, not just to - // two different but equal objects. - bool operator==(C* p) const { return ptr_ == p; } - bool operator!=(C* p) const { return ptr_ != p; } - - // Swap two scoped pointers. - void swap(unique_ptr& p2) { - C* tmp = ptr_; - ptr_ = p2.ptr_; - p2.ptr_ = tmp; - } - - // Release a pointer. - // The return value is the current pointer held by this object. - // If this object holds a NULL pointer, the return value is NULL. - // After this operation, this object will hold a NULL pointer, - // and will not own the object any more. - C* release() { - C* retVal = ptr_; - ptr_ = NULL; - return retVal; - } - - private: - C* ptr_; - - // Forbid comparison of unique_ptr types. If C2 != C, it totally doesn't - // make sense, and if C2 == C, it still doesn't make sense because you should - // never have the same object owned by two different unique_ptrs. - template - bool operator==(unique_ptr const& p2) const; - template - bool operator!=(unique_ptr const& p2) const; - - DISALLOW_COPY_AND_ASSIGN(unique_ptr); -}; - -// Free functions -template -void swap(unique_ptr& p1, unique_ptr& p2) { - p1.swap(p2); -} - -template -bool operator==(C* p1, const unique_ptr& p2) { - return p1 == p2.get(); -} - -template -bool operator!=(C* p1, const unique_ptr& p2) { - return p1 != p2.get(); -} - -// Specialization of unique_ptr used for holding arrays: -// -// unique_ptr array(new int[10]); -// -// This specialization provides operator[] instead of operator* and -// operator->, and by default it deletes the stored array using 'delete[]' -// rather than 'delete'. -template -class unique_ptr { - public: - // The element type - typedef C element_type; - - // Constructor. Defaults to intializing with NULL. - // There is no way to create an uninitialized unique_ptr. - // The input parameter must be allocated with new. - explicit unique_ptr(C* p = NULL) : ptr_(p) {} - - // Destructor. If there is a C object, delete it. - // We don't need to test ptr_ == NULL because C++ does that for us. - ~unique_ptr() { - enum { - type_must_be_complete = sizeof(C) - }; - delete[] ptr_; - } - - // Reset. Deletes the current owned object, if any. - // Then takes ownership of a new object, if given. - // this->reset(this->get()) works. - void reset(C* p = NULL) { - if (p != ptr_) { - enum { - type_must_be_complete = sizeof(C) - }; - delete[] ptr_; - ptr_ = p; - } - } - - // Accessors to get the owned object. - // operator[] and operator-> will assert() if there is no current object. - C& operator[](size_t i) const { - assert(ptr_ != NULL); - return ptr_[i]; - } - - C* get() const { return ptr_; } - - // Comparison operators. - // These return whether two unique_ptr refer to the same object, not just to - // two different but equal objects. - bool operator==(C* p) const { return ptr_ == p; } - bool operator!=(C* p) const { return ptr_ != p; } - - // Swap two scoped pointers. - void swap(unique_ptr& p2) { - C* tmp = ptr_; - ptr_ = p2.ptr_; - p2.ptr_ = tmp; - } - - // Release a pointer. - // The return value is the current pointer held by this object. - // If this object holds a NULL pointer, the return value is NULL. - // After this operation, this object will hold a NULL pointer, - // and will not own the object any more. - C* release() { - C* retVal = ptr_; - ptr_ = NULL; - return retVal; - } - - private: - C* ptr_; - DISALLOW_COPY_AND_ASSIGN(unique_ptr); -}; -} // namespace std -#endif // defined(__APPLE__) && defined(__GLIBCXX__) -#endif // OR_TOOLS_BASE_UNIQUE_PTR_H_ diff --git a/src/bop/bop_base.cc b/src/bop/bop_base.cc index 3a0e48c646..d3ce1a9a3f 100644 --- a/src/bop/bop_base.cc +++ b/src/bop/bop_base.cc @@ -26,8 +26,6 @@ namespace bop { BopOptimizerBase::BopOptimizerBase(const std::string& name) : name_(name), - local_time_limit_in_seconds_(std::numeric_limits::infinity()), - local_deterministic_time_limit_(std::numeric_limits::infinity()), stats_(name) { SCOPED_TIME_STAT(&stats_); } @@ -59,22 +57,6 @@ std::string BopOptimizerBase::GetStatusString(Status status) { return "UNKNOWN Status"; } -void BopOptimizerBase::SetLocalTimeLimits(double in_seconds, - double deterministic) { - local_time_limit_in_seconds_ = in_seconds; - local_deterministic_time_limit_ = deterministic; -} - -double BopOptimizerBase::LocalTimeLimitInSeconds(TimeLimit* time_limit) const { - return std::min(time_limit->GetTimeLeft(), local_time_limit_in_seconds_); -} - -double BopOptimizerBase::LocalDeterministicTimeLimit( - TimeLimit* time_limit) const { - return std::min(time_limit->GetDeterministicTimeLeft(), - local_deterministic_time_limit_); -} - //------------------------------------------------------------------------------ // ProblemState //------------------------------------------------------------------------------ diff --git a/src/bop/bop_base.h b/src/bop/bop_base.h index 1530342b7a..e0460b479b 100644 --- a/src/bop/bop_base.h +++ b/src/bop/bop_base.h @@ -93,20 +93,8 @@ class BopOptimizerBase { // Returns a std::string describing the status. static std::string GetStatusString(Status status); - // Sets the local time limits for each call to Optimize(). If the TimeLimit - // passed to Optimize() is lower than that, then it will be used instead. Note - // that the later always represents the global problem time limit. - void SetLocalTimeLimits(double in_seconds, double deterministic); - protected: - // Utility functions that return the min between the local limits and the - // corresponding time left in the passed TimeLimit object. - double LocalTimeLimitInSeconds(TimeLimit* time_limit) const; - double LocalDeterministicTimeLimit(TimeLimit* time_limit) const; - const std::string name_; - double local_time_limit_in_seconds_; - double local_deterministic_time_limit_; mutable StatsGroup stats_; }; diff --git a/src/bop/bop_fs.cc b/src/bop/bop_fs.cc index 14e9b6eca9..5247289c83 100644 --- a/src/bop/bop_fs.cc +++ b/src/bop/bop_fs.cc @@ -151,9 +151,8 @@ BopOptimizerBase::Status GuidedSatFirstSolutionGenerator::Optimize( if (sync_status != BopOptimizerBase::CONTINUE) return sync_status; sat::SatParameters sat_params; - sat_params.set_max_time_in_seconds(LocalTimeLimitInSeconds(time_limit)); - sat_params.set_max_deterministic_time( - LocalDeterministicTimeLimit(time_limit)); + sat_params.set_max_time_in_seconds(time_limit->GetTimeLeft()); + sat_params.set_max_deterministic_time(time_limit->GetDeterministicTimeLeft()); // We use a relatively small conflict limit so that other optimizer get a // chance to run if this one is slow. Note that if this limit is reached, we @@ -220,10 +219,6 @@ BopOptimizerBase::Status BopRandomFirstSolutionGenerator::Optimize( const std::vector> saved_prefs = sat_propagator_->AllPreferences(); - // We want to check both the local time limit and the global time limit, as - // during first solution phase the local time limit can be more restrictive. - TimeLimit local_time_limit(LocalTimeLimitInSeconds(time_limit), - LocalDeterministicTimeLimit(time_limit)); const int kMaxNumConflicts = 10; int64 best_cost = problem_state.solution().IsFeasible() ? problem_state.solution().GetCost() @@ -237,7 +232,7 @@ BopOptimizerBase::Status BopRandomFirstSolutionGenerator::Optimize( bool objective_need_to_be_overconstrained = (best_cost != kint64max); bool solution_found = false; - while (remaining_num_conflicts > 0 && !local_time_limit.LimitReached()) { + while (remaining_num_conflicts > 0 && !time_limit->LimitReached()) { ++sat_seed_; sat_propagator_->Backtrack(0); old_num_failures = sat_propagator_->num_failures(); @@ -278,7 +273,7 @@ BopOptimizerBase::Status BopRandomFirstSolutionGenerator::Optimize( } const sat::SatSolver::Status sat_status = - sat_propagator_->SolveWithTimeLimit(&local_time_limit); + sat_propagator_->SolveWithTimeLimit(time_limit); if (sat_status == sat::SatSolver::MODEL_SAT) { objective_need_to_be_overconstrained = true; solution_found = true; @@ -318,8 +313,6 @@ BopOptimizerBase::Status BopRandomFirstSolutionGenerator::Optimize( } ExtractLearnedInfoFromSatSolver(sat_propagator_, learned_info); - time_limit->AdvanceDeterministicTime( - local_time_limit.GetElapsedDeterministicTime()); return solution_found ? BopOptimizerBase::SOLUTION_FOUND : BopOptimizerBase::LIMIT_REACHED; @@ -340,7 +333,9 @@ LinearRelaxation::LinearRelaxation(const BopParameters& parameters, offset_(0), num_fixed_variables_(-1), problem_already_solved_(false), - scaled_solution_cost_(glop::kInfinity) {} + scaled_solution_cost_(glop::kInfinity), + deterministic_time_limit_( + parameters.initial_lp_max_deterministic_time()) {} LinearRelaxation::~LinearRelaxation() {} @@ -493,15 +488,29 @@ glop::ProblemStatus LinearRelaxation::Solve(bool incremental_solve, glop_params.set_allow_simplex_algorithm_change(true); glop_params.set_use_preprocessing(false); } - glop_params.set_max_time_in_seconds(LocalTimeLimitInSeconds(time_limit)); + // Due to the way the deterministic time works in Glop, an existing lp_solver + // might have an initial non zero deterministic time even when a new problem + // is loaded. + const double initial_deterministic_time = lp_solver_.DeterministicTime(); + glop_params.set_max_time_in_seconds(time_limit->GetTimeLeft()); glop_params.set_max_deterministic_time( - LocalDeterministicTimeLimit(time_limit)); + initial_deterministic_time + + std::min(time_limit->GetDeterministicTimeLeft(), + deterministic_time_limit_)); lp_solver_.SetParameters(glop_params); - const double initial_deterministic_time = lp_solver_.DeterministicTime(); const glop::ProblemStatus lp_status = lp_solver_.Solve(lp_model_); - time_limit->AdvanceDeterministicTime(lp_solver_.DeterministicTime() - - initial_deterministic_time); + const double elapsed_deterministic_time = + lp_solver_.DeterministicTime() - initial_deterministic_time; + time_limit->AdvanceDeterministicTime(elapsed_deterministic_time); + + // Double the deterministic time limit at each iteration until an optimal + // solution is found. + if (lp_status != glop::ProblemStatus::OPTIMAL && + deterministic_time_limit_ <= elapsed_deterministic_time) { + deterministic_time_limit_ *= 2.0; + } + return lp_status; } diff --git a/src/bop/bop_fs.h b/src/bop/bop_fs.h index 8e500313e8..19e82b3cde 100644 --- a/src/bop/bop_fs.h +++ b/src/bop/bop_fs.h @@ -148,6 +148,7 @@ class LinearRelaxation : public BopOptimizerBase { int num_fixed_variables_; bool problem_already_solved_; double scaled_solution_cost_; + double deterministic_time_limit_; }; } // namespace bop diff --git a/src/bop/bop_lns.cc b/src/bop/bop_lns.cc index e4ec9287bd..be4921723e 100644 --- a/src/bop/bop_lns.cc +++ b/src/bop/bop_lns.cc @@ -140,9 +140,8 @@ BopOptimizerBase::Status BopCompleteLNSOptimizer::Optimize( sat::SatParameters sat_params; sat_params.set_max_number_of_conflicts( parameters.max_number_of_conflicts_in_random_lns()); - sat_params.set_max_time_in_seconds(LocalTimeLimitInSeconds(time_limit)); - sat_params.set_max_deterministic_time( - LocalDeterministicTimeLimit(time_limit)); + sat_params.set_max_time_in_seconds(time_limit->GetTimeLeft()); + sat_params.set_max_deterministic_time(time_limit->GetDeterministicTimeLeft()); sat_solver_->SetParameters(sat_params); const sat::SatSolver::Status sat_status = sat_solver_->Solve(); @@ -167,8 +166,8 @@ BopOptimizerBase::Status BopCompleteLNSOptimizer::Optimize( namespace { // Returns false if the limit is reached while solving the LP. bool UseLinearRelaxationForSatAssignmentPreference( - const LinearBooleanProblem& problem, double local_time_limit_in_seconds, - double local_deterministic_time_limit, sat::SatSolver* sat_solver) { + const BopParameters& parameters, const LinearBooleanProblem& problem, + sat::SatSolver* sat_solver, TimeLimit* time_limit) { // TODO(user): Re-use the lp_model and lp_solver or build a model with only // needed constraints and variables. glop::LinearProgram lp_model; @@ -186,10 +185,14 @@ bool UseLinearRelaxationForSatAssignmentPreference( glop::LPSolver lp_solver; glop::GlopParameters params; - params.set_max_time_in_seconds(local_time_limit_in_seconds); - params.set_max_deterministic_time(local_deterministic_time_limit); + params.set_max_time_in_seconds(time_limit->GetTimeLeft()); + params.set_max_deterministic_time( + std::min(time_limit->GetDeterministicTimeLeft(), + parameters.initial_lp_max_deterministic_time())); lp_solver.SetParameters(params); const glop::ProblemStatus lp_status = lp_solver.Solve(lp_model); + time_limit->AdvanceDeterministicTime(lp_solver.DeterministicTime()); + if (lp_status != glop::ProblemStatus::OPTIMAL && lp_status != glop::ProblemStatus::PRIMAL_FEASIBLE && lp_status != glop::ProblemStatus::IMPRECISE) { @@ -241,17 +244,15 @@ BopOptimizerBase::Status BopAdaptiveLNSOptimizer::Optimize( // Set-up a sat_propagator_ cleanup task to catch all the exit cases. const double initial_dt = sat_propagator_->deterministic_time(); - double local_sat_solver_dt = 0.0; auto sat_propagator_cleanup = ::operations_research::util::MakeCleanup( - [initial_dt, this, &learned_info, &time_limit, &local_sat_solver_dt]() { + [initial_dt, this, &learned_info, &time_limit]() { if (!sat_propagator_->IsModelUnsat()) { sat_propagator_->SetAssumptionLevel(0); sat_propagator_->RestoreSolverToAssumptionLevel(); ExtractLearnedInfoFromSatSolver(sat_propagator_, learned_info); } time_limit->AdvanceDeterministicTime( - local_sat_solver_dt + sat_propagator_->deterministic_time() - - initial_dt); + sat_propagator_->deterministic_time() - initial_dt); }); // For the SAT conflicts limit of each LNS, we follow a luby sequence times @@ -294,9 +295,8 @@ BopOptimizerBase::Status BopAdaptiveLNSOptimizer::Optimize( sat::SatParameters params; params.set_max_number_of_conflicts( local_parameters.max_number_of_conflicts_for_quick_check()); - params.set_max_time_in_seconds(LocalTimeLimitInSeconds(time_limit)); - params.set_max_deterministic_time( - LocalDeterministicTimeLimit(time_limit)); + params.set_max_time_in_seconds(time_limit->GetTimeLeft()); + params.set_max_deterministic_time(time_limit->GetDeterministicTimeLeft()); sat_propagator_->SetParameters(params); sat_propagator_->SetAssumptionLevel( sat_propagator_->CurrentDecisionLevel()); @@ -335,8 +335,8 @@ BopOptimizerBase::Status BopAdaptiveLNSOptimizer::Optimize( sat::SatParameters params; params.set_max_number_of_conflicts(conflict_limit); - params.set_max_time_in_seconds(LocalTimeLimitInSeconds(time_limit)); - params.set_max_deterministic_time(LocalDeterministicTimeLimit(time_limit)); + params.set_max_time_in_seconds(time_limit->GetTimeLeft()); + params.set_max_deterministic_time(time_limit->GetDeterministicTimeLeft()); sat::SatSolver sat_solver; sat_solver.SetParameters(params); @@ -361,12 +361,8 @@ BopOptimizerBase::Status BopAdaptiveLNSOptimizer::Optimize( } if (use_lp_to_guide_sat_) { - // Note that we use a lower time limit for the relaxation so that we - // still have some time to exploit what this is returning. - const double ratio = 0.5; if (!UseLinearRelaxationForSatAssignmentPreference( - problem, ratio * LocalTimeLimitInSeconds(time_limit), - ratio * LocalDeterministicTimeLimit(time_limit), &sat_solver)) { + parameters, problem, &sat_solver, time_limit)) { return BopOptimizerBase::LIMIT_REACHED; } } else { @@ -383,7 +379,7 @@ BopOptimizerBase::Status BopAdaptiveLNSOptimizer::Optimize( // Solve the local problem. const sat::SatSolver::Status status = sat_solver.Solve(); - local_sat_solver_dt += sat_solver.deterministic_time(); + time_limit->AdvanceDeterministicTime(sat_solver.deterministic_time()); if (status == sat::SatSolver::MODEL_SAT) { // We found a solution! abort now. SatAssignmentToBopSolution(sat_solver.Assignment(), @@ -582,7 +578,7 @@ void RelationGraphBasedNeighborhood::GenerateNeighborhood( } if (sat_propagator->IsModelUnsat()) return; } - VLOG(1) << "target:" << target << " relaxed:" << num_relaxed << " actual:" + VLOG(2) << "target:" << target << " relaxed:" << num_relaxed << " actual:" << num_variables - sat_propagator->LiteralTrail().Index(); } diff --git a/src/bop/bop_parameters.proto b/src/bop/bop_parameters.proto index ca2a513371..bfbe444c7d 100644 --- a/src/bop/bop_parameters.proto +++ b/src/bop/bop_parameters.proto @@ -38,18 +38,6 @@ message BopOptimizerMethod { RELATION_GRAPH_LNS_GUIDED_BY_LP = 17; } optional OptimizerType type = 1; - - // The ratio is used to compute the max time the method might spend to look - // for a solution. For instance, with a ratio of 0.05 and a time limit of - // 300 seconds, the method can spend up to 15 seconds. - // - // Note that the LOCAL_SEARCH optimizer doesn't respect this and that some - // optimizers have a conflicts and/or iterations limit which usually result in - // running times a lot lower than this bound. - // - // TODO(user): This is a big source of undeterminism in the solver. Find a - // deterministic way to have a similar behavior. - optional double time_limit_ratio = 3 [default = 0.5]; } // Set of optimizer methods to be run by an instance of the portfolio optimizer. @@ -62,7 +50,7 @@ message BopSolverOptimizerSet { // Contains the definitions for all the bop algorithm parameters and their // default values. // -// NEXT TAG: 37 +// NEXT TAG: 38 message BopParameters { // Maximum time allowed in seconds to solve a problem. // The counter will starts as soon as Solve() is called. @@ -75,6 +63,11 @@ message BopParameters { // is called. optional double max_deterministic_time = 27 [default = inf]; + // Maximum time allowed in deterministic time to solve the LP relaxation at + // first. Note that the time allowed to solve the LP relaxation might increase + // during the search, especially when no solutions have been found. + optional double initial_lp_max_deterministic_time = 37 [default = 1.0]; + // Maximum number of consecutive optimizer calls without improving the // current solution. If this number is reached, the search will be aborted. // Note that this parameter only applies when an initial solution has been @@ -217,20 +210,20 @@ message BopParameters { repeated BopSolverOptimizerSet solver_optimizer_sets = 26; optional string default_solver_optimizer_sets = 33 [default = - "methods:{type:LOCAL_SEARCH } " - "methods:{type:RANDOM_FIRST_SOLUTION } " - "methods:{type:LINEAR_RELAXATION time_limit_ratio:0.15 } " - "methods:{type:LP_FIRST_SOLUTION } " - "methods:{type:OBJECTIVE_FIRST_SOLUTION } " - "methods:{type:USER_GUIDED_FIRST_SOLUTION } " - "methods:{type:RANDOM_CONSTRAINT_LNS_GUIDED_BY_LP } " - "methods:{type:RANDOM_VARIABLE_LNS_GUIDED_BY_LP } " - "methods:{type:RELATION_GRAPH_LNS } " - "methods:{type:RELATION_GRAPH_LNS_GUIDED_BY_LP } " - "methods:{type:RANDOM_CONSTRAINT_LNS } " - "methods:{type:RANDOM_VARIABLE_LNS } " - "methods:{type:SAT_CORE_BASED } " - "methods:{type:COMPLETE_LNS } "]; + "methods:{type:LOCAL_SEARCH } " + "methods:{type:RANDOM_FIRST_SOLUTION } " + "methods:{type:LINEAR_RELAXATION } " + "methods:{type:LP_FIRST_SOLUTION } " + "methods:{type:OBJECTIVE_FIRST_SOLUTION } " + "methods:{type:USER_GUIDED_FIRST_SOLUTION } " + "methods:{type:RANDOM_CONSTRAINT_LNS_GUIDED_BY_LP } " + "methods:{type:RANDOM_VARIABLE_LNS_GUIDED_BY_LP } " + "methods:{type:RELATION_GRAPH_LNS } " + "methods:{type:RELATION_GRAPH_LNS_GUIDED_BY_LP } " + "methods:{type:RANDOM_CONSTRAINT_LNS } " + "methods:{type:RANDOM_VARIABLE_LNS } " + "methods:{type:SAT_CORE_BASED } " + "methods:{type:COMPLETE_LNS } "]; // Use strong branching in the linear relaxation optimizer. // The strong branching is a what-if analysis on each variable v, i.e. diff --git a/src/bop/bop_portfolio.cc b/src/bop/bop_portfolio.cc index 5a1b9f451b..8aa4edd809 100644 --- a/src/bop/bop_portfolio.cc +++ b/src/bop/bop_portfolio.cc @@ -299,15 +299,6 @@ void PortfolioOptimizer::CreateOptimizers( for (const BopOptimizerMethod& optimizer_method : optimizer_set.methods()) { const OptimizerIndex old_size(optimizers_.size()); AddOptimizer(problem, parameters, optimizer_method); - - // Set the local time limits of the newly added optimizers. - // TODO(user): Remove these local time limits. - const double ratio = optimizer_method.time_limit_ratio(); - for (OptimizerIndex i = old_size; i < optimizers_.size(); ++i) { - optimizers_[i]->SetLocalTimeLimits( - ratio * parameters.max_time_in_seconds(), - ratio * parameters.max_deterministic_time()); - } } selector_.reset(new OptimizerSelector(optimizers_)); diff --git a/src/bop/complete_optimizer.cc b/src/bop/complete_optimizer.cc index faf2e1b8e8..01ac27479a 100644 --- a/src/bop/complete_optimizer.cc +++ b/src/bop/complete_optimizer.cc @@ -122,17 +122,13 @@ BopOptimizerBase::Status SatCoreBasedOptimizer::Optimize( return sync_status; } - // We want to check both the local time limit and the global time limit. - TimeLimit local_time_limit(LocalTimeLimitInSeconds(time_limit), - LocalDeterministicTimeLimit(time_limit)); - int64 conflict_limit = parameters.max_number_of_conflicts_in_random_lns(); double deterministic_time_at_last_sync = solver_.deterministic_time(); - while (!local_time_limit.LimitReached()) { + while (!time_limit->LimitReached()) { sat::SatParameters sat_params = solver_.parameters(); - sat_params.set_max_time_in_seconds(local_time_limit.GetTimeLeft()); + sat_params.set_max_time_in_seconds(time_limit->GetTimeLeft()); sat_params.set_max_deterministic_time( - local_time_limit.GetDeterministicTimeLeft()); + time_limit->GetDeterministicTimeLeft()); sat_params.set_max_number_of_conflicts(conflict_limit); solver_.SetParameters(sat_params); diff --git a/src/constraint_solver/alldiff_cst.cc b/src/constraint_solver/alldiff_cst.cc index 09cfaa4c72..6958884ade 100644 --- a/src/constraint_solver/alldiff_cst.cc +++ b/src/constraint_solver/alldiff_cst.cc @@ -15,7 +15,7 @@ // AllDifferent constraints #include -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/constraint_solver/constraint_solver.cc b/src/constraint_solver/constraint_solver.cc index 972e64be66..5e5b7fd5cf 100644 --- a/src/constraint_solver/constraint_solver.cc +++ b/src/constraint_solver/constraint_solver.cc @@ -19,7 +19,7 @@ #include #include -#include "base/unique_ptr.h" +#include #include #include "base/callback.h" @@ -680,8 +680,8 @@ class CompressedTrail { const int block_size_; Block* blocks_; Block* free_blocks_; - std::unique_ptr []> data_; - std::unique_ptr []> buffer_; + std::unique_ptr[]> data_; + std::unique_ptr[]> buffer_; bool buffer_used_; int current_; int size_; @@ -993,8 +993,7 @@ class Search { void set_created_by_solve(bool c) { created_by_solve_ = c; } bool created_by_solve() const { return created_by_solve_; } Solver::DecisionModification ModifyDecision(); - void SetBranchSelector( - ResultCallback1* const s); + void SetBranchSelector(Solver::BranchSelector s); void LeftMove() { search_depth_++; left_search_depth_++; @@ -1036,8 +1035,7 @@ class Search { int64 solution_counter_; DecisionBuilder* decision_builder_; bool created_by_solve_; - std::unique_ptr > - selector_; + Solver::BranchSelector selector_; int search_depth_; int left_search_depth_; bool should_restart_; @@ -1108,9 +1106,7 @@ class UndoBranchSelector : public Action { class ApplyBranchSelector : public DecisionBuilder { public: - explicit ApplyBranchSelector( - ResultCallback1* const bs) - : selector_(bs) {} + explicit ApplyBranchSelector(Solver::BranchSelector bs) : selector_(bs) {} ~ApplyBranchSelector() override {} Decision* Next(Solver* const s) override { @@ -1121,21 +1117,13 @@ class ApplyBranchSelector : public DecisionBuilder { std::string DebugString() const override { return "Apply(BranchSelector)"; } private: - ResultCallback1* const selector_; + Solver::BranchSelector const selector_; }; } // namespace -void Search::SetBranchSelector( - ResultCallback1* const bs) { - CHECK(bs == selector_.get() || selector_ == nullptr || bs == nullptr); - if (selector_.get() != bs) { - selector_.reset(bs); - } -} +void Search::SetBranchSelector(Solver::BranchSelector bs) { selector_ = bs; } -void Solver::SetBranchSelector( - ResultCallback1* const bs) { - bs->CheckIsRepeatable(); +void Solver::SetBranchSelector(BranchSelector bs) { // We cannot use the trail as the search can be nested and thus // deleted upon backtrack. Thus we guard the undo action by a // check on the number of nesting of solve(). @@ -1143,8 +1131,7 @@ void Solver::SetBranchSelector( searches_.back()->SetBranchSelector(bs); } -DecisionBuilder* Solver::MakeApplyBranchSelector( - ResultCallback1* const bs) { +DecisionBuilder* Solver::MakeApplyBranchSelector(BranchSelector bs) { return RevAlloc(new ApplyBranchSelector(bs)); } @@ -1160,7 +1147,7 @@ int Solver::SearchLeftDepth() const { Solver::DecisionModification Search::ModifyDecision() { if (selector_ != nullptr) { - return selector_->Run(solver_); + return selector_(); } return Solver::NO_CHANGE; } @@ -1175,7 +1162,7 @@ void Search::Clear() { monitors_.clear(); search_depth_ = 0; left_search_depth_ = 0; - selector_.reset(nullptr); + selector_ = nullptr; backtrack_at_the_end_of_the_search_ = true; } @@ -2231,7 +2218,9 @@ bool Solver::NextSolution() { search->AfterDecision(d, false); break; } - case KILL_BOTH: { Fail(); } + case KILL_BOTH: { + Fail(); + } } } else { break; @@ -2515,8 +2504,7 @@ std::ostream& operator<<(std::ostream& out, const Solver* const s) { return out; } -std::ostream& operator<<(std::ostream& out, - const BaseObject* const o) { +std::ostream& operator<<(std::ostream& out, const BaseObject* const o) { out << o->DebugString(); return out; } @@ -2836,31 +2824,27 @@ void ModelVisitor::VisitSequenceArrayArgument( // ----- Helpers ----- -void ModelVisitor::VisitInt64ToBoolExtension( - ResultCallback1* const callback, int64 index_min, - int64 index_max) { - if (callback == nullptr) { - return; +void ModelVisitor::VisitInt64ToBoolExtension(Solver::IndexFilter1 filter, + int64 index_min, int64 index_max) { + if (filter != nullptr) { + std::vector cached_results; + for (int i = index_min; i <= index_max; ++i) { + cached_results.push_back(filter(i)); + } + BeginVisitExtension(kInt64ToBoolExtension); + VisitIntegerArgument(kMinArgument, index_min); + VisitIntegerArgument(kMaxArgument, index_max); + VisitIntegerArrayArgument(kValuesArgument, cached_results); + EndVisitExtension(kInt64ToBoolExtension); } - std::vector cached_results; - for (int i = index_min; i <= index_max; ++i) { - cached_results.push_back(callback->Run(i)); - } - BeginVisitExtension(kInt64ToBoolExtension); - VisitIntegerArgument(kMinArgument, index_min); - VisitIntegerArgument(kMaxArgument, index_max); - VisitIntegerArrayArgument(kValuesArgument, cached_results); - EndVisitExtension(kInt64ToBoolExtension); } void ModelVisitor::VisitInt64ToInt64Extension( - Solver::IndexEvaluator1* const callback, int64 index_min, int64 index_max) { - if (callback == nullptr) { - return; - } + const Solver::IndexEvaluator1& eval, int64 index_min, int64 index_max) { + CHECK(eval != nullptr); std::vector cached_results; for (int i = index_min; i <= index_max; ++i) { - cached_results.push_back(callback->Run(i)); + cached_results.push_back(eval(i)); } BeginVisitExtension(kInt64ToInt64Extension); VisitIntegerArgument(kMinArgument, index_min); @@ -2869,15 +2853,13 @@ void ModelVisitor::VisitInt64ToInt64Extension( EndVisitExtension(kInt64ToInt64Extension); } -void ModelVisitor::VisitInt64ToInt64AsArray( - Solver::IndexEvaluator1* const callback, const std::string& arg_name, - int64 index_max) { - if (callback == nullptr) { - return; - } +void ModelVisitor::VisitInt64ToInt64AsArray(const Solver::IndexEvaluator1& eval, + const std::string& arg_name, + int64 index_max) { + CHECK(eval != nullptr); std::vector cached_results; for (int i = 0; i <= index_max; ++i) { - cached_results.push_back(callback->Run(i)); + cached_results.push_back(eval(i)); } VisitIntegerArrayArgument(arg_name, cached_results); } diff --git a/src/constraint_solver/constraint_solver.h b/src/constraint_solver/constraint_solver.h index d40c369d00..ba8d3815cb 100644 --- a/src/constraint_solver/constraint_solver.h +++ b/src/constraint_solver/constraint_solver.h @@ -65,7 +65,7 @@ #include "base/hash.h" #include "base/hash.h" #include -#include "base/unique_ptr.h" +#include #include #include #include @@ -83,20 +83,7 @@ #include "base/random.h" #include "util/tuple_set.h" -class Closure; class File; -template -class Callback3; -template -class ResultCallback3; -template -class ResultCallback2; -template -class ResultCallback1; -template -class Callback1; -template -class ResultCallback; namespace operations_research { @@ -313,21 +300,6 @@ struct DefaultPhaseParameters { class Solver { public: - // Callback typedefs - typedef ResultCallback1 IndexEvaluator1; - typedef ResultCallback2 IndexEvaluator2; - typedef ResultCallback3 IndexEvaluator3; -#ifndef SWIG - typedef std::function - IntegerExpressionBuilder; - typedef std::function - ConstraintBuilder; - typedef std::function IntervalVariableBuilder; - typedef std::function SequenceVariableBuilder; - // Holds semantic information stating that the 'expression' has been // cast into 'variable' using the Var() method, and that // 'maintainer' is responsible for maintaining the equality between @@ -341,7 +313,6 @@ class Solver { IntExpr* expression; Constraint* maintainer; }; -#endif // SWIG // Number of priorities for demons. static const int kNumPriorities = 3; @@ -464,7 +435,7 @@ class Solver { // In Solver::MakePhase(const std::vector&, IntVarStrategy, // IntValueStrategy), variables are selected first, and then the associated // value. - // In Solver::MakePhase(const std::vector& vars, IndexEvaluator2*, + // In Solver::MakePhase(const std::vector& vars, IndexEvaluator2, // EvaluatorStrategy), the selection is done scanning every pair // . The next selected pair is then the best among // all possibilities, i.e. the pair with the smallest evaluation. @@ -826,6 +797,35 @@ class Solver { PROBLEM_INFEASIBLE // After search, the model is infeasible. }; + // Callback typedefs + typedef std::function IndexEvaluator1; + typedef std::function IndexEvaluator2; + typedef std::function IndexEvaluator3; + + typedef std::function IndexFilter1; + + typedef std::function& vars, + int64 first_unbound, int64 last_unbound)> + VariableIndexSelector; + + typedef std::function VariableValueSelector; + typedef std::function VariableValueComparator; + typedef std::function ObjectiveWatcher; + typedef std::function BranchSelector; + +#ifndef SWIG + typedef std::function + IntegerExpressionBuilder; + typedef std::function + ConstraintBuilder; + typedef std::function IntervalVariableBuilder; + typedef std::function SequenceVariableBuilder; +#endif // SWIG + + // Solver API explicit Solver(const std::string& modelname); Solver(const std::string& modelname, const SolverParameters& parameters); ~Solver(); @@ -1222,17 +1222,17 @@ class Solver { // Function-based element. The constraint takes ownership of // callback The callback must be able to cope with any possible // value in the domain of 'index' (potentially negative ones too). - IntExpr* MakeElement(IndexEvaluator1* values, IntVar* const index); + IntExpr* MakeElement(IndexEvaluator1 values, IntVar* const index); // Function based element. The constraint takes ownership of // callback. The callback must be monotonic. It must be able to // cope with any possible value in the domain of 'index' // (potentially negative ones too). Furtermore, monotonicity is not // checked. Thus giving a non monotonic function, or specifying an // incorrect increasing parameter will result in undefined behavior. - IntExpr* MakeMonotonicElement(IndexEvaluator1* values, bool increasing, + IntExpr* MakeMonotonicElement(IndexEvaluator1 values, bool increasing, IntVar* const index); // 2D version of function-based element expression, values(expr1, expr2). - IntExpr* MakeElement(IndexEvaluator2* values, IntVar* const index1, + IntExpr* MakeElement(IndexEvaluator2 values, IntVar* const index1, IntVar* const index2); // vars[expr] @@ -1250,7 +1250,7 @@ class Solver { // std::min(vars) IntExpr* MakeMin(const std::vector& vars); - // min (left, right) + // std::min (left, right) IntExpr* MakeMin(IntExpr* const left, IntExpr* const right); // std::min(expr, val) IntExpr* MakeMin(IntExpr* const expr, int64 val); @@ -1446,8 +1446,10 @@ class Solver { // method to call the InitiatePropagate of the constraint 'ct' with // low priority. Demon* MakeDelayedConstraintInitialPropagateCallback(Constraint* const ct); +#if !defined(SWIG) // Other languages can use the closure version // Creates a demon from a callback. Demon* MakeCallbackDemon(std::function callback); +#endif // !defined(SWIG) // Creates a demon from a closure. Demon* MakeClosureDemon(std::function closure); @@ -1563,34 +1565,34 @@ class Solver { // TODO(user): Add void MakeSortedArray(const std::vector& vars, // std::vector* const sorted); - // Creates the constraint that enforces that left is lexicographically less + // Creates a constraint that enforces that left is lexicographically less // than right. Constraint* MakeLexicalLess(const std::vector& left, const std::vector& right); - // Creates the constraint that enforces that left is lexicographically less + // Creates a constraint that enforces that left is lexicographically less // or equal than right. Constraint* MakeLexicalLessOrEqual(const std::vector& left, const std::vector& right); - // Creates the constraint that enforces that 'left' and 'right' both + // Creates a constraint that enforces that 'left' and 'right' both // represent permutations of [0..left.size()-1], and that 'right' is // the inverse permutation of 'left', i.e. for all i in // [0..left.size()-1], right[left[i]] = i. Constraint* MakeInversePermutationConstraint(const std::vector& left, const std::vector& right); - // Creates that constraint that binds the index variable to the index of the + // Creates a constraint that binds the index variable to the index of the // first variable with the maximum value. Constraint* MakeIndexOfFirstMaxValueConstraint(IntVar* index, const std::vector& vars); - // Creates that constraint that binds the index variable to the index of the + // Creates a constraint that binds the index variable to the index of the // first variable with the minimum value. Constraint* MakeIndexOfFirstMinValueConstraint(IntVar* index, const std::vector& vars); - // Creates a constraints that states that all variables in the first + // Creates a constraint that states that all variables in the first // vector are different from all variables from the second // group. Thus the set of values in the first vector does not // intersect the set of values in the second vector. @@ -1620,11 +1622,10 @@ class Solver { // otherwise the constraint assumes the 'next' variables represent a forest. Constraint* MakeNoCycle(const std::vector& nexts, const std::vector& active, - ResultCallback1* sink_handler = nullptr); + IndexFilter1 sink_handler = nullptr); Constraint* MakeNoCycle(const std::vector& nexts, const std::vector& active, - ResultCallback1* sink_handler, - bool assume_paths); + IndexFilter1 sink_handler, bool assume_paths); // Force the nexts() variable to create a complete hamiltonian path. Constraint* MakeCircuit(const std::vector& nexts); @@ -1657,7 +1658,7 @@ class Solver { Constraint* MakePathCumul(const std::vector& nexts, const std::vector& active, const std::vector& cumuls, - IndexEvaluator2* transit_evaluator); + IndexEvaluator2 transit_evaluator); // Creates a constraint which accumulates values along a path such that: // cumuls[next[i]] = cumuls[i] + transit_evaluator(i, next[i]) + slacks[i]. @@ -1669,7 +1670,7 @@ class Solver { const std::vector& active, const std::vector& cumuls, const std::vector& slacks, - IndexEvaluator2* transit_evaluator); + IndexEvaluator2 transit_evaluator); // This constraint maps the domain of 'var' onto the array of // variables 'vars'. That is // for all i in [0 .. size - 1]: vars[i] == 1 <=> var->Contains(i); @@ -1754,22 +1755,16 @@ class Solver { // (x_vars[i] + x_size[i], y_vars[i]), // (x_vars[i] + x_size[i], y_vars[i] + y_size[i]). // The sizes must be positive. - // Boxes with one zero dimensions can be placed anywhere. + // Boxes with one zero dimension can be placed anywhere. Constraint* MakeNonOverlappingNonStrictBoxesConstraint( - const std::vector& x_vars, - const std::vector& y_vars, - const std::vector& x_size, - const std::vector& y_size); + const std::vector& x_vars, const std::vector& y_vars, + const std::vector& x_size, const std::vector& y_size); Constraint* MakeNonOverlappingNonStrictBoxesConstraint( - const std::vector& x_vars, - const std::vector& y_vars, - const std::vector& x_size, - const std::vector& y_size); + const std::vector& x_vars, const std::vector& y_vars, + const std::vector& x_size, const std::vector& y_size); Constraint* MakeNonOverlappingNonStrictBoxesConstraint( - const std::vector& x_vars, - const std::vector& y_vars, - const std::vector& x_size, - const std::vector& y_size); + const std::vector& x_vars, const std::vector& y_vars, + const std::vector& x_size, const std::vector& y_size); // ----- Packing constraint ----- @@ -1968,7 +1963,7 @@ class Solver { DisjunctiveConstraint* MakeDisjunctiveConstraint( const std::vector& intervals, const std::string& name); - // This constraint forces all interval vars into an non overlapping + // This constraint forces all interval vars into an non-overlapping // sequence. Intervals with zero durations cannot overlap with over intervals. DisjunctiveConstraint* MakeStrictDisjunctiveConstraint( const std::vector& intervals, const std::string& name); @@ -2170,11 +2165,11 @@ class Solver { // Creates a Guided Local Search monitor. // Description here: http://en.wikipedia.org/wiki/Guided_Local_Search SearchMonitor* MakeGuidedLocalSearch(bool maximize, IntVar* const objective, - IndexEvaluator2* objective_function, + IndexEvaluator2 objective_function, int64 step, const std::vector& vars, double penalty_factor); SearchMonitor* MakeGuidedLocalSearch(bool maximize, IntVar* const objective, - IndexEvaluator3* objective_function, + IndexEvaluator3 objective_function, int64 step, const std::vector& vars, const std::vector& secondary_vars, double penalty_factor); @@ -2237,7 +2232,7 @@ class Solver { // Callback-based search limit. Search stops when limiter returns true; if // this happens at a leaf the corresponding solution will be rejected. - SearchLimit* MakeCustomLimit(ResultCallback* limiter); + SearchLimit* MakeCustomLimit(std::function limiter); // ----- No Goods ----- @@ -2298,12 +2293,12 @@ class Solver { // At each solution, this monitor will also display result of @p // display_callback. SearchMonitor* MakeSearchLog(int branch_count, - ResultCallback* display_callback); + std::function display_callback); // At each solution, this monitor will display the objective value and the // result of @p display_callback. SearchMonitor* MakeSearchLog(int branch_count, IntVar* objective, - ResultCallback* display_callback); + std::function display_callback); // OptimizeVar Search Logs // At each solution, this monitor will also display the objective->Print(). @@ -2313,7 +2308,7 @@ class Solver { // Creates a search monitor that will also print the result of the // display callback. SearchMonitor* MakeSearchLog(int branch_count, OptimizeVar* const objective, - ResultCallback* display_callback); + std::function display_callback); // ----- Search Trace ------ @@ -2409,31 +2404,30 @@ class Solver { DecisionBuilder* MakePhase(const std::vector& vars, IntVarStrategy var_str, IntValueStrategy val_str); DecisionBuilder* MakePhase(const std::vector& vars, - IndexEvaluator1* var_evaluator, + IndexEvaluator1 var_evaluator, IntValueStrategy val_str); DecisionBuilder* MakePhase(const std::vector& vars, - IntVarStrategy var_str, IndexEvaluator2* val_eval); + IntVarStrategy var_str, IndexEvaluator2 val_eval); DecisionBuilder* MakePhase( const std::vector& vars, IntVarStrategy var_str, // var_val1_val2_comparator(var, val1, val2) is true iff assigning value // "val1" to variable "var" is better than assigning value "val2". - // Takes ownership of "var_val1_val2_comparator". - ResultCallback3* var_val1_val2_comparator); + VariableValueComparator var_val1_val2_comparator); DecisionBuilder* MakePhase(const std::vector& vars, - IndexEvaluator1* var_evaluator, - IndexEvaluator2* val_eval); + IndexEvaluator1 var_evaluator, + IndexEvaluator2 val_eval); DecisionBuilder* MakePhase(const std::vector& vars, - IntVarStrategy var_str, IndexEvaluator2* val_eval, - IndexEvaluator1* tie_breaker); + IntVarStrategy var_str, IndexEvaluator2 val_eval, + IndexEvaluator1 tie_breaker); DecisionBuilder* MakePhase(const std::vector& vars, - IndexEvaluator1* var_evaluator, - IndexEvaluator2* val_eval, - IndexEvaluator1* tie_breaker); + IndexEvaluator1 var_evaluator, + IndexEvaluator2 val_eval, + IndexEvaluator1 tie_breaker); DecisionBuilder* MakeDefaultPhase(const std::vector& vars); DecisionBuilder* MakeDefaultPhase(const std::vector& vars, @@ -2483,7 +2477,7 @@ class Solver { // of these variables. Ownership of the callback is passed to the decision // builder. DecisionBuilder* MakePhase(const std::vector& vars, - IndexEvaluator2* evaluator, EvaluatorStrategy str); + IndexEvaluator2 evaluator, EvaluatorStrategy str); // Returns a decision builder which assigns values to variables // which minimize the values returned by the evaluator. In case of @@ -2493,8 +2487,8 @@ class Solver { // variables in vars and the values of these variables. Ownership of // the callback is passed to the decision builder. DecisionBuilder* MakePhase(const std::vector& vars, - IndexEvaluator2* evaluator, - IndexEvaluator1* tie_breaker, + IndexEvaluator2 evaluator, + IndexEvaluator1 tie_breaker, EvaluatorStrategy str); // Scheduling phases. @@ -2589,11 +2583,11 @@ class Solver { // TODO(user): Make the callback an IndexEvaluator2 when there are no // secondary variables. LocalSearchOperator* MakeOperator(const std::vector& vars, - IndexEvaluator3* const evaluator, + IndexEvaluator3 evaluator, EvaluatorLocalSearchOperators op); LocalSearchOperator* MakeOperator(const std::vector& vars, const std::vector& secondary_vars, - IndexEvaluator3* const evaluator, + IndexEvaluator3 evaluator, EvaluatorLocalSearchOperators op); // Creates a large neighborhood search operator which creates fragments (set @@ -2660,7 +2654,7 @@ class Solver { const std::vector& ops, bool restart); LocalSearchOperator* ConcatenateOperators( const std::vector& ops, - ResultCallback2* const evaluator); + std::function evaluator); // Randomized version of local search concatenator; calls a random operator at // each call to MakeNextNeighbor(). LocalSearchOperator* RandomConcatenateOperators( @@ -2746,24 +2740,23 @@ class Solver { // Local Search Filters LocalSearchFilter* MakeVariableDomainFilter(); LocalSearchFilter* MakeLocalSearchObjectiveFilter( - const std::vector& vars, IndexEvaluator2* const values, + const std::vector& vars, IndexEvaluator2 values, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum); LocalSearchFilter* MakeLocalSearchObjectiveFilter( - const std::vector& vars, IndexEvaluator2* const values, - Callback1* delta_objective_callback, IntVar* const objective, + const std::vector& vars, IndexEvaluator2 values, + ObjectiveWatcher delta_objective_callback, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum); LocalSearchFilter* MakeLocalSearchObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* const values, IntVar* const objective, + Solver::IndexEvaluator3 values, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum); LocalSearchFilter* MakeLocalSearchObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* const values, - Callback1* delta_objective_callback, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, + Solver::IndexEvaluator3 values, ObjectiveWatcher delta_objective_callback, + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum); // Performs PeriodicCheck on the top-level search; can be called from a nested @@ -2793,12 +2786,10 @@ class Solver { int SolveDepth() const; // Sets the given branch selector on the current active search. - void SetBranchSelector( - ResultCallback1* const bs); + void SetBranchSelector(BranchSelector bs); // Creates a decision builder that will set the branch selector. - DecisionBuilder* MakeApplyBranchSelector( - ResultCallback1* const bs); + DecisionBuilder* MakeApplyBranchSelector(BranchSelector bs); // All-in-one SaveAndSetValue. template @@ -2856,9 +2847,11 @@ class Solver { Decision* balancing_decision() const { return balancing_decision_.get(); } // Internal +#if !defined(SWIG) void set_fail_intercept(std::function fail_intercept) { fail_intercept_ = fail_intercept; } +#endif void clear_fail_intercept() { fail_intercept_ = nullptr; } // Access to demon profiler. DemonProfiler* demon_profiler() const { return demon_profiler_; } @@ -2912,14 +2905,14 @@ class Solver { friend class SearchMonitor; friend class SearchLimit; -#ifndef SWIG +#if !defined(SWIG) friend void InternalSaveBooleanVarValue(Solver* const, IntVar* const); friend void SetQueueCleanerOnFail(Solver* const, IntVar* const); template friend class SimpleRevFIFO; template friend class RevImmutableMultiMap; -#endif + // Returns true if expr represents either boolean_var or 1 - // boolean_var. In that case, it fills sub_var and is_negated to be // true if the expression is 1 - boolean_var -- equivalent to @@ -2932,6 +2925,7 @@ class Solver { // these, and returns true. In the other case, it fills sub_expr // with expr, coefficient with 1, and returns false. bool IsProduct(IntExpr* const expr, IntExpr** sub_expr, int64* coefficient); +#endif // !defined(SWIG) // Internal. If the variables is the result of expr->Var(), this // method returns expr, nullptr otherwise. @@ -3541,12 +3535,12 @@ class ModelVisitor : public BaseObject { #if !defined(SWIG) // Using SWIG on calbacks is troublesome, let's hide these methods during // the wrapping. - void VisitInt64ToBoolExtension(ResultCallback1* const callback, - int64 index_min, int64 index_max); - void VisitInt64ToInt64Extension(ResultCallback1* const callback, + void VisitInt64ToBoolExtension(Solver::IndexFilter1 callback, int64 index_min, + int64 index_max); + void VisitInt64ToInt64Extension(const Solver::IndexEvaluator1& callback, int64 index_min, int64 index_max); // Expands function as array when index min is 0. - void VisitInt64ToInt64AsArray(ResultCallback1* const callback, + void VisitInt64ToInt64AsArray(const Solver::IndexEvaluator1& callback, const std::string& arg_name, int64 index_max); #endif // #if !defined(SWIG) }; @@ -4378,6 +4372,7 @@ class SequenceVar : public PropagationBaseObject { std::string DebugString() const override; +#if !defined(SWIG) // Returns the minimum and maximum duration of combined interval // vars in the sequence. void DurationRange(int64* const dmin, int64* const dmax) const; @@ -4393,6 +4388,7 @@ class SequenceVar : public PropagationBaseObject { // Compute statistics on the sequence. void ComputeStatistics(int* const ranked, int* const not_ranked, int* const unperformed) const; +#endif // !defined(SWIG) // Ranks the index_th interval var first of all unranked interval // vars. After that, it will no longer be considered ranked. @@ -5030,9 +5026,6 @@ std::ostream& operator<<(std::ostream& out, class Pack : public Constraint { public: - typedef ResultCallback1 ItemUsageEvaluator; - typedef ResultCallback2 ItemUsagePerBinEvaluator; - Pack(Solver* const s, const std::vector& vars, int number_of_bins); ~Pack() override; @@ -5054,15 +5047,15 @@ class Pack : public Constraint { // (weights->Run(i)) of all objects i assigned to 'b' is less or // equal to 'bounds[b]'. Ownership of the callback is transfered to // the pack constraint. - void AddWeightedSumLessOrEqualConstantDimension(ItemUsageEvaluator* weights, - const std::vector& bounds); + void AddWeightedSumLessOrEqualConstantDimension( + Solver::IndexEvaluator1 weights, const std::vector& bounds); // This dimension imposes that for all bins b, the weighted sum // (weights->Run(i, b) of all objects i assigned to 'b' is less or // equal to 'bounds[b]'. Ownership of the callback is transfered to // the pack constraint. void AddWeightedSumLessOrEqualConstantDimension( - ItemUsagePerBinEvaluator* weights, const std::vector& bounds); + Solver::IndexEvaluator2 weights, const std::vector& bounds); // This dimension imposes that for all bins b, the weighted sum // (weights[i]) of all objects i assigned to 'b' is equal to loads[b]. @@ -5071,7 +5064,7 @@ class Pack : public Constraint { // This dimension imposes that for all bins b, the weighted sum // (weights->Run(i, b)) of all objects i assigned to 'b' is equal to loads[b]. - void AddWeightedSumEqualVarDimension(ItemUsagePerBinEvaluator* weights, + void AddWeightedSumEqualVarDimension(Solver::IndexEvaluator2 weights, const std::vector& loads); // This dimension imposes: @@ -5152,20 +5145,13 @@ class DisjunctiveConstraint : public Constraint { // Add a transition time between intervals. It forces the distance between // 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 return + // least transit_evaluator(a, b). This function must always return // a positive or null value. - // This method takes ownership of the evaluator. - void SetTransitionTime(Solver::IndexEvaluator2* transit_evaluator) { - transition_time_.reset(transit_evaluator); - if (transition_time_.get() != nullptr) { - transition_time_->CheckIsRepeatable(); - } - } + void SetTransitionTime(Solver::IndexEvaluator2 transit_evaluator); int64 TransitionTime(int before_index, int after_index) { - return transition_time_ != nullptr - ? transition_time_->Run(before_index, after_index) - : 0; + DCHECK(transition_time_); + return transition_time_(before_index, after_index); } #if !defined(SWIG) @@ -5177,7 +5163,7 @@ class DisjunctiveConstraint : public Constraint { protected: const std::vector intervals_; - std::unique_ptr transition_time_; + Solver::IndexEvaluator2 transition_time_; private: DISALLOW_COPY_AND_ASSIGN(DisjunctiveConstraint); diff --git a/src/constraint_solver/constraint_solveri.h b/src/constraint_solver/constraint_solveri.h index 46bf750f53..35b1dd6cd5 100644 --- a/src/constraint_solver/constraint_solveri.h +++ b/src/constraint_solver/constraint_solveri.h @@ -55,8 +55,9 @@ #include #include #include +#include #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include @@ -148,9 +149,7 @@ enum VarTypes { template class SimpleRevFIFO { private: - enum { - CHUNK_SIZE = 16 - }; // TODO(user): could be an extra template param + enum { CHUNK_SIZE = 16 }; // TODO(user): could be an extra template param struct Chunk { T data_[CHUNK_SIZE]; const Chunk* const next_; @@ -299,7 +298,7 @@ class RevImmutableMultiMap { public: RevImmutableMultiMap(Solver* const solver, int initial_size) : solver_(solver), - array_(solver->UnsafeRevAllocArray(new Cell* [initial_size])), + array_(solver->UnsafeRevAllocArray(new Cell*[initial_size])), size_(initial_size), num_items_(0) { memset(array_, 0, sizeof(*array_) * size_.Value()); @@ -380,7 +379,7 @@ class RevImmutableMultiMap { solver_->SaveAndSetValue( reinterpret_cast(&array_), reinterpret_cast( - solver_->UnsafeRevAllocArray(new Cell* [size_.Value()]))); + solver_->UnsafeRevAllocArray(new Cell*[size_.Value()]))); memset(array_, 0, size_.Value() * sizeof(*array_)); for (int i = 0; i < old_size; ++i) { Cell* tmp = old_cell_array[i]; @@ -544,6 +543,17 @@ Demon* MakeConstraintDemon0(Solver* const s, T* const ct, void (T::*method)(), return s->RevAlloc(new CallMethod0(ct, method, name)); } +template +std::string ParameterDebugString(P param) { + return StrCat(param); +} + +// Support limited to pointers to classes which define DebugString(). +template +std::string ParameterDebugString(P* param) { + return param->DebugString(); +} + // Demon proxy to a method on the constraint with one argument. template class CallMethod1 : public Demon { @@ -557,7 +567,7 @@ class CallMethod1 : public Demon { std::string DebugString() const override { return StrCat("CallMethod_", name_, "(", constraint_->DebugString(), ", ", - param1_, ")"); + ParameterDebugString(param1_), ")"); } private: @@ -594,7 +604,8 @@ class CallMethod2 : public Demon { std::string DebugString() const override { return StrCat(StrCat("CallMethod_", name_), StrCat("(", constraint_->DebugString()), - StrCat(", ", param1_), StrCat(", ", param2_, ")")); + StrCat(", ", ParameterDebugString(param1_)), + StrCat(", ", ParameterDebugString(param2_), ")")); } private: @@ -634,8 +645,9 @@ class CallMethod3 : public Demon { std::string DebugString() const override { return StrCat(StrCat("CallMethod_", name_), StrCat("(", constraint_->DebugString()), - StrCat(", ", param1_), StrCat(", ", param2_), - StrCat(", ", param3_, ")")); + StrCat(", ", ParameterDebugString(param1_)), + StrCat(", ", ParameterDebugString(param2_)), + StrCat(", ", ParameterDebugString(param3_), ")")); } private: @@ -711,7 +723,7 @@ class DelayedCallMethod1 : public Demon { std::string DebugString() const override { return StrCat("DelayedCallMethod_", name_, "(", constraint_->DebugString(), - ", ", param1_, ")"); + ", ", ParameterDebugString(param1_), ")"); } private: @@ -753,7 +765,8 @@ class DelayedCallMethod2 : public Demon { std::string DebugString() const override { return StrCat(StrCat("DelayedCallMethod_", name_), StrCat("(", constraint_->DebugString()), - StrCat(", ", param1_), StrCat(", ", param2_, ")")); + StrCat(", ", ParameterDebugString(param1_)), + StrCat(", ", ParameterDebugString(param2_), ")")); } private: @@ -895,7 +908,6 @@ class VarLocalSearchOperator : public LocalSearchOperator { } } - // Called by Start() after synchronizing the operator with the current // assignment. Should be overridden instead of Start() to avoid calling // VarLocalSearchOperator::Start explicitly. @@ -903,9 +915,8 @@ class VarLocalSearchOperator : public LocalSearchOperator { // OnStart() should really be protected, but then SWIG doesn't see it. So we // make it public, but only subclasses should access to it (to override it). - protected: - + protected: void MarkChange(int64 index) { delta_changes_.Set(index); changes_.Set(index); @@ -977,13 +988,13 @@ class IntVarLocalSearchHandler { IntVarLocalSearchHandler>::IsIncremental; %unignore VarLocalSearchOperator::OnStart; -#endif +#endif // SWIGPYTHON %rename(IntVarLocalSearchOperatorTemplate) - VarLocalSearchOperator; + VarLocalSearchOperator; %template(IntVarLocalSearchOperatorTemplate) - VarLocalSearchOperator; -#endif + VarLocalSearchOperator; +#endif // SWIG class IntVarLocalSearchOperator : public VarLocalSearchOperator { @@ -1212,8 +1223,7 @@ class PathOperator : public IntVarLocalSearchOperator { // 'start_empty_path_class' must remain alive during the lifespan of the // path operator. PathOperator(const std::vector& next_vars, - const std::vector& path_vars, - int number_of_base_nodes, + const std::vector& path_vars, int number_of_base_nodes, ResultCallback1* start_empty_path_class); ~PathOperator() override {} virtual bool MakeNeighbor() = 0; @@ -1358,8 +1368,7 @@ class PathOperator : public IntVarLocalSearchOperator { template LocalSearchOperator* MakeLocalSearchOperator( - Solver* solver, - const std::vector& vars, + Solver* solver, const std::vector& vars, const std::vector& secondary_vars, ResultCallback1* start_empty_path_class); @@ -1595,7 +1604,7 @@ class SymmetryBreaker : public DecisionVisitor { class SearchLog : public SearchMonitor { public: SearchLog(Solver* const s, OptimizeVar* const obj, IntVar* const var, - ResultCallback* display_callback, int period); + std::function display_callback, int period); ~SearchLog() override; void EnterSearch() override; void ExitSearch() override; @@ -1621,7 +1630,7 @@ class SearchLog : public SearchMonitor { std::unique_ptr timer_; IntVar* const var_; OptimizeVar* const obj_; - std::unique_ptr > display_callback_; + std::function display_callback_; int nsol_; int64 tick_; int64 objective_min_; @@ -1785,9 +1794,9 @@ class ModelCache { // Expr Constant Expressions. - virtual IntExpr* FindExprConstantExpression(IntExpr* const expr, int64 value, - ExprConstantExpressionType type) - const = 0; + virtual IntExpr* FindExprConstantExpression( + IntExpr* const expr, int64 value, + ExprConstantExpressionType type) const = 0; virtual void InsertExprConstantExpression( IntExpr* const expression, IntExpr* const var, int64 value, @@ -1953,10 +1962,10 @@ class ArgumentHolder { // Getters. int64 FindIntegerArgumentWithDefault(const std::string& arg_name, int64 def) const; int64 FindIntegerArgumentOrDie(const std::string& arg_name) const; - const std::vector& FindIntegerArrayArgumentOrDie(const std::string& arg_name) - const; - const IntTupleSet& FindIntegerMatrixArgumentOrDie(const std::string& arg_name) - const; + const std::vector& FindIntegerArrayArgumentOrDie( + const std::string& arg_name) const; + const IntTupleSet& FindIntegerMatrixArgumentOrDie( + const std::string& arg_name) const; IntExpr* FindIntegerExpressionArgumentOrDie(const std::string& arg_name) const; const std::vector& FindIntegerVariableArrayArgumentOrDie( diff --git a/src/constraint_solver/constraints.cc b/src/constraint_solver/constraints.cc index 0dfd45edaf..84add13768 100644 --- a/src/constraint_solver/constraints.cc +++ b/src/constraint_solver/constraints.cc @@ -14,7 +14,7 @@ #include #include -#include "base/unique_ptr.h" +#include #include #include @@ -131,7 +131,7 @@ class MapDomain : public Constraint { MakeConstraintDemon0(solver(), this, &MapDomain::VarBound, "VarBound"); var_->WhenBound(vb); std::unique_ptr domain_it( - var_->MakeDomainIterator(/*reversible=*/ false)); + var_->MakeDomainIterator(/*reversible=*/false)); for (const int64 index : InitAndGetValues(domain_it.get())) { if (index >= 0 && index < actives_.size() && !actives_[index]->Bound()) { Demon* d = MakeConstraintDemon1( @@ -181,7 +181,8 @@ class MapDomain : public Constraint { actives_[j]->SetValue(0); } } - for (int64 j = std::max(vmax + 1LL, 0LL); j <= std::min(oldmax, size - 1LL); ++j) { + for (int64 j = std::max(vmax + 1LL, 0LL); j <= std::min(oldmax, size - 1LL); + ++j) { actives_[j]->SetValue(0LL); } } @@ -339,9 +340,9 @@ class InversePermutationConstraint : public Constraint { right_[i]->WhenDomain(right_demon); } solver()->AddConstraint( - solver()->MakeAllDifferent(left_, /*stronger_propagation=*/ false)); + solver()->MakeAllDifferent(left_, /*stronger_propagation=*/false)); solver()->AddConstraint( - solver()->MakeAllDifferent(right_, /*stronger_propagation=*/ false)); + solver()->MakeAllDifferent(right_, /*stronger_propagation=*/false)); } void InitialPropagate() override { @@ -448,7 +449,7 @@ class IndexOfFirstMaxValue : public Constraint { } } - void InitialPropagate() { + void InitialPropagate() override { const int64 vsize = vars_.size(); const int64 imin = std::max(0LL, index_->Min()); const int64 imax = std::min(vsize - 1, index_->Max()); @@ -488,7 +489,7 @@ class IndexOfFirstMaxValue : public Constraint { } void Accept(ModelVisitor* const visitor) const override { - // TODO(lperron): Implement me. + // TODO(user): Implement me. } private: diff --git a/src/constraint_solver/count_cst.cc b/src/constraint_solver/count_cst.cc index be9fff48c2..c1c2116317 100644 --- a/src/constraint_solver/count_cst.cc +++ b/src/constraint_solver/count_cst.cc @@ -370,8 +370,8 @@ void FastDistribute::OneDomain(int index) { const int64 oldmax = var->OldMax(); const int64 vmin = var->Min(); const int64 vmax = var->Max(); - for (int64 card_index = std::max(oldmin, 0LL); card_index < std::min(vmin, card_size()); - ++card_index) { + for (int64 card_index = std::max(oldmin, 0LL); + card_index < std::min(vmin, card_size()); ++card_index) { if (undecided_.IsSet(index, card_index)) { SetRevCannotContribute(index, card_index); } @@ -784,8 +784,8 @@ void BoundedFastDistribute::OneDomain(int index) { const int64 oldmax = var->OldMax(); const int64 vmin = var->Min(); const int64 vmax = var->Max(); - for (int64 card_index = std::max(oldmin, 0LL); card_index < std::min(vmin, card_size()); - ++card_index) { + for (int64 card_index = std::max(oldmin, 0LL); + card_index < std::min(vmin, card_size()); ++card_index) { if (undecided_.IsSet(index, card_index)) { SetRevCannotContribute(index, card_index); } diff --git a/src/constraint_solver/csharp/constraint_solver.swig b/src/constraint_solver/csharp/constraint_solver.swig index 6312216bed..a50b5baeaa 100644 --- a/src/constraint_solver/csharp/constraint_solver.swig +++ b/src/constraint_solver/csharp/constraint_solver.swig @@ -22,6 +22,7 @@ using System.Collections; %include "exception.i" %include "std_vector.i" %include "util/csharp/tuple_set.swig" +%include "util/csharp/functions.swig" %module(directors="1", allprotected="1") operations_research; #pragma SWIG nowarn=473 @@ -52,6 +53,7 @@ using System.Collections; #include "base/integral_types.h" #include "constraint_solver/constraint_solver.h" #include "constraint_solver/constraint_solveri.h" +#include "util/functions_swig_helpers.h" namespace operations_research { class LocalSearchPhaseParameters { @@ -188,7 +190,7 @@ class LongResultCallback3 { .getCPtr($csinput.GetPermanentCallback())"; // Since knapsack_solver.swig and constraint_solver.swig both need to -// instantiate the std::vector template, but their csharp_wrap.cc +// instantiate the vector template, but their csharp_wrap.cc // files end up being compiled into the same .dll, we must name the // vector template differently. %template(CpIntVector) std::vector; @@ -278,6 +280,324 @@ CS_TYPEMAP_STDVECTOR_OBJECT(operations_research::SymmetryBreaker, SymmetryBreake %typemap(csinterfaces_derived) operations_research::Constraint "IConstraintWithStatus"; namespace operations_research { +// Take care of API with function +%ignore Solver::MakeSearchLog( + int branch_count, + IntVar* objective, + std::function display_callback); +%ignore Solver::MakeSearchLog( + int branch_count, + OptimizeVar* const objective, + std::function display_callback); +%ignore Solver::MakeSearchLog( + int branch_count, + std::function display_callback); +%ignore Solver::MakeCustomLimit(std::function limiter); +%ignore Solver::MakeElement(IndexEvaluator1 values, IntVar* const index); +%ignore Solver::MakeMonotonicElement(IndexEvaluator1 values, bool increasing, + IntVar* const index); +%ignore Solver::MakeElement(IndexEvaluator2 values, IntVar* const index1, + IntVar* const index2); +%ignore Solver::MakePathCumul(const std::vector& nexts, + const std::vector& active, + const std::vector& cumuls, + IndexEvaluator2 transit_evaluator); +%ignore Solver::MakePathCumul(const std::vector& nexts, + const std::vector& active, + const std::vector& cumuls, + const std::vector& slacks, + IndexEvaluator2 transit_evaluator); +%ignore Solver::MakeNoCycle(const std::vector& nexts, + const std::vector& active, + IndexFilter1 sink_handler = nullptr); +%ignore Solver::MakeNoCycle(const std::vector& nexts, + const std::vector& active, + IndexFilter1 sink_handler, bool assume_paths); +%ignore Solver::MakeGuidedLocalSearch(bool maximize, IntVar* const objective, + IndexEvaluator2 objective_function, + int64 step, const std::vector& vars, + double penalty_factor); +%ignore Solver::MakeGuidedLocalSearch(bool maximize, IntVar* const objective, + IndexEvaluator3 objective_function, + int64 step, const std::vector& vars, + const std::vector& secondary_vars, + double penalty_factor); +%ignore Solver::MakePhase(const std::vector& vars, + IndexEvaluator1 var_evaluator, + IntValueStrategy val_str); + +%ignore Solver::MakePhase(const std::vector& vars, + IntVarStrategy var_str, IndexEvaluator2 val_eval); + +%ignore Solver::MakePhase( + const std::vector& vars, IntVarStrategy var_str, + VariableValueComparator var_val1_val2_comparator); + +%ignore Solver::MakePhase(const std::vector& vars, + IndexEvaluator1 var_evaluator, + IndexEvaluator2 val_eval); + +%ignore Solver::MakePhase(const std::vector& vars, + IntVarStrategy var_str, IndexEvaluator2 val_eval, + IndexEvaluator1 tie_breaker); + +%ignore Solver::MakePhase(const std::vector& vars, + IndexEvaluator1 var_evaluator, + IndexEvaluator2 val_eval, + IndexEvaluator1 tie_breaker); +%ignore Solver::MakePhase(const std::vector& vars, + IndexEvaluator2 evaluator, EvaluatorStrategy str); +%ignore Solver::MakePhase(const std::vector& vars, + IndexEvaluator2 evaluator, + IndexEvaluator1 tie_breaker, + EvaluatorStrategy str); +%ignore Solver::MakeOperator(const std::vector& vars, + IndexEvaluator3 evaluator, + EvaluatorLocalSearchOperators op); +%ignore Solver::MakeOperator(const std::vector& vars, + const std::vector& secondary_vars, + IndexEvaluator3 evaluator, + EvaluatorLocalSearchOperators op); +%ignore Solver::MakeLocalSearchObjectiveFilter( + const std::vector& vars, IndexEvaluator2 values, + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, + Solver::LocalSearchOperation op_enum); +%ignore Solver::MakeLocalSearchObjectiveFilter( + const std::vector& vars, IndexEvaluator2 values, + ObjectiveWatcher delta_objective_callback, IntVar* const objective, + Solver::LocalSearchFilterBound filter_enum, + Solver::LocalSearchOperation op_enum); +%ignore Solver::MakeLocalSearchObjectiveFilter( + const std::vector& vars, const std::vector& secondary_vars, + Solver::IndexEvaluator3 values, IntVar* const objective, + Solver::LocalSearchFilterBound filter_enum, + Solver::LocalSearchOperation op_enum); +%ignore Solver::MakeLocalSearchObjectiveFilter( + const std::vector& vars, const std::vector& secondary_vars, + Solver::IndexEvaluator3 values, ObjectiveWatcher delta_objective_callback, + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, + Solver::LocalSearchOperation op_enum); +%ignore Solver::ConcatenateOperators( + const std::vector& ops, + std::function evaluator); +%ignore Solver::MakeClosureDemon(std::function closure); + +%extend Solver { + IntExpr* MakeElement(swig_util::LongToLong* values, IntVar* const index) { + return $self->MakeElement(values->GetFunction(), index); + } + IntExpr* MakeMonotonicElement(swig_util::LongToLong* values, bool increasing, + IntVar* const index) { + return $self->MakeMonotonicElement(values->GetFunction(), increasing, index); + } + IntExpr* MakeElement(swig_util::LongLongToLong* values, IntVar* const index1, + IntVar* const index2) { + return $self->MakeElement(values->GetFunction(), index1, index2); + } + Constraint* MakePathCumul(const std::vector& nexts, + const std::vector& active, + const std::vector& cumuls, + swig_util::LongLongToLong* transit_evaluator) { + return $self->MakePathCumul(nexts, active, cumuls, transit_evaluator->GetFunction()); + } + Constraint* MakePathCumul(const std::vector& nexts, + const std::vector& active, + const std::vector& cumuls, + const std::vector& slacks, + swig_util::LongLongToLong* transit_evaluator) { + return $self->MakePathCumul(nexts, active, cumuls, slacks, transit_evaluator->GetFunction()); + } + Constraint* MakeNoCycle(const std::vector& nexts, + const std::vector& active, + swig_util::LongToBoolean* sink_handler = nullptr) { + if (sink_handler == nullptr) { + return $self->MakeNoCycle(nexts, active, nullptr); + } else { + return $self->MakeNoCycle(nexts, active, sink_handler->GetFunction()); + } + } + Constraint* MakeNoCycle(const std::vector& nexts, + const std::vector& active, + swig_util::LongToBoolean* sink_handler, bool assume_paths) { + return $self->MakeNoCycle(nexts, active, sink_handler->GetFunction()); + } + SearchMonitor* MakeGuidedLocalSearch(bool maximize, IntVar* const objective, + swig_util::LongLongToLong* objective_function, + int64 step, const std::vector& vars, + double penalty_factor) { + return $self->MakeGuidedLocalSearch( + maximize, objective, objective_function->GetFunction(), step, vars, penalty_factor); + } + SearchMonitor* MakeGuidedLocalSearch(bool maximize, IntVar* const objective, + swig_util::LongLongLongToLong* objective_function, + int64 step, const std::vector& vars, + const std::vector& secondary_vars, + double penalty_factor) { + return $self->MakeGuidedLocalSearch( + maximize, objective, objective_function->GetFunction(), + step, vars, secondary_vars, penalty_factor); + } + DecisionBuilder* MakePhase(const std::vector& vars, + swig_util::LongToLong* var_evaluator, + IntValueStrategy val_str) { + return $self->MakePhase(vars, var_evaluator->GetFunction(), val_str); + } + DecisionBuilder* MakePhase(const std::vector& vars, + IntVarStrategy var_str, swig_util::LongLongToLong* val_eval) { + return $self->MakePhase(vars, var_str, val_eval->GetFunction()); + } + DecisionBuilder* MakePhase( + const std::vector& vars, IntVarStrategy var_str, + swig_util::LongLongLongToBoolean* var_val1_val2_comparator) { + return $self->MakePhase( + vars, var_str, var_val1_val2_comparator->GetFunction()); + } + DecisionBuilder* MakePhase(const std::vector& vars, + swig_util::LongToLong* var_evaluator, + swig_util::LongLongToLong* val_eval) { + return $self->MakePhase(vars, var_evaluator->GetFunction(), val_eval->GetFunction()); + } + DecisionBuilder* MakePhase(const std::vector& vars, + IntVarStrategy var_str, + swig_util::LongLongToLong* val_eval, + swig_util::LongToLong* tie_breaker) { + return $self->MakePhase(vars, var_str, val_eval->GetFunction(), tie_breaker->GetFunction()); + } + DecisionBuilder* MakePhase(const std::vector& vars, + swig_util::LongToLong* var_evaluator, + swig_util::LongLongToLong* val_eval, + swig_util::LongToLong* tie_breaker) { + return $self->MakePhase(vars, + var_evaluator->GetFunction(), + val_eval->GetFunction(), + tie_breaker->GetFunction()); + } + DecisionBuilder* MakePhase(const std::vector& vars, + swig_util::LongLongToLong* evaluator, EvaluatorStrategy str) { + return $self->MakePhase(vars, evaluator->GetFunction(), str); + } + DecisionBuilder* MakePhase(const std::vector& vars, + swig_util::LongLongToLong* evaluator, + swig_util::LongToLong* tie_breaker, + EvaluatorStrategy str) { + return $self->MakePhase( + vars, evaluator->GetFunction(), tie_breaker->GetFunction(), str); + } + LocalSearchOperator* MakeOperator(const std::vector& vars, + swig_util::LongLongLongToLong* evaluator, + EvaluatorLocalSearchOperators op) { + return $self->MakeOperator(vars, evaluator->GetFunction(), op); + } + LocalSearchOperator* MakeOperator(const std::vector& vars, + const std::vector& secondary_vars, + swig_util::LongLongLongToLong* evaluator, + EvaluatorLocalSearchOperators op) { + return $self->MakeOperator(vars, secondary_vars, evaluator->GetFunction(), op); + } + LocalSearchFilter* MakeLocalSearchObjectiveFilter( + const std::vector& vars, swig_util::LongLongToLong* values, + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, + Solver::LocalSearchOperation op_enum) { + return $self->MakeLocalSearchObjectiveFilter(vars, values->GetFunction(), + objective, filter_enum, op_enum); + } + LocalSearchFilter* MakeLocalSearchObjectiveFilter( + const std::vector& vars, swig_util::LongLongToLong* values, + swig_util::LongToVoid* delta_objective_callback, IntVar* const objective, + Solver::LocalSearchFilterBound filter_enum, + Solver::LocalSearchOperation op_enum) { + return $self->MakeLocalSearchObjectiveFilter( + vars, values->GetFunction(), delta_objective_callback->GetFunction(), + objective, filter_enum, op_enum); + } + LocalSearchFilter* MakeLocalSearchObjectiveFilter( + const std::vector& vars, const std::vector& secondary_vars, + swig_util::LongLongLongToLong* values, IntVar* const objective, + Solver::LocalSearchFilterBound filter_enum, + Solver::LocalSearchOperation op_enum) { + return $self->MakeLocalSearchObjectiveFilter( + vars, secondary_vars, values->GetFunction(), + objective, filter_enum, op_enum); + } + LocalSearchFilter* MakeLocalSearchObjectiveFilter( + const std::vector& vars, const std::vector& secondary_vars, + swig_util::LongLongLongToLong* values, + swig_util::LongToVoid* delta_objective_callback, + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, + Solver::LocalSearchOperation op_enum) { + return $self->MakeLocalSearchObjectiveFilter( + vars, secondary_vars, values->GetFunction(), + delta_objective_callback->GetFunction(), + objective, filter_enum, op_enum); + } + LocalSearchOperator* ConcatenateOperators( + const std::vector& ops, + swig_util::IntIntToLong* evaluator) { + return $self->ConcatenateOperators(ops, evaluator->GetFunction()); + } + SearchMonitor* MakeSearchLog( + int branch_count, + OptimizeVar* const objective, + swig_util::VoidToString* display_callback) { + return $self->MakeSearchLog(branch_count, objective, [display_callback]() { + return display_callback->run(); + }); + } + SearchMonitor* MakeSearchLog( + int branch_count, + IntVar* const obj_var, + swig_util::VoidToString* display_callback) { + return $self->MakeSearchLog(branch_count, obj_var, [display_callback]() { + return display_callback->run(); + }); + } + SearchMonitor* MakeSearchLog( + int branch_count, + swig_util::VoidToString* display_callback) { + return $self->MakeSearchLog(branch_count, [display_callback]() { + return display_callback->run(); + }); + } + SearchLimit* MakeCustomLimit(swig_util::VoidToBoolean* limiter) { + return $self->MakeCustomLimit([limiter]() { return limiter->run(); }); + } + Demon* MakeClosureDemon(swig_util::VoidToVoid* closure) { + return $self->MakeClosureDemon(closure->GetFunction()); + } +} // extend Solver + +%ignore DisjunctiveConstraint::SetTransitionTime(Solver::IndexEvaluator2 transit_evaluator); +%extend DisjunctiveConstraint { + void SetTransitionTime(swig_util::LongLongToLong* transit_evaluator) { + $self->SetTransitionTime([transit_evaluator](int64 i, int64 j) { return transit_evaluator->run(i, j); }); + } +} // extend DisjunctiveConstraint + +%ignore Pack::AddWeightedSumLessOrEqualConstantDimension( + Solver::IndexEvaluator1 weights, const std::vector& bounds); +%ignore Pack::AddWeightedSumLessOrEqualConstantDimension( + Solver::IndexEvaluator2 weights, const std::vector& bounds); +%ignore Pack::AddWeightedSumEqualVarDimension(Solver::IndexEvaluator2 weights, + const std::vector& loads); +%extend Pack { +void Pack::AddWeightedSumLessOrEqualConstantDimension( + swig_util::LongToLong* weights, const std::vector& bounds) { + return $self->AddWeightedSumLessOrEqualConstantDimension(weights->GetFunction(), bounds); +} +void Pack::AddWeightedSumLessOrEqualConstantDimension( + swig_util::LongLongToLong* weights, const std::vector& bounds) { + return $self->AddWeightedSumLessOrEqualConstantDimension(weights->GetFunction(), bounds); +} +void Pack::AddWeightedSumEqualVarDimension( + swig_util::LongLongToLong* weights, const std::vector& loads) { + return $self->AddWeightedSumEqualVarDimension(weights->GetFunction(), loads); +} +} // extend Pack + +%ignore SearchLog::SearchLog( + Solver* const s, OptimizeVar* const obj, IntVar* const var, + std::function display_callback, int period); + %extend IntervalVar { Constraint* EndsAfterEnd(IntervalVar* other) { return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::ENDS_AFTER_END, other); @@ -295,57 +615,61 @@ namespace operations_research { return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::STARTS_AFTER_END, other); } Constraint* StartsAfterStart(IntervalVar* other) { - return $self->solver()->MakeIntervalVarRelation($self, - operations_research::Solver::STARTS_AFTER_START, - other); + return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::STARTS_AFTER_START, other); } Constraint* StartsAtEnd(IntervalVar* other) { return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::STARTS_AT_END, other); } Constraint* StartsAtStart(IntervalVar* other) { - return $self->solver()->MakeIntervalVarRelation($self, - operations_research::Solver::STARTS_AT_START, - other); + return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::STARTS_AT_START, other); + } + Constraint* EndsAfterEndWithDelay(IntervalVar* other, int64 delay) { + return $self->solver()->MakeIntervalVarRelationWithDelay($self, operations_research::Solver::ENDS_AFTER_END, other, delay); + } + Constraint* EndsAfterStartWithDelay(IntervalVar* other, int64 delay) { + return $self->solver()->MakeIntervalVarRelationWithDelay($self, operations_research::Solver::ENDS_AFTER_START, other, delay); + } + Constraint* EndsAtEndWithDelay(IntervalVar* other, int64 delay) { + return $self->solver()->MakeIntervalVarRelationWithDelay($self, operations_research::Solver::ENDS_AT_END, other, delay); + } + Constraint* EndsAtStartWithDelay(IntervalVar* other, int64 delay) { + return $self->solver()->MakeIntervalVarRelationWithDelay($self, operations_research::Solver::ENDS_AT_START, other, delay); + } + Constraint* StartsAfterEndWithDelay(IntervalVar* other, int64 delay) { + return $self->solver()->MakeIntervalVarRelationWithDelay($self, operations_research::Solver::STARTS_AFTER_END, other, delay); + } + Constraint* StartsAfterStartWithDelay(IntervalVar* other, int64 delay) { + return $self->solver()->MakeIntervalVarRelationWithDelay($self, operations_research::Solver::STARTS_AFTER_START, other, delay); + } + Constraint* StartsAtEndWithDelay(IntervalVar* other, int64 delay) { + return $self->solver()->MakeIntervalVarRelationWithDelay($self, operations_research::Solver::STARTS_AT_END, other, delay); + } + Constraint* StartsAtStartWithDelay(IntervalVar* other, int64 delay) { + return $self->solver()->MakeIntervalVarRelationWithDelay($self, operations_research::Solver::STARTS_AT_START, other, delay); } Constraint* EndsAfter(int64 date) { - return $self->solver()->MakeIntervalVarRelation($self, - operations_research::Solver::ENDS_AFTER, - date); + return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::ENDS_AFTER, date); } Constraint* EndsAt(int64 date) { - return $self->solver()->MakeIntervalVarRelation($self, - operations_research::Solver::ENDS_AT, - date); + return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::ENDS_AT, date); } Constraint* EndsBefore(int64 date) { - return $self->solver()->MakeIntervalVarRelation($self, - operations_research::Solver::ENDS_BEFORE, - date); + return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::ENDS_BEFORE, date); } Constraint* StartsAfter(int64 date) { - return $self->solver()->MakeIntervalVarRelation($self, - operations_research::Solver::STARTS_AFTER, - date); + return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::STARTS_AFTER, date); } Constraint* StartsAt(int64 date) { - return $self->solver()->MakeIntervalVarRelation($self, - operations_research::Solver::STARTS_AT, - date); + return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::STARTS_AT, date); } Constraint* StartsBefore(int64 date) { - return $self->solver()->MakeIntervalVarRelation($self, - operations_research::Solver::STARTS_BEFORE, - date); + return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::STARTS_BEFORE, date); } Constraint* CrossesDate(int64 date) { - return $self->solver()->MakeIntervalVarRelation($self, - operations_research::Solver::CROSS_DATE, - date); + return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::CROSS_DATE, date); } Constraint* AvoidsDate(int64 date) { - return $self->solver()->MakeIntervalVarRelation($self, - operations_research::Solver::AVOID_DATE, - date); + return $self->solver()->MakeIntervalVarRelation($self, operations_research::Solver::AVOID_DATE, date); } IntervalVar* RelaxedMax() { return $self->solver()->MakeIntervalRelaxedMax($self); diff --git a/src/constraint_solver/default_search.cc b/src/constraint_solver/default_search.cc index 2eae29cb68..8fc926fe88 100644 --- a/src/constraint_solver/default_search.cc +++ b/src/constraint_solver/default_search.cc @@ -16,7 +16,7 @@ #include #include "base/hash.h" #include -#include "base/unique_ptr.h" +#include #include #include @@ -379,7 +379,7 @@ class ImpactRecorder : public SearchMonitor { current_log_space_(0.0), impacts_(size_), original_min_(size_, 0LL), - domain_iterators_(new IntVarIterator* [size_]), + domain_iterators_(new IntVarIterator*[size_]), display_level_(display_level), current_var_(kUninitializedVarIndex), current_value_(0), @@ -434,8 +434,8 @@ class ImpactRecorder : public SearchMonitor { // By default, we init impacts to 2.0 -> equivalent to failure. // This will be overwritten to real impact values on valid domain // values during the FirstRun() method. - impacts_[i] - .resize(vars_[i]->Max() - vars_[i]->Min() + 1, kInitFailureImpact); + impacts_[i].resize(vars_[i]->Max() - vars_[i]->Min() + 1, + kInitFailureImpact); } for (int i = 0; i < size_; ++i) { @@ -483,7 +483,7 @@ class ImpactRecorder : public SearchMonitor { } IntVarIterator* const iterator = domain_iterators_[var_index]; DecisionBuilder* init_decision_builder = nullptr; - bool no_split = var->Size() < splits; + const bool no_split = var->Size() < splits; if (no_split) { // The domain is small enough, we scan it completely. container->without_split()->set_update_impact_callback( @@ -635,7 +635,7 @@ class ImpactRecorder : public SearchMonitor { // original_min_[i] + j to variable i. std::vector > impacts_; std::vector original_min_; - std::unique_ptr domain_iterators_; + std::unique_ptr domain_iterators_; int64 init_count_; const DefaultPhaseParameters::DisplayLevel display_level_; int current_var_; @@ -1118,8 +1118,8 @@ class DefaultIntegerSearch : public DecisionBuilder { } Decision* const decision = parameters_.decision_builder != nullptr - ? parameters_.decision_builder->Next(solver) - : ImpactNext(solver); + ? parameters_.decision_builder->Next(solver) + : ImpactNext(solver); // Returns early if the search tree is finished anyway. if (decision == nullptr) { @@ -1159,9 +1159,8 @@ class DefaultIntegerSearch : public DecisionBuilder { case FindVar::SPLIT_LOW: { if (last_int_var_->Max() > last_int_value_ && last_int_var_->Min() <= last_int_value_) { - Decision* const split = - solver->MakeVariableLessOrEqualValue(last_int_var_, - last_int_value_); + Decision* const split = solver->MakeVariableLessOrEqualValue( + last_int_var_, last_int_value_); ClearLastDecision(); last_conflict_count_++; return split; @@ -1171,18 +1170,15 @@ class DefaultIntegerSearch : public DecisionBuilder { case FindVar::SPLIT_HIGH: { if (last_int_var_->Min() < last_int_value_ && last_int_var_->Max() >= last_int_value_) { - Decision* const split = - solver->MakeVariableGreaterOrEqualValue(last_int_var_, - last_int_value_); + Decision* const split = solver->MakeVariableGreaterOrEqualValue( + last_int_var_, last_int_value_); ClearLastDecision(); last_conflict_count_++; return split; } break; } - default: { - break; - } + default: { break; } } } diff --git a/src/constraint_solver/deviation.cc b/src/constraint_solver/deviation.cc index 411b1d494b..3ea047bb33 100644 --- a/src/constraint_solver/deviation.cc +++ b/src/constraint_solver/deviation.cc @@ -14,7 +14,7 @@ #include #include -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/constraint_solver/diffn.cc b/src/constraint_solver/diffn.cc index 70e89fa909..9ba7332412 100644 --- a/src/constraint_solver/diffn.cc +++ b/src/constraint_solver/diffn.cc @@ -64,8 +64,7 @@ class Diffn : public Constraint { } delayed_demon_ = MakeDelayedConstraintDemon0(s, this, &Diffn::PropagateAll, "PropagateAll"); - if (FLAGS_cp_diffn_use_cumulative && - IsArrayInRange(x_, 0LL, kint64max) && + if (FLAGS_cp_diffn_use_cumulative && IsArrayInRange(x_, 0LL, kint64max) && IsArrayInRange(y_, 0LL, kint64max)) { Constraint* ct1 = nullptr; Constraint* ct2 = nullptr; @@ -84,14 +83,14 @@ class Diffn : public Constraint { if (AreAllBound(dx_)) { std::vector size_x; FillValues(dx_, &size_x); - ct1 = MakeCumulativeConstraint( - x_, size_x, dy_, max_size_y + max_y - min_y); + ct1 = MakeCumulativeConstraint(x_, size_x, dy_, + max_size_y + max_y - min_y); } if (AreAllBound(dy_)) { std::vector size_y; FillValues(dy_, &size_y); - ct2 = MakeCumulativeConstraint( - y_, size_y, dx_, max_size_x + max_x - min_x); + ct2 = MakeCumulativeConstraint(y_, size_y, dx_, + max_size_x + max_x - min_x); } } if (ct1 != nullptr) { @@ -177,7 +176,7 @@ class Diffn : public Constraint { bool AreBoxedDisjoingVerticallyForSure(int i, int j) const { return (y_[i]->Min() >= y_[j]->Max() + dy_[j]->Max()) || - (y_[j]->Min() >= y_[i]->Max() + dy_[i]->Max()); + (y_[j]->Min() >= y_[i]->Max() + dy_[i]->Max()) || (!strict_ && (dy_[i]->Min() == 0 || dy_[j]->Min() == 0)); } diff --git a/src/constraint_solver/element.cc b/src/constraint_solver/element.cc index 0aadb0fe9b..298b79f13c 100644 --- a/src/constraint_solver/element.cc +++ b/src/constraint_solver/element.cc @@ -13,11 +13,10 @@ #include -#include "base/unique_ptr.h" +#include #include #include -#include "base/callback.h" #include "base/integral_types.h" #include "base/logging.h" #include "base/stringprintf.h" @@ -541,8 +540,8 @@ IntExpr* Solver::MakeElement(const std::vector& values, IntVar* const index namespace { class IntExprFunctionElement : public BaseIntExprElement { public: - IntExprFunctionElement(Solver* const s, ResultCallback1* values, - IntVar* const expr, bool del); + IntExprFunctionElement(Solver* const s, Solver::IndexEvaluator1 values, + IntVar* const expr); ~IntExprFunctionElement() override; std::string name() const override { @@ -568,71 +567,64 @@ class IntExprFunctionElement : public BaseIntExprElement { } protected: - int64 ElementValue(int index) const override { return values_->Run(index); } + int64 ElementValue(int index) const override { return values_(index); } int64 ExprMin() const override { return expr_->Min(); } int64 ExprMax() const override { return expr_->Max(); } private: - ResultCallback1* values_; - const bool delete_; + Solver::IndexEvaluator1 values_; }; -IntExprFunctionElement::IntExprFunctionElement( - Solver* const s, ResultCallback1* values, IntVar* const e, - bool del) - : BaseIntExprElement(s, e), values_(values), delete_(del) { - CHECK(values) << "null pointer"; - values->CheckIsRepeatable(); +IntExprFunctionElement::IntExprFunctionElement(Solver* const s, + Solver::IndexEvaluator1 values, + IntVar* const e) + : BaseIntExprElement(s, e), values_(std::move(values)) { + CHECK(values_ != nullptr); } -IntExprFunctionElement::~IntExprFunctionElement() { - if (delete_) { - delete values_; - } -} +IntExprFunctionElement::~IntExprFunctionElement() {} // ----- Increasing Element ----- class IncreasingIntExprFunctionElement : public BaseIntExpr { public: IncreasingIntExprFunctionElement(Solver* const s, - ResultCallback1* values, + Solver::IndexEvaluator1 values, IntVar* const index) - : BaseIntExpr(s), values_(values), index_(index) { - DCHECK(values); + : BaseIntExpr(s), values_(std::move(values)), index_(index) { + DCHECK(values_ != nullptr); DCHECK(index); DCHECK(s); - values->CheckIsRepeatable(); } - ~IncreasingIntExprFunctionElement() override { delete values_; } + ~IncreasingIntExprFunctionElement() override {} - int64 Min() const override { return values_->Run(index_->Min()); } + int64 Min() const override { return values_(index_->Min()); } void SetMin(int64 m) override { const int64 expression_min = index_->Min(); const int64 expression_max = index_->Max(); - if (m > values_->Run(expression_max)) { + if (m > values_(expression_max)) { solver()->Fail(); } int64 nmin = expression_min; - while (nmin <= expression_max && values_->Run(nmin) < m) { + while (nmin <= expression_max && values_(nmin) < m) { nmin++; } DCHECK_LE(nmin, expression_max); index_->SetMin(nmin); } - int64 Max() const override { return values_->Run(index_->Max()); } + int64 Max() const override { return values_(index_->Max()); } void SetMax(int64 m) override { const int64 expression_min = index_->Min(); const int64 expression_max = index_->Max(); - if (m < values_->Run(expression_min)) { + if (m < values_(expression_min)) { solver()->Fail(); } int64 nmax = expression_max; - while (nmax >= expression_min && values_->Run(nmax) > m) { + while (nmax >= expression_min && values_(nmax) > m) { nmax--; } DCHECK_GE(nmax, expression_min); @@ -642,17 +634,17 @@ class IncreasingIntExprFunctionElement : public BaseIntExpr { void SetRange(int64 mi, int64 ma) override { const int64 expression_min = index_->Min(); const int64 expression_max = index_->Max(); - if (mi > ma || ma < values_->Run(expression_min) || - mi > values_->Run(expression_max)) { + if (mi > ma || ma < values_(expression_min) || + mi > values_(expression_max)) { solver()->Fail(); } int64 nmax = expression_max; - while (nmax >= expression_min && values_->Run(nmax) > ma) { + while (nmax >= expression_min && values_(nmax) > ma) { nmax--; } DCHECK_GE(nmax, expression_min); int64 nmin = expression_min; - while (nmin <= nmax && values_->Run(nmin) < mi) { + while (nmin <= nmax && values_(nmin) < mi) { nmin++; } DCHECK_LE(nmin, nmax); @@ -687,49 +679,30 @@ class IncreasingIntExprFunctionElement : public BaseIntExpr { } private: - ResultCallback1* values_; + Solver::IndexEvaluator1 values_; IntVar* const index_; }; } // namespace -IntExpr* Solver::MakeElement(ResultCallback1* values, +IntExpr* Solver::MakeElement(Solver::IndexEvaluator1 values, IntVar* const index) { CHECK_EQ(this, index->solver()); return RegisterIntExpr( - RevAlloc(new IntExprFunctionElement(this, values, index, true))); + RevAlloc(new IntExprFunctionElement(this, values, index))); } -namespace { -class OppositeCallback : public BaseObject { - public: - explicit OppositeCallback(ResultCallback1* const values) - : values_(values) { - CHECK(values_ != nullptr); - values_->CheckIsRepeatable(); - } - - ~OppositeCallback() override {} - - int64 Run(int64 index) { return -values_->Run(index); } - - std::string DebugString() const override { return "OppositeCallback"; } - - public: - std::unique_ptr> values_; -}; -} // namespace - -IntExpr* Solver::MakeMonotonicElement(ResultCallback1* values, +IntExpr* Solver::MakeMonotonicElement(Solver::IndexEvaluator1 values, bool increasing, IntVar* const index) { CHECK_EQ(this, index->solver()); if (increasing) { return RegisterIntExpr( RevAlloc(new IncreasingIntExprFunctionElement(this, values, index))); } else { - OppositeCallback* const opposite_callback = - RevAlloc(new OppositeCallback(values)); - ResultCallback1* opposite_values = - NewPermanentCallback(opposite_callback, &OppositeCallback::Run); + // You need to pass by copy such that opposite_value does not include a + // dandling reference when leaving this scope. + Solver::IndexEvaluator1 opposite_values = [values](int64 i) { + return -values(i); + }; return RegisterIntExpr(MakeOpposite(RevAlloc( new IncreasingIntExprFunctionElement(this, opposite_values, index)))); } @@ -740,8 +713,7 @@ IntExpr* Solver::MakeMonotonicElement(ResultCallback1* values, namespace { class IntIntExprFunctionElement : public BaseIntExpr { public: - IntIntExprFunctionElement(Solver* const s, - ResultCallback2* values, + IntIntExprFunctionElement(Solver* const s, Solver::IndexEvaluator2 values, IntVar* const expr1, IntVar* const expr2); ~IntIntExprFunctionElement() override; std::string DebugString() const override { @@ -774,7 +746,7 @@ class IntIntExprFunctionElement : public BaseIntExpr { private: int64 ElementValue(int index1, int index2) const { - return values_->Run(index1, index2); + return values_(index1, index2); } void UpdateSupports() const; @@ -787,14 +759,14 @@ class IntIntExprFunctionElement : public BaseIntExpr { mutable int max_support1_; mutable int max_support2_; mutable bool initial_update_; - std::unique_ptr> values_; + Solver::IndexEvaluator2 values_; IntVarIterator* const expr1_iterator_; IntVarIterator* const expr2_iterator_; }; IntIntExprFunctionElement::IntIntExprFunctionElement( - Solver* const s, ResultCallback2* values, - IntVar* const expr1, IntVar* const expr2) + Solver* const s, Solver::IndexEvaluator2 values, IntVar* const expr1, + IntVar* const expr2) : BaseIntExpr(s), expr1_(expr1), expr2_(expr2), @@ -805,11 +777,10 @@ IntIntExprFunctionElement::IntIntExprFunctionElement( max_support1_(-1), max_support2_(-1), initial_update_(true), - values_(values), + values_(std::move(values)), expr1_iterator_(expr1_->MakeDomainIterator(true)), expr2_iterator_(expr2_->MakeDomainIterator(true)) { - CHECK(values) << "null pointer"; - values->CheckIsRepeatable(); + CHECK(values_ != nullptr); } IntIntExprFunctionElement::~IntIntExprFunctionElement() {} @@ -955,7 +926,7 @@ void IntIntExprFunctionElement::UpdateSupports() const { } } // namespace -IntExpr* Solver::MakeElement(ResultCallback2* values, +IntExpr* Solver::MakeElement(Solver::IndexEvaluator2 values, IntVar* const index1, IntVar* const index2) { CHECK_EQ(this, index1->solver()); CHECK_EQ(this, index2->solver()); @@ -976,9 +947,9 @@ class IfThenElseCt : public CastConstraint { zero_(zero), one_(one) {} - virtual ~IfThenElseCt() {} + ~IfThenElseCt() override {} - virtual void Post() { + void Post() override { Demon* const demon = solver()->MakeConstraintInitialPropagateCallback(this); condition_->WhenBound(demon); one_->WhenRange(demon); @@ -986,7 +957,7 @@ class IfThenElseCt : public CastConstraint { target_var_->WhenRange(demon); } - virtual void InitialPropagate() { + void InitialPropagate() override { condition_->SetRange(0, 1); const int64 target_var_min = target_var_->Min(); const int64 target_var_max = target_var_->Max(); @@ -1021,14 +992,14 @@ class IfThenElseCt : public CastConstraint { target_var_->SetRange(new_min, new_max); } - virtual std::string DebugString() const { + std::string DebugString() const override { return StringPrintf( "(%s ? %s : %s) == %s", condition_->DebugString().c_str(), one_->DebugString().c_str(), zero_->DebugString().c_str(), target_var_->DebugString().c_str()); } - virtual void Accept(ModelVisitor* const visitor) const {} + void Accept(ModelVisitor* const visitor) const override {} private: IntVar* const condition_; @@ -1358,35 +1329,13 @@ Constraint* MakeElementEqualityFunc(Solver* const solver, } else { return solver->MakeEquality(target, vals[val]); } - } else if (IsArrayBoolean(vals)) { - std::vector ones; - int first_zero = -1; - for (int i = 0; i < vals.size(); ++i) { - if (vals[i] == 1LL) { - ones.push_back(i); - } else { - first_zero = i; - } - } - if (ones.size() == 1) { - DCHECK_EQ(1LL, vals[ones.back()]); - solver->AddConstraint(solver->MakeBetweenCt(index, 0, vals.size() - 1)); - return solver->MakeIsEqualCstCt(index, ones.back(), target); - } else if (ones.size() == vals.size() - 1) { - solver->AddConstraint(solver->MakeBetweenCt(index, 0, vals.size() - 1)); - return solver->MakeIsDifferentCstCt(index, first_zero, target); - } else if (ones.size() == ones.back() - ones.front() + 1) { // contiguous. - solver->AddConstraint(solver->MakeBetweenCt(index, 0, vals.size() - 1)); - return solver->MakeIsBetweenCt(index, ones.front(), ones.back(), target); - } else { - solver->AddConstraint(solver->MakeBetweenCt(index, 0, vals.size() - 1)); - return solver->MakeIsMemberCt(index, ones, target); - } - } else if (IsIncreasingContiguous(vals)) { - return solver->MakeEquality(target, solver->MakeSum(index, vals[0])); } else { - return solver->RevAlloc( - new IntElementConstraint(solver, vals, index, target)); + if (IsIncreasingContiguous(vals)) { + return solver->MakeEquality(target, solver->MakeSum(index, vals[0])); + } else { + return solver->RevAlloc( + new IntElementConstraint(solver, vals, index, target)); + } } } } // namespace @@ -1417,11 +1366,11 @@ IntExpr* Solver::MakeElement(const std::vector& vars, IntVar* const ind IntVar* const scaled_index = MakeSum(index, -index->Min())->Var(); IntVar* const zero = vars[index->Min()]; IntVar* const one = vars[index->Max()]; - const std::string name = StringPrintf( - "ElementVar([%s], %s)", JoinNamePtr(vars, ", ").c_str(), - index->name().c_str()); - IntVar* const target =MakeIntVar(std::min(zero->Min(), one->Min()), - std::max(zero->Max(), one->Max()), name); + const std::string name = + StringPrintf("ElementVar([%s], %s)", JoinNamePtr(vars, ", ").c_str(), + index->name().c_str()); + IntVar* const target = MakeIntVar(std::min(zero->Min(), one->Min()), + std::max(zero->Max(), one->Max()), name); AddConstraint( RevAlloc(new IfThenElseCt(this, scaled_index, one, zero, target))); return target; diff --git a/src/constraint_solver/expr_array.cc b/src/constraint_solver/expr_array.cc index 184d049e71..9aabd85c1e 100644 --- a/src/constraint_solver/expr_array.cc +++ b/src/constraint_solver/expr_array.cc @@ -306,7 +306,8 @@ class SmallSumConstraint : public Constraint { for (int i = 0; i < vars_.size(); ++i) { if (!vars_[i]->Bound()) { Demon* const demon = MakeConstraintDemon1( - solver(), this, &SmallSumConstraint::VarChanged, "VarChanged", i); + solver(), this, &SmallSumConstraint::VarChanged, "VarChanged", + vars_[i]); vars_[i]->WhenRange(demon); } } @@ -371,8 +372,7 @@ class SmallSumConstraint : public Constraint { } } - void VarChanged(int term_index) { - IntVar* const var = vars_[term_index]; + void VarChanged(IntVar* var) { const int64 delta_min = var->Min() - var->OldMin(); const int64 delta_max = var->OldMax() - var->Max(); computed_min_.Add(solver(), delta_min); @@ -793,7 +793,8 @@ class SmallMinConstraint : public Constraint { for (int i = 0; i < vars_.size(); ++i) { if (!vars_[i]->Bound()) { Demon* const demon = MakeConstraintDemon1( - solver(), this, &SmallMinConstraint::VarChanged, "VarChanged", i); + solver(), this, &SmallMinConstraint::VarChanged, "VarChanged", + vars_[i]); vars_[i]->WhenRange(demon); } } @@ -834,8 +835,7 @@ class SmallMinConstraint : public Constraint { } private: - void VarChanged(int index) { - IntVar* const var = vars_[index]; + void VarChanged(IntVar* var) { const int64 old_min = var->OldMin(); const int64 var_min = var->Min(); const int64 var_max = var->Max(); @@ -1071,7 +1071,8 @@ class SmallMaxConstraint : public Constraint { for (int i = 0; i < vars_.size(); ++i) { if (!vars_[i]->Bound()) { Demon* const demon = MakeConstraintDemon1( - solver(), this, &SmallMaxConstraint::VarChanged, "VarChanged", i); + solver(), this, &SmallMaxConstraint::VarChanged, "VarChanged", + vars_[i]); vars_[i]->WhenRange(demon); } } @@ -1112,8 +1113,7 @@ class SmallMaxConstraint : public Constraint { } private: - void VarChanged(int index) { - IntVar* const var = vars_[index]; + void VarChanged(IntVar* var) { const int64 old_max = var->OldMax(); const int64 var_min = var->Min(); const int64 var_max = var->Max(); @@ -1195,8 +1195,9 @@ class ArrayBoolAndEq : public CastConstraint { void Post() override { for (int i = 0; i < vars_.size(); ++i) { if (!vars_[i]->Bound()) { - demons_[i] = MakeConstraintDemon1( - solver(), this, &ArrayBoolAndEq::PropagateVar, "PropagateVar", i); + demons_[i] = + MakeConstraintDemon1(solver(), this, &ArrayBoolAndEq::PropagateVar, + "PropagateVar", vars_[i]); vars_[i]->WhenBound(demons_[i]); } } @@ -1241,8 +1242,8 @@ class ArrayBoolAndEq : public CastConstraint { } } - void PropagateVar(int index) { - if (vars_[index]->Min() == 1) { + void PropagateVar(IntVar* var) { + if (var->Min() == 1) { unbounded_.Decr(solver()); if (unbounded_.Value() == 0 && !decided_.Switched()) { target_var_->SetMin(1); @@ -1324,8 +1325,9 @@ class ArrayBoolOrEq : public CastConstraint { void Post() override { for (int i = 0; i < vars_.size(); ++i) { if (!vars_[i]->Bound()) { - demons_[i] = MakeConstraintDemon1( - solver(), this, &ArrayBoolOrEq::PropagateVar, "PropagateVar", i); + demons_[i] = + MakeConstraintDemon1(solver(), this, &ArrayBoolOrEq::PropagateVar, + "PropagateVar", vars_[i]); vars_[i]->WhenBound(demons_[i]); } } @@ -1370,8 +1372,8 @@ class ArrayBoolOrEq : public CastConstraint { } } - void PropagateVar(int index) { - if (vars_[index]->Min() == 0) { + void PropagateVar(IntVar* var) { + if (var->Min() == 0) { unbounded_.Decr(solver()); if (unbounded_.Value() == 0 && !decided_.Switched()) { target_var_->SetMax(0); @@ -1470,8 +1472,9 @@ class SumBooleanLessOrEqualToOne : public BaseSumBooleanConstraint { void Post() override { for (int i = 0; i < vars_.size(); ++i) { if (!vars_[i]->Bound()) { - Demon* u = MakeConstraintDemon1( - solver(), this, &SumBooleanLessOrEqualToOne::Update, "Update", i); + Demon* u = MakeConstraintDemon1(solver(), this, + &SumBooleanLessOrEqualToOne::Update, + "Update", vars_[i]); vars_[i]->WhenBound(u); } } @@ -1480,26 +1483,27 @@ class SumBooleanLessOrEqualToOne : public BaseSumBooleanConstraint { void InitialPropagate() override { for (int i = 0; i < vars_.size(); ++i) { if (vars_[i]->Min() == 1) { - PushAllToZeroExcept(i); + PushAllToZeroExcept(vars_[i]); return; } } } - void Update(int index) { + void Update(IntVar* var) { if (!inactive_.Switched()) { - DCHECK(vars_[index]->Bound()); - if (vars_[index]->Min() == 1) { - PushAllToZeroExcept(index); + DCHECK(var->Bound()); + if (var->Min() == 1) { + PushAllToZeroExcept(var); } } } - void PushAllToZeroExcept(int index) { + void PushAllToZeroExcept(IntVar* var) { inactive_.Switch(solver()); for (int i = 0; i < vars_.size(); ++i) { - if (i != index && vars_[i]->Max() != 0) { - vars_[i]->SetMax(0); + IntVar* const other = vars_[i]; + if (other != var && other->Max() != 0) { + other->SetMax(0); } } } @@ -3091,8 +3095,9 @@ IntExpr* MakeScalProdAux(Solver* solver, const std::vector& vars, if (vars.size() > 8) { return solver->MakeSum( solver->RegisterIntExpr( - solver->RevAlloc(new PositiveBooleanScalProd( - solver, vars, coefs)))->Var(), + solver->RevAlloc( + new PositiveBooleanScalProd(solver, vars, coefs))) + ->Var(), constant); } else { return solver->MakeSum( diff --git a/src/constraint_solver/expr_cst.cc b/src/constraint_solver/expr_cst.cc index a73e93625c..6f5bd6ba5a 100644 --- a/src/constraint_solver/expr_cst.cc +++ b/src/constraint_solver/expr_cst.cc @@ -1083,8 +1083,7 @@ class NotMemberCt : public Constraint { }; } // namespace -Constraint* Solver::MakeMemberCt(IntExpr* expr, - const std::vector& values) { +Constraint* Solver::MakeMemberCt(IntExpr* expr, const std::vector& values) { const int64 coeff = ExtractExprProductCoeff(&expr); if (coeff == 0) { return std::find(values.begin(), values.end(), 0) == values.end() diff --git a/src/constraint_solver/expressions.cc b/src/constraint_solver/expressions.cc index 99f1896d4d..e2f560efbe 100644 --- a/src/constraint_solver/expressions.cc +++ b/src/constraint_solver/expressions.cc @@ -15,7 +15,7 @@ #include #include #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include #include @@ -2496,8 +2496,9 @@ std::string DomainIntVar::DebugString() const { if (min_.Value() == max_.Value()) { StringAppendF(&out, "%" GG_LL_FORMAT "d", min_.Value()); } else if (bits_ != nullptr) { - StringAppendF(&out, "%s", bits_->pretty_DebugString(min_.Value(), - max_.Value()).c_str()); + StringAppendF( + &out, "%s", + bits_->pretty_DebugString(min_.Value(), max_.Value()).c_str()); } else { StringAppendF(&out, "%" GG_LL_FORMAT "d..%" GG_LL_FORMAT "d", min_.Value(), max_.Value()); @@ -4290,7 +4291,7 @@ class TimesIntExpr : public BaseIntExpr { const int64 rmin = right_->Min(); const int64 rmax = right_->Max(); return std::min(std::min(CapProd(lmin, rmin), CapProd(lmax, rmax)), - std::min(CapProd(lmax, rmin), CapProd(lmin, rmax))); + std::min(CapProd(lmax, rmin), CapProd(lmin, rmax))); } void SetMin(int64 m) override; int64 Max() const override { @@ -4299,7 +4300,7 @@ class TimesIntExpr : public BaseIntExpr { const int64 rmin = right_->Min(); const int64 rmax = right_->Max(); return std::max(std::max(CapProd(lmin, rmin), CapProd(lmax, rmax)), - std::max(CapProd(lmax, rmin), CapProd(lmin, rmax))); + std::max(CapProd(lmax, rmin), CapProd(lmin, rmax))); } void SetMax(int64 m) override; bool Bound() const override; @@ -4554,8 +4555,12 @@ class TimesBooleanIntExpr : public BaseIntExpr { ~TimesBooleanIntExpr() override {} int64 Min() const override { switch (boolvar_->RawValue()) { - case 0: { return 0LL; } - case 1: { return expr_->Min(); } + case 0: { + return 0LL; + } + case 1: { + return expr_->Min(); + } default: { DCHECK_EQ(BooleanVar::kUnboundBooleanVarValue, boolvar_->RawValue()); return std::min(0LL, expr_->Min()); @@ -4565,8 +4570,12 @@ class TimesBooleanIntExpr : public BaseIntExpr { void SetMin(int64 m) override; int64 Max() const override { switch (boolvar_->RawValue()) { - case 0: { return 0LL; } - case 1: { return expr_->Max(); } + case 0: { + return 0LL; + } + case 1: { + return expr_->Max(); + } default: { DCHECK_EQ(BooleanVar::kUnboundBooleanVarValue, boolvar_->RawValue()); return std::max(0LL, expr_->Max()); @@ -6566,7 +6575,7 @@ void Solver::MakeIntVarArray(int var_count, int64 vmin, int64 vmax, IntVar** Solver::MakeIntVarArray(int var_count, int64 vmin, int64 vmax, const std::string& name) { - IntVar** vars = new IntVar* [var_count]; + IntVar** vars = new IntVar*[var_count]; for (int i = 0; i < var_count; ++i) { vars[i] = MakeIntVar(vmin, vmax, IndexedName(name, i, var_count)); } @@ -6587,7 +6596,7 @@ void Solver::MakeBoolVarArray(int var_count, std::vector* vars) { } IntVar** Solver::MakeBoolVarArray(int var_count, const std::string& name) { - IntVar** vars = new IntVar* [var_count]; + IntVar** vars = new IntVar*[var_count]; for (int i = 0; i < var_count; ++i) { vars[i] = MakeBoolVar(IndexedName(name, i, var_count)); } @@ -7273,7 +7282,9 @@ void IntVar::RemoveValues(const std::vector& values) { const int size = values.size(); DCHECK_GE(size, 0); switch (size) { - case 0: { return; } + case 0: { + return; + } case 1: { RemoveValue(values[0]); return; diff --git a/src/constraint_solver/graph_constraints.cc b/src/constraint_solver/graph_constraints.cc index 15df1d3649..f8bebde522 100644 --- a/src/constraint_solver/graph_constraints.cc +++ b/src/constraint_solver/graph_constraints.cc @@ -12,7 +12,7 @@ // limitations under the License. #include -#include "base/unique_ptr.h" +#include #include #include @@ -43,14 +43,9 @@ namespace { class NoCycle : public Constraint { public: NoCycle(Solver* const s, const std::vector& nexts, - const std::vector& active, - ResultCallback1* sink_handler, bool owner, + const std::vector& active, Solver::IndexFilter1 sink_handler, bool assume_paths); - ~NoCycle() override { - if (owner_) { - delete sink_handler_; - } - } + ~NoCycle() override {} void Post() override; void InitialPropagate() override; void NextChange(int index); @@ -83,16 +78,14 @@ class NoCycle : public Constraint { std::vector outbound_supports_; std::vector support_leaves_; std::vector unsupported_; - ResultCallback1* sink_handler_; + Solver::IndexFilter1 sink_handler_; std::vector sinks_; - bool owner_; bool assume_paths_; }; NoCycle::NoCycle(Solver* const s, const std::vector& nexts, const std::vector& active, - ResultCallback1* sink_handler, bool owner, - bool assume_paths) + Solver::IndexFilter1 sink_handler, bool assume_paths) : Constraint(s), nexts_(nexts), active_(active), @@ -102,7 +95,6 @@ NoCycle::NoCycle(Solver* const s, const std::vector& nexts, all_nexts_bound_(false), outbound_supports_(nexts.size(), -1), sink_handler_(sink_handler), - owner_(owner), assume_paths_(assume_paths) { support_leaves_.reserve(size()); unsupported_.reserve(size()); @@ -111,7 +103,6 @@ NoCycle::NoCycle(Solver* const s, const std::vector& nexts, ends_[i] = i; iterators_[i] = nexts_[i]->MakeDomainIterator(true); } - sink_handler_->CheckIsRepeatable(); } void NoCycle::InitialPropagate() { @@ -120,12 +111,12 @@ void NoCycle::InitialPropagate() { outbound_supports_[i] = -1; IntVar* next = nexts_[i]; for (int j = next->Min(); j < 0; ++j) { - if (!sink_handler_->Run(j)) { + if (!sink_handler_(j)) { next->RemoveValue(j); } } for (int j = next->Max(); j >= size(); --j) { - if (!sink_handler_->Run(j)) { + if (!sink_handler_(j)) { next->RemoveValue(j); } } @@ -162,7 +153,7 @@ void NoCycle::Post() { } sinks_.clear(); for (int i = min_min; i <= max_max; ++i) { - if (sink_handler_->Run(i)) { + if (sink_handler_(i)) { sinks_.push_back(i); } } @@ -201,11 +192,11 @@ void NoCycle::NextBound(int index) { if (active_[index]->Min() == 0) return; const int64 next = nexts_[index]->Value(); const int64 chain_start = starts_[index]; - const int64 chain_end = !sink_handler_->Run(next) ? ends_[next] : next; + const int64 chain_end = !sink_handler_(next) ? ends_[next] : next; Solver* const s = solver(); - if (!sink_handler_->Run(chain_start)) { + if (!sink_handler_(chain_start)) { s->SaveAndSetValue(&ends_[chain_start], chain_end); - if (!sink_handler_->Run(chain_end)) { + if (!sink_handler_(chain_end)) { s->SaveAndSetValue(&starts_[chain_end], chain_start); nexts_[chain_end]->RemoveValue(chain_start); if (!assume_paths_) { @@ -214,7 +205,7 @@ void NoCycle::NextBound(int index) { bool found = (current == chain_end); // Counter to detect implicit cycles. int count = 0; - while (!found && count < size() && !sink_handler_->Run(current) && + while (!found && count < size() && !sink_handler_(current) && nexts_[current]->Bound()) { current = nexts_[current]->Value(); found = (current == chain_end); @@ -253,7 +244,7 @@ void NoCycle::ComputeSupports() { const int64 current_support = outbound_supports_[i]; // Optimization: if this node was already supported by a sink, check if // it's still a valid support. - if (current_support >= 0 && sink_handler_->Run(current_support) && + if (current_support >= 0 && sink_handler_(current_support) && next->Contains(current_support)) { support_leaves_.push_back(i); } else { @@ -270,7 +261,7 @@ void NoCycle::ComputeSupports() { } } else { for (const int64 value : InitAndGetValues(iterators_[i])) { - if (sink_handler_->Run(value)) { + if (sink_handler_(value)) { outbound_supports_[i] = value; support_leaves_.push_back(i); break; @@ -327,7 +318,7 @@ void NoCycle::ComputeSupport(int index) { // which is both supported and was not a descendant of the node in the tree. if (active_[index]->Max() != 0) { for (const int64 next : InitAndGetValues(iterators_[index])) { - if (sink_handler_->Run(next)) { + if (sink_handler_(next)) { outbound_supports_[index] = next; return; } @@ -337,7 +328,7 @@ void NoCycle::ComputeSupport(int index) { // Check if next is not already a descendant of index. bool ancestor_found = false; while (next_support < outbound_supports_.size() && - !sink_handler_->Run(next_support)) { + !sink_handler_(next_support)) { if (next_support == index) { ancestor_found = true; break; @@ -611,28 +602,23 @@ class Circuit : public Constraint { NumericalRev num_inactives_; const bool sub_circuit_; }; - -// ----- Misc ----- - -bool GreaterThan(int64 x, int64 y) { return y >= x; } } // namespace Constraint* Solver::MakeNoCycle(const std::vector& nexts, const std::vector& active, - ResultCallback1* sink_handler, + Solver::IndexFilter1 sink_handler, bool assume_paths) { CHECK_EQ(nexts.size(), active.size()); if (sink_handler == nullptr) { - sink_handler = - NewPermanentCallback(&GreaterThan, static_cast(nexts.size())); + const int64 size = nexts.size(); + sink_handler = [size](int64 index) { return index >= size; }; } - return RevAlloc( - new NoCycle(this, nexts, active, sink_handler, true, assume_paths)); + return RevAlloc(new NoCycle(this, nexts, active, sink_handler, assume_paths)); } Constraint* Solver::MakeNoCycle(const std::vector& nexts, const std::vector& active, - ResultCallback1* sink_handler) { + Solver::IndexFilter1 sink_handler) { return MakeNoCycle(nexts, active, sink_handler, true); } @@ -1133,13 +1119,13 @@ class DelayedPathCumul : public Constraint { // cumuls[next[i]] = cumuls[i] + transit_evaluator(i, next[i]) -class ResultCallback2PathCumul : public BasePathCumul { +class IndexEvaluator2PathCumul : public BasePathCumul { public: - ResultCallback2PathCumul(Solver* const s, const std::vector& nexts, + IndexEvaluator2PathCumul(Solver* const s, const std::vector& nexts, const std::vector& active, const std::vector& cumuls, - Solver::IndexEvaluator2* transit_evaluator); - ~ResultCallback2PathCumul() override {} + Solver::IndexEvaluator2 transit_evaluator); + ~IndexEvaluator2PathCumul() override {} void NextBound(int index) override; bool AcceptLink(int i, int j) const override; @@ -1159,24 +1145,22 @@ class ResultCallback2PathCumul : public BasePathCumul { } private: - std::unique_ptr transits_evaluator_; + Solver::IndexEvaluator2 transits_evaluator_; }; -ResultCallback2PathCumul::ResultCallback2PathCumul( +IndexEvaluator2PathCumul::IndexEvaluator2PathCumul( Solver* const s, const std::vector& nexts, const std::vector& active, const std::vector& cumuls, - Solver::IndexEvaluator2* transit_evaluator) + Solver::IndexEvaluator2 transit_evaluator) : BasePathCumul(s, nexts, active, cumuls), - transits_evaluator_(transit_evaluator) { - transits_evaluator_->CheckIsRepeatable(); -} + transits_evaluator_(std::move(transit_evaluator)) {} -void ResultCallback2PathCumul::NextBound(int index) { +void IndexEvaluator2PathCumul::NextBound(int index) { if (active_[index]->Min() == 0) return; const int64 next = nexts_[index]->Value(); IntVar* cumul = cumuls_[index]; IntVar* cumul_next = cumuls_[next]; - const int64 transit = transits_evaluator_->Run(index, next); + const int64 transit = transits_evaluator_(index, next); cumul_next->SetMin(cumul->Min() + transit); cumul_next->SetMax(CapAdd(cumul->Max(), transit)); cumul->SetMin(CapSub(cumul_next->Min(), transit)); @@ -1186,24 +1170,24 @@ void ResultCallback2PathCumul::NextBound(int index) { } } -bool ResultCallback2PathCumul::AcceptLink(int i, int j) const { +bool IndexEvaluator2PathCumul::AcceptLink(int i, int j) const { const IntVar* const cumul_i = cumuls_[i]; const IntVar* const cumul_j = cumuls_[j]; - const int64 transit = transits_evaluator_->Run(i, j); + const int64 transit = transits_evaluator_(i, j); return transit <= CapSub(cumul_j->Max(), cumul_i->Min()) && CapSub(cumul_j->Min(), cumul_i->Max()) <= transit; } // ----- ResulatCallback2SlackPathCumul ----- -class ResultCallback2SlackPathCumul : public BasePathCumul { +class IndexEvaluator2SlackPathCumul : public BasePathCumul { public: - ResultCallback2SlackPathCumul(Solver* const s, const std::vector& nexts, + IndexEvaluator2SlackPathCumul(Solver* const s, const std::vector& nexts, const std::vector& active, const std::vector& cumuls, const std::vector& slacks, - Solver::IndexEvaluator2* transit_evaluator); - ~ResultCallback2SlackPathCumul() override {} + Solver::IndexEvaluator2 transit_evaluator); + ~IndexEvaluator2SlackPathCumul() override {} void Post() override; void NextBound(int index) override; bool AcceptLink(int i, int j) const override; @@ -1226,30 +1210,28 @@ class ResultCallback2SlackPathCumul : public BasePathCumul { private: const std::vector slacks_; - std::unique_ptr transits_evaluator_; + Solver::IndexEvaluator2 transits_evaluator_; }; -ResultCallback2SlackPathCumul::ResultCallback2SlackPathCumul( +IndexEvaluator2SlackPathCumul::IndexEvaluator2SlackPathCumul( Solver* const s, const std::vector& nexts, const std::vector& active, const std::vector& cumuls, - const std::vector& slacks, Solver::IndexEvaluator2* transit_evaluator) + const std::vector& slacks, Solver::IndexEvaluator2 transit_evaluator) : BasePathCumul(s, nexts, active, cumuls), slacks_(slacks), - transits_evaluator_(transit_evaluator) { - transits_evaluator_->CheckIsRepeatable(); -} + transits_evaluator_(std::move(transit_evaluator)) {} -void ResultCallback2SlackPathCumul::Post() { +void IndexEvaluator2SlackPathCumul::Post() { BasePathCumul::Post(); for (int i = 0; i < size(); ++i) { Demon* slack_demon = MakeConstraintDemon1( - solver(), this, &ResultCallback2SlackPathCumul::SlackRange, + solver(), this, &IndexEvaluator2SlackPathCumul::SlackRange, "SlackRange", i); slacks_[i]->WhenRange(slack_demon); } } -void ResultCallback2SlackPathCumul::SlackRange(int index) { +void IndexEvaluator2SlackPathCumul::SlackRange(int index) { if (nexts_[index]->Bound()) { NextBound(index); } else { @@ -1266,13 +1248,13 @@ void ResultCallback2SlackPathCumul::SlackRange(int index) { } } -void ResultCallback2SlackPathCumul::NextBound(int index) { +void IndexEvaluator2SlackPathCumul::NextBound(int index) { if (active_[index]->Min() == 0) return; const int64 next = nexts_[index]->Value(); IntVar* const cumul = cumuls_[index]; IntVar* const cumul_next = cumuls_[next]; IntVar* const slack = slacks_[index]; - const int64 transit = transits_evaluator_->Run(index, next); + const int64 transit = transits_evaluator_(index, next); 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())); @@ -1286,11 +1268,11 @@ void ResultCallback2SlackPathCumul::NextBound(int index) { } } -bool ResultCallback2SlackPathCumul::AcceptLink(int i, int j) const { +bool IndexEvaluator2SlackPathCumul::AcceptLink(int i, int j) const { const IntVar* const cumul_i = cumuls_[i]; const IntVar* const cumul_j = cumuls_[j]; const IntVar* const slack = slacks_[i]; - const int64 transit = transits_evaluator_->Run(i, j); + const int64 transit = transits_evaluator_(i, j); return CapAdd(transit, slack->Min()) <= CapSub(cumul_j->Max(), cumul_i->Min()) && CapSub(cumul_j->Min(), cumul_i->Max()) <= @@ -1310,9 +1292,9 @@ Constraint* Solver::MakePathCumul(const std::vector& nexts, Constraint* Solver::MakePathCumul(const std::vector& nexts, const std::vector& active, const std::vector& cumuls, - Solver::IndexEvaluator2* transit_evaluator) { + Solver::IndexEvaluator2 transit_evaluator) { CHECK_EQ(nexts.size(), active.size()); - return RevAlloc(new ResultCallback2PathCumul(this, nexts, active, cumuls, + return RevAlloc(new IndexEvaluator2PathCumul(this, nexts, active, cumuls, transit_evaluator)); } @@ -1320,9 +1302,9 @@ Constraint* Solver::MakePathCumul(const std::vector& nexts, const std::vector& active, const std::vector& cumuls, const std::vector& slacks, - Solver::IndexEvaluator2* transit_evaluator) { + Solver::IndexEvaluator2 transit_evaluator) { CHECK_EQ(nexts.size(), active.size()); - return RevAlloc(new ResultCallback2SlackPathCumul(this, nexts, active, cumuls, + return RevAlloc(new IndexEvaluator2SlackPathCumul(this, nexts, active, cumuls, slacks, transit_evaluator)); } diff --git a/src/constraint_solver/hybrid.cc b/src/constraint_solver/hybrid.cc index 59c02b2a2f..0e953aa0e2 100644 --- a/src/constraint_solver/hybrid.cc +++ b/src/constraint_solver/hybrid.cc @@ -17,7 +17,7 @@ #include #include #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include "base/callback.h" #include "base/commandlineflags.h" #include "base/integral_types.h" diff --git a/src/constraint_solver/io.cc b/src/constraint_solver/io.cc index 65895ba751..43126f373c 100644 --- a/src/constraint_solver/io.cc +++ b/src/constraint_solver/io.cc @@ -16,7 +16,7 @@ #include #include #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include #include @@ -413,7 +413,8 @@ class ArgumentHolder { const IntTupleSet& values) { const int rows = values.NumTuples(); const int columns = values.Arity(); - std::pair> matrix = std::make_pair(columns, std::vector()); + std::pair> matrix = + std::make_pair(columns, std::vector()); integer_matrix_argument_[arg_name] = matrix; std::vector* const vals = &integer_matrix_argument_[arg_name].second; for (int i = 0; i < rows; ++i) { @@ -925,9 +926,9 @@ class ArrayWithOffset : public BaseObject { }; template -void MakeCallbackFromProto(CPModelLoader* const builder, - const CPExtensionProto& proto, int tag_index, - ResultCallback1** callback) { +std::function MakeFunctionFromProto(CPModelLoader* const builder, + const CPExtensionProto& proto, + int tag_index) { DCHECK_EQ(tag_index, proto.type_index()); Solver* const solver = builder->solver(); int64 index_min = 0; @@ -941,7 +942,7 @@ void MakeCallbackFromProto(CPModelLoader* const builder, for (int i = index_min; i <= index_max; ++i) { array->SetValue(i, values[i - index_min]); } - *callback = NewPermanentCallback(array, &ArrayWithOffset::Evaluate); + return [array](int64 index) { return array->Evaluate(index); }; } #define VERIFY(expr) \ @@ -1241,11 +1242,10 @@ IntExpr* BuildElement(CPModelLoader* const builder, std::vector values; if (proto.extensions_size() > 0) { VERIFY_EQ(1, proto.extensions_size()); - Solver::IndexEvaluator1* callback = nullptr; const int extension_tag_index = builder->TagIndex(ModelVisitor::kInt64ToInt64Extension); - MakeCallbackFromProto(builder, proto.extensions(0), extension_tag_index, - &callback); + Solver::IndexEvaluator1 callback = MakeFunctionFromProto( + builder, proto.extensions(0), extension_tag_index); return builder->solver()->MakeElement(callback, index->Var()); } if (builder->ScanArguments(ModelVisitor::kValuesArgument, proto, &values)) { @@ -1844,15 +1844,16 @@ Constraint* BuildNoCycle(CPModelLoader* const builder, int64 assume_paths = 0; VERIFY(builder->ScanArguments(ModelVisitor::kAssumePathsArgument, proto, &assume_paths)); - ResultCallback1* sink_handler = nullptr; + Solver::IndexFilter1 sink_handler = nullptr; if (proto.extensions_size() > 0) { VERIFY_EQ(1, proto.extensions_size()); const int tag_index = builder->TagIndex(ModelVisitor::kInt64ToBoolExtension); - MakeCallbackFromProto(builder, proto.extensions(0), tag_index, - &sink_handler); + sink_handler = + MakeFunctionFromProto(builder, proto.extensions(0), tag_index); } - return builder->solver()->MakeNoCycle(nexts, active, nullptr, assume_paths); + return builder->solver()->MakeNoCycle(nexts, active, sink_handler, + assume_paths); } // ----- kNonEqual ----- @@ -2295,7 +2296,8 @@ bool CPModelLoader::BuildFromProto(const CPIntegerExpressionProto& proto) { if (!built) { return false; } - expressions_.resize(std::max(static_cast(expressions_.size()), index + 1)); + expressions_.resize( + std::max(static_cast(expressions_.size()), index + 1)); expressions_[index] = built; return true; } diff --git a/src/constraint_solver/java/constraint_solver.swig b/src/constraint_solver/java/constraint_solver.swig index 6fe1242dfe..ec3c716886 100644 --- a/src/constraint_solver/java/constraint_solver.swig +++ b/src/constraint_solver/java/constraint_solver.swig @@ -15,9 +15,16 @@ %include "enumsimple.swg" %include "exception.i" +%include "std_vector.i" + +%include "base/base.swig" %include "util/java/proto.swig" %include "util/java/tuple_set.swig" %include "util/java/vector.swig" +%include "util/java/functions.swig" + +// Remove swig warnings +#pragma SWIG nowarn=473 // ############ BEGIN DUPLICATED CODE BLOCK ############ // IMPORTANT: keep this code block in sync with the .swig @@ -71,136 +78,8 @@ PROTECT_FROM_FAILURE(Solver::Fail(), arg1); // ############ END DUPLICATED CODE BLOCK ############ -// TODO(user): split out the callback code; it creates another file since -// it uses a different module. -%module(directors="1") main -%feature("director") LongResultCallback1; -%feature("director") LongResultCallback2; -%feature("director") LongResultCallback3; -%include -%{ -#include -#include "base/callback.h" -#include "base/integral_types.h" - -// When a director is created for a class with SWIG, the C++ part of the -// director keeps a JNI global reference to the Java part. This global reference -// only gets deleted in the destructor of the C++ part, but by default, this -// only happens when the Java part is processed by the GC (however, this never -// happens, because there is the JNI global reference...). -// -// To break the cycle, it is necessary to delete the C++ part manually. For the -// callback classes, this is done by deriving them from the respective C++ -// ResultCallback classes. When the java callback class is asked for a C++ -// callback class, it hands over its C++ part. It is expected, that whoever -// receives the C++ callback class, owns it and destroys it after they no longer -// need it. But by destroying it, they also break the reference cycle and the -// Java part may be processed by the GC. -// -// However, this behavior also means that the callback class can only be used -// in one context and that if its C++ callback class is not received by someone -// who destroys it in the end, it will stay in memory forever. -class LongResultCallback1 : private ResultCallback1 { - public: - LongResultCallback1() : used_as_permanent_handler_(false) {} - virtual int64 run(int64) = 0; - ResultCallback1* GetPermanentCallback() { - CHECK(!used_as_permanent_handler_); - used_as_permanent_handler_ = true; - return this; - } - virtual ~LongResultCallback1() {} - private: - virtual bool IsRepeatable() const { return true; } - - virtual int64 Run(int64 i) { - return run(i); - } - bool used_as_permanent_handler_; -}; - -class LongResultCallback2 : private ResultCallback2 { - public: - LongResultCallback2() : used_as_permanent_handler_(false) {} - virtual ~LongResultCallback2() {} - virtual int64 run(int64, int64) = 0; - ResultCallback2* GetPermanentCallback() { - CHECK(!used_as_permanent_handler_); - used_as_permanent_handler_ = true; - return this; - } - private: - virtual bool IsRepeatable() const { return true; } - - virtual int64 Run(int64 i, int64 j) { - return run(i, j); - } - bool used_as_permanent_handler_; -}; - -class LongResultCallback3 : private ResultCallback3 { - public: - LongResultCallback3() : used_as_permanent_handler_(false) {} - virtual ~LongResultCallback3() {} - virtual int64 run(int64, int64, int64) = 0; - ResultCallback3* GetPermanentCallback() { - CHECK(!used_as_permanent_handler_); - used_as_permanent_handler_ = true; - return this; - } - private: - virtual bool IsRepeatable() const { return true; } - - virtual int64 Run(int64 i, int64 j, int64 k) { - return run(i, j, k); - } - bool used_as_permanent_handler_; -}; -%} - -class LongResultCallback1 : private ResultCallback1 { - public: - virtual int64 run(int64) = 0; - ResultCallback1* GetPermanentCallback(); - virtual ~LongResultCallback1(); - private: - virtual bool IsRepeatable() const; - virtual int64 Run(int64 i); - bool used_as_permanent_handler_; -}; -class LongResultCallback2 : private ResultCallback2 { - public: - virtual int64 run(int64, int64) = 0; - ResultCallback2* GetPermanentCallback(); - virtual ~LongResultCallback2(); - private: - virtual bool IsRepeatable() const; - virtual int64 Run(int64 i, int64 j); - bool used_as_permanent_handler_; -}; -class LongResultCallback3 : private ResultCallback3 { - public: - virtual int64 run(int64, int64, int64) = 0; - ResultCallback3* GetPermanentCallback(); - virtual ~LongResultCallback3(); - private: - virtual bool IsRepeatable() const; - virtual int64 Run(int64 i, int64 j, int64 k); - bool used_as_permanent_handler_; -}; - -// Typemaps for callbacks in java. -%typemap(jstype) ResultCallback1* "LongResultCallback1"; -%typemap(javain) ResultCallback1* "$descriptor(ResultCallback1*).getCPtr($javainput.GetPermanentCallback())"; -%typemap(jstype) ResultCallback2* "LongResultCallback2"; -%typemap(javain) ResultCallback2* "$descriptor(ResultCallback2*).getCPtr($javainput.GetPermanentCallback())"; - -%typemap(jstype) ResultCallback3* -"LongResultCallback3"; -%typemap(javain) ResultCallback3* -"$descriptor(ResultCallback3*).getCPtr($javainput.GetPermanentCallback())"; - %module(directors="1") operations_research; + %feature("director") DecisionBuilder; %feature("director") Decision; %feature("director") SearchMonitor; @@ -211,13 +90,15 @@ class LongResultCallback3 : private ResultCallback3 %feature("director") IntVarLocalSearchOperator; %feature("director") SequenceVarLocalSearchOperator; %feature("director") IntVarLocalSearchFilter; -%include "std_vector.i" - %template(IntVector) std::vector; + %{ #include +#include + +#include "base/integral_types.h" #include "constraint_solver/constraint_solver.h" #include "constraint_solver/constraint_solveri.h" @@ -238,11 +119,15 @@ struct FailureProtect { }; %} +// Renaming + %ignore operations_research::Solver::MakeIntVarArray; %ignore operations_research::Solver::MakeBoolVarArray; %ignore operations_research::Solver::MakeFixedDurationIntervalVarArray; %ignore operations_research::IntVarLocalSearchFilter::FindIndex; +// This method causes issues with our std::vector wrapping. It's not really +// part of the public API anyway. %ignore operations_research::ToInt64Vector; %rename (nextWrap) operations_research::DecisionBuilder::Next; @@ -372,12 +257,43 @@ CONVERT_VECTOR(LocalSearchFilter); %extend IntVarLocalSearchFilter { int Index(IntVar* const var) { int64 index = -1; - self->FindIndex(var, &index); + $self->FindIndex(var, &index); return index; } } + } // namespace operations_research +// Create std::function wrappers. +WRAP_STD_FUNCTION_JAVA( + LongToLong, + "com/google/ortools/constraintsolver/", + int64, Long, int64) +WRAP_STD_FUNCTION_JAVA( + LongLongToLong, + "com/google/ortools/constraintsolver/", + int64, Long, int64, int64) +WRAP_STD_FUNCTION_JAVA( + IntIntToLong, + "com/google/ortools/constraintsolver/", + int64, Long, int, int) +WRAP_STD_FUNCTION_JAVA( + LongLongLongToLong, + "com/google/ortools/constraintsolver/", + int64, Long, int64, int64, int64) +WRAP_STD_FUNCTION_JAVA( + LongToBoolean, + "com/google/ortools/constraintsolver/", + bool, Boolean, int64) +WRAP_STD_FUNCTION_JAVA( + VoidToBoolean, + "com/google/ortools/constraintsolver/", + bool, Boolean) +WRAP_STD_FUNCTION_JAVA( + LongLongLongToBoolean, + "com/google/ortools/constraintsolver/", + bool, Boolean, int64, int64, int64) + namespace operations_research { class LocalSearchPhaseParameters { public: @@ -388,13 +304,13 @@ class LocalSearchPhaseParameters { // Wrap cp includes -%include constraint_solver/constraint_solver.h -%include constraint_solver/constraint_solveri.h +%include "constraint_solver/constraint_solver.h" +%include "constraint_solver/constraint_solveri.h" // Define templates instantiation after wrapping. namespace operations_research { -%template(RevInteger) Rev; +%template(RevInteger) Rev; +%template(RevLong) Rev; %template(RevBool) Rev; -typedef Assignment::AssignmentContainer AssignmentContainer; %template(AssignmentIntContainer) AssignmentContainer; -} +} // namespace operations_research diff --git a/src/constraint_solver/java/routing.swig b/src/constraint_solver/java/routing.swig index e6a4a72ad8..647795e0ba 100644 --- a/src/constraint_solver/java/routing.swig +++ b/src/constraint_solver/java/routing.swig @@ -45,35 +45,93 @@ VECTOR_AS_JAVA_ARRAY(operations_research::RoutingModel::NodeIndex, int, Int); %typemap(jstype) const std::vector >& "int[][]" %typemap(javain) const std::vector >& "$javainput" -%typemap(in) const std::vector >& -(std::vector > temp) { - if ($input) { - const int size = jenv->GetArrayLength($input); - temp.clear(); - temp.resize(size); - for (int i = 0; i < size; ++i) { - jintArray values = - (jintArray)jenv->GetObjectArrayElement((jobjectArray)$input, i); - const int inner_size = jenv->GetArrayLength(values); - jint* inner_values = jenv->GetIntArrayElements(values, nullptr); - for (int j = 0; j < inner_size; ++j) { - const int value = inner_values[j]; - temp[i].push_back(operations_research::RoutingModel::NodeIndex(value)); - } - jenv->ReleaseIntArrayElements(values, inner_values, 0); - jenv->DeleteLocalRef(values); - } - $1 = &temp; - } else { - SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null table"); - return $null; - } -} - -// Create input mapping for NodeEvaluator2 -%module(directors="1") main +// Useful directors. +%feature("director") LongResultCallback1; +%feature("director") LongResultCallback2; +%feature("director") LongResultCallback3; %feature("director") NodeEvaluator2; + +%include "std_vector.i" + %{ +#include +#include "base/callback.h" +#include "base/integral_types.h" + +// When a director is created for a class with SWIG, the C++ part of the +// director keeps a JNI global reference to the Java part. This global reference +// only gets deleted in the destructor of the C++ part, but by default, this +// only happens when the Java part is processed by the GC (however, this never +// happens, because there is the JNI global reference...). +// +// To break the cycle, it is necessary to delete the C++ part manually. For the +// callback classes, this is done by deriving them from the respective C++ +// ResultCallback classes. When the java callback class is asked for a C++ +// callback class, it hands over its C++ part. It is expected, that whoever +// receives the C++ callback class, owns it and destroys it after they no longer +// need it. But by destroying it, they also break the reference cycle and the +// Java part may be processed by the GC. +// +// However, this behavior also means that the callback class can only be used +// in one context and that if its C++ callback class is not received by someone +// who destroys it in the end, it will stay in memory forever. +class LongResultCallback1 : private ResultCallback1 { + public: + LongResultCallback1() : used_as_permanent_handler_(false) {} + virtual int64 run(int64) = 0; + ResultCallback1* getPermanentCallback() { + CHECK(!used_as_permanent_handler_); + used_as_permanent_handler_ = true; + return this; + } + virtual ~LongResultCallback1() {} + private: + virtual bool IsRepeatable() const { return true; } + + virtual int64 Run(int64 i) { + return run(i); + } + bool used_as_permanent_handler_; +}; + +class LongResultCallback2 : private ResultCallback2 { + public: + LongResultCallback2() : used_as_permanent_handler_(false) {} + virtual ~LongResultCallback2() {} + virtual int64 run(int64, int64) = 0; + ResultCallback2* getPermanentCallback() { + CHECK(!used_as_permanent_handler_); + used_as_permanent_handler_ = true; + return this; + } + private: + virtual bool IsRepeatable() const { return true; } + + virtual int64 Run(int64 i, int64 j) { + return run(i, j); + } + bool used_as_permanent_handler_; +}; + +class LongResultCallback3 : private ResultCallback3 { + public: + LongResultCallback3() : used_as_permanent_handler_(false) {} + virtual ~LongResultCallback3() {} + virtual int64 run(int64, int64, int64) = 0; + ResultCallback3* getPermanentCallback() { + CHECK(!used_as_permanent_handler_); + used_as_permanent_handler_ = true; + return this; + } + private: + virtual bool IsRepeatable() const { return true; } + + virtual int64 Run(int64 i, int64 j, int64 k) { + return run(i, j, k); + } + bool used_as_permanent_handler_; +}; + // When created, instances of NodeEvaluator2 must be used in a context where // someone takes ownership of the C++ part of the NodeEvaluator2 and deletes // it when no longer needed. Otherwise, the object would remain on the heap @@ -99,6 +157,40 @@ class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 }; %} +class LongResultCallback1 : private ResultCallback1 { + public: + virtual int64 run(int64) = 0; + ResultCallback1* getPermanentCallback(); + virtual ~LongResultCallback1(); + + private: + virtual bool IsRepeatable() const; + virtual int64 Run(int64 i); + bool used_as_permanent_handler_; +}; +class LongResultCallback2 : private ResultCallback2 { + public: + virtual int64 run(int64, int64) = 0; + ResultCallback2* getPermanentCallback(); + virtual ~LongResultCallback2(); + + private: + virtual bool IsRepeatable() const; + virtual int64 Run(int64 i, int64 j); + bool used_as_permanent_handler_; +}; +class LongResultCallback3 : private ResultCallback3 { + public: + virtual int64 run(int64, int64, int64) = 0; + ResultCallback3* getPermanentCallback(); + virtual ~LongResultCallback3(); + + private: + virtual bool IsRepeatable() const; + virtual int64 Run(int64 i, int64 j, int64 k); + bool used_as_permanent_handler_; +}; + class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 { public: NodeEvaluator2() : used_as_permanent_handler_(false) {} @@ -119,9 +211,44 @@ class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 bool used_as_permanent_handler_; }; +// Typemaps for callbacks in java. +%typemap(jstype) ResultCallback1* "LongResultCallback1"; +%typemap(javain) ResultCallback1* "$descriptor(ResultCallback1*).getCPtr($javainput.getPermanentCallback())"; + +%typemap(jstype) ResultCallback2* "LongResultCallback2"; +%typemap(javain) ResultCallback2* "$descriptor(ResultCallback2*).getCPtr($javainput.getPermanentCallback())"; + +%typemap(jstype) ResultCallback3* "LongResultCallback3"; +%typemap(javain) ResultCallback3* "$descriptor(ResultCallback3*).getCPtr($javainput.getPermanentCallback())"; + %typemap(jstype) operations_research::RoutingModel::NodeEvaluator2* "NodeEvaluator2"; %typemap(javain) operations_research::RoutingModel::NodeEvaluator2* "$descriptor(ResultCallback2*).getCPtr($javainput.getPermanentCallback())"; +%typemap(in) const std::vector >& +(std::vector > temp) { + if ($input) { + const int size = jenv->GetArrayLength($input); + temp.clear(); + temp.resize(size); + for (int i = 0; i < size; ++i) { + jintArray values = + (jintArray)jenv->GetObjectArrayElement((jobjectArray)$input, i); + const int inner_size = jenv->GetArrayLength(values); + jint* inner_values = jenv->GetIntArrayElements(values, nullptr); + for (int j = 0; j < inner_size; ++j) { + const int value = inner_values[j]; + temp[i].push_back(operations_research::RoutingModel::NodeIndex(value)); + } + jenv->ReleaseIntArrayElements(values, inner_values, 0); + jenv->DeleteLocalRef(values); + } + $1 = &temp; + } else { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null table"); + return $null; + } +} + %ignore operations_research::RoutingModel::AddVectorDimension( const int64* values, int64 capacity, @@ -137,15 +264,12 @@ class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 int64 capacity, bool fix_start_cumul_to_zero, const std::string& name) { - DCHECK_EQ(values.size(), self->nodes()); - self->AddVectorDimension(values.data(), capacity, - fix_start_cumul_to_zero, name); + DCHECK_EQ(values.size(), $self->nodes()); + $self->AddVectorDimension(values.data(), capacity, + fix_start_cumul_to_zero, name); } } -%ignore operations_research::RoutingModel::WrapIndexEvaluator( - Solver::IndexEvaluator2* evaluator); - %ignore operations_research::RoutingModel::RoutingModel( int nodes, int vehicles, const std::vector >& start_end); @@ -216,4 +340,4 @@ class NodeEvaluator2 : private operations_research::RoutingModel::NodeEvaluator2 // Wrap cp includes -%include constraint_solver/routing.h +%include "constraint_solver/routing.h" diff --git a/src/constraint_solver/local_search.cc b/src/constraint_solver/local_search.cc index 1fb854e7ec..aa7644706f 100644 --- a/src/constraint_solver/local_search.cc +++ b/src/constraint_solver/local_search.cc @@ -16,7 +16,7 @@ #include "base/hash.h" #include "base/hash.h" #include -#include "base/unique_ptr.h" +#include #include #include #include @@ -692,8 +692,7 @@ bool TwoOpt::MakeNeighbor() { if (ReverseChain(BaseNode(0), BaseNode(1), &chain_last) // Check there are more than one node in the chain (reversing a // single node is a NOP). - && - last_ != chain_last) { + && last_ != chain_last) { return true; } else { last_ = -1; @@ -937,11 +936,13 @@ class MakeActiveAndRelocate : public BaseInactiveNodeToPathOperator { bool MakeActiveAndRelocate::MakeNeighbor() { const int64 before_chain = BaseNode(1); - if (IsPathEnd(before_chain)) { return false; } + if (IsPathEnd(before_chain)) { + return false; + } const int64 chain_end = Next(before_chain); const int64 destination = BaseNode(0); return MoveChain(before_chain, chain_end, destination) && - MakeActive(GetInactiveNode(), destination); + MakeActive(GetInactiveNode(), destination); } // ----- MakeInactiveOperator ----- @@ -1093,7 +1094,7 @@ bool ExtendedSwapActiveOperator::MakeNeighbor() { class TSPOpt : public PathOperator { public: TSPOpt(const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* evaluator, int chain_length); + Solver::IndexEvaluator3 evaluator, int chain_length); ~TSPOpt() override {} bool MakeNeighbor() override; @@ -1102,13 +1103,13 @@ class TSPOpt : public PathOperator { private: std::vector > cost_; HamiltonianPathSolver hamiltonian_path_solver_; - std::unique_ptr evaluator_; + Solver::IndexEvaluator3 evaluator_; const int chain_length_; }; TSPOpt::TSPOpt(const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* evaluator, int chain_length) + Solver::IndexEvaluator3 evaluator, int chain_length) : PathOperator(vars, secondary_vars, 1, nullptr), hamiltonian_path_solver_(cost_), evaluator_(evaluator), @@ -1132,9 +1133,9 @@ bool TSPOpt::MakeNeighbor() { cost_.resize(size); for (int i = 0; i < size; ++i) { cost_[i].resize(size); - cost_[i][0] = evaluator_->Run(nodes[i], nodes[size], chain_path); + cost_[i][0] = evaluator_(nodes[i], nodes[size], chain_path); for (int j = 1; j < size; ++j) { - cost_[i][j] = evaluator_->Run(nodes[i], nodes[j], chain_path); + cost_[i][j] = evaluator_(nodes[i], nodes[j], chain_path); } } hamiltonian_path_solver_.ChangeCostMatrix(cost_); @@ -1159,7 +1160,7 @@ bool TSPOpt::MakeNeighbor() { class TSPLns : public PathOperator { public: TSPLns(const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* evaluator, int tsp_size); + Solver::IndexEvaluator3 evaluator, int tsp_size); ~TSPLns() override {} bool MakeNeighbor() override; @@ -1171,17 +1172,17 @@ class TSPLns : public PathOperator { private: std::vector > cost_; HamiltonianPathSolver hamiltonian_path_solver_; - std::unique_ptr evaluator_; + Solver::IndexEvaluator3 evaluator_; const int tsp_size_; ACMRandom rand_; }; TSPLns::TSPLns(const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* evaluator, int tsp_size) + Solver::IndexEvaluator3 evaluator, int tsp_size) : PathOperator(vars, secondary_vars, 1, nullptr), hamiltonian_path_solver_(cost_), - evaluator_(evaluator), + evaluator_(std::move(evaluator)), tsp_size_(tsp_size), rand_(ACMRandom::HostnamePidTimeSeed()) { cost_.resize(tsp_size_); @@ -1239,7 +1240,7 @@ bool TSPLns::MakeNeighbor() { meta_node_costs.push_back(cost); cost = 0; } else { - cost += evaluator_->Run(node, next, node_path); + cost += evaluator_(node, next, node_path); } node = next; } @@ -1248,12 +1249,11 @@ bool TSPLns::MakeNeighbor() { // Setup TSP cost matrix CHECK_EQ(meta_node_costs.size(), tsp_size_); for (int i = 0; i < tsp_size_; ++i) { - cost_[i][0] = - meta_node_costs[i] + - evaluator_->Run(breaks[i], Next(breaks[tsp_size_ - 1]), node_path); + cost_[i][0] = meta_node_costs[i] + + evaluator_(breaks[i], Next(breaks[tsp_size_ - 1]), node_path); for (int j = 1; j < tsp_size_; ++j) { cost_[i][j] = meta_node_costs[i] + - evaluator_->Run(breaks[i], Next(breaks[j - 1]), node_path); + evaluator_(breaks[i], Next(breaks[j - 1]), node_path); } cost_[i][i] = 0; } @@ -1290,7 +1290,7 @@ bool TSPLns::MakeNeighbor() { class NearestNeighbors { public: - NearestNeighbors(Solver::IndexEvaluator3* evaluator, + NearestNeighbors(Solver::IndexEvaluator3 evaluator, const PathOperator& path_operator, int size); virtual ~NearestNeighbors() {} void Initialize(); @@ -1304,7 +1304,7 @@ class NearestNeighbors { static void Swap(int i, int j, int* neighbors, int64* row); std::vector > neighbors_; - Solver::IndexEvaluator3* evaluator_; + Solver::IndexEvaluator3 evaluator_; const PathOperator& path_operator_; const int size_; bool initialized_; @@ -1312,7 +1312,7 @@ class NearestNeighbors { DISALLOW_COPY_AND_ASSIGN(NearestNeighbors); }; -NearestNeighbors::NearestNeighbors(Solver::IndexEvaluator3* evaluator, +NearestNeighbors::NearestNeighbors(Solver::IndexEvaluator3 evaluator, const PathOperator& path_operator, int size) : evaluator_(evaluator), path_operator_(path_operator), @@ -1345,7 +1345,7 @@ void NearestNeighbors::ComputeNearest(int row) { for (int i = 0; i < var_size; ++i) { const int index = i + var_min; neighbors[i] = index; - row_data[i] = evaluator_->Run(row, index, path); + row_data[i] = evaluator_(row, index, path); } if (var_size > size_) { @@ -1398,9 +1398,7 @@ class LinKernighan : public PathOperator { public: LinKernighan(const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* evaluator, - bool owner, // Owner of callback - bool topt); + Solver::IndexEvaluator3 evaluator, bool topt); ~LinKernighan() override; bool MakeNeighbor() override; @@ -1413,8 +1411,7 @@ class LinKernighan : public PathOperator { bool InFromOut(int64 in_i, int64 in_j, int64* out, int64* gain); - Solver::IndexEvaluator3* const evaluator_; - bool owner_; + Solver::IndexEvaluator3 const evaluator_; NearestNeighbors neighbors_; hash_set marked_; const bool topt_; @@ -1426,19 +1423,13 @@ class LinKernighan : public PathOperator { LinKernighan::LinKernighan(const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* evaluator, bool owner, - bool topt) + Solver::IndexEvaluator3 evaluator, bool topt) : PathOperator(vars, secondary_vars, 1, nullptr), evaluator_(evaluator), - owner_(owner), neighbors_(evaluator, *this, kNeighbors), topt_(topt) {} -LinKernighan::~LinKernighan() { - if (owner_) { - delete evaluator_; - } -} +LinKernighan::~LinKernighan() {} void LinKernighan::OnNodeInitialization() { neighbors_.Initialize(); } @@ -1466,8 +1457,8 @@ bool LinKernighan::MakeNeighbor() { marked_.insert(out); if (MoveChain(out, node1, node)) { const int64 next_out = Next(out); - int64 in_cost = evaluator_->Run(node, next_out, path); - int64 out_cost = evaluator_->Run(out, next_out, path); + int64 in_cost = evaluator_(node, next_out, path); + int64 out_cost = evaluator_(out, next_out, path); if (gain - in_cost + out_cost > 0) return true; node = out; if (IsPathEnd(node)) { @@ -1495,8 +1486,8 @@ bool LinKernighan::MakeNeighbor() { if (!ReverseChain(node, out, &chain_last)) { return false; } - int64 in_cost = evaluator_->Run(base, chain_last, path); - int64 out_cost = evaluator_->Run(chain_last, out, path); + int64 in_cost = evaluator_(base, chain_last, path); + int64 out_cost = evaluator_(chain_last, out, path); if (gain - in_cost + out_cost > 0) { return true; } @@ -1518,12 +1509,12 @@ bool LinKernighan::InFromOut(int64 in_i, int64 in_j, int64* out, int64* gain) { const std::vector& nexts = neighbors_.Neighbors(in_j); int64 best_gain = kint64min; int64 path = Path(in_i); - int64 out_cost = evaluator_->Run(in_i, in_j, path); + int64 out_cost = evaluator_(in_i, in_j, path); const int64 current_gain = *gain + out_cost; for (int k = 0; k < nexts.size(); ++k) { const int64 next = nexts[k]; if (next != in_j) { - int64 in_cost = evaluator_->Run(in_j, next, path); + int64 in_cost = evaluator_(in_j, next, path); int64 new_gain = current_gain - in_cost; if (new_gain > 0 && next != Next(in_j) && marked_.count(in_j) == 0 && marked_.count(next) == 0) { @@ -1651,7 +1642,7 @@ namespace { class CompoundOperator : public LocalSearchOperator { public: CompoundOperator(const std::vector& operators, - ResultCallback2* const evaluator); + std::function evaluator); ~CompoundOperator() override {} void Start(const Assignment* assignment) override; bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override; @@ -1661,11 +1652,9 @@ class CompoundOperator : public LocalSearchOperator { private: class OperatorComparator { public: - OperatorComparator(ResultCallback2* const evaluator, + OperatorComparator(std::function evaluator, int active_operator) - : evaluator_(evaluator), active_operator_(active_operator) { - evaluator_->CheckIsRepeatable(); - } + : evaluator_(evaluator), active_operator_(active_operator) {} bool operator()(int lhs, int rhs) const { const int64 lhs_value = Evaluate(lhs); const int64 rhs_value = Evaluate(rhs); @@ -1674,27 +1663,30 @@ class CompoundOperator : public LocalSearchOperator { private: int64 Evaluate(int operator_index) const { - return evaluator_->Run(active_operator_, operator_index); + return evaluator_(active_operator_, operator_index); } - ResultCallback2* const evaluator_; + std::function evaluator_; const int active_operator_; }; int64 index_; int64 size_; - std::unique_ptr operators_; + std::unique_ptr operators_; std::unique_ptr operator_indices_; - std::unique_ptr > evaluator_; + std::function evaluator_; Bitset64<> started_; const Assignment* start_assignment_; }; CompoundOperator::CompoundOperator( const std::vector& operators, - ResultCallback2* const evaluator) - : index_(0), size_(0), evaluator_(evaluator), - started_(operators.size()), start_assignment_(nullptr) { + std::function evaluator) + : index_(0), + size_(0), + evaluator_(std::move(evaluator)), + started_(operators.size()), + start_assignment_(nullptr) { for (int i = 0; i < operators.size(); ++i) { if (operators[i] != nullptr) { ++size_; @@ -1716,7 +1708,7 @@ void CompoundOperator::Start(const Assignment* assignment) { start_assignment_ = assignment; started_.ClearAll(); if (size_ > 0) { - OperatorComparator comparator(evaluator_.get(), operator_indices_[index_]); + OperatorComparator comparator(evaluator_, operator_indices_[index_]); std::sort(operator_indices_.get(), operator_indices_.get() + size_, comparator); index_ = 0; @@ -1734,8 +1726,7 @@ bool CompoundOperator::MakeNextNeighbor(Assignment* delta, operators_[operator_index]->Start(start_assignment_); started_.Set(operator_index); } - if (operators_[operator_index] - ->MakeNextNeighbor(delta, deltadelta)) { + if (operators_[operator_index]->MakeNextNeighbor(delta, deltadelta)) { return true; } ++index_; @@ -1769,18 +1760,19 @@ LocalSearchOperator* Solver::ConcatenateOperators( LocalSearchOperator* Solver::ConcatenateOperators( const std::vector& ops, bool restart) { if (restart) { - return ConcatenateOperators(ops, - NewPermanentCallback(&CompoundOperatorRestart)); + std::function eval = CompoundOperatorRestart; + return ConcatenateOperators(ops, eval); } else { - return ConcatenateOperators( - ops, NewPermanentCallback(&CompoundOperatorNoRestart, - static_cast(ops.size()))); + const int size = ops.size(); + return ConcatenateOperators(ops, [size](int i, int j) { + return CompoundOperatorNoRestart(size, i, j); + }); } } LocalSearchOperator* Solver::ConcatenateOperators( const std::vector& ops, - ResultCallback2* const evaluator) { + std::function evaluator) { return RevAlloc(new CompoundOperator(ops, evaluator)); } @@ -1800,7 +1792,7 @@ class RandomCompoundOperator : public LocalSearchOperator { private: const int size_; ACMRandom rand_; - std::unique_ptr operators_; + std::unique_ptr operators_; }; void RandomCompoundOperator::Start(const Assignment* assignment) { @@ -1813,7 +1805,7 @@ RandomCompoundOperator::RandomCompoundOperator( const std::vector& operators) : size_(operators.size()), rand_(ACMRandom::HostnamePidTimeSeed()), - operators_(new LocalSearchOperator* [size_]) { + operators_(new LocalSearchOperator*[size_]) { for (int i = 0; i < size_; ++i) { operators_[i] = operators[i]; } @@ -1823,7 +1815,7 @@ RandomCompoundOperator::RandomCompoundOperator( const std::vector& operators, int32 seed) : size_(operators.size()), rand_(seed), - operators_(new LocalSearchOperator* [size_]) { + operators_(new LocalSearchOperator*[size_]) { for (int i = 0; i < size_; ++i) { operators_[i] = operators[i]; } @@ -1868,7 +1860,7 @@ LocalSearchOperator* MakeLocalSearchOperator( #define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass) \ template <> \ LocalSearchOperator* MakeLocalSearchOperator( \ - Solver* solver, const std::vector& vars, \ + Solver * solver, const std::vector& vars, \ const std::vector& secondary_vars, \ ResultCallback1* start_empty_path_class) { \ return solver->RevAlloc( \ @@ -2000,23 +1992,23 @@ LocalSearchOperator* Solver::MakeOperator(const std::vector& vars, } LocalSearchOperator* Solver::MakeOperator( - const std::vector& vars, Solver::IndexEvaluator3* const evaluator, + const std::vector& vars, Solver::IndexEvaluator3 evaluator, Solver::EvaluatorLocalSearchOperators op) { return MakeOperator(vars, std::vector(), evaluator, op); } LocalSearchOperator* Solver::MakeOperator( const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* const evaluator, + Solver::IndexEvaluator3 evaluator, Solver::EvaluatorLocalSearchOperators op) { LocalSearchOperator* result = nullptr; switch (op) { case Solver::LK: { std::vector operators; operators.push_back(RevAlloc( - new LinKernighan(vars, secondary_vars, evaluator, true, false))); + new LinKernighan(vars, secondary_vars, evaluator, /*topt=*/false))); operators.push_back(RevAlloc( - new LinKernighan(vars, secondary_vars, evaluator, false, true))); + new LinKernighan(vars, secondary_vars, evaluator, /*topt=*/true))); result = ConcatenateOperators(operators); break; } @@ -2201,7 +2193,7 @@ template class ObjectiveFilter : public IntVarLocalSearchFilter { public: ObjectiveFilter(const std::vector& vars, - Callback1* delta_objective_callback, + Solver::ObjectiveWatcher delta_objective_callback, const IntVar* const objective, Solver::LocalSearchFilterBound filter_enum) : IntVarLocalSearchFilter(vars), @@ -2256,7 +2248,7 @@ class ObjectiveFilter : public IntVarLocalSearchFilter { var_max = std::min(var_max, delta->ObjectiveMax()); } if (delta_objective_callback_ != nullptr) { - delta_objective_callback_->Run(value); + delta_objective_callback_(value); } switch (filter_enum_) { case Solver::LE: { @@ -2286,7 +2278,7 @@ class ObjectiveFilter : public IntVarLocalSearchFilter { const int primary_vars_size_; int64* const cache_; int64* const delta_cache_; - std::unique_ptr > delta_objective_callback_; + Solver::ObjectiveWatcher delta_objective_callback_; const IntVar* const objective_; Solver::LocalSearchFilterBound filter_enum_; Operator op_; @@ -2297,62 +2289,59 @@ class ObjectiveFilter : public IntVarLocalSearchFilter { private: void OnSynchronize(const Assignment* delta) override { op_.Init(); - for (int i = 0; i < primary_vars_size_; ++i) { - const int64 obj_value = SynchronizedElementValue(i); - cache_[i] = obj_value; - delta_cache_[i] = obj_value; - op_.Update(obj_value); + for (int i = 0; i < primary_vars_size_; ++i) { + const int64 obj_value = SynchronizedElementValue(i); + cache_[i] = obj_value; + delta_cache_[i] = obj_value; + op_.Update(obj_value); + } + old_value_ = op_.value(); + old_delta_value_ = old_value_; + incremental_ = false; + if (delta_objective_callback_ != nullptr) { + delta_objective_callback_(op_.value()); + } } - old_value_ = op_.value(); - old_delta_value_ = old_value_; - incremental_ = false; - if (delta_objective_callback_ != nullptr) { - delta_objective_callback_->Run(op_.value()); - } -} -int64 Evaluate(const Assignment* delta, int64 current_value, - const int64* const out_values, bool cache_delta_values) { - if (current_value == kint64max) return current_value; - op_.set_value(current_value); - const Assignment::IntContainer& container = delta->IntVarContainer(); - const int size = container.Size(); - for (int i = 0; i < size; ++i) { - const IntVarElement& new_element = container.Element(i); - IntVar* const var = new_element.Var(); - int64 index = -1; - if (FindIndex(var, &index) && index < primary_vars_size_) { - op_.Remove(out_values[index]); - int64 obj_value = 0LL; - if (EvaluateElementValue(container, index, &i, &obj_value)) { - op_.Update(obj_value); - if (cache_delta_values) { - delta_cache_[index] = obj_value; + int64 Evaluate(const Assignment* delta, int64 current_value, + const int64* const out_values, bool cache_delta_values) { + if (current_value == kint64max) return current_value; + op_.set_value(current_value); + const Assignment::IntContainer& container = delta->IntVarContainer(); + const int size = container.Size(); + for (int i = 0; i < size; ++i) { + const IntVarElement& new_element = container.Element(i); + IntVar* const var = new_element.Var(); + int64 index = -1; + if (FindIndex(var, &index) && index < primary_vars_size_) { + op_.Remove(out_values[index]); + int64 obj_value = 0LL; + if (EvaluateElementValue(container, index, &i, &obj_value)) { + op_.Update(obj_value); + if (cache_delta_values) { + delta_cache_[index] = obj_value; + } } } } + return op_.value(); } - return op_.value(); -} }; template class BinaryObjectiveFilter : public ObjectiveFilter { public: BinaryObjectiveFilter(const std::vector& vars, - Solver::IndexEvaluator2* value_evaluator, - Callback1* delta_objective_callback, + Solver::IndexEvaluator2 value_evaluator, + Solver::ObjectiveWatcher delta_objective_callback, const IntVar* const objective, Solver::LocalSearchFilterBound filter_enum) : ObjectiveFilter(vars, delta_objective_callback, objective, filter_enum), - value_evaluator_(value_evaluator) { - value_evaluator_->CheckIsRepeatable(); - } + value_evaluator_(value_evaluator) {} ~BinaryObjectiveFilter() override {} int64 SynchronizedElementValue(int64 index) override { return IntVarLocalSearchFilter::IsVarSynced(index) - ? value_evaluator_->Run(index, - IntVarLocalSearchFilter::Value(index)) + ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index)) : 0; } bool EvaluateElementValue(const Assignment::IntContainer& container, @@ -2360,12 +2349,12 @@ class BinaryObjectiveFilter : public ObjectiveFilter { int64* obj_value) override { const IntVarElement& element = container.Element(*container_index); if (element.Activated()) { - *obj_value = value_evaluator_->Run(index, element.Value()); + *obj_value = value_evaluator_(index, element.Value()); return true; } else { const IntVar* var = element.Var(); if (var->Bound()) { - *obj_value = value_evaluator_->Run(index, var->Min()); + *obj_value = value_evaluator_(index, var->Min()); return true; } } @@ -2373,7 +2362,7 @@ class BinaryObjectiveFilter : public ObjectiveFilter { } private: - std::unique_ptr value_evaluator_; + Solver::IndexEvaluator2 value_evaluator_; }; template @@ -2381,15 +2370,14 @@ class TernaryObjectiveFilter : public ObjectiveFilter { public: TernaryObjectiveFilter(const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* value_evaluator, - Callback1* delta_objective_callback, + Solver::IndexEvaluator3 value_evaluator, + Solver::ObjectiveWatcher delta_objective_callback, const IntVar* const objective, Solver::LocalSearchFilterBound filter_enum) : ObjectiveFilter(vars, delta_objective_callback, objective, filter_enum), secondary_vars_offset_(vars.size()), value_evaluator_(value_evaluator) { - value_evaluator_->CheckIsRepeatable(); IntVarLocalSearchFilter::AddVars(secondary_vars); CHECK_GE(IntVarLocalSearchFilter::Size(), 0); } @@ -2397,10 +2385,9 @@ class TernaryObjectiveFilter : public ObjectiveFilter { int64 SynchronizedElementValue(int64 index) override { DCHECK_LT(index, secondary_vars_offset_); return IntVarLocalSearchFilter::IsVarSynced(index) - ? value_evaluator_->Run(index, - IntVarLocalSearchFilter::Value(index), - IntVarLocalSearchFilter::Value( - index + secondary_vars_offset_)) + ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index), + IntVarLocalSearchFilter::Value( + index + secondary_vars_offset_)) : 0; } bool EvaluateElementValue(const Assignment::IntContainer& container, @@ -2416,19 +2403,18 @@ class TernaryObjectiveFilter : public ObjectiveFilter { int hint_index = *container_index + 1; if (hint_index < container.Size() && secondary_var == container.Element(hint_index).Var()) { - *obj_value = value_evaluator_->Run( - index, value, container.Element(hint_index).Value()); + *obj_value = value_evaluator_(index, value, + container.Element(hint_index).Value()); *container_index = hint_index; } else { - *obj_value = value_evaluator_->Run( - index, value, container.Element(secondary_var).Value()); + *obj_value = value_evaluator_(index, value, + container.Element(secondary_var).Value()); } return true; } else { const IntVar* var = element.Var(); if (var->Bound() && secondary_var->Bound()) { - *obj_value = - value_evaluator_->Run(index, var->Min(), secondary_var->Min()); + *obj_value = value_evaluator_(index, var->Min(), secondary_var->Min()); return true; } } @@ -2437,7 +2423,7 @@ class TernaryObjectiveFilter : public ObjectiveFilter { private: int secondary_vars_offset_; - std::unique_ptr value_evaluator_; + Solver::IndexEvaluator3 value_evaluator_; }; } // namespace @@ -2488,7 +2474,7 @@ class TernaryObjectiveFilter : public ObjectiveFilter { return nullptr; LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( - const std::vector& vars, Solver::IndexEvaluator2* const values, + const std::vector& vars, Solver::IndexEvaluator2 values, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum) { ReturnObjectiveFilter5(BinaryObjectiveFilter, op_enum, vars, values, nullptr, @@ -2496,8 +2482,8 @@ LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( } LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( - const std::vector& vars, Solver::IndexEvaluator2* const values, - Callback1* delta_objective_callback, IntVar* const objective, + const std::vector& vars, Solver::IndexEvaluator2 values, + ObjectiveWatcher delta_objective_callback, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum) { ReturnObjectiveFilter5(BinaryObjectiveFilter, op_enum, vars, values, @@ -2506,7 +2492,7 @@ LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* const values, IntVar* const objective, + Solver::IndexEvaluator3 values, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum) { ReturnObjectiveFilter6(TernaryObjectiveFilter, op_enum, vars, secondary_vars, @@ -2515,9 +2501,8 @@ LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( LocalSearchFilter* Solver::MakeLocalSearchObjectiveFilter( const std::vector& vars, const std::vector& secondary_vars, - Solver::IndexEvaluator3* const values, - Callback1* delta_objective_callback, IntVar* const objective, - Solver::LocalSearchFilterBound filter_enum, + Solver::IndexEvaluator3 values, ObjectiveWatcher delta_objective_callback, + IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum) { ReturnObjectiveFilter6(TernaryObjectiveFilter, op_enum, vars, secondary_vars, values, delta_objective_callback, objective, @@ -2785,11 +2770,7 @@ namespace { class NestedSolveDecision : public Decision { public: // This enum is used internally to tag states in the local search tree. - enum StateType { - DECISION_PENDING, - DECISION_FAILED, - DECISION_FOUND - }; + enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND }; NestedSolveDecision(DecisionBuilder* const db, bool restore, const std::vector& monitors); diff --git a/src/constraint_solver/pack.cc b/src/constraint_solver/pack.cc index 32f6859698..32175cea36 100644 --- a/src/constraint_solver/pack.cc +++ b/src/constraint_solver/pack.cc @@ -16,7 +16,7 @@ #include #include -#include "base/unique_ptr.h" +#include #include #include #include @@ -530,10 +530,10 @@ void SortIndexByWeight(std::vector* const indices, } void SortIndexByWeight(std::vector* const indices, - Pack::ItemUsageEvaluator* weights) { + Solver::IndexEvaluator1 weights) { std::vector to_sort; for (int index = 0; index < indices->size(); ++index) { - const int w = weights->Run(index); + const int w = weights(index); if (w != 0) { to_sort.push_back(WeightContainer((*indices)[index], w)); } @@ -542,10 +542,10 @@ void SortIndexByWeight(std::vector* const indices, } void SortIndexByWeight(std::vector* const indices, - Pack::ItemUsagePerBinEvaluator* weights, int bin_index) { + Solver::IndexEvaluator2 weights, int bin_index) { std::vector to_sort; for (int index = 0; index < indices->size(); ++index) { - const int w = weights->Run(index, bin_index); + const int w = weights(index, bin_index); if (w != 0) { to_sort.push_back(WeightContainer((*indices)[index], w)); } @@ -652,7 +652,7 @@ class DimensionSumCallbackLessThanConstant : public Dimension { public: // Ownership is taken by the class. The callback has to be permanent. DimensionSumCallbackLessThanConstant(Solver* const s, Pack* const p, - Pack::ItemUsageEvaluator* weights, + Solver::IndexEvaluator1 weights, int vars_count, const std::vector& upper_bounds) : Dimension(s, p), @@ -665,11 +665,10 @@ class DimensionSumCallbackLessThanConstant : public Dimension { ranked_(vars_count_) { DCHECK(weights); DCHECK_GT(vars_count, 0); - weights->CheckIsRepeatable(); for (int i = 0; i < vars_count_; ++i) { ranked_[i] = i; } - SortIndexByWeight(&ranked_, weights_.get()); + SortIndexByWeight(&ranked_, weights_); } ~DimensionSumCallbackLessThanConstant() override {} @@ -686,7 +685,7 @@ class DimensionSumCallbackLessThanConstant : public Dimension { for (; last_unbound >= 0; --last_unbound) { const int var_index = ranked_[last_unbound]; if (IsUndecided(var_index, bin_index)) { - if (weights_->Run(var_index) > slack) { + if (weights_(var_index) > slack) { SetImpossible(var_index, bin_index); } else { break; @@ -701,7 +700,7 @@ class DimensionSumCallbackLessThanConstant : public Dimension { Solver* const s = solver(); int64 sum = 0LL; for (const int value : forced) { - sum += weights_->Run(value); + sum += weights_(value); } sum_of_bound_variables_vector_.SetValue(s, bin_index, sum); first_unbound_backward_vector_.SetValue(s, bin_index, ranked_.size() - 1); @@ -716,7 +715,7 @@ class DimensionSumCallbackLessThanConstant : public Dimension { Solver* const s = solver(); int64 sum = sum_of_bound_variables_vector_[bin_index]; for (const int value : forced) { - sum += weights_->Run(value); + sum += weights_(value); } sum_of_bound_variables_vector_.SetValue(s, bin_index, sum); PushFromTop(bin_index); @@ -741,7 +740,7 @@ class DimensionSumCallbackLessThanConstant : public Dimension { private: const int vars_count_; - std::unique_ptr weights_; + Solver::IndexEvaluator1 weights_; const int bins_count_; const std::vector upper_bounds_; RevArray first_unbound_backward_vector_; @@ -753,7 +752,7 @@ class DimensionLessThanConstantCallback2 : public Dimension { public: // Ownership is taken by the class. The callback has to be permanent. DimensionLessThanConstantCallback2(Solver* const s, Pack* const p, - Pack::ItemUsagePerBinEvaluator* weights, + Solver::IndexEvaluator2 weights, int vars_count, const std::vector& upper_bounds) : Dimension(s, p), @@ -766,13 +765,12 @@ class DimensionLessThanConstantCallback2 : public Dimension { ranked_(bins_count_) { DCHECK(weights); DCHECK_GT(vars_count, 0); - weights->CheckIsRepeatable(); for (int b = 0; b < bins_count_; ++b) { ranked_[b].resize(vars_count); for (int i = 0; i < vars_count_; ++i) { ranked_[b][i] = i; } - SortIndexByWeight(&ranked_[b], weights_.get(), b); + SortIndexByWeight(&ranked_[b], weights_, b); } } @@ -790,7 +788,7 @@ class DimensionLessThanConstantCallback2 : public Dimension { for (; last_unbound >= 0; --last_unbound) { const int var_index = ranked_[bin_index][last_unbound]; if (IsUndecided(var_index, bin_index)) { - if (weights_->Run(var_index, bin_index) > slack) { + if (weights_(var_index, bin_index) > slack) { SetImpossible(var_index, bin_index); } else { break; @@ -805,7 +803,7 @@ class DimensionLessThanConstantCallback2 : public Dimension { Solver* const s = solver(); int64 sum = 0LL; for (const int value : forced) { - sum += weights_->Run(value, bin_index); + sum += weights_(value, bin_index); } sum_of_bound_variables_vector_.SetValue(s, bin_index, sum); first_unbound_backward_vector_.SetValue(s, bin_index, @@ -821,7 +819,7 @@ class DimensionLessThanConstantCallback2 : public Dimension { Solver* const s = solver(); int64 sum = sum_of_bound_variables_vector_[bin_index]; for (const int value : forced) { - sum += weights_->Run(value, bin_index); + sum += weights_(value, bin_index); } sum_of_bound_variables_vector_.SetValue(s, bin_index, sum); PushFromTop(bin_index); @@ -846,7 +844,7 @@ class DimensionLessThanConstantCallback2 : public Dimension { private: const int vars_count_; - std::unique_ptr weights_; + Solver::IndexEvaluator2 weights_; const int bins_count_; const std::vector upper_bounds_; RevArray first_unbound_backward_vector_; @@ -1002,7 +1000,7 @@ class DimensionWeightedCallback2SumEqVar : public Dimension { }; DimensionWeightedCallback2SumEqVar(Solver* const s, Pack* const p, - Pack::ItemUsagePerBinEvaluator* weights, + Solver::IndexEvaluator2 weights, int vars_count, const std::vector& loads) : Dimension(s, p), @@ -1017,13 +1015,12 @@ class DimensionWeightedCallback2SumEqVar : public Dimension { DCHECK(weights); DCHECK_GT(vars_count_, 0); DCHECK_GT(bins_count_, 0); - weights->CheckIsRepeatable(); for (int b = 0; b < bins_count_; ++b) { ranked_[b].resize(vars_count); for (int i = 0; i < vars_count_; ++i) { ranked_[b][i] = i; } - SortIndexByWeight(&ranked_[b], weights_.get(), b); + SortIndexByWeight(&ranked_[b], weights_, b); } } @@ -1052,7 +1049,7 @@ class DimensionWeightedCallback2SumEqVar : public Dimension { int last_unbound = first_unbound_backward_vector_[bin_index]; for (; last_unbound >= 0; --last_unbound) { const int var_index = ranked_[bin_index][last_unbound]; - const int64 weight = weights_->Run(var_index, bin_index); + const int64 weight = weights_(var_index, bin_index); if (IsUndecided(var_index, bin_index)) { if (weight > slack_up) { SetImpossible(var_index, bin_index); @@ -1071,11 +1068,11 @@ class DimensionWeightedCallback2SumEqVar : public Dimension { Solver* const s = solver(); int64 sum = 0LL; for (const int value : forced) { - sum += weights_->Run(value, bin_index); + sum += weights_(value, bin_index); } sum_of_bound_variables_vector_.SetValue(s, bin_index, sum); for (const int value : undecided) { - sum += weights_->Run(value, bin_index); + sum += weights_(value, bin_index); } sum_of_all_variables_vector_.SetValue(s, bin_index, sum); first_unbound_backward_vector_.SetValue(s, bin_index, @@ -1090,12 +1087,12 @@ class DimensionWeightedCallback2SumEqVar : public Dimension { Solver* const s = solver(); int64 down = sum_of_bound_variables_vector_[bin_index]; for (const int value : forced) { - down += weights_->Run(value, bin_index); + down += weights_(value, bin_index); } sum_of_bound_variables_vector_.SetValue(s, bin_index, down); int64 up = sum_of_all_variables_vector_[bin_index]; for (const int value : removed) { - up -= weights_->Run(value, bin_index); + up -= weights_(value, bin_index); } sum_of_all_variables_vector_.SetValue(s, bin_index, up); PushFromTop(bin_index); @@ -1119,7 +1116,7 @@ class DimensionWeightedCallback2SumEqVar : public Dimension { private: const int vars_count_; - std::unique_ptr weights_; + Solver::IndexEvaluator2 weights_; const int bins_count_; const std::vector loads_; RevArray first_unbound_backward_vector_; @@ -1530,7 +1527,7 @@ void Pack::AddWeightedSumLessOrEqualConstantDimension( } void Pack::AddWeightedSumLessOrEqualConstantDimension( - ItemUsageEvaluator* weights, const std::vector& bounds) { + Solver::IndexEvaluator1 weights, const std::vector& bounds) { CHECK(weights != nullptr); CHECK_EQ(bounds.size(), bins_); Solver* const s = solver(); @@ -1540,7 +1537,7 @@ void Pack::AddWeightedSumLessOrEqualConstantDimension( } void Pack::AddWeightedSumLessOrEqualConstantDimension( - ItemUsagePerBinEvaluator* weights, const std::vector& bounds) { + Solver::IndexEvaluator2 weights, const std::vector& bounds) { CHECK(weights != nullptr); CHECK_EQ(bounds.size(), bins_); Solver* const s = solver(); @@ -1559,7 +1556,7 @@ void Pack::AddWeightedSumEqualVarDimension(const std::vector& weights, dims_.push_back(dim); } -void Pack::AddWeightedSumEqualVarDimension(ItemUsagePerBinEvaluator* weights, +void Pack::AddWeightedSumEqualVarDimension(Solver::IndexEvaluator2 weights, const std::vector& loads) { CHECK(weights != nullptr); CHECK_EQ(loads.size(), bins_); diff --git a/src/constraint_solver/python/constraint_solver.swig b/src/constraint_solver/python/constraint_solver.swig index 6e52d16d29..e23c9be3ca 100644 --- a/src/constraint_solver/python/constraint_solver.swig +++ b/src/constraint_solver/python/constraint_solver.swig @@ -33,6 +33,7 @@ %include "base/base.swig" %import "util/python/data.swig" +%include "util/python/functions.swig" // Callback wrapping. See base/python/callbacks.swig. #define FATAL_CALLBACK_EXCEPTION @@ -192,116 +193,6 @@ PY_CONVERT(LocalSearchOperator); PY_CONVERT(LocalSearchFilter); #undef PY_CONVERT -// Create input mapping for Solver::IndexEvaluator1 -%{ -static int64 PyCallbackIndexEvaluator1( - PyObject* pyfunc, - int64 i) { - int64 result = 0; - // Cast to int needed, no int64 support - PyObject* arglist = Py_BuildValue("l", i); - PyObject* pyresult = PyEval_CallObject(pyfunc, arglist); - Py_DECREF(arglist); - if (pyresult) { - result = PyInt_AsLong(pyresult); - } - Py_XDECREF(pyresult); - return result; -} -%} -%typemap(in) operations_research::Solver::IndexEvaluator1* { - if (!PyCallable_Check($input)) { - PyErr_SetString(PyExc_TypeError, "Need a callable object!"); - SWIG_fail; - } - $1 = NewPermanentCallback(&PyCallbackIndexEvaluator1, $input); -} -// Create conversion of vectors of Solver::IndexEvaluator1 -%{ -template<> -bool PyObjAs(PyObject* py_obj, - operations_research::Solver::IndexEvaluator1** b) { - if (!PyCallable_Check(py_obj)) { - PyErr_SetString(PyExc_TypeError, "Need a callable object!"); - return false; - } - *b = NewPermanentCallback(&PyCallbackIndexEvaluator1, py_obj); - return true; -} -%} - -// Create input mapping for Solver::IndexEvaluator2 -%{ -static int64 PyCallbackIndexEvaluator2(PyObject* pyfunc, int64 i, int64 j) { - int64 result = 0; - // Cast to int needed, no int64 support - PyObject* arglist = Py_BuildValue("ll", i, j); - PyObject* pyresult = PyEval_CallObject(pyfunc, arglist); - Py_DECREF(arglist); - if (pyresult) { - result = PyInt_AsLong(pyresult); - } - Py_XDECREF(pyresult); - return result; -} -%} -%typemap(in) operations_research::Solver::IndexEvaluator2* { - if (!PyCallable_Check($input)) { - PyErr_SetString(PyExc_TypeError, "Need a callable object!"); - SWIG_fail; - } - $1 = NewPermanentCallback(&PyCallbackIndexEvaluator2, $input); -} -// Create conversion of vectors of Solver::IndexEvaluator2 -%{ -template<> -bool PyObjAs(PyObject* py_obj, - operations_research::Solver::IndexEvaluator2** b) { - if (!PyCallable_Check(py_obj)) { - PyErr_SetString(PyExc_TypeError, "Need a callable object!"); - return false; - } - *b = NewPermanentCallback(&PyCallbackIndexEvaluator2, py_obj); - return true; -} -%} - -// Create input mapping for Solver::IndexEvaluator2 -%{ -static int64 PyCallbackIndexEvaluator3(PyObject* pyfunc, int64 i, int64 j, - int64 k) { - int64 result = 0; - // Cast to int needed, no int64 support - PyObject* arglist = Py_BuildValue("lll", i, j, k); - PyObject* pyresult = PyEval_CallObject(pyfunc, arglist); - Py_DECREF(arglist); - if (pyresult) { - result = PyInt_AsLong(pyresult); - } - Py_XDECREF(pyresult); - return result; -} -%} -%typemap(in) operations_research::Solver::IndexEvaluator3* { - if (!PyCallable_Check($input)) { - PyErr_SetString(PyExc_TypeError, "Need a callable object!"); - SWIG_fail; - } - $1 = NewPermanentCallback(&PyCallbackIndexEvaluator3, $input); -} -// Create conversion of vectors of Solver::IndexEvaluator3 -%{ -template<> -bool PyObjAs(PyObject* py_obj, - operations_research::Solver::IndexEvaluator3** b) { - if (!PyCallable_Check(py_obj)) { - PyErr_SetString(PyExc_TypeError, "Need a callable object!"); - return false; - } - *b = NewPermanentCallback(&PyCallbackIndexEvaluator3, py_obj); - return true; -} -%} // ============= Extensions ============== @@ -324,6 +215,11 @@ Constraint* PythonMethodName(IntervalVar* other) { return $self->solver()->MakeIntervalVarRelation( $self, operations_research::Solver::CppEnumName, other); } + +Constraint* PythonMethodName##WithDelay(IntervalVar* other, int64 delay) { + return $self->solver()->MakeIntervalVarRelationWithDelay( + $self, operations_research::Solver::CppEnumName, other, delay); +} %enddef %define SCHEDULING_CONSTRAINT(PythonMethodName, CppEnumName) Constraint* PythonMethodName(int64 date) { @@ -377,16 +273,16 @@ PY_STRINGIFY_DEBUGSTRING(Decision); %extend operations_research::Solver { Constraint* TreeNoCycle(const std::vector& nexts, const std::vector& active, - ResultCallback1* callback = nullptr) { + Solver::IndexFilter1 callback = nullptr) { return $self->MakeNoCycle(nexts, active, callback, false); } SearchMonitor* SearchLogWithCallback(int period, - ResultCallback* callback) { + std::function callback) { return $self->MakeSearchLog(period, callback); } - IntExpr* ElementFunction(ResultCallback1* values, + IntExpr* ElementFunction(std::function values, IntVar* const index) { return $self->MakeElement(values, index); } @@ -403,7 +299,7 @@ PY_STRINGIFY_DEBUGSTRING(Decision); DecisionBuilder* VarEvalValStrPhase( const std::vector& vars, - ResultCallback1* var_evaluator, + std::function var_evaluator, operations_research::Solver::IntValueStrategy val_str) { return $self->MakePhase(vars, var_evaluator, val_str); } @@ -411,44 +307,44 @@ PY_STRINGIFY_DEBUGSTRING(Decision); DecisionBuilder* VarStrValEvalPhase( const std::vector& vars, operations_research::Solver::IntVarStrategy var_str, - ResultCallback2* val_eval) { + Solver::IndexEvaluator2 val_eval) { return $self->MakePhase(vars, var_str, val_eval); } DecisionBuilder* VarEvalValEvalPhase( const std::vector& vars, - ResultCallback1* var_eval, - ResultCallback2* val_eval) { + std::function var_eval, + Solver::IndexEvaluator2 val_eval) { return $self->MakePhase(vars, var_eval, val_eval); } DecisionBuilder* VarStrValEvalTieBreakPhase( const std::vector& vars, operations_research::Solver::IntVarStrategy var_str, - ResultCallback2* val_eval, - ResultCallback1* tie_breaker) { + Solver::IndexEvaluator2 val_eval, + std::function tie_breaker) { return $self->MakePhase(vars, var_str, val_eval, tie_breaker); } DecisionBuilder* VarEvalValEvalTieBreakPhase( const std::vector& vars, - ResultCallback1* var_eval, - ResultCallback2* val_eval, - ResultCallback1* tie_breaker) { + std::function var_eval, + Solver::IndexEvaluator2 val_eval, + std::function tie_breaker) { return $self->MakePhase(vars, var_eval, val_eval, tie_breaker); } DecisionBuilder* EvalEvalStrPhase( const std::vector& vars, - ResultCallback2* evaluator, + Solver::IndexEvaluator2 evaluator, operations_research::Solver::EvaluatorStrategy str) { return $self->MakePhase(vars, evaluator, str); } DecisionBuilder* EvalEvalStrTieBreakPhase( const std::vector& vars, - ResultCallback2* evaluator, - ResultCallback1* tie_breaker, + Solver::IndexEvaluator2 evaluator, + Solver::IndexEvaluator1 tie_breaker, operations_research::Solver::EvaluatorStrategy str) { return $self->MakePhase(vars, evaluator, tie_breaker, str); } @@ -456,7 +352,7 @@ PY_STRINGIFY_DEBUGSTRING(Decision); SearchMonitor* GuidedLocalSearch( bool maximize, IntVar* const objective, - ResultCallback2* objective_function, + Solver::IndexEvaluator2 objective_function, int64 step, const std::vector& vars, double penalty_factor) { @@ -470,7 +366,7 @@ PY_STRINGIFY_DEBUGSTRING(Decision); LocalSearchFilter* LocalSearchObjectiveFilter( const std::vector& vars, - ResultCallback2* values, + Solver::IndexEvaluator2 values, IntVar* const objective, Solver::LocalSearchFilterBound filter_enum, Solver::LocalSearchOperation op_enum) { @@ -835,8 +731,6 @@ PROTECT_FROM_FAILURE(Solver::Fail(), arg1); // We "convert" the C++ flags to python gflags. We also need to actually // pass the value from the python flags to the C++ ones, upon the Solver // construction. -// TODO(user): cp_trace_propagation is set too late to be useful. Investigate -// and fix it. // Phase 1: declare the C++ flags. %import "base/commandlineflags.h" %{ @@ -1494,23 +1388,41 @@ namespace operations_research { %unignore SearchMonitor::SearchMonitor; %unignore SearchMonitor::~SearchMonitor; %unignore SearchMonitor::EnterSearch; +%feature("director") SearchMonitor::EnterSearch; %unignore SearchMonitor::RestartSearch; +%feature("director") SearchMonitor::RestartSearch; %unignore SearchMonitor::ExitSearch; +%feature("director") SearchMonitor::ExitSearch; %unignore SearchMonitor::BeginNextDecision; +%feature("director") SearchMonitor::BeginNextDecision; %unignore SearchMonitor::EndNextDecision; +%feature("director") SearchMonitor::EndNextDecision; %unignore SearchMonitor::ApplyDecision; +%feature("director") SearchMonitor::ApplyDecision; %unignore SearchMonitor::RefuteDecision; +%feature("director") SearchMonitor::RefuteDecision; %unignore SearchMonitor::AfterDecision; +%feature("director") SearchMonitor::AfterDecision; %unignore SearchMonitor::BeginFail; +%feature("director") SearchMonitor::BeginFail; %unignore SearchMonitor::EndFail; +%feature("director") SearchMonitor::EndFail; %unignore SearchMonitor::BeginInitialPropagation; +%feature("director") SearchMonitor::BeginInitialPropagation; %unignore SearchMonitor::EndInitialPropagation; +%feature("director") SearchMonitor::EndInitialPropagation; %unignore SearchMonitor::AcceptSolution; +%feature("director") SearchMonitor::AcceptSolution; %unignore SearchMonitor::AtSolution; +%feature("director") SearchMonitor::AtSolution; %unignore SearchMonitor::NoMoreSolutions; +%feature("director") SearchMonitor::NoMoreSolutions; %unignore SearchMonitor::LocalOptimum; +%feature("director") SearchMonitor::LocalOptimum; %unignore SearchMonitor::AcceptDelta; +%feature("director") SearchMonitor::AcceptDelta; %unignore SearchMonitor::AcceptNeighbor; +%feature("director") SearchMonitor::AcceptNeighbor; %rename (solver) SearchMonitor::solver; %feature("nodirector") SearchMonitor::solver; @@ -2308,3 +2220,4 @@ class PyLns(BaseLNS): PushBackIntVector(x, output_cpp_vector_fragments); return True } // %pythoncode + diff --git a/src/constraint_solver/python/routing.swig b/src/constraint_solver/python/routing.swig index fe5eb84b43..07c034d053 100644 --- a/src/constraint_solver/python/routing.swig +++ b/src/constraint_solver/python/routing.swig @@ -20,6 +20,11 @@ #include "constraint_solver/routing.h" %} +// We "convert" the C++ flags to python gflags. We also need to actually +// pass the value from the python flags to the C++ ones, upon the +// RoutingModel construction. This is a copy paste of the same code on the +// Solver class in ./constraint_solver.swig. + // As with the solver ctor, at the beginning of the routing model's // constructor code (in python), call SetFlags() with the python flags // as parameters. diff --git a/src/constraint_solver/resource.cc b/src/constraint_solver/resource.cc index 13e6f64692..29a425b62d 100644 --- a/src/constraint_solver/resource.cc +++ b/src/constraint_solver/resource.cc @@ -180,7 +180,8 @@ struct ThetaNode { void Compute(const ThetaNode& left, const ThetaNode& right) { total_processing = left.total_processing + right.total_processing; - total_ect = std::max(left.total_ect + right.total_processing, right.total_ect); + total_ect = + std::max(left.total_ect + right.total_processing, right.total_ect); } bool IsIdentity() const { @@ -312,8 +313,8 @@ struct LambdaThetaNode { // associated with such sets. void Compute(const LambdaThetaNode& left, const LambdaThetaNode& right) { energy = left.energy + right.energy; - energetic_end_min = - std::max(right.energetic_end_min, left.energetic_end_min + right.energy); + energetic_end_min = std::max(right.energetic_end_min, + left.energetic_end_min + right.energy); const int64 energy_left_opt = left.energy_opt + right.energy; const int64 energy_right_opt = left.energy + right.energy_opt; if (energy_left_opt > energy_right_opt) { @@ -526,7 +527,7 @@ bool NotLast::Propagate() { bool modified = false; for (int i = 0; i < by_start_min_.size(); ++i) { IntervalVar* const var = by_start_min_[i]->interval; - if ((strict_ || var->DurationMin() > 0 ) && var->EndMax() > new_lct_[i]) { + if ((strict_ || var->DurationMin() > 0) && var->EndMax() > new_lct_[i]) { modified = true; var->SetEndMax(new_lct_[i]); } @@ -578,7 +579,8 @@ class EdgeFinderAndDetectablePrecedences { }; EdgeFinderAndDetectablePrecedences::EdgeFinderAndDetectablePrecedences( - Solver* const solver, const std::vector& intervals, bool mirror, bool strict) + Solver* const solver, const std::vector& intervals, bool mirror, + bool strict) : solver_(solver), theta_tree_(intervals.size()), lt_tree_(intervals.size()), @@ -1071,11 +1073,11 @@ class FullDisjunctiveConstraint : public DisjunctiveConstraint { private: int64 Distance(int64 activity_plus_one, int64 next_activity_plus_one) { - return (transition_time_.get() == nullptr || activity_plus_one == 0 || + return (activity_plus_one == 0 || next_activity_plus_one > intervals_.size()) ? 0 - : transition_time_->Run(activity_plus_one - 1, - next_activity_plus_one - 1); + : transition_time_(activity_plus_one - 1, + next_activity_plus_one - 1); } void BuildNextModelIfNeeded() { @@ -1140,9 +1142,9 @@ class FullDisjunctiveConstraint : public DisjunctiveConstraint { } // 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::Distance))); + s->AddConstraint( + s->MakePathCumul(nexts_, actives_, time_cumuls_, time_slacks_, + [this](int64 x, int64 y) { return Distance(x, y); })); std::vector short_slacks(time_slacks_.begin() + 1, time_slacks_.end()); s->AddConstraint(s->RevAlloc( @@ -1205,11 +1207,11 @@ struct DualCapacityThetaNode { void Compute(const DualCapacityThetaNode& left, const DualCapacityThetaNode& right) { energy = left.energy + right.energy; - energetic_end_min = - std::max(left.energetic_end_min + right.energy, right.energetic_end_min); + energetic_end_min = std::max(left.energetic_end_min + right.energy, + right.energetic_end_min); residual_energetic_end_min = std::max(left.residual_energetic_end_min + right.energy, - right.residual_energetic_end_min); + right.residual_energetic_end_min); } // Amount of resource consumed by the Theta set, in units of demand X time. @@ -1520,8 +1522,8 @@ class EdgeFinder : public Constraint { const int64 end_min = task->interval->EndMin(); while (end_max_index < by_start_min_.size() && by_end_max_[end_max_index]->interval->EndMax() <= end_min) { - max_start_min = std::max(max_start_min, - by_end_max_[end_max_index]->interval->StartMin()); + max_start_min = std::max( + max_start_min, by_end_max_[end_max_index]->interval->StartMin()); ++end_max_index; } if (end_max_index > 0 && task->interval->StartMin() <= max_start_min && @@ -1762,8 +1764,9 @@ class CumulativeTimeTable : public Constraint { for (const Task* const task : by_start_min_) { const IntervalVar* const interval = task->interval; if (interval->StartMin() == interval->StartMax() && - interval->EndMin() == interval->EndMax()) + interval->EndMin() == interval->EndMax()) { continue; + } while (interval->StartMin() > profile_unique_time_[profile_index].time) { DCHECK(profile_index < profile_unique_time_.size()); ++profile_index; @@ -1996,10 +1999,10 @@ class CumulativeConstraint : public Constraint { } else { Solver* const s = solver(); if (edge_finder) { - return useful_tasks.size() < FLAGS_cp_max_edge_finder_size ? - s->RevAlloc( - new EdgeFinder(s, useful_tasks, capacity_)) : - nullptr; + return useful_tasks.size() < FLAGS_cp_max_edge_finder_size + ? s->RevAlloc(new EdgeFinder(s, useful_tasks, + capacity_)) + : nullptr; } else { return s->RevAlloc(new CumulativeTimeTable( s, useful_tasks, capacity_)); @@ -2132,9 +2135,8 @@ class VariableDemandCumulativeConstraint : public Constraint { // If there are less than 2 such intervals, the constraint would do // nothing const std::string seq_name = StrCat(name(), "-HighDemandSequence"); - constraint = - solver()->MakeStrictDisjunctiveConstraint(high_demand_intervals, - seq_name); + constraint = solver()->MakeStrictDisjunctiveConstraint( + high_demand_intervals, seq_name); } } if (constraint != nullptr) { @@ -2155,7 +2157,7 @@ class VariableDemandCumulativeConstraint : public Constraint { interval->SetPerformed(false); } // Add to the useful_task vector if it may be performed and that it - // actually consumes some of the resource. + // may actually consume some of the resource. if (interval->MayBePerformed() && original_task.demand->Max() > 0) { Solver* const s = solver(); IntervalVar* const original_interval = original_task.interval; @@ -2221,10 +2223,20 @@ DisjunctiveConstraint::DisjunctiveConstraint( if (!name.empty()) { set_name(name); } + transition_time_ = [](int64 x, int64 y) { return 0; }; } DisjunctiveConstraint::~DisjunctiveConstraint() {} +void DisjunctiveConstraint::SetTransitionTime( + std::function transition_time) { + if (transition_time != nullptr) { + transition_time_ = transition_time; + } else { + transition_time_ = [](int64 x, int64 y) { return 0; }; + } +} + // ---------- Factory methods ---------- DisjunctiveConstraint* Solver::MakeDisjunctiveConstraint( diff --git a/src/constraint_solver/routing.cc b/src/constraint_solver/routing.cc index 6004f44847..a9e8664b30 100644 --- a/src/constraint_solver/routing.cc +++ b/src/constraint_solver/routing.cc @@ -20,7 +20,7 @@ #include #include "base/hash.h" #include -#include "base/unique_ptr.h" +#include #include "base/callback.h" #include "base/casts.h" @@ -173,7 +173,7 @@ class LightFunctionElementConstraint : public Constraint { public: LightFunctionElementConstraint(Solver* const solver, IntVar* const var, IntVar* const index, - std::function values) + Solver::IndexEvaluator1 values) : Constraint(solver), var_(var), index_(index), @@ -209,12 +209,12 @@ class LightFunctionElementConstraint : public Constraint { IntVar* const var_; IntVar* const index_; - std::function values_; + Solver::IndexEvaluator1 values_; }; Constraint* MakeLightElement(Solver* const solver, IntVar* const var, IntVar* const index, - std::function values) { + Solver::IndexEvaluator1 values) { return solver->RevAlloc(new LightFunctionElementConstraint( solver, var, index, std::move(values))); } @@ -228,7 +228,7 @@ class LightFunctionElement2Constraint : public Constraint { public: LightFunctionElement2Constraint(Solver* const solver, IntVar* const var, IntVar* const index1, IntVar* const index2, - std::function values) + Solver::IndexEvaluator2 values) : Constraint(solver), var_(var), index1_(index1), @@ -265,12 +265,12 @@ class LightFunctionElement2Constraint : public Constraint { IntVar* const var_; IntVar* const index1_; IntVar* const index2_; - std::function values_; + Solver::IndexEvaluator2 values_; }; Constraint* MakeLightElement2(Solver* const solver, IntVar* const var, IntVar* const index1, IntVar* const index2, - std::function values) { + Solver::IndexEvaluator2 values) { return solver->RevAlloc(new LightFunctionElement2Constraint( solver, var, index1, index2, std::move(values))); } @@ -303,7 +303,7 @@ class MakeRelocateNeighborsOperator : public PathOperator { MakeRelocateNeighborsOperator( const std::vector& vars, const std::vector& secondary_vars, ResultCallback1* start_empty_path_class, - Solver::IndexEvaluator2* arc_evaluator) + RoutingModel::TransitEvaluator2* arc_evaluator) : PathOperator(vars, secondary_vars, 2, start_empty_path_class), arc_evaluator_(arc_evaluator) { int64 max_next = -1; @@ -391,7 +391,7 @@ class MakeRelocateNeighborsOperator : public PathOperator { return kNoChange; } - std::unique_ptr arc_evaluator_; + std::unique_ptr arc_evaluator_; std::vector prevs_; }; @@ -399,7 +399,7 @@ LocalSearchOperator* MakeRelocateNeighbors( Solver* solver, const std::vector& vars, const std::vector& secondary_vars, ResultCallback1* start_empty_path_class, - Solver::IndexEvaluator2* arc_evaluator) { + RoutingModel::TransitEvaluator2* arc_evaluator) { return solver->RevAlloc(new MakeRelocateNeighborsOperator( vars, secondary_vars, start_empty_path_class, arc_evaluator)); } @@ -592,9 +592,9 @@ bool PairRelocateOperator::MakeNeighbor() { if (prev_sibling < 0) { return false; } - return - MoveChain(prev_sibling, sibling, BaseNode(kPairSecondNodeDestination)) && - MoveChain(prev, first_pair_node, BaseNode(kPairFirstNodeDestination)); + return MoveChain(prev_sibling, sibling, + BaseNode(kPairSecondNodeDestination)) && + MoveChain(prev, first_pair_node, BaseNode(kPairFirstNodeDestination)); } void PairRelocateOperator::OnNodeInitialization() { @@ -661,7 +661,7 @@ class RoutingCache : public RoutingModel::NodeEvaluator2 { class MatrixEvaluator : public BaseObject { public: MatrixEvaluator(const int64* const* values, int nodes, RoutingModel* model) - : values_(new int64* [nodes]), nodes_(nodes), model_(model) { + : values_(new int64*[nodes]), nodes_(nodes), model_(model) { CHECK(values) << "null pointer"; for (int i = 0; i < nodes_; ++i) { values_[i] = new int64[nodes_]; @@ -719,9 +719,7 @@ class ConstantEvaluator : public BaseObject { // Left-branch dive branch selector -Solver::DecisionModification LeftDive(Solver* const s) { - return Solver::KEEP_LEFT; -} +Solver::DecisionModification LeftDive() { return Solver::KEEP_LEFT; } } // namespace @@ -986,14 +984,15 @@ bool RoutingModel::AddDimensionWithCapacityInternal( } } -bool RoutingModel::AddConstantDimension(int64 value, int64 capacity, - bool fix_start_cumul_to_zero, - const std::string& dimension_name) { +bool RoutingModel::AddConstantDimensionWithSlack(int64 value, int64 capacity, + int64 slack_max, + bool fix_start_cumul_to_zero, + const std::string& dimension_name) { ConstantEvaluator* evaluator = solver_->RevAlloc(new ConstantEvaluator(value)); return AddDimension( - NewPermanentCallback(evaluator, &ConstantEvaluator::Value), 0, capacity, - fix_start_cumul_to_zero, dimension_name); + NewPermanentCallback(evaluator, &ConstantEvaluator::Value), slack_max, + capacity, fix_start_cumul_to_zero, dimension_name); } bool RoutingModel::AddVectorDimension(const int64* values, int64 capacity, @@ -1143,10 +1142,10 @@ uint64 RoutingModel::GetFingerprintOfEvaluator( const int row_num_bytes = row_size * sizeof(int64); const uint64 fprint = ThoroughHash( reinterpret_cast(row.get()), row_num_bytes); - // ThoroughHash never returns 0. + // MixTwoUInt64 never returns 0. evaluator_fprint = evaluator_fprint != 0 - ? MixTwoUInt64(evaluator_fprint, fprint) - : fprint; + ? MixTwoUInt64(evaluator_fprint, fprint) + : fprint; } return evaluator_fprint; } @@ -1156,8 +1155,8 @@ void RoutingModel::ComputeCostClasses() { bool all_evaluators_equal = true; // Find first non-null evaluator. int evaluator_index = 0; - while (evaluator_index < transit_cost_of_vehicle_.size() - && transit_cost_of_vehicle_[evaluator_index] == nullptr) { + while (evaluator_index < transit_cost_of_vehicle_.size() && + transit_cost_of_vehicle_[evaluator_index] == nullptr) { ++evaluator_index; } // Compare non-null evaluators. @@ -1210,10 +1209,9 @@ void RoutingModel::ComputeCostClasses() { // there's only one evaluator callback used, we don't bother computing its // fingerprint. if (!FindCopy(evaluator_to_fprint, uncached_evaluator, &evaluator_fprint)) { - evaluator_fprint = - all_evaluators_equal - ? kAllEquivalentEvaluatorFprint - : GetFingerprintOfEvaluator(uncached_evaluator); + evaluator_fprint = all_evaluators_equal + ? kAllEquivalentEvaluatorFprint + : GetFingerprintOfEvaluator(uncached_evaluator); evaluator_to_fprint[uncached_evaluator] = evaluator_fprint; } NodeEvaluator2** cached_evaluator = @@ -1368,7 +1366,7 @@ void RoutingModel::AddDisjunctionInternal(const std::vector& nodes, CHECK_NE(kUnassigned, node_to_index_[nodes[i]]); disjunction_nodes[i] = node_to_index_[nodes[i]]; } - disjunctions_.back().penalty = penalty; + disjunctions_.back().value = penalty; for (const NodeIndex node : nodes) { // TODO(user): support multiple disjunction per node node_to_disjunction_[node_to_index_[node]] = DisjunctionIndex(size); @@ -1387,7 +1385,7 @@ IntVar* RoutingModel::CreateDisjunction(DisjunctionIndex disjunction) { IntVar* no_active_var = solver_->MakeBoolVar(); disjunction_vars[nodes_size] = no_active_var; solver_->AddConstraint(solver_->MakeSumEquality(disjunction_vars, 1)); - const int64 penalty = disjunctions_[disjunction].penalty; + const int64 penalty = disjunctions_[disjunction].value; if (penalty < 0) { no_active_var->SetMax(0); return nullptr; @@ -1396,6 +1394,45 @@ IntVar* RoutingModel::CreateDisjunction(DisjunctionIndex disjunction) { } } +void RoutingModel::AddSoftSameVehicleConstraint(const std::vector& nodes, + int64 cost) { + if (!nodes.empty()) { + ValuedNodes same_vehicle_cost; + for (const NodeIndex node : nodes) { + same_vehicle_cost.nodes.push_back(node_to_index_[node]); + } + same_vehicle_cost.value = cost; + same_vehicle_costs_.push_back(same_vehicle_cost); + } +} + +IntVar* RoutingModel::CreateSameVehicleCost(int index) { + const std::vector& nodes = same_vehicle_costs_[index].nodes; + CHECK(!nodes.empty()); + std::vector vehicle_counts; + solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, nodes.size() + 1, + &vehicle_counts); + std::vector vehicle_values(vehicle_vars_.size() + 1); + for (int i = 0; i < vehicle_vars_.size(); ++i) { + vehicle_values[i] = i; + } + vehicle_values[vehicle_vars_.size()] = -1; + std::vector vehicle_vars; + for (const int node : nodes) { + vehicle_vars.push_back(vehicle_vars_[node]); + } + solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts)); + std::vector vehicle_used; + for (int i = 0; i < vehicle_vars_.size() + 1; ++i) { + vehicle_used.push_back( + solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1)); + } + vehicle_used.push_back(solver_->MakeIntConst(-1)); + return solver_->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0), + same_vehicle_costs_[index].value) + ->Var(); +} + void RoutingModel::AddLocalSearchOperator(LocalSearchOperator* ls_operator) { extra_operators_.push_back(ls_operator); } @@ -1403,8 +1440,8 @@ void RoutingModel::AddLocalSearchOperator(LocalSearchOperator* ls_operator) { int64 RoutingModel::GetDepot() const { return vehicles() > 0 ? Start(0) : -1; } void RoutingModel::SetDepot(NodeIndex depot) { - std::vector> start_end(vehicles_, - std::make_pair(depot, depot)); + std::vector> start_end( + vehicles_, std::make_pair(depot, depot)); SetStartEnd(start_end); } @@ -1489,23 +1526,19 @@ void RoutingModel::SetStartEnd( void RoutingModel::AppendHomogeneousArcCosts(int node_index, std::vector* cost_elements) { CHECK(cost_elements != nullptr); + Solver::IndexEvaluator1 arc_cost_evaluator = [this, node_index]( + int64 next_index) { return GetHomogeneousCost(node_index, next_index); }; if (UsesLightPropagation()) { // Only supporting positive costs. // TODO(user): Detect why changing lower bound to kint64min stalls // the search in GLS in some cases (Solomon instances for instance). IntVar* const base_cost_var = solver_->MakeIntVar(0, kint64max); - solver_->AddConstraint( - MakeLightElement(solver_.get(), base_cost_var, nexts_[node_index], - [this, node_index](int64 next_index) { - return GetHomogeneousCost(node_index, next_index); - })); + solver_->AddConstraint(MakeLightElement( + solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator)); IntVar* const var = solver_->MakeProd(base_cost_var, active_[node_index])->Var(); cost_elements->push_back(var); } else { - Solver::IndexEvaluator1* arc_cost_evaluator = - NewPermanentCallback(this, &RoutingModel::GetHomogeneousCost, - static_cast(node_index)); IntExpr* const expr = solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]); IntVar* const var = solver_->MakeProd(expr, active_[node_index])->Var(); @@ -1532,13 +1565,13 @@ void RoutingModel::AppendArcCosts(int node_index, cost_elements->push_back(var); } else { IntVar* const vehicle_class_var = - solver_->MakeElement( - NewPermanentCallback( - this, &RoutingModel::SafeGetCostClassInt64OfVehicle), - vehicle_vars_[node_index])->Var(); + solver_->MakeElement([this](int64 index) { + return SafeGetCostClassInt64OfVehicle(index); + }, vehicle_vars_[node_index])->Var(); IntExpr* const expr = solver_->MakeElement( - NewPermanentCallback(this, &RoutingModel::GetArcCostForClass, - static_cast(node_index)), + [this, node_index](int64 next, int64 vehicle_class) { + return GetArcCostForClass(node_index, next, vehicle_class); + }, nexts_[node_index], vehicle_class_var); IntVar* const var = solver_->MakeProd(expr, active_[node_index])->Var(); cost_elements->push_back(var); @@ -1666,6 +1699,10 @@ void RoutingModel::CloseModel() { dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements); dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements); } + // Same vehicle costs + for (int i = 0; i < same_vehicle_costs_.size(); ++i) { + cost_elements.push_back(CreateSameVehicleCost(i)); + } cost_ = solver_->MakeSum(cost_elements)->Var(); cost_->set_name("Cost"); @@ -1681,8 +1718,8 @@ void RoutingModel::CloseModel() { } struct Link { - Link(std::pair link, double value, int vehicle_class, int64 start_depot, - int64 end_depot) + Link(std::pair link, double value, int vehicle_class, + int64 start_depot, int64 end_depot) : link(link), value(value), vehicle_class(vehicle_class), @@ -1797,18 +1834,18 @@ class RouteConstructor { for (int dimension_index = 0; dimension_index < dimensions_.size(); ++dimension_index) { cumuls_[dimension_index][node1] = - std::max(dimensions_[dimension_index]->GetTransitValue(start_depot, - node1, 0), - dimensions_[dimension_index]->CumulVar(node1)->Min()); + std::max(dimensions_[dimension_index]->GetTransitValue( + start_depot, node1, 0), + dimensions_[dimension_index]->CumulVar(node1)->Min()); } } if (node_to_vehicle_class_index_[node2] < 0) { for (int dimension_index = 0; dimension_index < dimensions_.size(); ++dimension_index) { cumuls_[dimension_index][node2] = - std::max(dimensions_[dimension_index]->GetTransitValue(start_depot, - node2, 0), - dimensions_[dimension_index]->CumulVar(node2)->Min()); + std::max(dimensions_[dimension_index]->GetTransitValue( + start_depot, node2, 0), + dimensions_[dimension_index]->CumulVar(node2)->Min()); } } @@ -1842,8 +1879,8 @@ class RouteConstructor { } std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator); - const int extra_vehicles = - std::max(0, static_cast(final_chains_.size()) - model_->vehicles()); + const int extra_vehicles = std::max( + 0, static_cast(final_chains_.size()) - model_->vehicles()); // Bind the Start and End of each chain int chain_index = 0; for (chain_index = extra_vehicles; chain_index < final_chains_.size(); @@ -1980,7 +2017,7 @@ class RouteConstructor { CHECK_GE(non_depot_node, 0); const int64 depot_threashold = std::max(dimension.SlackVar(non_depot_node)->Max(), - dimension.CumulVar(non_depot_node)->Max()); + dimension.CumulVar(non_depot_node)->Max()); int64 available_from_tail1 = cumuls_[dimension_index][tail1] + dimension.GetTransitValue(tail1, head2, 0); @@ -3398,7 +3435,7 @@ int64 RoutingModel::GetArcCostForFirstSolution(int64 i, int64 j) { int64 RoutingModel::GetDimensionTransitCostSum( int64 i, int64 j, const CostClass& cost_class) const { int64 cost = 0; - for (const std::pair evaluator_and_coefficient : + for (const std::pair evaluator_and_coefficient : cost_class.dimension_transit_evaluator_and_cost_coefficient) { DCHECK_GT(evaluator_and_coefficient.second, 0); cost += evaluator_and_coefficient.second * @@ -3504,8 +3541,8 @@ int64 RoutingModel::UnperformedPenalty(int64 var_index) const { return UnperformedPenaltyOrValue(0, var_index); } -int64 RoutingModel::UnperformedPenaltyOrValue( - int64 default_value, int64 var_index) const { +int64 RoutingModel::UnperformedPenaltyOrValue(int64 default_value, + int64 var_index) const { if (active_[var_index]->Min() == 1) return kint64max; // Forced active. DisjunctionIndex disjunction_index = kNoDisjunction; GetDisjunctionIndexFromVariableIndex(var_index, &disjunction_index); @@ -3514,8 +3551,8 @@ int64 RoutingModel::UnperformedPenaltyOrValue( DCHECK_EQ(var_index, disjunctions_[disjunction_index].nodes[0]); // The disjunction penalty can't be kNoPenalty, otherwise we would have // caught it earlier (the node would be forced active). - DCHECK_GE(disjunctions_[disjunction_index].penalty, 0); - return disjunctions_[disjunction_index].penalty; + DCHECK_GE(disjunctions_[disjunction_index].value, 0); + return disjunctions_[disjunction_index].value; } std::string RoutingModel::DebugOutputAssignment( @@ -3666,15 +3703,15 @@ LocalSearchOperator* RoutingModel::CreateInsertionOperator() { #define CP_ROUTING_ADD_CALLBACK_OPERATOR(operator_type, cp_operator_type) \ if (CostsAreHomogeneousAcrossVehicles()) { \ - local_search_operators_[operator_type] = solver_->MakeOperator( \ - nexts_, \ - NewPermanentCallback(this, &RoutingModel::GetArcCostForVehicle), \ - cp_operator_type); \ + local_search_operators_[operator_type] = \ + solver_->MakeOperator(nexts_, [this](int64 i, int64 j, int64 k) { \ + return GetArcCostForVehicle(i, j, k); \ + }, cp_operator_type); \ } else { \ local_search_operators_[operator_type] = solver_->MakeOperator( \ - nexts_, vehicle_vars_, \ - NewPermanentCallback(this, &RoutingModel::GetArcCostForVehicle), \ - cp_operator_type); \ + nexts_, vehicle_vars_, [this](int64 i, int64 j, int64 k) { \ + return GetArcCostForVehicle(i, j, k); \ + }, cp_operator_type); \ } void RoutingModel::CreateNeighborhoodOperators() { @@ -3805,11 +3842,11 @@ RoutingModel::GetOrCreateLocalSearchFilters() { RoutingLocalSearchFilter* path_cumul_filter = nullptr; if (FLAGS_routing_use_path_cumul_filter) { for (const RoutingDimension* dimension : dimensions_) { - Callback1* objective_callback = nullptr; + Solver::ObjectiveWatcher objective_callback = nullptr; if (path_cumul_filter != nullptr) { - objective_callback = NewPermanentCallback( - path_cumul_filter, - &RoutingLocalSearchFilter::InjectObjectiveValue); + objective_callback = [path_cumul_filter](int64 value) { + return path_cumul_filter->InjectObjectiveValue(value); + }; } path_cumul_filter = MakePathCumulFilter(*this, *dimension, objective_callback); @@ -3821,35 +3858,37 @@ RoutingModel::GetOrCreateLocalSearchFilters() { } RoutingLocalSearchFilter* node_disjunction_filter = nullptr; if (FLAGS_routing_use_disjunction_filter && !disjunctions_.empty()) { - Callback1* objective_callback = nullptr; + Solver::ObjectiveWatcher objective_callback = nullptr; if (path_cumul_filter != nullptr) { - objective_callback = NewPermanentCallback( - path_cumul_filter, &RoutingLocalSearchFilter::InjectObjectiveValue); + objective_callback = [path_cumul_filter](int64 value) { + return path_cumul_filter->InjectObjectiveValue(value); + }; } node_disjunction_filter = MakeNodeDisjunctionFilter(*this, objective_callback); } if (FLAGS_routing_use_objective_filter) { - Callback1* objective_callback = nullptr; + Solver::ObjectiveWatcher objective_callback = nullptr; if (node_disjunction_filter != nullptr) { - objective_callback = NewPermanentCallback( - node_disjunction_filter, - &RoutingLocalSearchFilter::InjectObjectiveValue); + objective_callback = [node_disjunction_filter](int64 value) { + return node_disjunction_filter->InjectObjectiveValue(value); + }; } else if (path_cumul_filter != nullptr) { - objective_callback = NewPermanentCallback( - path_cumul_filter, &RoutingLocalSearchFilter::InjectObjectiveValue); + objective_callback = [path_cumul_filter](int64 value) { + return path_cumul_filter->InjectObjectiveValue(value); + }; } if (CostsAreHomogeneousAcrossVehicles()) { LocalSearchFilter* filter = solver_->MakeLocalSearchObjectiveFilter( - nexts_, - NewPermanentCallback(this, &RoutingModel::GetHomogeneousCost), - objective_callback, cost_, Solver::LE, Solver::SUM); + nexts_, [this](int64 i, int64 j) { + return GetHomogeneousCost(i, j); + }, objective_callback, cost_, Solver::LE, Solver::SUM); filters_.push_back(filter); } else { LocalSearchFilter* filter = solver_->MakeLocalSearchObjectiveFilter( - nexts_, vehicle_vars_, - NewPermanentCallback(this, &RoutingModel::GetArcCostForVehicle), - objective_callback, cost_, Solver::LE, Solver::SUM); + nexts_, vehicle_vars_, [this](int64 i, int64 j, int64 k) { + return GetArcCostForVehicle(i, j, k); + }, objective_callback, cost_, Solver::LE, Solver::SUM); filters_.push_back(filter); } } @@ -3871,8 +3910,8 @@ RoutingModel::GetOrCreateLocalSearchFilters() { filters_.insert(filters_.end(), path_cumul_filters.begin(), path_cumul_filters.end()); } - filters_.insert(filters_.end(), - extra_filters_.begin(), extra_filters_.end()); + filters_.insert(filters_.end(), extra_filters_.begin(), + extra_filters_.end()); } return filters_; } @@ -3929,20 +3968,20 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders() { finalize_solution; // Global cheapest addition heuristic. first_solution_decision_builders_[ROUTING_GLOBAL_CHEAPEST_ARC] = - solver_->MakePhase( - nexts_, - NewPermanentCallback(this, &RoutingModel::GetArcCostForFirstSolution), - Solver::CHOOSE_STATIC_GLOBAL_BEST); + solver_->MakePhase(nexts_, [this](int64 i, int64 j) { + return GetArcCostForFirstSolution(i, j); + }, Solver::CHOOSE_STATIC_GLOBAL_BEST); // Cheapest addition heuristic. first_solution_decision_builders_[ROUTING_LOCAL_CHEAPEST_ARC] = solver_->MakePhase(nexts_, Solver::CHOOSE_FIRST_UNBOUND, - NewPermanentCallback( - this, &RoutingModel::GetArcCostForFirstSolution)); + [this](int64 i, int64 j) { + return GetArcCostForFirstSolution(i, j); + }); // Path-based cheapest addition heuristic. first_solution_decision_builders_[ROUTING_PATH_CHEAPEST_ARC] = - solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, - NewPermanentCallback( - this, &RoutingModel::GetArcCostForFirstSolution)); + solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, [this](int64 i, int64 j) { + return GetArcCostForFirstSolution(i, j); + }); if (vehicles() == 1) { DecisionBuilder* fast_one_path_builder = solver_->RevAlloc(new FastOnePathBuilder( @@ -3964,15 +4003,16 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders() { // Path-based most constrained arc addition heuristic. first_solution_decision_builders_[ROUTING_PATH_MOST_CONSTRAINED_ARC] = solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, - NewPermanentCallback( - this, &RoutingModel::ArcIsMoreConstrainedThanArc)); + [this](int64 i, int64 j, int64 k) { + return ArcIsMoreConstrainedThanArc(i, j, k); + }); if (FLAGS_routing_use_filtered_first_solutions) { first_solution_filtered_decision_builders_ [ROUTING_PATH_MOST_CONSTRAINED_ARC] = solver_->RevAlloc( new ComparatorCheapestAdditionFilteredDecisionBuilder( - this, NewPermanentCallback( - this, &RoutingModel::ArcIsMoreConstrainedThanArc), - GetOrCreateFeasibilityFilters())); + this, [this](int64 i, int64 j, int64 k) { + return ArcIsMoreConstrainedThanArc(i, j, k); + }, GetOrCreateFeasibilityFilters())); first_solution_decision_builders_[ROUTING_PATH_MOST_CONSTRAINED_ARC] = solver_->Try(first_solution_filtered_decision_builders_ [ROUTING_PATH_MOST_CONSTRAINED_ARC], @@ -3983,8 +4023,7 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders() { 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)); + first_solution_evaluator_); } else { first_solution_decision_builders_[ROUTING_EVALUATOR_STRATEGY] = nullptr; } @@ -4026,20 +4065,18 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders() { &RoutingModel::UnperformedPenaltyOrValue, 0), GetOrCreateFeasibilityFilters())); first_solution_decision_builders_[ROUTING_GLOBAL_CHEAPEST_INSERTION] = - solver_->Try( - first_solution_filtered_decision_builders_ - [ROUTING_GLOBAL_CHEAPEST_INSERTION], - first_solution_decision_builders_[ROUTING_BEST_INSERTION]); + solver_->Try(first_solution_filtered_decision_builders_ + [ROUTING_GLOBAL_CHEAPEST_INSERTION], + first_solution_decision_builders_[ROUTING_BEST_INSERTION]); // Local cheapest insertion first_solution_filtered_decision_builders_[ROUTING_LOCAL_CHEAPEST_INSERTION] = solver_->RevAlloc(new LocalCheapestInsertionFilteredDecisionBuilder( this, NewPermanentCallback(this, &RoutingModel::GetArcCostForVehicle), GetOrCreateFeasibilityFilters())); first_solution_decision_builders_[ROUTING_LOCAL_CHEAPEST_INSERTION] = - solver_->Try( - first_solution_filtered_decision_builders_ - [ROUTING_LOCAL_CHEAPEST_INSERTION], - first_solution_decision_builders_[ROUTING_BEST_INSERTION]); + solver_->Try(first_solution_filtered_decision_builders_ + [ROUTING_LOCAL_CHEAPEST_INSERTION], + first_solution_decision_builders_[ROUTING_BEST_INSERTION]); // Savings if (FLAGS_routing_use_filtered_first_solutions) { first_solution_filtered_decision_builders_[ROUTING_SAVINGS] = @@ -4065,8 +4102,7 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders() { first_solution_decision_builders_[ROUTING_SWEEP] = solver_->Try( sweep_builder, first_solution_decision_builders_[ROUTING_SWEEP]); if (FLAGS_routing_use_first_solution_dive) { - DecisionBuilder* const apply = - solver_->MakeApplyBranchSelector(NewPermanentCallback(&LeftDive)); + DecisionBuilder* const apply = solver_->MakeApplyBranchSelector(LeftDive); for (int i = 0; i < first_solution_decision_builders_.size(); ++i) { first_solution_decision_builders_[i] = solver_->Compose(apply, first_solution_decision_builders_[i]); @@ -4148,13 +4184,15 @@ void RoutingModel::SetupMetaheuristics() { if (CostsAreHomogeneousAcrossVehicles()) { optimize = solver_->MakeGuidedLocalSearch( false, cost_, - NewPermanentCallback(this, &RoutingModel::GetHomogeneousCost), + [this](int64 i, int64 j) { return GetHomogeneousCost(i, j); }, FLAGS_routing_optimization_step, nexts_, FLAGS_routing_guided_local_search_lambda_coefficient); } else { optimize = solver_->MakeGuidedLocalSearch( false, cost_, - NewPermanentCallback(this, &RoutingModel::GetArcCostForVehicle), + [this](int64 i, int64 j, int64 k) { + return GetArcCostForVehicle(i, j, k); + }, FLAGS_routing_optimization_step, nexts_, vehicle_vars_, FLAGS_routing_guided_local_search_lambda_coefficient); } @@ -4337,9 +4375,10 @@ int64 RoutingModel::GetCumulVarSoftUpperBound(NodeIndex node, } int64 RoutingModel::GetCumulVarSoftUpperBoundCoefficient( NodeIndex node, const std::string& name) const { - return HasDimension(name) ? GetDimensionOrDie(name) - .GetCumulVarSoftUpperBoundCoefficient(node) - : 0; + return HasDimension(name) + ? GetDimensionOrDie(name) + .GetCumulVarSoftUpperBoundCoefficient(node) + : 0; } void RoutingModel::SetStartCumulVarSoftUpperBound(int vehicle, const std::string& name, int64 ub, @@ -4511,9 +4550,9 @@ void RoutingDimension::InitializeCumuls( })); } else { capacity_var = - solver->MakeElement(NewPermanentCallback(&WrappedVehicleEvaluator, - vehicle_capacity), - model_->VehicleVar(i))->Var(); + solver->MakeElement([vehicle_capacity](int64 index) { + return WrappedVehicleEvaluator(vehicle_capacity, index); + }, model_->VehicleVar(i))->Var(); } if (i < model_->Size()) { IntVar* const capacity_active = solver->MakeBoolVar(); @@ -4543,12 +4582,6 @@ template int64 IthElementOrValue(const std::vector& v, int64 index) { return index >= 0 ? v[index] : value; } - -template -int64 IthEvaluatorValueOrValue(const std::vector* evaluators, int64 from, - int64 to, int64 eval_index) { - return eval_index >= 0 ? (*evaluators)[eval_index]->Run(from, to) : value; -} } // namespace void RoutingDimension::InitializeTransits( @@ -4568,7 +4601,7 @@ void RoutingDimension::InitializeTransits( class_evaluators_.clear(); transit_evaluators_.clear(); hash_map evaluator_to_class; - std::vector vehicle_to_class(transit_evaluators.size(), -1); + vehicle_to_class_.resize(transit_evaluators.size(), -1); for (int i = 0; i < transit_evaluators.size(); ++i) { RoutingModel::NodeEvaluator2* const evaluator = transit_evaluators[i]; int evaluator_class = -1; @@ -4578,12 +4611,21 @@ void RoutingDimension::InitializeTransits( class_evaluators_.emplace_back( NewPermanentCallback(&WrappedEvaluator, model_, evaluator)); } - vehicle_to_class[i] = evaluator_class; + vehicle_to_class_[i] = evaluator_class; transit_evaluators_.push_back(class_evaluators_[evaluator_class].get()); } + Solver::IndexEvaluator1 vehicle_class_function = [this](int index) { + return IthElementOrValue<-1>(vehicle_to_class_, index); + }; + CHECK(!class_evaluators_.empty()); for (int i = 0; i < size; ++i) { IntVar* fixed_transit = nullptr; + Solver::IndexEvaluator2 transit_vehicle_evaluator = [this, i]( + int64 to, int64 eval_index) { + return eval_index >= 0 ? transit_evaluators_[eval_index]->Run(i, to) + : 0LL; + }; if (model_->UsesLightPropagation()) { if (class_evaluators_.size() == 1) { fixed_transit = solver->MakeIntVar(kint64min, kint64max); @@ -4599,31 +4641,23 @@ void RoutingDimension::InitializeTransits( fixed_transit = solver->MakeIntVar(kint64min, kint64max); solver->AddConstraint(MakeLightElement2( solver, fixed_transit, model_->NextVar(i), model_->VehicleVar(i), - [this, i](int64 to, int64 eval_index) { - return eval_index >= 0 - ? transit_evaluators_[eval_index]->Run(i, to) - : 0LL; - })); + transit_vehicle_evaluator)); } } else { if (class_evaluators_.size() == 1) { fixed_transit = - solver->MakeElement(NewPermanentCallback(&WrappedEvaluator, model_, - transit_evaluators[0], - static_cast(i)), - model_->NextVar(i))->Var(); + solver->MakeElement([this, transit_evaluators, i](int64 index) { + return WrappedEvaluator(model_, transit_evaluators[0], + static_cast(i), index); + }, model_->NextVar(i))->Var(); } else { IntVar* const vehicle_class_var = - solver->MakeElement(NewPermanentCallback(&IthElementOrValue<-1>, - vehicle_to_class), - model_->VehicleVar(i))->Var(); + solver->MakeElement(vehicle_class_function, model_->VehicleVar(i)) + ->Var(); fixed_transit = - solver->MakeElement( - NewPermanentCallback( - &IthEvaluatorValueOrValue< - std::unique_ptr, 0LL>, - &class_evaluators_, static_cast(i)), - model_->NextVar(i), vehicle_class_var)->Var(); + solver->MakeElement(transit_vehicle_evaluator, model_->NextVar(i), + vehicle_class_var) + ->Var(); } } if (slack_max == 0) { @@ -4652,8 +4686,8 @@ void RoutingDimension::SetSpanUpperBoundForVehicle(int64 upper_bound, Solver* const solver = model_->solver(); IntVar* const start = cumuls_[model_->Start(vehicle)]; IntVar* const end = cumuls_[model_->End(vehicle)]; - solver->AddConstraint(solver->MakeLessOrEqual( - solver->MakeDifference(end, start), upper_bound)); + solver->AddConstraint( + solver->MakeLessOrEqual(solver->MakeDifference(end, start), upper_bound)); } void RoutingDimension::SetSpanCostCoefficientForVehicle(int64 coefficient, @@ -4805,8 +4839,9 @@ void RoutingDimension::SetupCumulVarSoftUpperBoundCosts( if (soft_bound.var != nullptr) { IntVar* const cost_var = solver->MakeSemiContinuousExpr( - solver->MakeSum(soft_bound.var, -soft_bound.bound), 0, - soft_bound.coefficient)->Var(); + solver->MakeSum(soft_bound.var, -soft_bound.bound), 0, + soft_bound.coefficient) + ->Var(); cost_elements->push_back(cost_var); // TODO(user): Check if it wouldn't be better to minimize // soft_bound.var here. @@ -4945,8 +4980,9 @@ void RoutingDimension::SetupCumulVarSoftLowerBoundCosts( if (soft_bound.var != nullptr) { IntVar* const cost_var = solver->MakeSemiContinuousExpr( - solver->MakeDifference(soft_bound.bound, soft_bound.var), 0, - soft_bound.coefficient)->Var(); + solver->MakeDifference(soft_bound.bound, soft_bound.var), 0, + soft_bound.coefficient) + ->Var(); cost_elements->push_back(cost_var); model_->AddVariableMaximizedByFinalizer(soft_bound.var); } @@ -4972,7 +5008,8 @@ void RoutingDimension::SetupGlobalSpanCost( model_->AddVariableMaximizedByFinalizer(min_start_cumul); cost_elements->push_back( solver->MakeProd(solver->MakeDifference(max_end_cumul, min_start_cumul), - global_span_cost_coefficient_)->Var()); + global_span_cost_coefficient_) + ->Var()); } } @@ -5013,13 +5050,14 @@ void RoutingDimension::SetupSlackCosts(std::vector* cost_elements) cons cost_elements->push_back( solver->MakeProd(solver->MakeProd(slacks_[var_index], vehicle_span_cost_coefficients_[0]), - model_->ActiveVar(var_index))->Var()); + model_->ActiveVar(var_index)) + ->Var()); } else { IntVar* cost_coefficient_var = - solver->MakeElement( - NewPermanentCallback(&IthElementOrValue<0LL>, - vehicle_span_cost_coefficients_), - model_->VehicleVar(var_index))->Var(); + solver->MakeElement([this](int index) { + return IthElementOrValue<0LL>(vehicle_span_cost_coefficients_, + index); + }, model_->VehicleVar(var_index))->Var(); cost_elements->push_back( solver->MakeProd(slacks_[var_index], cost_coefficient_var)->Var()); // TODO(user): verify that there isn't any benefit in explicitly making diff --git a/src/constraint_solver/routing.h b/src/constraint_solver/routing.h index 0ced8583b8..e571701b45 100644 --- a/src/constraint_solver/routing.h +++ b/src/constraint_solver/routing.h @@ -156,7 +156,7 @@ #include #include "base/hash.h" #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include #include @@ -404,6 +404,7 @@ class RoutingModel { typedef _RoutingModel_VehicleClassIndex VehicleClassIndex; typedef ResultCallback1 VehicleEvaluator; typedef ResultCallback2 NodeEvaluator2; + typedef ResultCallback2 TransitEvaluator2; typedef std::pair NodePair; typedef std::vector NodePairs; @@ -433,7 +434,7 @@ class RoutingModel { // their transit evaluator (the raw version that takes var index, not Node // Index) and their span cost coefficient, we just store those. // This is sorted by the natural operator < (and *not* by DimensionIndex). - std::vector > + std::vector > dimension_transit_evaluator_and_cost_coefficient; explicit CostClass(NodeEvaluator2* arc_cost_evaluator) @@ -472,7 +473,7 @@ class RoutingModel { ITIVector dimension_capacities; // dimension_evaluators[d]->Run(from, to) is the transit value of arc // from->to for a dimension d. - ITIVector dimension_evaluators; + ITIVector dimension_evaluators; // Fingerprint of unvisitable non-start/end nodes. uint64 unvisitable_nodes_fprint; @@ -555,8 +556,15 @@ class RoutingModel { // get cumul and transit variables from the routing model. // Returns false if a dimension with the same name has already been created // (and doesn't create the new dimension). + bool AddConstantDimensionWithSlack(int64 value, int64 capacity, + int64 slack_max, + bool fix_start_cumul_to_zero, + const std::string& name); bool AddConstantDimension(int64 value, int64 capacity, - bool fix_start_cumul_to_zero, const std::string& name); + bool fix_start_cumul_to_zero, const std::string& name) { + return AddConstantDimensionWithSlack(value, capacity, 0, + fix_start_cumul_to_zero, name); + } // Creates a dimension where the transit variable is constrained to be // equal to 'values[i]' for node i; 'capacity' is the upper bound of // the cumul variables. 'name' is the name used to reference the dimension; @@ -650,10 +658,16 @@ class RoutingModel { #endif // !defined(SWIGPYTHON) && !defined(SWIGJAVA) // Returns the penalty of the node disjunction of index 'index'. int64 GetDisjunctionPenalty(DisjunctionIndex index) const { - return disjunctions_[index].penalty; + return disjunctions_[index].value; } // Returns the number of node disjunctions in the model. int GetNumberOfDisjunctions() const { return disjunctions_.size(); } + + // Adds a soft contraint to force a set of nodes to be on the same vehicle. + // If all nodes are not on the same vehicle, each extra vehicle used adds + // 'cost' to the cost function. + void AddSoftSameVehicleConstraint(const std::vector& nodes, int64 cost); + // Notifies that node1 and node2 form a pair of nodes which should belong // to the same route. This methods helps the search find better solutions, // especially in the local search phase. @@ -722,13 +736,13 @@ class RoutingModel { // ROUTING_EVALUATOR_STRATEGY (variant of ROUTING_PATH_CHEAPEST_ARC using // 'evaluator' to sort node segments). #ifndef SWIG - Solver::IndexEvaluator2* first_solution_evaluator() const { - return first_solution_evaluator_.get(); + const Solver::IndexEvaluator2& first_solution_evaluator() const { + return first_solution_evaluator_; } #endif // Takes ownership of evaluator. - void SetFirstSolutionEvaluator(Solver::IndexEvaluator2* evaluator) { - first_solution_evaluator_.reset(evaluator); + void SetFirstSolutionEvaluator(Solver::IndexEvaluator2 evaluator) { + first_solution_evaluator_ = evaluator; } // If a first solution flag has been set (to a value different than Default), // returns the corresponding strategy, otherwise returns the strategy which @@ -1103,11 +1117,11 @@ class RoutingModel { ROUTING_LOCAL_SEARCH_OPERATOR_COUNTER }; - // Structure storing node disjunction information (nodes and penalty when - // unperformed). - struct Disjunction { + // Structure storing a value for a set of nodes. Is used to store for node + // disjunction information (nodes and penalty when unperformed). + struct ValuedNodes { std::vector nodes; - int64 penalty; + int64 value; }; // Storage of a cost cache element corresponding to a cost arc ending at @@ -1145,12 +1159,16 @@ class RoutingModel { int64 SafeGetCostClassInt64OfVehicle(int64 vehicle) const { DCHECK_LT(0, vehicles_); return (vehicle >= 0 ? GetCostClassIndexOfVehicle(vehicle) - : kCostClassIndexOfZeroCost).value(); + : kCostClassIndexOfZeroCost) + .value(); } int64 GetDimensionTransitCostSum(int64 i, int64 j, const CostClass& cost_class) const; // Returns nullptr if no penalty cost, otherwise returns penalty variable. IntVar* CreateDisjunction(DisjunctionIndex disjunction); + // Returns the cost variable related to the soft same vehicle constraint of + // index 'index'. + IntVar* CreateSameVehicleCost(int index); // Returns the first active node in nodes starting from index + 1. int FindNextActive(int index, const std::vector& nodes) const; @@ -1245,8 +1263,10 @@ class RoutingModel { // Cached callbacks hash_map cached_node_callbacks_; // Disjunctions - ITIVector disjunctions_; + ITIVector disjunctions_; std::vector node_to_disjunction_; + // Same vehicle costs + std::vector same_vehicle_costs_; // Pickup and delivery NodePairs pickup_delivery_pairs_; // Index management @@ -1266,7 +1286,7 @@ class RoutingModel { std::vector first_solution_filtered_decision_builders_; RoutingStrategy first_solution_strategy_; - std::unique_ptr first_solution_evaluator_; + Solver::IndexEvaluator2 first_solution_evaluator_; std::vector local_search_operators_; RoutingMetaheuristic metaheuristic_; std::vector monitors_; @@ -1296,7 +1316,7 @@ class RoutingModel { // Callbacks to be deleted hash_set owned_node_callbacks_; - hash_set owned_index_callbacks_; + hash_set owned_index_callbacks_; friend class RoutingDimension; @@ -1336,7 +1356,7 @@ class RoutingDimension { } // Returns the callback evaluating the transit value between two node indices // for a given vehicle. - Solver::IndexEvaluator2* transit_evaluator(int vehicle) const { + RoutingModel::TransitEvaluator2* transit_evaluator(int vehicle) const { return transit_evaluators_[vehicle]; } #endif // SWIGCSHARP @@ -1408,8 +1428,8 @@ class RoutingDimension { int64 GetCumulVarSoftUpperBoundFromIndex(int64 index) const; // Returns the cost coefficient of the soft upper bound of a cumul variable // for a given node. If no soft upper bound has been set, 0 is returned. - int64 GetCumulVarSoftUpperBoundCoefficient(RoutingModel::NodeIndex node) - const; + int64 GetCumulVarSoftUpperBoundCoefficient( + RoutingModel::NodeIndex node) const; // Returns the cost coefficient of the soft upper bound of a cumul variable // for a given vehicle start node. If no soft upper bound has been set, 0 is // returned. @@ -1472,8 +1492,8 @@ class RoutingDimension { int64 GetCumulVarSoftLowerBoundFromIndex(int64 index) const; // Returns the cost coefficient of the soft lower bound of a cumul variable // for a given node. If no soft lower bound has been set, 0 is returned. - int64 GetCumulVarSoftLowerBoundCoefficient(RoutingModel::NodeIndex node) - const; + int64 GetCumulVarSoftLowerBoundCoefficient( + RoutingModel::NodeIndex node) const; // Returns the cost coefficient of the soft lower bound of a cumul variable // for a given vehicle start node. If no soft lower bound has been set, 0 is // returned. @@ -1542,8 +1562,9 @@ class RoutingDimension { std::vector transits_; // "transit_evaluators_" does the indexing by vehicle, while // "class_evaluators_" does the de-duplicated ownership. - std::vector transit_evaluators_; - std::vector > class_evaluators_; + std::vector transit_evaluators_; + std::vector > class_evaluators_; + std::vector vehicle_to_class_; std::vector slacks_; std::vector vehicle_span_upper_bounds_; int64 global_span_cost_coefficient_; @@ -1564,8 +1585,8 @@ class RoutingDimension { // depot. Used in the Sweep first solution heuristic. class SweepArranger { public: - explicit SweepArranger( - const ITIVector >& points); + explicit SweepArranger(const ITIVector >& points); virtual ~SweepArranger() {} void ArrangeNodes(std::vector* nodes); void SetSectors(int sectors) { sectors_ = sectors; } @@ -1790,9 +1811,8 @@ class GlobalCheapestInsertionFilteredDecisionBuilder std::vector* delivery_to_entries); // Initializes the priority queue and the node entries with the current state // of the solution. - void InitializePositions( - AdjustablePriorityQueue* priority_queue, - std::vector* position_to_node_entries); + void InitializePositions(AdjustablePriorityQueue* priority_queue, + std::vector* position_to_node_entries); // Updates all node entries inserting a node after node "insert_after" and // updates the priority queue accordingly. void UpdatePositions(int vehicle, int64 insert_after, @@ -1891,8 +1911,7 @@ class ComparatorCheapestAdditionFilteredDecisionBuilder public: // Takes ownership of evaluator. ComparatorCheapestAdditionFilteredDecisionBuilder( - RoutingModel* model, ResultCallback3* comparator, + RoutingModel* model, Solver::VariableValueComparator comparator, const std::vector& filters); ~ComparatorCheapestAdditionFilteredDecisionBuilder() override {} @@ -1900,7 +1919,7 @@ class ComparatorCheapestAdditionFilteredDecisionBuilder // Next nodes are sorted according to the current comparator. void SortPossibleNexts(int64 from, std::vector* sorted_nexts) override; - std::unique_ptr > comparator_; + Solver::VariableValueComparator comparator_; }; // Filter-based decision builder which builds a solution by using @@ -1916,9 +1935,8 @@ class SavingsFilteredDecisionBuilder : public RoutingFilteredDecisionBuilder { public: // If savings_neighbors > 0 then for each node only its 'saving_neighbors' // neighbors leading to the smallest arc costs are considered. - SavingsFilteredDecisionBuilder( - RoutingModel* model, int64 saving_neighbors, - const std::vector& filters); + SavingsFilteredDecisionBuilder(RoutingModel* model, int64 saving_neighbors, + const std::vector& filters); ~SavingsFilteredDecisionBuilder() override {} bool BuildSolution() override; @@ -1959,8 +1977,8 @@ class SavingsFilteredDecisionBuilder : public RoutingFilteredDecisionBuilder { class RoutingLocalSearchFilter : public IntVarLocalSearchFilter { public: - RoutingLocalSearchFilter(const std::vector nexts, - Callback1* objective_callback); + RoutingLocalSearchFilter(const std::vector& nexts, + std::function objective_callback); ~RoutingLocalSearchFilter() override {} virtual void InjectObjectiveValue(int64 objective_value); @@ -1973,7 +1991,7 @@ class RoutingLocalSearchFilter : public IntVarLocalSearchFilter { int64 injected_objective_value_; private: - std::unique_ptr > objective_callback_; + std::function objective_callback_; }; // Generic path-based filter class. @@ -1981,7 +1999,7 @@ class RoutingLocalSearchFilter : public IntVarLocalSearchFilter { class BasePathFilter : public RoutingLocalSearchFilter { public: BasePathFilter(const std::vector& nexts, int next_domain_size, - Callback1* objective_callback); + std::function objective_callback); ~BasePathFilter() override {} bool Accept(const Assignment* delta, const Assignment* deltadelta) override; void OnSynchronize(const Assignment* delta) override; @@ -2025,10 +2043,11 @@ class BasePathFilter : public RoutingLocalSearchFilter { }; RoutingLocalSearchFilter* MakeNodeDisjunctionFilter( - const RoutingModel& routing_model, Callback1* objective_callback); + const RoutingModel& routing_model, + std::function objective_callback); RoutingLocalSearchFilter* MakePathCumulFilter( const RoutingModel& routing_model, const RoutingDimension& dimension, - Callback1* objective_callback); + std::function objective_callback); RoutingLocalSearchFilter* MakeNodePrecedenceFilter( const RoutingModel& routing_model, const RoutingModel::NodePairs& pairs); RoutingLocalSearchFilter* MakeVehicleVarFilter( diff --git a/src/constraint_solver/routing_search.cc b/src/constraint_solver/routing_search.cc index 51dd62cc05..e4fc7ad17b 100644 --- a/src/constraint_solver/routing_search.cc +++ b/src/constraint_solver/routing_search.cc @@ -37,7 +37,7 @@ namespace operations_research { // RoutingLocalSearchFilter RoutingLocalSearchFilter::RoutingLocalSearchFilter( - const std::vector nexts, Callback1* objective_callback) + const std::vector& nexts, Solver::ObjectiveWatcher objective_callback) : IntVarLocalSearchFilter(nexts), injected_objective_value_(0), objective_callback_(objective_callback) {} @@ -48,7 +48,7 @@ void RoutingLocalSearchFilter::InjectObjectiveValue(int64 objective_value) { void RoutingLocalSearchFilter::PropagateObjectiveValue(int64 objective_value) { if (objective_callback_ != nullptr) { - objective_callback_->Run(objective_value); + objective_callback_(objective_value); } } @@ -59,7 +59,7 @@ namespace { class NodeDisjunctionFilter : public RoutingLocalSearchFilter { public: NodeDisjunctionFilter(const RoutingModel& routing_model, - Callback1* objective_callback) + Solver::ObjectiveWatcher objective_callback) : RoutingLocalSearchFilter(routing_model.Nexts(), objective_callback), routing_model_(routing_model), active_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0), @@ -162,7 +162,8 @@ class NodeDisjunctionFilter : public RoutingLocalSearchFilter { } // namespace RoutingLocalSearchFilter* MakeNodeDisjunctionFilter( - const RoutingModel& routing_model, Callback1* objective_callback) { + const RoutingModel& routing_model, + Solver::ObjectiveWatcher objective_callback) { return routing_model.solver()->RevAlloc( new NodeDisjunctionFilter(routing_model, objective_callback)); } @@ -171,7 +172,7 @@ const int64 BasePathFilter::kUnassigned = -1; BasePathFilter::BasePathFilter(const std::vector& nexts, int next_domain_size, - Callback1* objective_callback) + Solver::ObjectiveWatcher objective_callback) : RoutingLocalSearchFilter(nexts, objective_callback), node_path_starts_(next_domain_size, kUnassigned), paths_(nexts.size(), -1), @@ -393,7 +394,7 @@ class ChainCumulFilter : public BasePathFilter { public: ChainCumulFilter(const RoutingModel& routing_model, const RoutingDimension& dimension, - Callback1* objective_callback); + Solver::ObjectiveWatcher objective_callback); ~ChainCumulFilter() override {} std::string DebugString() const override { return "ChainCumulFilter(" + name_ + ")"; @@ -407,7 +408,7 @@ class ChainCumulFilter : public BasePathFilter { const std::vector cumuls_; std::vector start_to_vehicle_; std::vector start_to_end_; - std::vector evaluators_; + std::vector evaluators_; RoutingModel::VehicleEvaluator* const capacity_evaluator_; std::vector current_path_cumul_mins_; std::vector current_max_of_path_end_cumul_mins_; @@ -419,7 +420,7 @@ class ChainCumulFilter : public BasePathFilter { ChainCumulFilter::ChainCumulFilter(const RoutingModel& routing_model, const RoutingDimension& dimension, - Callback1* objective_callback) + Solver::ObjectiveWatcher objective_callback) : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size(), objective_callback), cumuls_(dimension.cumuls()), @@ -445,7 +446,6 @@ ChainCumulFilter::ChainCumulFilter(const RoutingModel& routing_model, // incrementally check feasibility. void ChainCumulFilter::OnSynchronizePathFromStart(int64 start) { const int vehicle = start_to_vehicle_[start]; - Solver::IndexEvaluator2* const evaluator = evaluators_[vehicle]; std::vector path_nodes; int64 node = start; int64 cumul = cumuls_[node]->Min(); @@ -456,7 +456,7 @@ void ChainCumulFilter::OnSynchronizePathFromStart(int64 start) { if (next != old_nexts_[node] || vehicle != old_vehicles_[node]) { old_nexts_[node] = next; old_vehicles_[node] = vehicle; - current_transits_[node] = evaluator->Run(node, next); + current_transits_[node] = evaluators_[vehicle]->Run(node, next); } cumul = CapAdd(cumul, current_transits_[node]); cumul = std::max(cumuls_[next]->Min(), cumul); @@ -479,7 +479,6 @@ bool ChainCumulFilter::AcceptPath(int64 path_start, int64 chain_start, const int64 capacity = capacity_evaluator_ == nullptr ? kint64max : capacity_evaluator_->Run(vehicle); - Solver::IndexEvaluator2* const evaluator = evaluators_[vehicle]; int64 node = chain_start; int64 cumul = current_path_cumul_mins_[node]; while (node != chain_end) { @@ -488,7 +487,7 @@ bool ChainCumulFilter::AcceptPath(int64 path_start, int64 chain_start, vehicle == old_vehicles_[node]) { cumul = CapAdd(cumul, current_transits_[node]); } else { - cumul = CapAdd(cumul, evaluator->Run(node, next)); + cumul = CapAdd(cumul, evaluators_[vehicle]->Run(node, next)); } cumul = std::max(cumuls_[next]->Min(), cumul); if (cumul > capacity) return false; @@ -510,7 +509,7 @@ class PathCumulFilter : public BasePathFilter { public: PathCumulFilter(const RoutingModel& routing_model, const RoutingDimension& dimension, - Callback1* objective_callback); + Solver::ObjectiveWatcher objective_callback); ~PathCumulFilter() override {} std::string DebugString() const override { return "PathCumulFilter(" + name_ + ")"; @@ -521,6 +520,7 @@ class PathCumulFilter : public BasePathFilter { // supporting this value, and the corresponding path cumul values for all // paths. struct SupportedPathCumul { + SupportedPathCumul() : cumul_value(0), cumul_value_support(0) {} int64 cumul_value; int cumul_value_support; std::vector path_values; @@ -586,8 +586,8 @@ class PathCumulFilter : public BasePathFilter { bool FilterSpanCost() const { return global_span_cost_coefficient_ != 0; } bool FilterSlackCost() const { - return has_nonzero_vehicle_span_cost_coefficients_ - || has_vehicle_span_upper_bounds_; + return has_nonzero_vehicle_span_cost_coefficients_ || + has_vehicle_span_upper_bounds_; } bool FilterCumulSoftBounds() const { return !cumul_soft_bounds_.empty(); } @@ -614,7 +614,7 @@ class PathCumulFilter : public BasePathFilter { const std::vector cumuls_; const std::vector slacks_; std::vector start_to_vehicle_; - std::vector evaluators_; + std::vector evaluators_; std::vector vehicle_span_upper_bounds_; bool has_vehicle_span_upper_bounds_; int64 total_current_cumul_cost_value_; @@ -647,7 +647,7 @@ class PathCumulFilter : public BasePathFilter { PathCumulFilter::PathCumulFilter(const RoutingModel& routing_model, const RoutingDimension& dimension, - Callback1* objective_callback) + Solver::ObjectiveWatcher objective_callback) : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size(), objective_callback), cumuls_(dimension.cumuls()), @@ -783,7 +783,6 @@ void PathCumulFilter::OnBeforeSynchronizePaths() { for (int r = 0; r < NumPaths(); ++r) { int64 node = Start(r); const int vehicle = start_to_vehicle_[Start(r)]; - Solver::IndexEvaluator2* const evaluator = evaluators_[vehicle]; // First pass: evaluating route length to reserve memory to store route // information. int number_of_route_arcs = 0; @@ -799,7 +798,7 @@ void PathCumulFilter::OnBeforeSynchronizePaths() { int64 total_transit = 0; while (node < Size()) { const int64 next = Value(node); - const int64 transit = evaluator->Run(node, next); + const int64 transit = evaluators_[vehicle]->Run(node, next); total_transit = CapAdd(total_transit, transit); const int64 transit_slack = CapAdd(transit, slacks_[node]->Min()); current_path_transits_.PushTransit(r, node, next, transit_slack); @@ -867,7 +866,6 @@ bool PathCumulFilter::AcceptPath(int64 path_start, int64 chain_start, const int64 capacity = capacity_evaluator_ == nullptr ? kint64max : capacity_evaluator_->Run(vehicle); - Solver::IndexEvaluator2* const evaluator = evaluators_[vehicle]; // Evaluating route length to reserve memory to store transit information. int number_of_route_arcs = 0; while (node < Size()) { @@ -889,7 +887,7 @@ bool PathCumulFilter::AcceptPath(int64 path_start, int64 chain_start, node = path_start; while (node < Size()) { const int64 next = GetNext(node); - const int64 transit = evaluator->Run(node, next); + const int64 transit = evaluators_[vehicle]->Run(node, next); total_transit = CapAdd(total_transit, transit); const int64 transit_slack = CapAdd(transit, slacks_[node]->Min()); delta_path_transits_.PushTransit(path, node, next, transit_slack); @@ -971,9 +969,9 @@ bool PathCumulFilter::FinalizeAcceptPath() { if (ContainsKey(delta_paths_, r)) { continue; } - new_min_start = - std::min(new_min_start, ComputePathMaxStartFromEndCumul( - current_path_transits_, r, new_max_end)); + new_min_start = std::min(new_min_start, + ComputePathMaxStartFromEndCumul( + current_path_transits_, r, new_max_end)); } } else if (new_min_start > current_min_start_.cumul_value) { // Delta min start is greater than the current solution one. @@ -1027,7 +1025,7 @@ int64 PathCumulFilter::ComputePathMaxStartFromEndCumul( RoutingLocalSearchFilter* MakePathCumulFilter( const RoutingModel& routing_model, const RoutingDimension& dimension, - Callback1* objective_callback) { + Solver::ObjectiveWatcher objective_callback) { for (const int64 upper_bound : dimension.vehicle_span_upper_bounds()) { if (upper_bound != kint64max) { return routing_model.solver()->RevAlloc( @@ -1166,8 +1164,7 @@ class VehicleVarFilter : public BasePathFilter { VehicleVarFilter::VehicleVarFilter(const RoutingModel& routing_model) : BasePathFilter(routing_model.Nexts(), - routing_model.Size() + routing_model.vehicles(), - nullptr), + routing_model.Size() + routing_model.vehicles(), nullptr), vehicle_vars_(routing_model.VehicleVars()), unconstrained_vehicle_var_domain_size_(routing_model.vehicles()) { start_to_vehicle_.resize(Size(), -1); @@ -1190,11 +1187,10 @@ bool VehicleVarFilter::Accept(const Assignment* delta, // add it to the "unconstrained" domain. Impact we don't filter mandatory // nodes made inactive here, but it is covered by other filters. const int adjusted_unconstrained_vehicle_var_domain_size = - vehicle_var->Min() >= 0 - ? unconstrained_vehicle_var_domain_size_ - : unconstrained_vehicle_var_domain_size_ + 1; - if (vehicle_var->Size() - != adjusted_unconstrained_vehicle_var_domain_size) { + vehicle_var->Min() >= 0 ? unconstrained_vehicle_var_domain_size_ + : unconstrained_vehicle_var_domain_size_ + 1; + if (vehicle_var->Size() != + adjusted_unconstrained_vehicle_var_domain_size) { all_unconstrained = false; break; } @@ -1417,7 +1413,8 @@ CheapestInsertionFilteredDecisionBuilder:: ResultCallback3* evaluator, ResultCallback1* penalty_evaluator, const std::vector& filters) - : RoutingFilteredDecisionBuilder(model, filters), evaluator_(evaluator), + : RoutingFilteredDecisionBuilder(model, filters), + evaluator_(evaluator), penalty_evaluator_(penalty_evaluator) { evaluator_->CheckIsRepeatable(); if (penalty_evaluator_ != nullptr) { @@ -1567,8 +1564,8 @@ GlobalCheapestInsertionFilteredDecisionBuilder:: ResultCallback3* evaluator, ResultCallback1* penalty_evaluator, const std::vector& filters) - : CheapestInsertionFilteredDecisionBuilder( - model, evaluator, penalty_evaluator, filters) {} + : CheapestInsertionFilteredDecisionBuilder(model, evaluator, + penalty_evaluator, filters) {} bool GlobalCheapestInsertionFilteredDecisionBuilder::BuildSolution() { if (!InitializeRoutes()) { @@ -1611,8 +1608,7 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InsertPairs() { ? pickup_insert_before : Value(entry->delivery_insert_after()); InsertBetween(entry->delivery_to_insert(), - entry->delivery_insert_after(), - delivery_insert_before); + entry->delivery_insert_after(), delivery_insert_before); if (Commit()) { const int64 pickup_after = entry->pickup_insert_after(); const int64 pickup = entry->pickup_to_insert(); @@ -1709,7 +1705,8 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InitializePairPositions( priority_queue->Add(entry); } // Add all other insertion entries with pair performed. - std::vector, std::pair>> valued_positions; + std::vector, std::pair>> + valued_positions; for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { std::vector valued_pickup_positions; const int64 start = model()->Start(vehicle); @@ -1733,8 +1730,8 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InitializePairPositions( } } } - for (const std::pair, std::pair>& valued_position : - valued_positions) { + for (const std::pair, std::pair>& + valued_position : valued_positions) { PairEntry* const entry = new PairEntry( pickup, valued_position.second.first, delivery, valued_position.second.second, valued_position.first.second); @@ -1795,8 +1792,9 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdatePickupPositions( if (!Contains(pickup) && !Contains(delivery)) { int64 delivery_insert_after = pickup; while (!model()->IsEnd(delivery_insert_after)) { - const std::pair insertion = std::make_pair( - std::make_pair(pickup, delivery), delivery_insert_after); + const std::pair insertion = + std::make_pair(std::make_pair(pickup, delivery), + delivery_insert_after); if (!ContainsKey(existing_insertions, insertion)) { PairEntry* const entry = new PairEntry(pickup, pickup_insert_after, delivery, @@ -2005,8 +2003,7 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::InitializePositions( NodeEntry* const node_entry = new NodeEntry(node, valued_position.second, vehicle); node_entry->set_value(CapSub(valued_position.first, penalty)); - position_to_node_entries->at(valued_position.second) - .insert(node_entry); + position_to_node_entries->at(valued_position.second).insert(node_entry); priority_queue->Add(node_entry); } } @@ -2077,8 +2074,8 @@ void GlobalCheapestInsertionFilteredDecisionBuilder::UpdatePositions( void GlobalCheapestInsertionFilteredDecisionBuilder::DeleteNodeEntry( GlobalCheapestInsertionFilteredDecisionBuilder::NodeEntry* entry, AdjustablePriorityQueue< - GlobalCheapestInsertionFilteredDecisionBuilder::NodeEntry>* - priority_queue, + GlobalCheapestInsertionFilteredDecisionBuilder::NodeEntry>* + priority_queue, std::vector* node_entries) { priority_queue->Remove(entry); if (entry->insert_after() != -1) { @@ -2094,8 +2091,8 @@ LocalCheapestInsertionFilteredDecisionBuilder:: RoutingModel* model, ResultCallback3* evaluator, const std::vector& filters) - : CheapestInsertionFilteredDecisionBuilder( - model, evaluator, nullptr, filters) {} + : CheapestInsertionFilteredDecisionBuilder(model, evaluator, nullptr, + filters) {} bool LocalCheapestInsertionFilteredDecisionBuilder::BuildSolution() { if (!InitializeRoutes()) { @@ -2163,9 +2160,9 @@ bool LocalCheapestInsertionFilteredDecisionBuilder::BuildSolution() { return Commit(); } -void -LocalCheapestInsertionFilteredDecisionBuilder::ComputeEvaluatorSortedPositions( - int64 node, std::vector* sorted_positions) { +void LocalCheapestInsertionFilteredDecisionBuilder:: + ComputeEvaluatorSortedPositions(int64 node, + std::vector* sorted_positions) { CHECK(sorted_positions != nullptr); CHECK(!Contains(node)); sorted_positions->clear(); @@ -2283,8 +2280,8 @@ bool CheapestAdditionFilteredDecisionBuilder::BuildSolution() { } bool CheapestAdditionFilteredDecisionBuilder:: - PartialRoutesAndLargeVehicleIndicesFirst:: - operator()(int vehicle1, int vehicle2) const { + PartialRoutesAndLargeVehicleIndicesFirst::operator()(int vehicle1, + int vehicle2) const { const bool has_partial_route1 = (builder_.model()->Start(vehicle1) != builder_.GetStartChainEnd(vehicle1)); const bool has_partial_route2 = (builder_.model()->Start(vehicle2) != @@ -2337,29 +2334,10 @@ void EvaluatorCheapestAdditionFilteredDecisionBuilder::SortPossibleNexts( ComparatorCheapestAdditionFilteredDecisionBuilder:: ComparatorCheapestAdditionFilteredDecisionBuilder( - RoutingModel* model, - ResultCallback3* comparator, + RoutingModel* model, Solver::VariableValueComparator comparator, const std::vector& filters) : CheapestAdditionFilteredDecisionBuilder(model, filters), - comparator_(comparator) { - comparator_->CheckIsRepeatable(); -} - -namespace { -class ArcComparator { - public: - ArcComparator(int from, - ResultCallback3* comparator) - : from_(from), comparator_(comparator) {} - bool operator()(int next1, int next2) const { - return comparator_->Run(from_, next1, next2); - } - - private: - const int from_; - ResultCallback3* const comparator_; -}; -} // namespace + comparator_(comparator) {} void ComparatorCheapestAdditionFilteredDecisionBuilder::SortPossibleNexts( int64 from, std::vector* sorted_nexts) { @@ -2375,8 +2353,10 @@ void ComparatorCheapestAdditionFilteredDecisionBuilder::SortPossibleNexts( sorted_nexts->push_back(value); } } - ArcComparator comparator(from, comparator_.get()); - std::sort(sorted_nexts->begin(), sorted_nexts->end(), comparator); + std::sort(sorted_nexts->begin(), sorted_nexts->end(), + [this, from](int next1, int next2) { + return comparator_(from, next1, next2); + }); } } diff --git a/src/constraint_solver/sat_constraint.h b/src/constraint_solver/sat_constraint.h index f6f820444e..d6bd140c23 100644 --- a/src/constraint_solver/sat_constraint.h +++ b/src/constraint_solver/sat_constraint.h @@ -113,7 +113,8 @@ class BooleanVariableManager { // A Boolean variable associated to an IntVar value means (var == value) if // it is true. This returns the IntVar and the value. If the pointer is // nullptr, then this variable index wasn't created by this class. - std::pair BooleanVariableMeaning(sat::VariableIndex var) const { + std::pair BooleanVariableMeaning( + sat::VariableIndex var) const { DCHECK_GE(var, 0); // This test is necessary because the SAT solver may know of variables not // registered by this class. @@ -173,7 +174,8 @@ class SatConstraint : public Constraint { const sat::Literal literal = trail[propagated_trail_index_]; const sat::VariableIndex var = literal.Variable(); if (trail.Info(var).type != sat::AssignmentInfo::SEARCH_DECISION) { - std::pair p = variable_manager_.BooleanVariableMeaning(var); + std::pair p = + variable_manager_.BooleanVariableMeaning(var); IntVar* int_var = p.first; const int64 value = p.second; if (int_var != nullptr) { diff --git a/src/constraint_solver/sched_constraints.cc b/src/constraint_solver/sched_constraints.cc index fe177ffa4b..14e053fbd4 100644 --- a/src/constraint_solver/sched_constraints.cc +++ b/src/constraint_solver/sched_constraints.cc @@ -113,8 +113,8 @@ class TreeArrayConstraint : public Constraint { tree_[depth][position].start_max.SetValue(solver(), start_max); tree_[depth][position].end_min.SetValue(solver(), end_min); tree_[depth][position].end_max.SetValue(solver(), end_max); - tree_[depth][position] - .performed.SetValue(solver(), static_cast(performed)); + tree_[depth][position].performed.SetValue(solver(), + static_cast(performed)); } int64 StartMin(int depth, int position) const { @@ -505,12 +505,14 @@ class CoverConstraint : public TreeArrayConstraint { if (performed != UNPERFORMED) { *bucket_start_min = std::min(*bucket_start_min, StartMin(parent_depth + 1, k)); - *bucket_end_max = std::max(*bucket_end_max, EndMax(parent_depth + 1, k)); + *bucket_end_max = + std::max(*bucket_end_max, EndMax(parent_depth + 1, k)); may_be_performed_count++; if (performed == PERFORMED) { *bucket_start_max = std::min(*bucket_start_max, StartMax(parent_depth + 1, k)); - *bucket_end_min = std::max(*bucket_end_min, EndMin(parent_depth + 1, k)); + *bucket_end_min = + std::max(*bucket_end_min, EndMin(parent_depth + 1, k)); must_be_performed_count++; } } diff --git a/src/constraint_solver/search.cc b/src/constraint_solver/search.cc index d5f5a72044..d3a7307742 100644 --- a/src/constraint_solver/search.cc +++ b/src/constraint_solver/search.cc @@ -13,14 +13,14 @@ #include +#include #include "base/hash.h" #include -#include "base/unique_ptr.h" +#include #include #include #include -#include "base/callback.h" #include "base/casts.h" #include "base/commandlineflags.h" #include "base/integral_types.h" @@ -51,7 +51,7 @@ namespace operations_research { // ---------- Search Log --------- SearchLog::SearchLog(Solver* const s, OptimizeVar* const obj, IntVar* const var, - ResultCallback* display_callback, int period) + std::function display_callback, int period) : SearchMonitor(s), period_(period), timer_(new WallTimer), @@ -68,9 +68,6 @@ SearchLog::SearchLog(Solver* const s, OptimizeVar* const obj, IntVar* const var, sliding_max_depth_(0) { CHECK(obj == nullptr || var == nullptr) << "Either var or obj need to be nullptr."; - if (display_callback_ != nullptr) { - display_callback_->CheckIsRepeatable(); - } } SearchLog::~SearchLog() {} @@ -151,8 +148,8 @@ bool SearchLog::AtSolution() { } log.append(")"); OutputLine(log); - if (display_callback_.get() != nullptr) { - LOG(INFO) << display_callback_->Run(); + if (display_callback_) { + LOG(INFO) << display_callback_(); } return false; } @@ -273,13 +270,13 @@ SearchMonitor* Solver::MakeSearchLog(int period, IntVar* const var) { } SearchMonitor* Solver::MakeSearchLog(int period, - ResultCallback* display_callback) { + std::function display_callback) { return RevAlloc( new SearchLog(this, nullptr, nullptr, display_callback, period)); } SearchMonitor* Solver::MakeSearchLog(int period, IntVar* const var, - ResultCallback* display_callback) { + std::function display_callback) { return RevAlloc(new SearchLog(this, nullptr, var, display_callback, period)); } @@ -288,7 +285,7 @@ SearchMonitor* Solver::MakeSearchLog(int period, OptimizeVar* const obj) { } SearchMonitor* Solver::MakeSearchLog(int period, OptimizeVar* const obj, - ResultCallback* display_callback) { + std::function display_callback) { return RevAlloc(new SearchLog(this, obj, nullptr, display_callback, period)); } @@ -618,23 +615,39 @@ DecisionBuilder* Solver::Try(const std::vector& dbs) { namespace { class BaseVariableAssignmentSelector : public BaseObject { public: - BaseVariableAssignmentSelector() {} + BaseVariableAssignmentSelector(Solver* solver, const std::vector& vars) + : solver_(solver), + vars_(vars), + first_unbound_(0), + last_unbound_(vars.size() - 1) {} + ~BaseVariableAssignmentSelector() override {} - virtual int64 SelectValue(const IntVar* const v, int64 id) = 0; - virtual IntVar* SelectVariable(Solver* const s, int64* id) = 0; - virtual void Accept(ModelVisitor* const visitor) const = 0; -}; -// ----- Variable selector ----- + virtual int64 SelectValue(const IntVar* v, int64 id) = 0; -class VariableSelector : public BaseObject { - public: - explicit VariableSelector(const std::vector& vars) : vars_(vars) {} - ~VariableSelector() override {} - virtual IntVar* Select(Solver* const s, int64* id) = 0; - std::string VarDebugString() const { - return StringPrintf("(%s)", JoinDebugStringPtr(vars_, ", ").c_str()); + // Returns -1 if no variable are suitable. + virtual int64 ChooseVariable() = 0; + + int64 ChooseVariableWrapper() { + int64 i; + for (i = first_unbound_.Value(); i <= last_unbound_.Value(); ++i) { + if (!vars_[i]->Bound()) { + break; + } + } + first_unbound_.SetValue(solver_, i); + if (i > last_unbound_.Value()) { + return -1; + } + for (i = last_unbound_.Value(); i >= first_unbound_.Value(); --i) { + if (!vars_[i]->Bound()) { + break; + } + } + last_unbound_.SetValue(solver_, i); + return ChooseVariable(); } + void Accept(ModelVisitor* const visitor) const { visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension); visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument, @@ -642,341 +655,201 @@ class VariableSelector : public BaseObject { visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension); } + const std::vector& vars() const { return vars_; } + protected: - const std::vector vars_; + Solver* const solver_; + std::vector vars_; + Rev first_unbound_; + Rev last_unbound_; }; // ----- Choose first unbound -- -class FirstUnboundSelector : public VariableSelector { - public: - explicit FirstUnboundSelector(const std::vector& vars) - : VariableSelector(vars), first_(0) {} - ~FirstUnboundSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; - std::string DebugString() const override { return "ChooseFirstUnbound"; } - - private: - int first_; -}; - -IntVar* FirstUnboundSelector::Select(Solver* const s, int64* id) { - for (int i = first_; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; - if (!var->Bound()) { - s->SaveAndSetValue(&first_, i); - *id = i; - return var; +int64 ChooseFirstUnbound(Solver* solver, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { + for (int64 i = first_unbound; i <= last_unbound; ++i) { + if (!vars[i]->Bound()) { + return i; } } - s->SaveAndSetValue(&first_, static_cast(vars_.size())); - *id = vars_.size(); - return nullptr; + return -1; } // ----- Choose Min Size Lowest Min ----- -class MinSizeLowestMinSelector : public VariableSelector { - public: - explicit MinSizeLowestMinSelector(const std::vector& vars) - : VariableSelector(vars) {} - ~MinSizeLowestMinSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; - std::string DebugString() const override { return "MinSizeLowestMinSelector"; } -}; - -IntVar* MinSizeLowestMinSelector::Select(Solver* const s, int64* id) { - IntVar* result = nullptr; +int64 ChooseMinSizeLowestMin(Solver* solver, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { uint64 best_size = kuint64max; int64 best_min = kint64max; - int index = -1; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; + int64 best_index = -1; + for (int64 i = first_unbound; i <= last_unbound; ++i) { + IntVar* const var = vars[i]; if (!var->Bound()) { if (var->Size() < best_size || (var->Size() == best_size && var->Min() < best_min)) { best_size = var->Size(); best_min = var->Min(); - index = i; - result = var; + best_index = i; } } } - if (index == -1) { - *id = vars_.size(); - return nullptr; - } else { - *id = index; - return result; - } + return best_index; } // ----- Choose Min Size Highest Min ----- -class MinSizeHighestMinSelector : public VariableSelector { - public: - explicit MinSizeHighestMinSelector(const std::vector& vars) - : VariableSelector(vars) {} - ~MinSizeHighestMinSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; - std::string DebugString() const override { return "MinSizeHighestMinSelector"; } -}; - -IntVar* MinSizeHighestMinSelector::Select(Solver* const s, int64* id) { - IntVar* result = nullptr; - uint64 best_size = kint64max; +int64 ChooseMinSizeHighestMin(Solver* solver, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { + uint64 best_size = kuint64max; int64 best_min = kint64min; - int index = -1; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; + int64 best_index = -1; + for (int64 i = first_unbound; i <= last_unbound; ++i) { + IntVar* const var = vars[i]; if (!var->Bound()) { if (var->Size() < best_size || (var->Size() == best_size && var->Min() > best_min)) { best_size = var->Size(); best_min = var->Min(); - index = i; - result = var; + best_index = i; } } } - if (index == -1) { - *id = vars_.size(); - return nullptr; - } else { - *id = index; - return result; - } + return best_index; } // ----- Choose Min Size Lowest Max ----- -class MinSizeLowestMaxSelector : public VariableSelector { - public: - explicit MinSizeLowestMaxSelector(const std::vector& vars) - : VariableSelector(vars) {} - ~MinSizeLowestMaxSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; - std::string DebugString() const override { return "MinSizeLowestMaxSelector"; } -}; - -IntVar* MinSizeLowestMaxSelector::Select(Solver* const s, int64* id) { - IntVar* result = nullptr; - uint64 best_size = kint64max; +int64 ChooseMinSizeLowestMax(Solver* solver, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { + uint64 best_size = kuint64max; int64 best_max = kint64max; - int index = -1; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; + int64 best_index = -1; + for (int64 i = first_unbound; i <= last_unbound; ++i) { + IntVar* const var = vars[i]; if (!var->Bound()) { if (var->Size() < best_size || (var->Size() == best_size && var->Max() < best_max)) { best_size = var->Size(); best_max = var->Max(); - index = i; - result = var; + best_index = i; } } } - if (index == -1) { - *id = vars_.size(); - return nullptr; - } else { - *id = index; - return result; - } + return best_index; } // ----- Choose Min Size Highest Max ----- -class MinSizeHighestMaxSelector : public VariableSelector { - public: - explicit MinSizeHighestMaxSelector(const std::vector& vars) - : VariableSelector(vars) {} - ~MinSizeHighestMaxSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; - std::string DebugString() const override { return "MinSizeHighestMaxSelector"; } -}; - -IntVar* MinSizeHighestMaxSelector::Select(Solver* const s, int64* id) { - IntVar* result = nullptr; - uint64 best_size = kint64max; +int64 ChooseMinSizeHighestMax(Solver* solver, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { + uint64 best_size = kuint64max; int64 best_max = kint64min; - int index = -1; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; + int64 best_index = -1; + for (int64 i = first_unbound; i <= last_unbound; ++i) { + IntVar* const var = vars[i]; if (!var->Bound()) { if (var->Size() < best_size || (var->Size() == best_size && var->Max() > best_max)) { best_size = var->Size(); best_max = var->Max(); - index = i; - result = var; + best_index = i; } } } - if (index == -1) { - *id = vars_.size(); - return nullptr; - } else { - *id = index; - return result; - } + return best_index; } // ----- Choose Lowest Min -- -class LowestMinSelector : public VariableSelector { - public: - explicit LowestMinSelector(const std::vector& vars) - : VariableSelector(vars) {} - ~LowestMinSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; - std::string DebugString() const override { return "LowestMinSelector"; } -}; - -IntVar* LowestMinSelector::Select(Solver* const s, int64* id) { - IntVar* result = nullptr; +int64 ChooseLowestMin(Solver* solver, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { int64 best_min = kint64max; - int index = -1; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; + int64 best_index = -1; + for (int64 i = first_unbound; i <= last_unbound; ++i) { + IntVar* const var = vars[i]; if (!var->Bound()) { if (var->Min() < best_min) { best_min = var->Min(); - index = i; - result = var; + best_index = i; } } } - if (index == -1) { - *id = vars_.size(); - return nullptr; - } else { - *id = index; - return result; - } + return best_index; } // ----- Choose Highest Max ----- -class HighestMaxSelector : public VariableSelector { - public: - explicit HighestMaxSelector(const std::vector& vars) - : VariableSelector(vars) {} - ~HighestMaxSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; - std::string DebugString() const override { return "HighestMaxSelector"; } -}; - -IntVar* HighestMaxSelector::Select(Solver* const s, int64* id) { - IntVar* result = nullptr; +int64 ChooseHighestMax(Solver* solver, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { int64 best_max = kint64min; - int index = -1; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; + int64 best_index = -1; + for (int64 i = first_unbound; i <= last_unbound; ++i) { + IntVar* const var = vars[i]; if (!var->Bound()) { - if (var->Max() > best_max) { + if (var->Max() < best_max) { best_max = var->Max(); - index = i; - result = var; + best_index = i; } } } - if (index == -1) { - *id = vars_.size(); - return nullptr; - } else { - *id = index; - return result; - } + return best_index; } // ----- Choose Lowest Size -- -class LowestSizeSelector : public VariableSelector { - public: - explicit LowestSizeSelector(const std::vector& vars) - : VariableSelector(vars) {} - ~LowestSizeSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; - std::string DebugString() const override { return "MinSizeSelector"; } -}; - -IntVar* LowestSizeSelector::Select(Solver* const s, int64* id) { - IntVar* result = nullptr; - uint64 best_size = kint64max; - int index = -1; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; +int64 ChooseMinSize(Solver* solver, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { + uint64 best_size = kuint64max; + int64 best_index = -1; + for (int64 i = first_unbound; i <= last_unbound; ++i) { + IntVar* const var = vars[i]; if (!var->Bound()) { - const uint64 size = var->Size(); - if (size < best_size) { - best_size = size; - index = i; - result = var; + if (var->Size() < best_size) { + best_size = var->Size(); + best_index = i; } } } - if (index == -1) { - *id = vars_.size(); - return nullptr; - } else { - *id = index; - return result; - } + return best_index; } // ----- Choose Highest Size ----- -class HighestSizeSelector : public VariableSelector { - public: - explicit HighestSizeSelector(const std::vector& vars) - : VariableSelector(vars) {} - ~HighestSizeSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; - std::string DebugString() const override { return "MaxSizeSelector"; } -}; - -IntVar* HighestSizeSelector::Select(Solver* const s, int64* id) { - IntVar* result = nullptr; +int64 ChooseMaxSize(Solver* solver, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { uint64 best_size = 0; - int index = -1; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; + int64 best_index = -1; + for (int64 i = first_unbound; i <= last_unbound; ++i) { + IntVar* const var = vars[i]; if (!var->Bound()) { - const uint64 size = var->Size(); - if (size > best_size) { - best_size = size; - index = i; - result = var; + if (var->Size() > best_size) { + best_size = var->Size(); + best_index = i; } } } - if (index == -1) { - *id = vars_.size(); - return nullptr; - } else { - *id = index; - return result; - } + return best_index; } // ----- Choose Highest Regret ----- -class HighestRegretSelectorOnMin : public VariableSelector { +class HighestRegretSelectorOnMin : public BaseObject { public: explicit HighestRegretSelectorOnMin(const std::vector& vars) - : VariableSelector(vars), iterators_(vars_.size()) { - for (int i = 0; i < vars_.size(); ++i) { + : iterators_(vars.size()) { + for (int64 i = 0; i < vars.size(); ++i) { iterators_[i] = vars[i]->MakeDomainIterator(true); } } ~HighestRegretSelectorOnMin() override {} - IntVar* Select(Solver* const s, int64* id) override; + int64 Choose(Solver* const s, const std::vector& vars, + int64 first_unbound, int64 last_unbound); std::string DebugString() const override { return "MaxRegretSelector"; } - int64 ComputeRegret(int index) const { - DCHECK(!vars_[index]->Bound()); - const int64 vmin = vars_[index]->Min(); + int64 ComputeRegret(IntVar* var, int64 index) const { + DCHECK(!var->Bound()); + const int64 vmin = var->Min(); IntVarIterator* const iterator = iterators_[index]; iterator->Init(); iterator->Next(); @@ -987,136 +860,115 @@ class HighestRegretSelectorOnMin : public VariableSelector { std::vector iterators_; }; -IntVar* HighestRegretSelectorOnMin::Select(Solver* const s, int64* id) { - IntVar* result = nullptr; +int64 HighestRegretSelectorOnMin::Choose(Solver* const s, + const std::vector& vars, + int64 first_unbound, + int64 last_unbound) { int64 best_regret = 0; - int index = -1; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; + int64 index = -1; + for (int64 i = first_unbound; i <= last_unbound; ++i) { + IntVar* const var = vars[i]; if (!var->Bound()) { - const int64 regret = ComputeRegret(i); + const int64 regret = ComputeRegret(var, i); if (regret > best_regret) { best_regret = regret; index = i; - result = var; } } } - if (index == -1) { - *id = vars_.size(); - return nullptr; - } else { - *id = index; - return result; - } + return index; } // ----- Choose random unbound -- -class RandomSelector : public VariableSelector { - public: - explicit RandomSelector(const std::vector& vars) - : VariableSelector(vars) {} - ~RandomSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; - std::string DebugString() const override { return "RandomSelector"; } -}; - -IntVar* RandomSelector::Select(Solver* const s, int64* id) { - const int shift = s->Rand32(vars_.size()); - for (int i = 0; i < vars_.size(); ++i) { - const int index = (i + shift) % vars_.size(); - IntVar* const var = vars_[index]; - if (!var->Bound()) { - *id = index; - return var; +int64 ChooseRandom(Solver* solver, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { + const int64 span = last_unbound - first_unbound + 1; + const int64 shift = solver->Rand32(span); + for (int64 i = 0; i < span; ++i) { + const int64 index = (i + shift) % span + first_unbound; + if (!vars[index]->Bound()) { + return index; } } - *id = vars_.size(); - return nullptr; + return -1; } // ----- Choose min eval ----- -class CheapestVarSelector : public VariableSelector { +class CheapestVarSelector : public BaseObject { public: - CheapestVarSelector(const std::vector& vars, - ResultCallback1* var_eval) - : VariableSelector(vars), var_evaluator_(var_eval) {} + explicit CheapestVarSelector(std::function var_evaluator) + : var_evaluator_(std::move(var_evaluator)) {} ~CheapestVarSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; + int64 Choose(Solver* const s, const std::vector& vars, + int64 first_unbound, int64 last_unbound); std::string DebugString() const override { return "CheapestVarSelector"; } private: - std::unique_ptr > var_evaluator_; + std::function var_evaluator_; }; -IntVar* CheapestVarSelector::Select(Solver* const s, int64* id) { - IntVar* result = nullptr; +int64 CheapestVarSelector::Choose(Solver* const s, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { int64 best_eval = kint64max; - int index = -1; - for (int i = 0; i < vars_.size(); ++i) { - IntVar* const var = vars_[i]; - if (!var->Bound()) { - const int64 eval = var_evaluator_->Run(i); + int64 index = -1; + for (int64 i = first_unbound; i <= last_unbound; ++i) { + if (!vars[i]->Bound()) { + const int64 eval = var_evaluator_(i); if (eval < best_eval) { best_eval = eval; index = i; - result = var; } } } - if (index == -1) { - *id = vars_.size(); - return nullptr; - } else { - *id = index; - return result; - } + return index; } // ----- Path selector ----- // Follow path, where var[i] is represents the next of i -class PathSelector : public VariableSelector { +class PathSelector : public BaseObject { public: - explicit PathSelector(const std::vector& vars) - : VariableSelector(vars), first_(kint64max) {} + PathSelector() : first_(kint64max) {} ~PathSelector() override {} - IntVar* Select(Solver* const s, int64* id) override; + int64 Choose(Solver* const s, const std::vector& vars, + int64 first_unbound, int64 last_unbound); std::string DebugString() const override { return "ChooseNextOnPath"; } private: - bool UpdateIndex(int64* index) const; - bool FindPathStart(int64* index) const; + bool UpdateIndex(const std::vector& vars, int64* index) const; + bool FindPathStart(const std::vector& vars, int64* index) const; - int64 first_; + Rev first_; }; -IntVar* PathSelector::Select(Solver* const s, int64* id) { - *id = first_; - if (!UpdateIndex(id)) { - return nullptr; +int64 PathSelector::Choose(Solver* const s, const std::vector& vars, + int64 first_unbound, int64 last_unbound) { + int64 index = first_.Value(); + if (!UpdateIndex(vars, &index)) { + return -1; } - int count = 0; - while (vars_[*id]->Bound()) { - *id = vars_[*id]->Value(); - if (!UpdateIndex(id)) { - return nullptr; + int64 count = 0; + while (vars[index]->Bound()) { + index = vars[index]->Value(); + if (!UpdateIndex(vars, &index)) { + return -1; } ++count; - if (count >= vars_.size() && !FindPathStart(id)) { // Cycle detected - return nullptr; + if (count >= vars.size() && + !FindPathStart(vars, &index)) { // Cycle detected + return -1; } } - IntVar* const var = vars_[*id]; - s->SaveAndSetValue(&first_, *id); - return var; + first_.SetValue(s, index); + return index; } -bool PathSelector::UpdateIndex(int64* index) const { - if (*index >= vars_.size()) { - if (!FindPathStart(index)) { +bool PathSelector::UpdateIndex(const std::vector& vars, + int64* index) const { + if (*index >= vars.size()) { + if (!FindPathStart(vars, index)) { return false; } } @@ -1129,23 +981,24 @@ bool PathSelector::UpdateIndex(int64* index) const { // 2. If no such road is found, try to find a start node of a route: look for // an unbound variable, to which no other variable can point. // 3. If everything else fails, pick the first unbound variable. -bool PathSelector::FindPathStart(int64* index) const { +bool PathSelector::FindPathStart(const std::vector& vars, + int64* index) const { // Try to extend an existing path - for (int i = vars_.size() - 1; i >= 0; --i) { - if (vars_[i]->Bound()) { - const int next = vars_[i]->Value(); - if (next < vars_.size() && !vars_[next]->Bound()) { + for (int64 i = vars.size() - 1; i >= 0; --i) { + if (vars[i]->Bound()) { + const int64 next = vars[i]->Value(); + if (next < vars.size() && !vars[next]->Bound()) { *index = next; return true; } } } // Pick path start - for (int i = vars_.size() - 1; i >= 0; --i) { - if (!vars_[i]->Bound()) { + for (int64 i = vars.size() - 1; i >= 0; --i) { + if (!vars[i]->Bound()) { bool has_possible_prev = false; - for (int j = 0; j < vars_.size(); ++j) { - if (vars_[j]->Contains(i)) { + for (int64 j = 0; j < vars.size(); ++j) { + if (vars[j]->Contains(i)) { has_possible_prev = true; break; } @@ -1157,8 +1010,8 @@ bool PathSelector::FindPathStart(int64* index) const { } } // Pick first unbound - for (int i = 0; i < vars_.size(); ++i) { - if (!vars_[i]->Bound()) { + for (int64 i = 0; i < vars.size(); ++i) { + if (!vars[i]->Bound()) { *index = i; return true; } @@ -1166,46 +1019,17 @@ bool PathSelector::FindPathStart(int64* index) const { return false; } -// ----- Value selector ----- - -class ValueSelector : public BaseObject { - public: - ValueSelector() {} - ~ValueSelector() override {} - virtual int64 Select(const IntVar* const v, int64 id) = 0; -}; - // ----- Select min ----- -class MinValueSelector : public ValueSelector { - public: - MinValueSelector() {} - ~MinValueSelector() override {} - int64 Select(const IntVar* const v, int64 id) override { return v->Min(); } - std::string DebugString() const override { return "AssignMin"; } -}; +int64 SelectMinValue(const IntVar* v, int64 id) { return v->Min(); } // ----- Select max ----- -class MaxValueSelector : public ValueSelector { - public: - MaxValueSelector() {} - ~MaxValueSelector() override {} - int64 Select(const IntVar* const v, int64 id) override { return v->Max(); } - std::string DebugString() const override { return "AssignMax"; } -}; +int64 SelectMaxValue(const IntVar* v, int64 id) { return v->Max(); } // ----- Select random ----- -class RandomValueSelector : public ValueSelector { - public: - RandomValueSelector() {} - ~RandomValueSelector() override {} - int64 Select(const IntVar* const v, int64 id) override; - std::string DebugString() const override { return "AssignRandom"; } -}; - -int64 RandomValueSelector::Select(const IntVar* const v, int64 id) { +int64 SelectRandomValue(const IntVar* v, int64 id) { const uint64 span = v->Max() - v->Min() + 1; if (span > FLAGS_cp_large_domain_no_splitting_limit) { // Do not create holes in large domains. @@ -1248,15 +1072,7 @@ int64 RandomValueSelector::Select(const IntVar* const v, int64 id) { // ----- Select center ----- -class CenterValueSelector : public ValueSelector { - public: - CenterValueSelector() {} - ~CenterValueSelector() override {} - int64 Select(const IntVar* const v, int64 id) override; - std::string DebugString() const override { return "AssignCenter"; } -}; - -int64 CenterValueSelector::Select(const IntVar* const v, int64 id) { +int64 SelectCenterValue(const IntVar* v, int64 id) { const int64 vmin = v->Min(); const int64 vmax = v->Max(); if (vmax - vmin > FLAGS_cp_large_domain_no_splitting_limit) { @@ -1281,18 +1097,7 @@ int64 CenterValueSelector::Select(const IntVar* const v, int64 id) { // ----- Select center ----- -class SplitValueSelector : public ValueSelector { - public: - explicit SplitValueSelector(const std::string& name) : name_(name) {} - ~SplitValueSelector() override {} - int64 Select(const IntVar* const v, int64 id) override; - std::string DebugString() const override { return name_; } - - private: - const std::string name_; -}; - -int64 SplitValueSelector::Select(const IntVar* const v, int64 id) { +int64 SelectSplitValue(const IntVar* v, int64 id) { const int64 vmin = v->Min(); const int64 vmax = v->Max(); const uint64 delta = vmax - vmin; @@ -1302,27 +1107,27 @@ int64 SplitValueSelector::Select(const IntVar* const v, int64 id) { // ----- Select the value yielding the cheapest "eval" for a var ----- -class CheapestValueSelector : public ValueSelector { +class CheapestValueSelector : public BaseObject { public: - CheapestValueSelector(ResultCallback2* eval, - ResultCallback1* tie_breaker) + CheapestValueSelector(std::function eval, + std::function tie_breaker) : eval_(eval), tie_breaker_(tie_breaker) {} ~CheapestValueSelector() override {} - int64 Select(const IntVar* const v, int64 id) override; + int64 Select(const IntVar* v, int64 id); std::string DebugString() const override { return "CheapestValue"; } private: - std::unique_ptr > eval_; - std::unique_ptr > tie_breaker_; + std::function eval_; + std::function tie_breaker_; std::vector cache_; }; -int64 CheapestValueSelector::Select(const IntVar* const v, int64 id) { +int64 CheapestValueSelector::Select(const IntVar* v, int64 id) { cache_.clear(); int64 best = kint64max; std::unique_ptr it(v->MakeDomainIterator(false)); for (const int64 i : InitAndGetValues(it.get())) { - int64 eval = eval_->Run(id, i); + int64 eval = eval_(id, i); if (eval < best) { best = eval; cache_.clear(); @@ -1332,10 +1137,10 @@ int64 CheapestValueSelector::Select(const IntVar* const v, int64 id) { } } DCHECK_GT(cache_.size(), 0); - if (tie_breaker_.get() == nullptr || cache_.size() == 1) { + if (tie_breaker_ == nullptr || cache_.size() == 1) { return cache_.back(); } else { - return cache_[tie_breaker_->Run(cache_.size())]; + return cache_[tie_breaker_(cache_.size())]; } } @@ -1347,31 +1152,29 @@ int64 CheapestValueSelector::Select(const IntVar* const v, int64 id) { // the lowest value wins. // comparator(var_id, val1, val2) == true means than val1 should be preferred // over val2 for variable var_id. -class BestValueByComparisonSelector : public ValueSelector { +class BestValueByComparisonSelector : public BaseObject { public: explicit BestValueByComparisonSelector( - // Takes ownership. - ResultCallback3* comparator) + Solver::VariableValueComparator comparator) : comparator_(comparator) {} ~BestValueByComparisonSelector() override {} - int64 Select(const IntVar* const v, int64 id) override; + int64 Select(const IntVar* v, int64 id); std::string DebugString() const override { return "BestValueByComparisonSelector"; } private: - std::unique_ptr > comparator_; + Solver::VariableValueComparator comparator_; }; -int64 BestValueByComparisonSelector::Select(const IntVar* const v, int64 id) { +int64 BestValueByComparisonSelector::Select(const IntVar* v, int64 id) { std::unique_ptr it(v->MakeDomainIterator(false)); it->Init(); DCHECK(it->Ok()); // At least one value. int64 best_value = it->Value(); for (it->Next(); it->Ok(); it->Next()) { - const int candidate_value = it->Value(); - if (comparator_->Run(id, candidate_value, best_value)) { + const int64 candidate_value = it->Value(); + if (comparator_(id, candidate_value, best_value)) { best_value = candidate_value; } } @@ -1382,52 +1185,48 @@ int64 BestValueByComparisonSelector::Select(const IntVar* const v, int64 id) { class VariableAssignmentSelector : public BaseVariableAssignmentSelector { public: - VariableAssignmentSelector(VariableSelector* const var_selector, - ValueSelector* const value_selector) - : var_selector_(var_selector), value_selector_(value_selector) {} + VariableAssignmentSelector(Solver* solver, const std::vector& vars, + Solver::VariableIndexSelector var_selector, + Solver::VariableValueSelector value_selector, + const std::string& name) + : BaseVariableAssignmentSelector(solver, vars), + var_selector_(var_selector), + value_selector_(value_selector), + name_(name) {} ~VariableAssignmentSelector() override {} - int64 SelectValue(const IntVar* const var, int64 id) override { - return value_selector_->Select(var, id); + int64 SelectValue(const IntVar* var, int64 id) override { + return value_selector_(var, id); } - IntVar* SelectVariable(Solver* const s, int64* id) override { - return var_selector_->Select(s, id); + int64 ChooseVariable() override { + return var_selector_(solver_, vars_, first_unbound_.Value(), + last_unbound_.Value()); } std::string DebugString() const override; - void Accept(ModelVisitor* const visitor) const override { - var_selector_->Accept(visitor); - } - private: - VariableSelector* const var_selector_; - ValueSelector* const value_selector_; + Solver::VariableIndexSelector var_selector_; + Solver::VariableValueSelector value_selector_; + const std::string name_; }; std::string VariableAssignmentSelector::DebugString() const { - return var_selector_->DebugString() + "_" + value_selector_->DebugString() + - var_selector_->VarDebugString(); + return StringPrintf("%s(%s)", name_.c_str(), + JoinDebugStringPtr(vars_, ", ").c_str()); } // ----- Base Global Evaluator-based selector ----- class BaseEvaluatorSelector : public BaseVariableAssignmentSelector { public: - BaseEvaluatorSelector(const std::vector& vars, - ResultCallback2* evaluator); + BaseEvaluatorSelector(Solver* solver, const std::vector& vars, + std::function evaluator); ~BaseEvaluatorSelector() override {} - void Accept(ModelVisitor* const visitor) const override { - visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension); - visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument, - vars_); - visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension); - } - protected: struct Element { Element() : var(0), value(0) {} - Element(int i, int64 j) : var(i), value(j) {} - int var; + Element(int64 i, int64 j) : var(i), value(j) {} + int64 var; int64 value; }; @@ -1436,76 +1235,74 @@ class BaseEvaluatorSelector : public BaseVariableAssignmentSelector { JoinDebugStringPtr(vars_, ", ").c_str()); } - const std::vector vars_; - std::unique_ptr > evaluator_; + std::function evaluator_; }; BaseEvaluatorSelector::BaseEvaluatorSelector( - const std::vector& vars, - ResultCallback2* evaluator) - : vars_(vars), evaluator_(evaluator) {} + Solver* solver, const std::vector& vars, + std::function evaluator) + : BaseVariableAssignmentSelector(solver, vars), evaluator_(evaluator) {} // ----- Global Dynamic Evaluator-based selector ----- class DynamicEvaluatorSelector : public BaseEvaluatorSelector { public: - DynamicEvaluatorSelector(const std::vector& vars, - ResultCallback2* evaluator, - ResultCallback1* tie_breaker); + DynamicEvaluatorSelector(Solver* solver, const std::vector& vars, + std::function evaluator, + std::function tie_breaker); ~DynamicEvaluatorSelector() override {} - int64 SelectValue(const IntVar* const var, int64 id) override; - IntVar* SelectVariable(Solver* const s, int64* id) override; + int64 SelectValue(const IntVar* var, int64 id) override; + int64 ChooseVariable() override; std::string DebugString() const override; private: - int first_; - std::unique_ptr > tie_breaker_; + int64 first_; + std::function tie_breaker_; std::vector cache_; }; DynamicEvaluatorSelector::DynamicEvaluatorSelector( - const std::vector& vars, - ResultCallback2* evaluator, - ResultCallback1* tie_breaker) - : BaseEvaluatorSelector(vars, evaluator), + Solver* solver, const std::vector& vars, + std::function evaluator, + std::function tie_breaker) + : BaseEvaluatorSelector(solver, vars, evaluator), first_(-1), tie_breaker_(tie_breaker) {} -int64 DynamicEvaluatorSelector::SelectValue(const IntVar* const var, int64 id) { +int64 DynamicEvaluatorSelector::SelectValue(const IntVar* var, int64 id) { return cache_[first_].value; } -IntVar* DynamicEvaluatorSelector::SelectVariable(Solver* const s, int64* id) { +int64 DynamicEvaluatorSelector::ChooseVariable() { int64 best_evaluation = kint64max; cache_.clear(); - for (int i = 0; i < vars_.size(); ++i) { + for (int64 i = 0; i < vars_.size(); ++i) { const IntVar* const var = vars_[i]; if (!var->Bound()) { std::unique_ptr it(var->MakeDomainIterator(false)); for (const int64 j : InitAndGetValues(it.get())) { - const int64 value = evaluator_->Run(i, j); + const int64 value = evaluator_(i, j); if (value < best_evaluation) { best_evaluation = value; cache_.clear(); cache_.push_back(Element(i, j)); - } else if (value == best_evaluation && tie_breaker_.get() != nullptr) { + } else if (value == best_evaluation && tie_breaker_) { cache_.push_back(Element(i, j)); } } } } + if (cache_.size() == 0) { - *id = kint64max; - return nullptr; + return -1; } - if (tie_breaker_.get() == nullptr || cache_.size() == 1) { - *id = cache_[0].var; + + if (tie_breaker_ == nullptr || cache_.size() == 1) { first_ = 0; - return vars_[*id]; + return cache_.front().var; } else { - first_ = tie_breaker_->Run(cache_.size()); - *id = cache_[first_].var; - return vars_[*id]; + first_ = tie_breaker_(cache_.size()); + return cache_[first_].var; } } @@ -1517,17 +1314,17 @@ std::string DynamicEvaluatorSelector::DebugString() const { class StaticEvaluatorSelector : public BaseEvaluatorSelector { public: - StaticEvaluatorSelector(const std::vector& vars, - ResultCallback2* evaluator); + StaticEvaluatorSelector(Solver* solver, const std::vector& vars, + std::function evaluator); ~StaticEvaluatorSelector() override {} - int64 SelectValue(const IntVar* const var, int64 id) override; - IntVar* SelectVariable(Solver* const s, int64* id) override; + int64 SelectValue(const IntVar* var, int64 id) override; + int64 ChooseVariable() override; std::string DebugString() const override; private: class Compare { public: - explicit Compare(ResultCallback2* evaluator) + explicit Compare(std::function evaluator) : evaluator_(evaluator) {} bool operator()(const Element& lhs, const Element& rhs) const { const int64 value_lhs = Value(lhs); @@ -1538,32 +1335,34 @@ class StaticEvaluatorSelector : public BaseEvaluatorSelector { (lhs.var == rhs.var && lhs.value < rhs.value))); } int64 Value(const Element& element) const { - return evaluator_->Run(element.var, element.value); + return evaluator_(element.var, element.value); } private: - ResultCallback2* evaluator_; + std::function evaluator_; }; Compare comp_; std::vector elements_; - int first_; + int64 first_; }; StaticEvaluatorSelector::StaticEvaluatorSelector( - const std::vector& vars, - ResultCallback2* evaluator) - : BaseEvaluatorSelector(vars, evaluator), comp_(evaluator), first_(-1) {} + Solver* solver, const std::vector& vars, + std::function evaluator) + : BaseEvaluatorSelector(solver, vars, evaluator), + comp_(evaluator), + first_(-1) {} -int64 StaticEvaluatorSelector::SelectValue(const IntVar* const var, int64 id) { +int64 StaticEvaluatorSelector::SelectValue(const IntVar* var, int64 id) { return elements_[first_].value; } -IntVar* StaticEvaluatorSelector::SelectVariable(Solver* const s, int64* id) { +int64 StaticEvaluatorSelector::ChooseVariable() { if (first_ == -1) { // first call to select. update assignment costs // Two phases: compute size then filland sort - int element_size = 0; - for (int i = 0; i < vars_.size(); ++i) { + int64 element_size = 0; + for (int64 i = 0; i < vars_.size(); ++i) { if (!vars_[i]->Bound()) { element_size += vars_[i]->Size(); } @@ -1581,20 +1380,18 @@ IntVar* StaticEvaluatorSelector::SelectVariable(Solver* const s, int64* id) { } // Sort is stable here given the tie-breaking rules in comp_. std::sort(elements_.begin(), elements_.end(), comp_); - s->SaveAndSetValue(&first_, 0); + solver_->SaveAndSetValue(&first_, 0LL); } - for (int i = first_; i < elements_.size(); ++i) { + for (int64 i = first_; i < elements_.size(); ++i) { const Element& element = elements_[i]; IntVar* const var = vars_[element.var]; if (!var->Bound() && var->Contains(element.value)) { - s->SaveAndSetValue(&first_, i); - *id = element.var; - return var; + solver_->SaveAndSetValue(&first_, i); + return element.var; } } - s->SaveAndSetValue(&first_, static_cast(elements_.size())); - *id = vars_.size(); - return nullptr; + solver_->SaveAndSetValue(&first_, static_cast(elements_.size())); + return -1; } std::string StaticEvaluatorSelector::DebugString() const { @@ -1818,96 +1615,86 @@ class BaseAssignVariables : public DecisionBuilder { SPLIT_UPPER, }; - explicit BaseAssignVariables(BaseVariableAssignmentSelector* const selector, - Mode mode) + BaseAssignVariables(BaseVariableAssignmentSelector* const selector, Mode mode) : selector_(selector), mode_(mode) {} + ~BaseAssignVariables() override; Decision* Next(Solver* const s) override; std::string DebugString() const override; - static BaseAssignVariables* MakePhase(Solver* const s, - const std::vector& vars, - VariableSelector* const var_selector, - ValueSelector* const value_selector, - BaseAssignVariables::Mode mode); - static VariableSelector* MakeVariableSelector(Solver* const s, - const std::vector& vars, - Solver::IntVarStrategy str) { - VariableSelector* var_selector = nullptr; + static BaseAssignVariables* MakePhase( + Solver* const s, const std::vector& vars, + Solver::VariableIndexSelector var_selector, + Solver::VariableValueSelector value_selector, + const std::string& value_selector_name, BaseAssignVariables::Mode mode); + + static Solver::VariableIndexSelector MakeVariableSelector( + Solver* const s, const std::vector& vars, + Solver::IntVarStrategy str) { switch (str) { case Solver::INT_VAR_DEFAULT: case Solver::INT_VAR_SIMPLE: case Solver::CHOOSE_FIRST_UNBOUND: - var_selector = s->RevAlloc(new FirstUnboundSelector(vars)); - break; + return ChooseFirstUnbound; case Solver::CHOOSE_RANDOM: - var_selector = s->RevAlloc(new RandomSelector(vars)); - break; + return ChooseRandom; case Solver::CHOOSE_MIN_SIZE_LOWEST_MIN: - var_selector = s->RevAlloc(new MinSizeLowestMinSelector(vars)); - break; + return ChooseMinSizeLowestMin; case Solver::CHOOSE_MIN_SIZE_HIGHEST_MIN: - var_selector = s->RevAlloc(new MinSizeHighestMinSelector(vars)); - break; + return ChooseMinSizeHighestMin; case Solver::CHOOSE_MIN_SIZE_LOWEST_MAX: - var_selector = s->RevAlloc(new MinSizeLowestMaxSelector(vars)); - break; + return ChooseMinSizeLowestMax; case Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX: - var_selector = s->RevAlloc(new MinSizeHighestMaxSelector(vars)); - break; + return ChooseMinSizeHighestMax; case Solver::CHOOSE_LOWEST_MIN: - var_selector = s->RevAlloc(new LowestMinSelector(vars)); - break; + return ChooseLowestMin; case Solver::CHOOSE_HIGHEST_MAX: - var_selector = s->RevAlloc(new HighestMaxSelector(vars)); - break; + return ChooseHighestMax; case Solver::CHOOSE_MIN_SIZE: - var_selector = s->RevAlloc(new LowestSizeSelector(vars)); - break; + return ChooseMinSize; case Solver::CHOOSE_MAX_SIZE: - var_selector = s->RevAlloc(new HighestSizeSelector(vars)); - break; - case Solver::CHOOSE_MAX_REGRET_ON_MIN: - var_selector = s->RevAlloc(new HighestRegretSelectorOnMin(vars)); - break; - case Solver::CHOOSE_PATH: - var_selector = s->RevAlloc(new PathSelector(vars)); - break; + return ChooseMaxSize; + case Solver::CHOOSE_MAX_REGRET_ON_MIN: { + HighestRegretSelectorOnMin* const selector = + s->RevAlloc(new HighestRegretSelectorOnMin(vars)); + return [selector](Solver* solver, const std::vector& vars, + int first_unbound, int last_unbound) { + return selector->Choose(solver, vars, first_unbound, last_unbound); + }; + } + case Solver::CHOOSE_PATH: { + PathSelector* const selector = s->RevAlloc(new PathSelector()); + return [selector](Solver* solver, const std::vector& vars, + int first_unbound, int last_unbound) { + return selector->Choose(solver, vars, first_unbound, last_unbound); + }; + } default: LOG(FATAL) << "Unknown int var strategy " << str; - break; + return nullptr; } - return var_selector; } - static ValueSelector* MakeValueSelector(Solver* const s, - Solver::IntValueStrategy val_str) { - ValueSelector* value_selector = nullptr; + static Solver::VariableValueSelector MakeValueSelector( + Solver* const s, Solver::IntValueStrategy val_str) { switch (val_str) { case Solver::INT_VALUE_DEFAULT: case Solver::INT_VALUE_SIMPLE: case Solver::ASSIGN_MIN_VALUE: - value_selector = s->RevAlloc(new MinValueSelector); - break; + return SelectMinValue; case Solver::ASSIGN_MAX_VALUE: - value_selector = s->RevAlloc(new MaxValueSelector); - break; + return SelectMaxValue; case Solver::ASSIGN_RANDOM_VALUE: - value_selector = s->RevAlloc(new RandomValueSelector); - break; + return SelectRandomValue; case Solver::ASSIGN_CENTER_VALUE: - value_selector = s->RevAlloc(new CenterValueSelector()); - break; + return SelectCenterValue; case Solver::SPLIT_LOWER_HALF: - value_selector = s->RevAlloc(new SplitValueSelector("SplitLower")); - break; + return SelectSplitValue; case Solver::SPLIT_UPPER_HALF: - value_selector = s->RevAlloc(new SplitValueSelector("SplitUpper")); - break; + return SelectSplitValue; default: LOG(FATAL) << "Unknown int value strategy " << val_str; - break; + return nullptr; } - return value_selector; } void Accept(ModelVisitor* const visitor) const override { @@ -1922,9 +1709,10 @@ class BaseAssignVariables : public DecisionBuilder { BaseAssignVariables::~BaseAssignVariables() {} Decision* BaseAssignVariables::Next(Solver* const s) { - int64 id = 0; - IntVar* const var = selector_->SelectVariable(s, &id); - if (nullptr != var) { + const std::vector& vars = selector_->vars(); + int id = selector_->ChooseVariableWrapper(); + if (id >= 0 && id < vars.size()) { + IntVar* const var = vars[id]; const int64 value = selector_->SelectValue(var, id); switch (mode_) { case ASSIGN: @@ -1944,12 +1732,75 @@ std::string BaseAssignVariables::DebugString() const { BaseAssignVariables* BaseAssignVariables::MakePhase( Solver* const s, const std::vector& vars, - VariableSelector* const var_selector, ValueSelector* const value_selector, - BaseAssignVariables::Mode mode) { - BaseVariableAssignmentSelector* selector = - s->RevAlloc(new VariableAssignmentSelector(var_selector, value_selector)); + Solver::VariableIndexSelector var_selector, + Solver::VariableValueSelector value_selector, + const std::string& value_selector_name, BaseAssignVariables::Mode mode) { + BaseVariableAssignmentSelector* const selector = + s->RevAlloc(new VariableAssignmentSelector( + s, vars, var_selector, value_selector, value_selector_name)); return s->RevAlloc(new BaseAssignVariables(selector, mode)); } + +std::string ChooseVariableName(Solver::IntVarStrategy var_str) { + switch (var_str) { + case Solver::INT_VAR_DEFAULT: + case Solver::INT_VAR_SIMPLE: + case Solver::CHOOSE_FIRST_UNBOUND: + return "ChooseFirstUnbound"; + case Solver::CHOOSE_RANDOM: + return "ChooseRandom"; + case Solver::CHOOSE_MIN_SIZE_LOWEST_MIN: + return "ChooseMinSizeLowestMin"; + case Solver::CHOOSE_MIN_SIZE_HIGHEST_MIN: + return "ChooseMinSizeHighestMin"; + case Solver::CHOOSE_MIN_SIZE_LOWEST_MAX: + return "ChooseMinSizeLowestMax"; + case Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX: + return "ChooseMinSizeHighestMax"; + case Solver::CHOOSE_LOWEST_MIN: + return "ChooseLowestMin"; + case Solver::CHOOSE_HIGHEST_MAX: + return "ChooseHighestMax"; + case Solver::CHOOSE_MIN_SIZE: + return "ChooseMinSize"; + case Solver::CHOOSE_MAX_SIZE: + return "ChooseMaxSize;"; + case Solver::CHOOSE_MAX_REGRET_ON_MIN: + return "HighestRegretSelectorOnMin"; + case Solver::CHOOSE_PATH: + return "PathSelector"; + default: + LOG(FATAL) << "Unknown int var strategy " << var_str; + return ""; + } +} + +std::string SelectValueName(Solver::IntValueStrategy val_str) { + switch (val_str) { + case Solver::INT_VALUE_DEFAULT: + case Solver::INT_VALUE_SIMPLE: + case Solver::ASSIGN_MIN_VALUE: + return "SelectMinValue"; + case Solver::ASSIGN_MAX_VALUE: + return "SelectMaxValue"; + case Solver::ASSIGN_RANDOM_VALUE: + return "SelectRandomValue"; + case Solver::ASSIGN_CENTER_VALUE: + return "SelectCenterValue"; + case Solver::SPLIT_LOWER_HALF: + return "SelectSplitValue"; + case Solver::SPLIT_UPPER_HALF: + return "SelectSplitValue"; + default: + LOG(FATAL) << "Unknown int value strategy " << val_str; + return ""; + } +} + +std::string BuildHeuristicsName(Solver::IntVarStrategy var_str, + Solver::IntValueStrategy val_str) { + return ChooseVariableName(var_str) + "_" + SelectValueName(val_str); +} } // namespace DecisionBuilder* Solver::MakePhase(IntVar* const v0, @@ -2005,114 +1856,136 @@ BaseAssignVariables::Mode ChooseMode(Solver::IntValueStrategy val_str) { DecisionBuilder* Solver::MakePhase(const std::vector& vars, Solver::IntVarStrategy var_str, Solver::IntValueStrategy val_str) { - VariableSelector* const var_selector = + Solver::VariableIndexSelector var_selector = BaseAssignVariables::MakeVariableSelector(this, vars, var_str); - ValueSelector* const value_selector = + Solver::VariableValueSelector value_selector = BaseAssignVariables::MakeValueSelector(this, val_str); - return BaseAssignVariables::MakePhase(this, vars, var_selector, - value_selector, ChooseMode(val_str)); + const std::string name = BuildHeuristicsName(var_str, val_str); + return BaseAssignVariables::MakePhase( + this, vars, var_selector, value_selector, name, ChooseMode(val_str)); } DecisionBuilder* Solver::MakePhase(const std::vector& vars, - ResultCallback1* var_evaluator, + Solver::IndexEvaluator1 var_evaluator, Solver::IntValueStrategy val_str) { - var_evaluator->CheckIsRepeatable(); - VariableSelector* const var_selector = - RevAlloc(new CheapestVarSelector(vars, var_evaluator)); - ValueSelector* const value_selector = + CHECK(var_evaluator != nullptr); + CheapestVarSelector* const var_selector = + RevAlloc(new CheapestVarSelector(std::move(var_evaluator))); + Solver::VariableIndexSelector choose_variable = [var_selector]( + Solver* solver, const std::vector& vars, int first_unbound, + int last_unbound) { + return var_selector->Choose(solver, vars, first_unbound, last_unbound); + }; + Solver::VariableValueSelector select_value = BaseAssignVariables::MakeValueSelector(this, val_str); - return BaseAssignVariables::MakePhase(this, vars, var_selector, - value_selector, ChooseMode(val_str)); + const std::string name = "ChooseCheapestVariable_" + SelectValueName(val_str); + return BaseAssignVariables::MakePhase( + this, vars, choose_variable, select_value, name, ChooseMode(val_str)); } -DecisionBuilder* Solver::MakePhase( - const std::vector& vars, Solver::IntVarStrategy var_str, - ResultCallback2* value_evaluator) { - VariableSelector* const var_selector = +DecisionBuilder* Solver::MakePhase(const std::vector& vars, + Solver::IntVarStrategy var_str, + Solver::IndexEvaluator2 value_evaluator) { + Solver::VariableIndexSelector choose_variable = BaseAssignVariables::MakeVariableSelector(this, vars, var_str); - value_evaluator->CheckIsRepeatable(); - ValueSelector* value_selector = + CheapestValueSelector* const value_selector = RevAlloc(new CheapestValueSelector(value_evaluator, nullptr)); - return BaseAssignVariables::MakePhase( - this, vars, var_selector, value_selector, BaseAssignVariables::ASSIGN); + Solver::VariableValueSelector select_value = [value_selector]( + const IntVar* var, int64 id) { return value_selector->Select(var, id); }; + const std::string name = ChooseVariableName(var_str) + "_SelectCheapestValue"; + return BaseAssignVariables::MakePhase(this, vars, choose_variable, + select_value, name, + BaseAssignVariables::ASSIGN); } DecisionBuilder* Solver::MakePhase( const std::vector& vars, IntVarStrategy var_str, - ResultCallback3* var_val1_val2_comparator) { - VariableSelector* const var_selector = + VariableValueComparator var_val1_val2_comparator) { + Solver::VariableIndexSelector choose_variable = BaseAssignVariables::MakeVariableSelector(this, vars, var_str); - var_val1_val2_comparator->CheckIsRepeatable(); - ValueSelector* value_selector = + BestValueByComparisonSelector* const value_selector = RevAlloc(new BestValueByComparisonSelector(var_val1_val2_comparator)); - return BaseAssignVariables::MakePhase( - this, vars, var_selector, value_selector, BaseAssignVariables::ASSIGN); -} - -DecisionBuilder* Solver::MakePhase( - const std::vector& vars, ResultCallback1* var_evaluator, - ResultCallback2* value_evaluator) { - var_evaluator->CheckIsRepeatable(); - VariableSelector* const var_selector = - RevAlloc(new CheapestVarSelector(vars, var_evaluator)); - value_evaluator->CheckIsRepeatable(); - ValueSelector* value_selector = - RevAlloc(new CheapestValueSelector(value_evaluator, nullptr)); - return BaseAssignVariables::MakePhase( - this, vars, var_selector, value_selector, BaseAssignVariables::ASSIGN); -} - -DecisionBuilder* Solver::MakePhase( - const std::vector& vars, Solver::IntVarStrategy var_str, - ResultCallback2* value_evaluator, - ResultCallback1* tie_breaker) { - VariableSelector* const var_selector = - BaseAssignVariables::MakeVariableSelector(this, vars, var_str); - value_evaluator->CheckIsRepeatable(); - ValueSelector* value_selector = - RevAlloc(new CheapestValueSelector(value_evaluator, tie_breaker)); - return BaseAssignVariables::MakePhase( - this, vars, var_selector, value_selector, BaseAssignVariables::ASSIGN); -} - -DecisionBuilder* Solver::MakePhase( - const std::vector& vars, ResultCallback1* var_evaluator, - ResultCallback2* value_evaluator, - ResultCallback1* tie_breaker) { - var_evaluator->CheckIsRepeatable(); - VariableSelector* const var_selector = - RevAlloc(new CheapestVarSelector(vars, var_evaluator)); - value_evaluator->CheckIsRepeatable(); - ValueSelector* value_selector = - RevAlloc(new CheapestValueSelector(value_evaluator, tie_breaker)); - return BaseAssignVariables::MakePhase( - this, vars, var_selector, value_selector, BaseAssignVariables::ASSIGN); + Solver::VariableValueSelector select_value = [value_selector]( + const IntVar* var, int64 id) { return value_selector->Select(var, id); }; + return BaseAssignVariables::MakePhase(this, vars, choose_variable, + select_value, "CheapestValue", + BaseAssignVariables::ASSIGN); } DecisionBuilder* Solver::MakePhase(const std::vector& vars, - ResultCallback2* eval, + Solver::IndexEvaluator1 var_evaluator, + Solver::IndexEvaluator2 value_evaluator) { + CheapestVarSelector* const var_selector = + RevAlloc(new CheapestVarSelector(std::move(var_evaluator))); + Solver::VariableIndexSelector choose_variable = [var_selector]( + Solver* solver, const std::vector& vars, int first_unbound, + int last_unbound) { + return var_selector->Choose(solver, vars, first_unbound, last_unbound); + }; + CheapestValueSelector* value_selector = + RevAlloc(new CheapestValueSelector(value_evaluator, nullptr)); + Solver::VariableValueSelector select_value = [value_selector]( + const IntVar* var, int64 id) { return value_selector->Select(var, id); }; + return BaseAssignVariables::MakePhase(this, vars, choose_variable, + select_value, "CheapestValue", + BaseAssignVariables::ASSIGN); +} + +DecisionBuilder* Solver::MakePhase(const std::vector& vars, + Solver::IntVarStrategy var_str, + Solver::IndexEvaluator2 value_evaluator, + Solver::IndexEvaluator1 tie_breaker) { + Solver::VariableIndexSelector choose_variable = + BaseAssignVariables::MakeVariableSelector(this, vars, var_str); + CheapestValueSelector* value_selector = + RevAlloc(new CheapestValueSelector(value_evaluator, tie_breaker)); + Solver::VariableValueSelector select_value = [value_selector]( + const IntVar* var, int64 id) { return value_selector->Select(var, id); }; + return BaseAssignVariables::MakePhase(this, vars, choose_variable, + select_value, "CheapestValue", + BaseAssignVariables::ASSIGN); +} + +DecisionBuilder* Solver::MakePhase(const std::vector& vars, + Solver::IndexEvaluator1 var_evaluator, + Solver::IndexEvaluator2 value_evaluator, + Solver::IndexEvaluator1 tie_breaker) { + CheapestVarSelector* const var_selector = + RevAlloc(new CheapestVarSelector(std::move(var_evaluator))); + Solver::VariableIndexSelector choose_variable = [var_selector]( + Solver* solver, const std::vector& vars, int first_unbound, + int last_unbound) { + return var_selector->Choose(solver, vars, first_unbound, last_unbound); + }; + CheapestValueSelector* value_selector = + RevAlloc(new CheapestValueSelector(value_evaluator, tie_breaker)); + Solver::VariableValueSelector select_value = [value_selector]( + const IntVar* var, int64 id) { return value_selector->Select(var, id); }; + return BaseAssignVariables::MakePhase(this, vars, choose_variable, + select_value, "CheapestValue", + BaseAssignVariables::ASSIGN); +} + +DecisionBuilder* Solver::MakePhase(const std::vector& vars, + Solver::IndexEvaluator2 eval, Solver::EvaluatorStrategy str) { return MakePhase(vars, eval, nullptr, str); } DecisionBuilder* Solver::MakePhase(const std::vector& vars, - ResultCallback2* eval, - ResultCallback1* tie_breaker, + Solver::IndexEvaluator2 eval, + Solver::IndexEvaluator1 tie_breaker, Solver::EvaluatorStrategy str) { - eval->CheckIsRepeatable(); - if (tie_breaker) { - tie_breaker->CheckIsRepeatable(); - } BaseVariableAssignmentSelector* selector = nullptr; switch (str) { case Solver::CHOOSE_STATIC_GLOBAL_BEST: { // TODO(user): support tie breaker - selector = RevAlloc(new StaticEvaluatorSelector(vars, eval)); + selector = RevAlloc(new StaticEvaluatorSelector(this, vars, eval)); break; } case Solver::CHOOSE_DYNAMIC_GLOBAL_BEST: { selector = - RevAlloc(new DynamicEvaluatorSelector(vars, eval, tie_breaker)); + RevAlloc(new DynamicEvaluatorSelector(this, vars, eval, tie_breaker)); break; } } @@ -2180,43 +2053,43 @@ SolutionCollector::~SolutionCollector() { } void SolutionCollector::Add(IntVar* const var) { - if (prototype_.get() != nullptr) { + if (prototype_ != nullptr) { prototype_->Add(var); } } void SolutionCollector::Add(const std::vector& vars) { - if (prototype_.get() != nullptr) { + if (prototype_ != nullptr) { prototype_->Add(vars); } } void SolutionCollector::Add(IntervalVar* const var) { - if (prototype_.get() != nullptr) { + if (prototype_ != nullptr) { prototype_->Add(var); } } void SolutionCollector::Add(const std::vector& vars) { - if (prototype_.get() != nullptr) { + if (prototype_ != nullptr) { prototype_->Add(vars); } } void SolutionCollector::Add(SequenceVar* const var) { - if (prototype_.get() != nullptr) { + if (prototype_ != nullptr) { prototype_->Add(var); } } void SolutionCollector::Add(const std::vector& vars) { - if (prototype_.get() != nullptr) { + if (prototype_ != nullptr) { prototype_->Add(vars); } } void SolutionCollector::AddObjective(IntVar* const objective) { - if (prototype_.get() != nullptr && objective != nullptr) { + if (prototype_ != nullptr && objective != nullptr) { prototype_->AddObjective(objective); } } @@ -2234,7 +2107,7 @@ void SolutionCollector::EnterSearch() { void SolutionCollector::PushSolution() { Assignment* new_sol = nullptr; - if (prototype_.get() != nullptr) { + if (prototype_ != nullptr) { if (!recycle_solutions_.empty()) { new_sol = recycle_solutions_.back(); DCHECK(new_sol != nullptr); @@ -2382,7 +2255,7 @@ bool FirstSolutionCollector::AtSolution() { } std::string FirstSolutionCollector::DebugString() const { - if (prototype_.get() == nullptr) { + if (prototype_ == nullptr) { return "FirstSolutionCollector()"; } else { return "FirstSolutionCollector(" + prototype_->DebugString() + ")"; @@ -2428,7 +2301,7 @@ bool LastSolutionCollector::AtSolution() { } std::string LastSolutionCollector::DebugString() const { - if (prototype_.get() == nullptr) { + if (prototype_ == nullptr) { return "LastSolutionCollector()"; } else { return "LastSolutionCollector(" + prototype_->DebugString() + ")"; @@ -2481,7 +2354,7 @@ void BestValueSolutionCollector::EnterSearch() { } bool BestValueSolutionCollector::AtSolution() { - if (prototype_.get() != nullptr) { + if (prototype_ != nullptr) { const IntVar* objective = prototype_->Objective(); if (objective != nullptr) { if (maximize_ && objective->Max() > best_) { @@ -2499,7 +2372,7 @@ bool BestValueSolutionCollector::AtSolution() { } std::string BestValueSolutionCollector::DebugString() const { - if (prototype_.get() == nullptr) { + if (prototype_ == nullptr) { return "BestValueSolutionCollector()"; } else { return "BestValueSolutionCollector(" + prototype_->DebugString() + ")"; @@ -2544,7 +2417,7 @@ bool AllSolutionCollector::AtSolution() { } std::string AllSolutionCollector::DebugString() const { - if (prototype_.get() == nullptr) { + if (prototype_ == nullptr) { return "AllSolutionCollector()"; } else { return "AllSolutionCollector(" + prototype_->DebugString() + ")"; @@ -2889,21 +2762,21 @@ void TabuSearch::ApplyDecision(Decision* const d) { // allowed, a factor of 0 means all violations allowed. std::vector tabu_vars; for (const VarValue& vv : keep_tabu_list_) { - IntVar* tabu_var = s->MakeBoolVar(); - Constraint* keep_cst = + IntVar* const tabu_var = s->MakeBoolVar(); + Constraint* const keep_cst = s->MakeIsEqualCstCt(vv.var_, vv.value_, tabu_var); s->AddConstraint(keep_cst); tabu_vars.push_back(tabu_var); } for (const VarValue& vv : forbid_tabu_list_) { IntVar* tabu_var = s->MakeBoolVar(); - Constraint* forbid_cst = + Constraint* const forbid_cst = s->MakeIsDifferentCstCt(vv.var_, vv.value_, tabu_var); s->AddConstraint(forbid_cst); tabu_vars.push_back(tabu_var); } if (tabu_vars.size() > 0) { - IntVar* tabu = s->MakeBoolVar(); + IntVar* const tabu = s->MakeBoolVar(); s->AddConstraint(s->MakeIsGreaterOrEqualCstCt( s->MakeSum(tabu_vars)->Var(), tabu_vars.size() * tabu_factor_, tabu)); s->AddConstraint(s->MakeGreaterOrEqual(s->MakeSum(aspiration, tabu), 1LL)); @@ -3214,7 +3087,8 @@ class GuidedLocalSearch : public Metaheuristic { protected: struct Comparator { - bool operator()(const std::pair& i, const std::pair& j) { + bool operator()(const std::pair& i, + const std::pair& j) { return i.second > j.second; } }; @@ -3368,11 +3242,11 @@ bool GuidedLocalSearch::AcceptDelta(Assignment* delta, Assignment* deltadelta) { if (maximize_) { delta->SetObjectiveMin( std::max(std::min(current_ + step_ - penalty, best_ + step_), - delta->ObjectiveMin())); + delta->ObjectiveMin())); } else { delta->SetObjectiveMax( std::min(std::max(current_ - step_ - penalty, best_ - step_), - delta->ObjectiveMax())); + delta->ObjectiveMax())); } } } @@ -3439,7 +3313,7 @@ bool GuidedLocalSearch::LocalOptimum() { class BinaryGuidedLocalSearch : public GuidedLocalSearch { public: BinaryGuidedLocalSearch(Solver* const solver, IntVar* const objective, - Solver::IndexEvaluator2* objective_function, + std::function objective_function, bool maximize, int64 step, const std::vector& vars, double penalty_factor); ~BinaryGuidedLocalSearch() override {} @@ -3454,24 +3328,21 @@ class BinaryGuidedLocalSearch : public GuidedLocalSearch { private: int64 PenalizedValue(int64 i, int64 j); - std::unique_ptr objective_function_; + std::function objective_function_; }; BinaryGuidedLocalSearch::BinaryGuidedLocalSearch( Solver* const solver, IntVar* const objective, - Solver::IndexEvaluator2* objective_function, bool maximize, int64 step, - const std::vector& vars, double penalty_factor) + std::function objective_function, bool maximize, + int64 step, const std::vector& vars, double penalty_factor) : GuidedLocalSearch(solver, objective, maximize, step, vars, penalty_factor), - objective_function_(objective_function) { - objective_function_->CheckIsRepeatable(); -} + objective_function_(objective_function) {} IntExpr* BinaryGuidedLocalSearch::MakeElementPenalty(int index) { - return solver()->MakeElement( - NewPermanentCallback(this, &BinaryGuidedLocalSearch::PenalizedValue, - static_cast(index)), - vars_[index]); + return solver()->MakeElement([this, index](int64 i) { + return PenalizedValue(index, i); + }, vars_[index]); } int64 BinaryGuidedLocalSearch::AssignmentElementPenalty( @@ -3481,7 +3352,7 @@ int64 BinaryGuidedLocalSearch::AssignmentElementPenalty( int64 BinaryGuidedLocalSearch::AssignmentPenalty(const Assignment& assignment, int index, int64 next) { - return objective_function_->Run(index, next); + return objective_function_(index, next); } bool BinaryGuidedLocalSearch::EvaluateElementValue( @@ -3501,7 +3372,7 @@ int64 BinaryGuidedLocalSearch::PenalizedValue(int64 i, int64 j) { const int64 penalty = penalties_->Value(arc); if (penalty != 0) { // objective_function_->Run(i, j) can be costly const int64 penalized_value = - penalty_factor_ * penalty * objective_function_->Run(i, j); + penalty_factor_ * penalty * objective_function_(i, j); if (maximize_) { return -penalized_value; } else { @@ -3514,12 +3385,11 @@ int64 BinaryGuidedLocalSearch::PenalizedValue(int64 i, int64 j) { class TernaryGuidedLocalSearch : public GuidedLocalSearch { public: - TernaryGuidedLocalSearch(Solver* const solver, IntVar* const objective, - Solver::IndexEvaluator3* objective_function, - bool maximize, int64 step, - const std::vector& vars, - const std::vector& secondary_vars, - double penalty_factor); + TernaryGuidedLocalSearch( + Solver* const solver, IntVar* const objective, + std::function objective_function, + bool maximize, int64 step, const std::vector& vars, + const std::vector& secondary_vars, double penalty_factor); ~TernaryGuidedLocalSearch() override {} IntExpr* MakeElementPenalty(int index) override; int64 AssignmentElementPenalty(const Assignment& assignment, @@ -3536,29 +3406,27 @@ class TernaryGuidedLocalSearch : public GuidedLocalSearch { int index, int* container_index) const; const std::vector secondary_vars_; - std::unique_ptr objective_function_; + std::function objective_function_; }; TernaryGuidedLocalSearch::TernaryGuidedLocalSearch( Solver* const solver, IntVar* const objective, - Solver::IndexEvaluator3* objective_function, bool maximize, int64 step, - const std::vector& vars, const std::vector& secondary_vars, - double penalty_factor) + std::function objective_function, bool maximize, + int64 step, const std::vector& vars, + const std::vector& secondary_vars, double penalty_factor) : GuidedLocalSearch(solver, objective, maximize, step, vars, penalty_factor), secondary_vars_(secondary_vars), objective_function_(objective_function) { - objective_function_->CheckIsRepeatable(); if (!secondary_vars.empty()) { assignment_.Add(secondary_vars); } } IntExpr* TernaryGuidedLocalSearch::MakeElementPenalty(int index) { - return solver()->MakeElement( - NewPermanentCallback(this, &TernaryGuidedLocalSearch::PenalizedValue, - static_cast(index)), - vars_[index], secondary_vars_[index]); + return solver()->MakeElement([this, index](int64 i, int64 j) { + return PenalizedValue(index, i, j); + }, vars_[index], secondary_vars_[index]); } int64 TernaryGuidedLocalSearch::AssignmentElementPenalty( @@ -3569,8 +3437,8 @@ int64 TernaryGuidedLocalSearch::AssignmentElementPenalty( int64 TernaryGuidedLocalSearch::AssignmentPenalty(const Assignment& assignment, int index, int64 next) { - return objective_function_->Run(index, next, - assignment.Value(secondary_vars_[index])); + return objective_function_(index, next, + assignment.Value(secondary_vars_[index])); } bool TernaryGuidedLocalSearch::EvaluateElementValue( @@ -3590,9 +3458,9 @@ bool TernaryGuidedLocalSearch::EvaluateElementValue( int64 TernaryGuidedLocalSearch::PenalizedValue(int64 i, int64 j, int64 k) { const Arc arc(i, j); const int64 penalty = penalties_->Value(arc); - if (penalty != 0) { // objective_function_->Run(i, j, k) can be costly + if (penalty != 0) { // objective_function_(i, j, k) can be costly const int64 penalized_value = - penalty_factor_ * penalty * objective_function_->Run(i, j, k); + penalty_factor_ * penalty * objective_function_(i, j, k); if (maximize_) { return -penalized_value; } else { @@ -3620,7 +3488,7 @@ int64 TernaryGuidedLocalSearch::GetAssignmentSecondaryValue( SearchMonitor* Solver::MakeGuidedLocalSearch( bool maximize, IntVar* const objective, - ResultCallback2* objective_function, int64 step, + Solver::IndexEvaluator2 objective_function, int64 step, const std::vector& vars, double penalty_factor) { return RevAlloc(new BinaryGuidedLocalSearch(this, objective, objective_function, maximize, @@ -3629,7 +3497,7 @@ SearchMonitor* Solver::MakeGuidedLocalSearch( SearchMonitor* Solver::MakeGuidedLocalSearch( bool maximize, IntVar* const objective, - ResultCallback3* objective_function, int64 step, + Solver::IndexEvaluator3 objective_function, int64 step, const std::vector& vars, const std::vector& secondary_vars, double penalty_factor) { return RevAlloc(new TernaryGuidedLocalSearch( @@ -3786,10 +3654,10 @@ bool RegularLimit::Check() { int RegularLimit::ProgressPercent() { Solver* const s = solver(); int64 progress = GetPercent(s->branches(), branches_offset_, branches_); - progress = - std::max(progress, GetPercent(s->failures(), failures_offset_, failures_)); - progress = - std::max(progress, GetPercent(s->solutions(), solutions_offset_, solutions_)); + progress = std::max(progress, + GetPercent(s->failures(), failures_offset_, failures_)); + progress = std::max( + progress, GetPercent(s->solutions(), solutions_offset_, solutions_)); if (wall_time_ < kint64max) { progress = std::max(progress, (100 * TimeDelta()) / wall_time_); } @@ -3976,48 +3844,39 @@ int64 Solver::GetTime(SearchLimit* limit) { namespace { class CustomLimit : public SearchLimit { public: - CustomLimit(Solver* const s, ResultCallback* limiter, bool del); - ~CustomLimit() override; + CustomLimit(Solver* const s, std::function limiter); bool Check() override; void Init() override; void Copy(const SearchLimit* const limit) override; SearchLimit* MakeClone() const override; private: - ResultCallback* limiter_; - bool delete_; + std::function limiter_; }; -CustomLimit::CustomLimit(Solver* const s, ResultCallback* limiter, - bool del) - : SearchLimit(s), limiter_(limiter), delete_(del) { - limiter_->CheckIsRepeatable(); -} +CustomLimit::CustomLimit(Solver* const s, std::function limiter) + : SearchLimit(s), limiter_(limiter) {} -CustomLimit::~CustomLimit() { - if (delete_) { - delete limiter_; - } +bool CustomLimit::Check() { + if (limiter_) return limiter_(); + return false; } -bool CustomLimit::Check() { return limiter_->Run(); } - void CustomLimit::Init() {} void CustomLimit::Copy(const SearchLimit* const limit) { - const CustomLimit* const custom = reinterpret_cast(limit); - CHECK(!delete_) << "Cannot copy to non-cloned custom limit"; + const CustomLimit* const custom = + reinterpret_cast(limit); limiter_ = custom->limiter_; } SearchLimit* CustomLimit::MakeClone() const { - Solver* const s = solver(); - return s->RevAlloc(new CustomLimit(s, limiter_, false)); + return solver()->RevAlloc(new CustomLimit(solver(), limiter_)); } } // namespace -SearchLimit* Solver::MakeCustomLimit(ResultCallback* limiter) { - return RevAlloc(new CustomLimit(this, limiter, true)); +SearchLimit* Solver::MakeCustomLimit(std::function limiter) { + return RevAlloc(new CustomLimit(this, limiter)); } // ---------- SolveOnce ---------- diff --git a/src/constraint_solver/table.cc b/src/constraint_solver/table.cc index de2f72a1e0..a694162016 100644 --- a/src/constraint_solver/table.cc +++ b/src/constraint_solver/table.cc @@ -16,7 +16,7 @@ #include #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include @@ -871,7 +871,8 @@ class CompactPositiveTableConstraint : public BasePositiveTableConstraint { if (mask) { const int start = std::max(first_active_.Value(), starts_[var_index][value_index]); - const int end = std::min(ends_[var_index][value_index], last_active_.Value()); + const int end = + std::min(ends_[var_index][value_index], last_active_.Value()); for (int offset = start; offset <= end; ++offset) { temp_mask_[offset] |= mask[offset]; } @@ -1319,10 +1320,12 @@ class TransitionConstraint : public Constraint { int64 state_max = kint64min; const int nb_vars = vars_.size(); for (int i = 0; i < transition_table_.NumTuples(); ++i) { - state_max = std::max(state_max, transition_table_.Value(i, kStatePosition)); + state_max = + std::max(state_max, transition_table_.Value(i, kStatePosition)); state_max = std::max(state_max, transition_table_.Value(i, kNextStatePosition)); - state_min = std::min(state_min, transition_table_.Value(i, kStatePosition)); + state_min = + std::min(state_min, transition_table_.Value(i, kStatePosition)); state_min = std::min(state_min, transition_table_.Value(i, kNextStatePosition)); } diff --git a/src/constraint_solver/tree_monitor.cc b/src/constraint_solver/tree_monitor.cc index de1c3a87ce..7b98410dee 100644 --- a/src/constraint_solver/tree_monitor.cc +++ b/src/constraint_solver/tree_monitor.cc @@ -16,7 +16,7 @@ #include "base/hash.h" #include #include -#include "base/unique_ptr.h" +#include #include #include #include @@ -761,7 +761,7 @@ std::string TreeMonitor::StripSpecialCharacters(std::string attribute) { for (int i = 0; i < attribute.length(); ++i) { if (character_set.find(attribute[i]) == character_set.end()) { - attribute.replace(i,1,"_"); + attribute.replace(i, 1, "_"); } } diff --git a/src/constraint_solver/visitor.cc b/src/constraint_solver/visitor.cc index 9a3fc1344f..3b2b5afbc2 100644 --- a/src/constraint_solver/visitor.cc +++ b/src/constraint_solver/visitor.cc @@ -80,13 +80,13 @@ void ArgumentHolder::SetSequenceArrayArgument( sequence_array_argument_[arg_name] = vars; } -bool ArgumentHolder::HasIntegerExpressionArgument(const std::string& arg_name) - const { +bool ArgumentHolder::HasIntegerExpressionArgument( + const std::string& arg_name) const { return ContainsKey(integer_expression_argument_, arg_name); } -bool ArgumentHolder::HasIntegerVariableArrayArgument(const std::string& arg_name) - const { +bool ArgumentHolder::HasIntegerVariableArrayArgument( + const std::string& arg_name) const { return ContainsKey(integer_variable_array_argument_, arg_name); } diff --git a/src/flatzinc/constraints.cc b/src/flatzinc/constraints.cc index 83c47ed295..7e694db3b5 100644 --- a/src/flatzinc/constraints.cc +++ b/src/flatzinc/constraints.cc @@ -383,8 +383,7 @@ void ExtractBoolAnd(FzSolver* fzsolver, FzConstraint* ct) { void ExtractBoolClause(FzSolver* fzsolver, FzConstraint* ct) { Solver* const solver = fzsolver->solver(); - std::vector positive_variables = - fzsolver->GetVariableArray(ct->Arg(0)); + std::vector positive_variables = fzsolver->GetVariableArray(ct->Arg(0)); const std::vector negative_variables = fzsolver->GetVariableArray(ct->Arg(1)); std::vector vars; @@ -481,7 +480,7 @@ void ExtractCircuit(FzSolver* fzsolver, FzConstraint* ct) { const std::vector tmp_vars = fzsolver->GetVariableArray(ct->Arg(0)); const int size = tmp_vars.size(); bool found_zero = false; - bool found_size= false; + bool found_size = false; for (IntVar* const var : tmp_vars) { if (var->Min() == 0) { found_zero = true; @@ -831,10 +830,8 @@ void ExtractCumulative(FzSolver* fzsolver, FzConstraint* ct) { void ExtractDiffn(FzSolver* fzsolver, FzConstraint* ct) { Solver* const solver = fzsolver->solver(); - const std::vector x_variables = - fzsolver->GetVariableArray(ct->Arg(0)); - const std::vector y_variables = - fzsolver->GetVariableArray(ct->Arg(1)); + const std::vector x_variables = fzsolver->GetVariableArray(ct->Arg(0)); + const std::vector y_variables = fzsolver->GetVariableArray(ct->Arg(1)); if (ct->Arg(2).type == FzArgument::INT_LIST && ct->Arg(3).type == FzArgument::INT_LIST) { const std::vector& x_sizes = ct->Arg(2).values; @@ -875,26 +872,25 @@ void ExtractDiffnK(FzSolver* fzsolver, FzConstraint* ct) { void ExtractDiffnNonStrict(FzSolver* fzsolver, FzConstraint* ct) { Solver* const solver = fzsolver->solver(); - const std::vector x_variables = - fzsolver->GetVariableArray(ct->Arg(0)); - const std::vector y_variables = - fzsolver->GetVariableArray(ct->Arg(1)); - if (ct->Arg(2).type == FzArgument::INT_LIST && - ct->Arg(3).type == FzArgument::INT_LIST) { - const std::vector& x_sizes = ct->Arg(2).values; - const std::vector& y_sizes = ct->Arg(3).values; - Constraint* const constraint = - solver->MakeNonOverlappingNonStrictBoxesConstraint( - x_variables, y_variables, x_sizes, y_sizes); - AddConstraint(solver, ct, constraint); - } else { - const std::vector x_sizes = fzsolver->GetVariableArray(ct->Arg(2)); - const std::vector y_sizes = fzsolver->GetVariableArray(ct->Arg(3)); - Constraint* const constraint = - solver->MakeNonOverlappingNonStrictBoxesConstraint( - x_variables, y_variables, x_sizes, y_sizes); - AddConstraint(solver, ct, constraint); - } + const std::vector x_variables = fzsolver->GetVariableArray(ct->Arg(0)); + const std::vector y_variables = fzsolver->GetVariableArray(ct->Arg(1)); + if (ct->Arg(2).type == FzArgument::INT_LIST && + ct->Arg(3).type == FzArgument::INT_LIST) { + const std::vector& x_sizes = ct->Arg(2).values; + const std::vector& y_sizes = ct->Arg(3).values; + Constraint* const constraint = + solver->MakeNonOverlappingNonStrictBoxesConstraint( + x_variables, y_variables, x_sizes, y_sizes); + AddConstraint(solver, ct, constraint); + } else { + const std::vector x_sizes = fzsolver->GetVariableArray(ct->Arg(2)); + const std::vector y_sizes = fzsolver->GetVariableArray(ct->Arg(3)); + Constraint* const constraint = + solver->MakeNonOverlappingNonStrictBoxesConstraint( + x_variables, y_variables, x_sizes, y_sizes); + AddConstraint(solver, ct, constraint); + } + } void ExtractDiffnNonStrictK(FzSolver* fzsolver, FzConstraint* ct) { @@ -1549,8 +1545,7 @@ void ParseShortIntLin(FzSolver* fzsolver, FzConstraint* ct, IntExpr** left, } void ParseLongIntLin(FzSolver* fzsolver, FzConstraint* ct, - std::vector* vars, std::vector* coeffs, - int64* rhs) { + std::vector* vars, std::vector* coeffs, int64* rhs) { CHECK(vars != nullptr); CHECK(coeffs != nullptr); CHECK(rhs != nullptr); @@ -1573,8 +1568,8 @@ void ParseLongIntLin(FzSolver* fzsolver, FzConstraint* ct, } } -bool AreAllExtractedAsVariables( - FzSolver* const fzsolver, const std::vector& fz_vars) { +bool AreAllExtractedAsVariables(FzSolver* const fzsolver, + const std::vector& fz_vars) { for (FzIntegerVariable* const fz_var : fz_vars) { IntExpr* const expr = fzsolver->Extract(fz_var); if (!expr->IsVar()) { @@ -1835,8 +1830,7 @@ void ExtractIntLinGeReif(FzSolver* fzsolver, FzConstraint* ct) { } } -bool PostHiddenClause(SatPropagator* const sat, - const std::vector& coeffs, +bool PostHiddenClause(SatPropagator* const sat, const std::vector& coeffs, const std::vector& vars) { std::vector others; others.reserve(vars.size() - 1); @@ -2397,8 +2391,7 @@ void ExtractMinimumInt(FzSolver* fzsolver, FzConstraint* ct) { fzsolver->SetExtracted(ct->target_variable, target); } else { IntVar* const target = fzsolver->GetExpression(ct->Arg(0))->Var(); - const std::vector variables = - fzsolver->GetVariableArray(ct->Arg(1)); + const std::vector variables = fzsolver->GetVariableArray(ct->Arg(1)); Constraint* const constraint = solver->MakeMinEquality(variables, target); AddConstraint(solver, ct, constraint); } @@ -2645,7 +2638,7 @@ void ExtractSubCircuit(FzSolver* fzsolver, FzConstraint* ct) { const std::vector tmp_vars = fzsolver->GetVariableArray(ct->Arg(0)); const int size = tmp_vars.size(); bool found_zero = false; - bool found_size= false; + bool found_size = false; for (IntVar* const var : tmp_vars) { if (var->Min() == 0) { found_zero = true; diff --git a/src/flatzinc/presolve.h b/src/flatzinc/presolve.h index b2cbcf9b77..8f18b6ae54 100644 --- a/src/flatzinc/presolve.h +++ b/src/flatzinc/presolve.h @@ -172,8 +172,8 @@ class FzPresolver { std::pair> difference_map_; // Stores (x == y) == b - hash_map> int_eq_reif_map_; + hash_map> + int_eq_reif_map_; // Stores all variables defined in the search annotations. hash_set decision_variables_; diff --git a/src/flatzinc/search.cc b/src/flatzinc/search.cc index ee977e5051..e2b4948432 100644 --- a/src/flatzinc/search.cc +++ b/src/flatzinc/search.cc @@ -585,7 +585,7 @@ void FzSolver::Solve(FzSolverParameters p, SearchLimit* const shadow = limit == nullptr ? nullptr : solver()->MakeCustomLimit( - NewPermanentCallback(limit, &SearchLimit::Check)); + [limit]() { return limit->Check(); }); DecisionBuilder* const db = CreateDecisionBuilders(p, shadow); std::vector monitors; if (model_.objective() != nullptr) { diff --git a/src/flatzinc/solver.cc b/src/flatzinc/solver.cc index 7691a3e2ea..589d30a470 100644 --- a/src/flatzinc/solver.cc +++ b/src/flatzinc/solver.cc @@ -179,8 +179,8 @@ struct ConstraintWithIo { } std::string DebugString() const { - return StringPrintf("Ctio(%s, %d, deps_size = %lu)", - ct->type.c_str(), index, required.size()); + return StringPrintf("Ctio(%s, %d, deps_size = %lu)", ct->type.c_str(), + index, required.size()); } }; @@ -276,7 +276,7 @@ bool FzSolver::Extract() { // Recovery. We pick the last constraint (min number of required variable) // And we clean all of them (mark as non target). std::vector required_vars(ctio->required.begin(), - ctio->required.end()); + ctio->required.end()); for (FzIntegerVariable* const fz_var : required_vars) { FZDLOG << " - clean " << fz_var->DebugString() << FZENDL; if (fz_var->defining_constraint != nullptr) { diff --git a/src/glop/initial_basis.cc b/src/glop/initial_basis.cc index 51eef26bdb..75261e0e8f 100644 --- a/src/glop/initial_basis.cc +++ b/src/glop/initial_basis.cc @@ -184,9 +184,9 @@ bool InitialBasis::CompleteTriangularBasis(ColIndex num_cols, partial_diagonal_product *= coeff; if (fabs(partial_diagonal_product) < kMinimumProductMagnitude) { - LOG(WARNING) << "Numerical difficulties detected. The product of the " - << "diagonal coefficients is currently equal to " - << partial_diagonal_product; + LOG(INFO) << "Numerical difficulties detected. The product of the " + << "diagonal coefficients is currently equal to " + << partial_diagonal_product; break; } diff --git a/src/glop/lp_solver.h b/src/glop/lp_solver.h index f30d3a2f6f..b8d8cafac0 100644 --- a/src/glop/lp_solver.h +++ b/src/glop/lp_solver.h @@ -14,7 +14,7 @@ #ifndef OR_TOOLS_GLOP_LP_SOLVER_H_ #define OR_TOOLS_GLOP_LP_SOLVER_H_ -#include "base/unique_ptr.h" +#include #include "glop/parameters.pb.h" #include "glop/preprocessor.h" diff --git a/src/glop/preprocessor.h b/src/glop/preprocessor.h index 3781934fa5..6b8e7db308 100644 --- a/src/glop/preprocessor.h +++ b/src/glop/preprocessor.h @@ -21,7 +21,7 @@ #ifndef OR_TOOLS_GLOP_PREPROCESSOR_H_ #define OR_TOOLS_GLOP_PREPROCESSOR_H_ -#include "base/unique_ptr.h" +#include #include "glop/parameters.pb.h" #include "glop/revised_simplex.h" diff --git a/src/glop/proto_driver.cc b/src/glop/proto_driver.cc index f3e7eb4b2c..0758b055c2 100644 --- a/src/glop/proto_driver.cc +++ b/src/glop/proto_driver.cc @@ -18,7 +18,7 @@ #include #include -#include "base/unique_ptr.h" +#include #include #include "base/commandlineflags.h" diff --git a/src/graph/bellman_ford.cc b/src/graph/bellman_ford.cc index 545111c719..a0448500f1 100644 --- a/src/graph/bellman_ford.cc +++ b/src/graph/bellman_ford.cc @@ -12,7 +12,7 @@ // limitations under the License. -#include "base/unique_ptr.h" +#include #include #include "base/callback.h" diff --git a/src/graph/cliques.cc b/src/graph/cliques.cc index c4dbb25be9..1b80095e05 100644 --- a/src/graph/cliques.cc +++ b/src/graph/cliques.cc @@ -16,7 +16,7 @@ #include #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/graph/dijkstra.cc b/src/graph/dijkstra.cc index 1db9888ae2..c860d818c8 100644 --- a/src/graph/dijkstra.cc +++ b/src/graph/dijkstra.cc @@ -13,7 +13,7 @@ #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include "base/callback.h" diff --git a/src/graph/ebert_graph.h b/src/graph/ebert_graph.h index e99ca6aa34..cd15b73c48 100644 --- a/src/graph/ebert_graph.h +++ b/src/graph/ebert_graph.h @@ -170,7 +170,7 @@ #include #include -#include "base/unique_ptr.h" +#include #include #include #include diff --git a/src/graph/graph.h b/src/graph/graph.h index db743e02f9..d395d928d4 100644 --- a/src/graph/graph.h +++ b/src/graph/graph.h @@ -147,6 +147,7 @@ #include "base/integral_types.h" #include "base/logging.h" #include "base/macros.h" +#include "base/port.h" #include "util/iterators.h" @@ -317,6 +318,13 @@ class ListGraph : public BaseGraph { class OutgoingArcIterator; class OutgoingHeadIterator; + // Graph jargon: the "degree" of a node is its number of arcs. The out-degree + // is the number of outgoing arcs. The in-degree is the number of incoming + // arcs, and is only available for some graph implementations, below. + // + // ListGraph<>::OutDegree() works in O(degree). + ArcIndexType OutDegree(NodeIndexType node) const; + // Allows to iterate over the forward arcs that verify Tail(arc) == node. // This is meant to be used as: // for (const ArcIndex arc : graph.OutgoingArcs(node)) { ... } @@ -392,6 +400,7 @@ class StaticGraph : public BaseGraph { NodeIndexType Head(ArcIndexType arc) const; NodeIndexType Tail(ArcIndexType arc) const; + ArcIndexType OutDegree(NodeIndexType node) const; // Work in O(1). IntegerRange OutgoingArcs(NodeIndexType node) const; IntegerRange OutgoingArcsStartingFrom(NodeIndexType node, ArcIndexType from) const; @@ -416,6 +425,8 @@ class StaticGraph : public BaseGraph { private: ArcIndexType DirectArcLimit(NodeIndexType node) const { + DCHECK(is_built_); + DCHECK(Base::IsNodeValid(node)); return node + 1 < num_nodes_ ? start_[node + 1] : num_arcs_; } @@ -471,6 +482,10 @@ class ReverseArcListGraph class OutgoingArcIterator; class OutgoingHeadIterator; + // ReverseArcListGraph<>::OutDegree() and ::InDegree() work in O(degree). + ArcIndexType OutDegree(NodeIndexType node) const; + ArcIndexType InDegree(NodeIndexType node) const; + // Arc iterations functions over the arcs leaving a node (Tail(arc) == node). // To be used like: // for (const Graph::ArcIndex arc : IterationFunction(node)) { ... } @@ -553,6 +568,10 @@ class ReverseArcStaticGraph class IncomingArcIterator; class OutgoingArcIterator; + // ReverseArcStaticGraph<>::OutDegree() and ::InDegree() work in O(1). + ArcIndexType OutDegree(NodeIndexType node) const; + ArcIndexType InDegree(NodeIndexType node) const; + IntegerRange OutgoingArcs(NodeIndexType node) const; IntegerRange IncomingArcs(NodeIndexType node) const; BeginEndWrapper IncidentArcs(NodeIndexType node) const; @@ -584,9 +603,13 @@ class ReverseArcStaticGraph private: ArcIndexType DirectArcLimit(NodeIndexType node) const { + DCHECK(is_built_); + DCHECK(Base::IsNodeValid(node)); return node + 1 < num_nodes_ ? start_[node + 1] : num_arcs_; } ArcIndexType ReverseArcLimit(NodeIndexType node) const { + DCHECK(is_built_); + DCHECK(Base::IsNodeValid(node)); return node + 1 < num_nodes_ ? reverse_start_[node + 1] : 0; } @@ -629,6 +652,9 @@ class ReverseArcMixedGraph class IncomingArcIterator; class OutgoingArcIterator; + ArcIndexType OutDegree(NodeIndexType node) const; // O(1) + ArcIndexType InDegree(NodeIndexType node) const; // O(in-degree) + IntegerRange OutgoingArcs(NodeIndexType node) const; BeginEndWrapper IncomingArcs(NodeIndexType node) const; BeginEndWrapper IncidentArcs(NodeIndexType node) const; @@ -660,6 +686,8 @@ class ReverseArcMixedGraph private: ArcIndexType DirectArcLimit(NodeIndexType node) const { + DCHECK(is_built_); + DCHECK(Base::IsNodeValid(node)); return node + 1 < num_nodes_ ? start_[node + 1] : num_arcs_; } @@ -1039,6 +1067,14 @@ NodeIndexType ListGraph::Head( return head_[arc]; } +template +ArcIndexType ListGraph::OutDegree( + NodeIndexType node) const { + ArcIndexType degree(0); + for (auto arc ATTRIBUTE_UNUSED : OutgoingArcs(node)) ++degree; + return degree; +} + template void ListGraph::AddNode(NodeIndexType node) { if (node < num_nodes_) return; @@ -1050,6 +1086,8 @@ void ListGraph::AddNode(NodeIndexType node) { template ArcIndexType ListGraph::AddArc( NodeIndexType tail, NodeIndexType head) { + DCHECK_GE(tail, 0); + DCHECK_GE(head, 0); AddNode(tail > head ? tail : head); head_.push_back(head); next_.push_back(start_[tail]); @@ -1193,6 +1231,12 @@ StaticGraph::OutgoingArcsStartingFrom( return IntegerRange(from, DirectArcLimit(node)); } +template +ArcIndexType StaticGraph::OutDegree( + NodeIndexType node) const { + return DirectArcLimit(node) - start_[node]; +} + template void StaticGraph::ReserveNodes( NodeIndexType bound) { @@ -1226,6 +1270,8 @@ void StaticGraph::AddNode(NodeIndexType node) { template ArcIndexType StaticGraph::AddArc( NodeIndexType tail, NodeIndexType head) { + DCHECK_GE(tail, 0); + DCHECK_GE(head, 0); DCHECK(!is_built_); AddNode(tail > head ? tail : head); if (arc_in_order_) { @@ -1403,14 +1449,11 @@ class StaticGraph::OutgoingArcIterator OutgoingArcIterator(const StaticGraph& graph, NodeIndexType node) : Base::BaseStaticArcIterator(graph.start_[node], graph.DirectArcLimit(node)) { - DCHECK(graph.is_built_); DCHECK(graph.IsNodeValid(node)); } OutgoingArcIterator(const StaticGraph& graph, NodeIndexType node, ArcIndexType arc) : Base::BaseStaticArcIterator(arc, graph.DirectArcLimit(node)) { - DCHECK(graph.is_built_); - DCHECK(graph.IsNodeValid(node)); DCHECK_GE(arc, graph.start_[node]); } }; @@ -1431,6 +1474,22 @@ ReverseArcListGraph::operator[]( OutgoingHeadIterator(*this, node, Base::kNilArc)); } +template +ArcIndexType ReverseArcListGraph::OutDegree( + NodeIndexType node) const { + ArcIndexType degree(0); + for (auto arc ATTRIBUTE_UNUSED : OutgoingArcs(node)) ++degree; + return degree; +} + +template +ArcIndexType ReverseArcListGraph::InDegree( + NodeIndexType node) const { + ArcIndexType degree(0); + for (auto arc ATTRIBUTE_UNUSED : IncomingArcs(node)) ++degree; + return degree; +} + template ArcIndexType ReverseArcListGraph::OppositeArc( ArcIndexType arc) const { @@ -1486,6 +1545,8 @@ void ReverseArcListGraph::AddNode( template ArcIndexType ReverseArcListGraph::AddArc( NodeIndexType tail, NodeIndexType head) { + DCHECK_GE(tail, 0); + DCHECK_GE(head, 0); AddNode(tail > head ? tail : head); head_.grow(tail, head); next_.grow(reverse_start_[head], start_[tail]); @@ -1626,6 +1687,18 @@ class ReverseArcListGraph::OutgoingHeadIterator { // ReverseArcStaticGraph definition ------------------------------------------ +template +ArcIndexType ReverseArcStaticGraph::OutDegree( + NodeIndexType node) const { + return DirectArcLimit(node) - start_[node]; +} + +template +ArcIndexType ReverseArcStaticGraph::InDegree( + NodeIndexType node) const { + return ReverseArcLimit(node) - reverse_start_[node]; +} + template IntegerRange ReverseArcStaticGraph< NodeIndexType, ArcIndexType>::OutgoingArcs(NodeIndexType node) const { @@ -1716,6 +1789,8 @@ void ReverseArcStaticGraph::AddNode( template ArcIndexType ReverseArcStaticGraph::AddArc( NodeIndexType tail, NodeIndexType head) { + DCHECK_GE(tail, 0); + DCHECK_GE(head, 0); AddNode(tail > head ? tail : head); // We inverse head and tail here because it is more convenient this way @@ -1777,14 +1852,11 @@ class ReverseArcStaticGraph::OutgoingArcIterator OutgoingArcIterator(const ReverseArcStaticGraph& graph, NodeIndexType node) : Base::BaseStaticArcIterator(graph.start_[node], graph.DirectArcLimit(node)) { - DCHECK(graph.is_built_); DCHECK(graph.IsNodeValid(node)); } OutgoingArcIterator(const ReverseArcStaticGraph& graph, NodeIndexType node, ArcIndexType arc) : Base::BaseStaticArcIterator(arc, graph.DirectArcLimit(node)) { - DCHECK(graph.is_built_); - DCHECK(graph.IsNodeValid(node)); DCHECK_GE(arc, graph.start_[node]); } }; @@ -1796,14 +1868,10 @@ class ReverseArcStaticGraph::IncomingArcIterator IncomingArcIterator(const ReverseArcStaticGraph& graph, NodeIndexType node) : Base::BaseStaticArcIterator(graph.reverse_start_[node], graph.ReverseArcLimit(node)) { - DCHECK(graph.is_built_); - DCHECK(graph.IsNodeValid(node)); } IncomingArcIterator(const ReverseArcStaticGraph& graph, NodeIndexType node, ArcIndexType arc) : Base::BaseStaticArcIterator(arc, graph.ReverseArcLimit(node)) { - DCHECK(graph.is_built_); - DCHECK(graph.IsNodeValid(node)); DCHECK_GE(arc, graph.reverse_start_[node]); } }; @@ -1821,7 +1889,6 @@ class ReverseArcStaticGraph::IncidentArcIterator next_start_(graph.start_[node]), first_limit_(graph.ReverseArcLimit(node)) { if (index_ == first_limit_) index_ = next_start_; - DCHECK(graph.IsNodeValid(node)); DCHECK((index_ >= graph.reverse_start_[node] && index_ < first_limit_) || (index_ >= next_start_)); } @@ -1830,7 +1897,6 @@ class ReverseArcStaticGraph::IncidentArcIterator : Base::BaseStaticArcIterator(arc, graph.DirectArcLimit(node)), next_start_(graph.start_[node]), first_limit_(graph.ReverseArcLimit(node)) { - DCHECK(graph.IsNodeValid(node)); DCHECK((index_ >= graph.reverse_start_[node] && index_ < first_limit_) || (index_ >= next_start_)); } @@ -1851,6 +1917,20 @@ class ReverseArcStaticGraph::IncidentArcIterator // ReverseArcMixedGraph definition ------------------------------------------- +template +ArcIndexType ReverseArcMixedGraph::OutDegree( + NodeIndexType node) const { + return DirectArcLimit(node) - start_[node]; +} + +template +ArcIndexType ReverseArcMixedGraph::InDegree( + NodeIndexType node) const { + ArcIndexType degree(0); + for (auto arc ATTRIBUTE_UNUSED : IncomingArcs(node)) ++degree; + return degree; +} + template IntegerRange ReverseArcMixedGraph< NodeIndexType, ArcIndexType>::OutgoingArcs(NodeIndexType node) const { @@ -1927,6 +2007,8 @@ void ReverseArcMixedGraph::AddNode( template ArcIndexType ReverseArcMixedGraph::AddArc( NodeIndexType tail, NodeIndexType head) { + DCHECK_GE(tail, 0); + DCHECK_GE(head, 0); AddNode(tail > head ? tail : head); // We inverse head and tail here because it is more convenient this way @@ -1970,14 +2052,10 @@ class ReverseArcMixedGraph::OutgoingArcIterator OutgoingArcIterator(const ReverseArcMixedGraph& graph, NodeIndexType node) : Base::BaseStaticArcIterator(graph.start_[node], graph.DirectArcLimit(node)) { - DCHECK(graph.is_built_); - DCHECK(graph.IsNodeValid(node)); } OutgoingArcIterator(const ReverseArcMixedGraph& graph, NodeIndexType node, ArcIndexType arc) : Base::BaseStaticArcIterator(arc, graph.DirectArcLimit(node)) { - DCHECK(graph.is_built_); - DCHECK(graph.IsNodeValid(node)); DCHECK_GE(arc, graph.start_[node]); } }; @@ -2019,11 +2097,9 @@ class ReverseArcMixedGraph::IncidentArcIterator { public: IncidentArcIterator(const ReverseArcMixedGraph& graph, NodeIndexType node) : graph_(&graph) { - DCHECK(graph.is_built_); - DCHECK(graph.IsNodeValid(node)); + limit_ = graph.DirectArcLimit(node); // also DCHECKs node and is_built_. index_ = graph.reverse_start_[node]; restart_ = graph.start_[node]; - limit_ = graph.DirectArcLimit(node); if (index_ == Base::kNilArc) { index_ = restart_; } @@ -2031,11 +2107,9 @@ class ReverseArcMixedGraph::IncidentArcIterator { IncidentArcIterator(const ReverseArcMixedGraph& graph, NodeIndexType node, ArcIndexType arc) : graph_(&graph) { - DCHECK(graph.is_built_); - DCHECK(graph.IsNodeValid(node)); + limit_ = graph.DirectArcLimit(node); index_ = arc; restart_ = graph.start_[node]; - limit_ = graph.DirectArcLimit(node); DCHECK(arc == Base::kNilArc || arc == limit_ || graph.Tail(arc) == node); } bool Ok() const { diff --git a/src/graph/hamiltonian_path.h b/src/graph/hamiltonian_path.h index 3db80e6aa1..e9982bfa96 100644 --- a/src/graph/hamiltonian_path.h +++ b/src/graph/hamiltonian_path.h @@ -85,7 +85,7 @@ #include #include #include -#include "base/unique_ptr.h" +#include #include #include #include diff --git a/src/graph/linear_assignment.h b/src/graph/linear_assignment.h index 28c5dd9e1c..f78777e757 100644 --- a/src/graph/linear_assignment.h +++ b/src/graph/linear_assignment.h @@ -200,7 +200,7 @@ #include #include #include -#include "base/unique_ptr.h" +#include #include #include #include diff --git a/src/graph/max_flow.h b/src/graph/max_flow.h index 7d4f7dd3ed..5f32783cad 100644 --- a/src/graph/max_flow.h +++ b/src/graph/max_flow.h @@ -115,7 +115,7 @@ #define OR_TOOLS_GRAPH_MAX_FLOW_H_ #include -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/graph/shortestpaths.cc b/src/graph/shortestpaths.cc index 045053709c..49bb514e47 100644 --- a/src/graph/shortestpaths.cc +++ b/src/graph/shortestpaths.cc @@ -18,7 +18,7 @@ #include #include #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include "base/callback.h" diff --git a/src/graph/shortestpaths.h b/src/graph/shortestpaths.h index 47e0b0bbcf..28df590753 100644 --- a/src/graph/shortestpaths.h +++ b/src/graph/shortestpaths.h @@ -19,7 +19,7 @@ #ifndef OR_TOOLS_GRAPH_SHORTESTPATHS_H_ #define OR_TOOLS_GRAPH_SHORTESTPATHS_H_ -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/graph/util.h b/src/graph/util.h index a92470e485..730c97ed17 100644 --- a/src/graph/util.h +++ b/src/graph/util.h @@ -17,7 +17,7 @@ #define OR_TOOLS_GRAPH_UTIL_H_ #include -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/linear_solver/cbc_interface.cc b/src/linear_solver/cbc_interface.cc index fe10d00e00..5ea49a782d 100644 --- a/src/linear_solver/cbc_interface.cc +++ b/src/linear_solver/cbc_interface.cc @@ -15,7 +15,7 @@ #include "base/hash.h" #include -#include "base/unique_ptr.h" +#include #include #include #include diff --git a/src/linear_solver/clp_interface.cc b/src/linear_solver/clp_interface.cc index 04c4c9de2f..3575f7b4a4 100644 --- a/src/linear_solver/clp_interface.cc +++ b/src/linear_solver/clp_interface.cc @@ -15,7 +15,7 @@ #include #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/linear_solver/cplex_interface.cc b/src/linear_solver/cplex_interface.cc index bb61ed8db8..d05ee567e3 100644 --- a/src/linear_solver/cplex_interface.cc +++ b/src/linear_solver/cplex_interface.cc @@ -14,8 +14,8 @@ // Initial version of this code was written by Daniel Junglas (IBM) #include +#include -#include "base/unique_ptr.h" #include "base/integral_types.h" #include "base/logging.h" #include "base/stringprintf.h" diff --git a/src/linear_solver/glpk_interface.cc b/src/linear_solver/glpk_interface.cc index 456e5adaf4..4639dd9ee9 100644 --- a/src/linear_solver/glpk_interface.cc +++ b/src/linear_solver/glpk_interface.cc @@ -19,7 +19,7 @@ #include #include "base/hash.h" #include -#include "base/unique_ptr.h" +#include #include #include #include diff --git a/src/linear_solver/gurobi_interface.cc b/src/linear_solver/gurobi_interface.cc index 3f62ca857c..2d3969034a 100644 --- a/src/linear_solver/gurobi_interface.cc +++ b/src/linear_solver/gurobi_interface.cc @@ -17,7 +17,7 @@ #include #include "base/hash.h" #include -#include "base/unique_ptr.h" +#include #include #include #include diff --git a/src/linear_solver/java/linear_solver.swig b/src/linear_solver/java/linear_solver.swig index d14aa93dfd..e1de70bd47 100644 --- a/src/linear_solver/java/linear_solver.swig +++ b/src/linear_solver/java/linear_solver.swig @@ -16,7 +16,7 @@ // // The java API is pretty much identical to the C++ API, with methods // systematically renamed to the Java-style "lowerCamelCase", and using -// the Java-stype getProperty() instead of the C++ Property(), for getters. +// the Java-style getProperty() instead of the C++ Property(), for getters. // // USAGE EXAMPLES (j/c/g and jt/c/g refer to java/com/google and javatests/...): // - j/c/g/ortools/samples/LinearProgramming.java diff --git a/src/linear_solver/linear_solver.cc b/src/linear_solver/linear_solver.cc index 7df4187fd2..e2edff262f 100644 --- a/src/linear_solver/linear_solver.cc +++ b/src/linear_solver/linear_solver.cc @@ -368,11 +368,9 @@ extern MPSolverInterface* BuildGurobiInterface(bool mip, MPSolver* const solver); #endif #if defined(USE_CPLEX) -extern MPSolverInterface* BuildCplexInterface(bool mip, - MPSolver* const solver); +extern MPSolverInterface* BuildCplexInterface(bool mip, MPSolver* const solver); #endif - #ifdef ANDROID_JNI extern MPSolverInterface* BuildGLOPInterface(MPSolver* const solver); #endif @@ -619,6 +617,9 @@ void MPSolver::SolveWithProto(const MPModelRequest& model_request, const MPModelProto& model = model_request.model(); MPSolver solver(model.name(), static_cast( model_request.solver_type())); + if (model_request.enable_internal_solver_output()) { + solver.EnableOutput(); + } std::string error_message; response->set_status(solver.LoadModelFromProto(model, &error_message)); if (response->status() != MPSOLVER_MODEL_IS_VALID) { @@ -1099,8 +1100,8 @@ bool MPSolver::VerifySolution(double tolerance, bool log_errors) const { if (!AreWithinAbsoluteOrRelativeTolerances( objective.Value(), actual_objective_value, tolerance, tolerance)) { ++num_errors; - max_observed_error = std::max(max_observed_error, - fabs(actual_objective_value - objective.Value())); + max_observed_error = std::max( + max_observed_error, fabs(actual_objective_value - objective.Value())); LOG_IF(ERROR, log_errors) << "Objective value " << objective.Value() << " isn't accurate" << ", it should be " << actual_objective_value diff --git a/src/linear_solver/linear_solver.h b/src/linear_solver/linear_solver.h index 14c6375497..0c8f4a2b8c 100644 --- a/src/linear_solver/linear_solver.h +++ b/src/linear_solver/linear_solver.h @@ -136,7 +136,7 @@ #include "base/hash.h" #include #include -#include "base/unique_ptr.h" +#include #include #include #include @@ -209,7 +209,7 @@ class MPSolver { #if defined(USE_CPLEX) CPLEX_MIXED_INTEGER_PROGRAMMING = 11, #endif - #if defined(USE_BOP) + #if defined(USE_BOP) BOP_INTEGER_PROGRAMMING = 12, #endif }; @@ -534,6 +534,7 @@ class MPSolver { friend class MPSolverInterface; friend class GLOPInterface; friend class BopInterface; + friend class KnapsackInterface; // Debugging: verify that the given MPVariable* belongs to this solver. bool OwnsVariable(const MPVariable* var) const; @@ -678,6 +679,7 @@ class MPObjective { friend class CplexInterface; friend class GLOPInterface; friend class BopInterface; + friend class KnapsackInterface; // Constructor. An objective points to a single MPSolverInterface // that is specified in the constructor. An objective cannot belong @@ -752,6 +754,7 @@ class MPVariable { friend class GLOPInterface; friend class MPVariableSolutionValueTest; friend class BopInterface; + friend class KnapsackInterface; // Constructor. A variable points to a single MPSolverInterface that // is specified in the constructor. A variable cannot belong to @@ -847,6 +850,7 @@ class MPConstraint { friend class CplexInterface; friend class GLOPInterface; friend class BopInterface; + friend class KnapsackInterface; // Constructor. A constraint points to a single MPSolverInterface // that is specified in the constructor. A constraint cannot belong diff --git a/src/linear_solver/linear_solver.proto b/src/linear_solver/linear_solver.proto index a4fa3b2084..32f76f8f62 100644 --- a/src/linear_solver/linear_solver.proto +++ b/src/linear_solver/linear_solver.proto @@ -159,6 +159,7 @@ message MPModelRequest { CPLEX_MIXED_INTEGER_PROGRAMMING = 11; // Commercial, needs a valid license. BOP_INTEGER_PROGRAMMING = 12; + KNAPSACK_MIXED_INTEGER_PROGRAMMING = 13; } optional SolverType solver_type = 2; @@ -172,6 +173,12 @@ message MPModelRequest { // // If not specified, the time limit on the solver is the RPC's deadline_left. optional double solver_time_limit_seconds = 3; + + // If this is set, then EnableOutput() will be set on the internal MPSolver + // that solves the model. + // WARNING: if you set this on a request to prod servers, it will be rejected + // and yield the RPC Application Error code MPSOLVER_SOLVER_TYPE_UNAVAILABLE. + optional bool enable_internal_solver_output = 4 [default = false]; } // Status returned by the solver. They follow a hierarchical nomenclature, to diff --git a/src/linear_solver/python/linear_solver.swig b/src/linear_solver/python/linear_solver.swig index 4af3eaa2cd..995ff59ed9 100644 --- a/src/linear_solver/python/linear_solver.swig +++ b/src/linear_solver/python/linear_solver.swig @@ -362,9 +362,15 @@ from ortools.linear_solver.linear_solver_natural_api import LinearConstraint %unignore operations_research::MPSolverParameters; %unignore operations_research::MPSolverParameters::MPSolverParameters; %unignore operations_research::MPSolverParameters::DoubleParam; -%unignore operations_research::MPSolverParameters::RELATIVE_MIP_GAP; +%unignore operations_research::MPSolverParameters::GetDoubleParam; %unignore operations_research::MPSolverParameters::SetDoubleParam; +%unignore operations_research::MPSolverParameters::IntegerParam; +%unignore operations_research::MPSolverParameters::GetIntegerParam; +%unignore operations_research::MPSolverParameters::SetIntegerParam; +%unignore operations_research::MPSolverParameters::PRESOLVE; +%unignore operations_research::MPSolverParameters::RELATIVE_MIP_GAP; %unignore operations_research::MPSolverParameters::kDefaultPrimalTolerance; +// TODO(user): unit test kDefaultPrimalTolerance. // We want to ignore the CoeffMap class; but since it inherits from some // hash_map<>, swig complains about an undefined base class. Silence it. diff --git a/src/linear_solver/scip_interface.cc b/src/linear_solver/scip_interface.cc index 57740250e4..174586eef2 100644 --- a/src/linear_solver/scip_interface.cc +++ b/src/linear_solver/scip_interface.cc @@ -18,7 +18,7 @@ #include #include #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/linear_solver/sulum_interface.cc b/src/linear_solver/sulum_interface.cc index 30cec47061..fc12bdfd67 100644 --- a/src/linear_solver/sulum_interface.cc +++ b/src/linear_solver/sulum_interface.cc @@ -16,6 +16,7 @@ #include #include "base/hash.h" #include +#include #include #include #include @@ -27,7 +28,6 @@ #include "base/logging.h" #include "base/stringprintf.h" #include "base/timer.h" -#include "base/unique_ptr.h" #include "base/hash.h" #include "linear_solver/linear_solver.h" diff --git a/src/lp_data/lp_decomposer.h b/src/lp_data/lp_decomposer.h index 8a51fe6eee..df81ac70b9 100644 --- a/src/lp_data/lp_decomposer.h +++ b/src/lp_data/lp_decomposer.h @@ -14,7 +14,7 @@ #ifndef OR_TOOLS_LP_DATA_LP_DECOMPOSER_H_ #define OR_TOOLS_LP_DATA_LP_DECOMPOSER_H_ -#include "base/unique_ptr.h" +#include #include #include "base/mutex.h" diff --git a/src/lp_data/mps_reader.cc b/src/lp_data/mps_reader.cc index 95afb4e299..ba2bc3d2cf 100644 --- a/src/lp_data/mps_reader.cc +++ b/src/lp_data/mps_reader.cc @@ -16,7 +16,7 @@ #include #include -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/sat/clause.cc b/src/sat/clause.cc index b10436edec..10dd3d5a72 100644 --- a/src/sat/clause.cc +++ b/src/sat/clause.cc @@ -15,7 +15,7 @@ #include #include -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/sat/clause.h b/src/sat/clause.h index 8deaf875aa..a476ae8036 100644 --- a/src/sat/clause.h +++ b/src/sat/clause.h @@ -18,7 +18,7 @@ #define OR_TOOLS_SAT_CLAUSE_H_ #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include #include diff --git a/src/sat/pb_constraint.cc b/src/sat/pb_constraint.cc index 67ed3b1684..28921c90a7 100644 --- a/src/sat/pb_constraint.cc +++ b/src/sat/pb_constraint.cc @@ -422,7 +422,7 @@ UpperBoundedLinearConstraint::UpperBoundedLinearConstraint( starts_.push_back(literals_.size()); hash_ = ThoroughHash(reinterpret_cast(cst.data()), - cst.size() * sizeof(LiteralWithCoeff)); + cst.size() * sizeof(LiteralWithCoeff)); } void UpperBoundedLinearConstraint::AddToConflict( diff --git a/src/sat/sat_base.h b/src/sat/sat_base.h index aceb2a2187..a7a2d20500 100644 --- a/src/sat/sat_base.h +++ b/src/sat/sat_base.h @@ -16,7 +16,7 @@ #ifndef OR_TOOLS_SAT_SAT_BASE_H_ #define OR_TOOLS_SAT_SAT_BASE_H_ -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/sat/sat_solver.cc b/src/sat/sat_solver.cc index 29068aa370..4138d5c17a 100644 --- a/src/sat/sat_solver.cc +++ b/src/sat/sat_solver.cc @@ -14,7 +14,7 @@ #include "sat/sat_solver.h" #include -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 1dde39c043..7aa6b3d881 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -20,7 +20,7 @@ #define OR_TOOLS_SAT_SAT_SOLVER_H_ #include "base/hash.h" -#include "base/unique_ptr.h" +#include #include #include #include diff --git a/src/util/csharp/functions.swig b/src/util/csharp/functions.swig new file mode 100644 index 0000000000..316461e684 --- /dev/null +++ b/src/util/csharp/functions.swig @@ -0,0 +1,59 @@ +// 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. + +// This file provides swig wrapping for some specialization of std::function +// parameters. Currently, swig does not support much of C++11 features, and +// especially not the std::function. + +// C# callers will need to use a specific "type" of callbacks: they must +// specialize one of the existing generic callback classes defined in +// ../functions_swig_helpers.h (which are SWIG-wrapped to C# in a +// straightforward way). See the examples. + +%include "base/base.swig" + +%include "std_common.i" +%include "std_string.i" + +%{ +#include +#include "base/integral_types.h" +#include "util/functions_swig_helpers.h" +%} + +%module(directors="1") operations_research_swig_util + +// --------- Include the swig helpers file to create the director classes ------ +// We cannot use %ignoreall/%unignoreall as this is not compatible with nested +// swig files. + +%feature("director") operations_research::swig_util::LongToLong; +%rename (Run) operations_research::swig_util::LongToLong::run; +%feature("director") operations_research::swig_util::LongLongToLong; +%rename (Run) operations_research::swig_util::LongLongToLong::run; +%feature("director") operations_research::swig_util::IntIntToLong; +%rename (Run) operations_research::swig_util::IntIntToLong::run; +%feature("director") operations_research::swig_util::LongLongLongToLong; +%rename (Run) operations_research::swig_util::LongLongLongToLong::run; +%feature("director") operations_research::swig_util::LongToBoolean; +%rename (Run) operations_research::swig_util::LongToBoolean::run; +%feature("director") operations_research::swig_util::VoidToString; +%rename (Run) operations_research::swig_util::VoidToString::run; +%feature("director") operations_research::swig_util::VoidToBoolean; +%rename (Run) operations_research::swig_util::VoidToBoolean::run; +%feature("director") operations_research::swig_util::LongLongLongToBoolean; +%rename (Run) operations_research::swig_util::LongLongLongToBoolean::run; +%feature("director") operations_research::swig_util::LongToVoid; +%rename (Run) operations_research::swig_util::LongToVoid::run; + +%include "util/functions_swig_helpers.h" diff --git a/src/util/functions_swig_helpers.h b/src/util/functions_swig_helpers.h new file mode 100644 index 0000000000..4683c562c4 --- /dev/null +++ b/src/util/functions_swig_helpers.h @@ -0,0 +1,139 @@ +// 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. + +#ifndef OR_TOOLS_UTIL_FUNCTIONS_SWIG_HELPERS_H_ +#define OR_TOOLS_UTIL_FUNCTIONS_SWIG_HELPERS_H_ + +// This file contains class definitions for the wrapping of C++ std::functions +// in Java and C#. It is #included by java/functions.swig and +// csharp/functions.swig. + +#include +#include + +#include "base/integral_types.h" + +namespace operations_research { +namespace swig_util { +class LongToLong { + public: + virtual ~LongToLong() {} + virtual int64 run(int64) = 0; +#if !defined(SWIG) + std::function GetFunction() { + return [this](int64 i) { return run(i); }; + } +#endif +}; + +class LongLongToLong { + public: + virtual ~LongLongToLong() {} + virtual int64 run(int64, int64) = 0; +#if !defined(SWIG) + std::function GetFunction() { + return [this](int64 i, int64 j) { return run(i, j); }; + } +#endif +}; + +class IntIntToLong { + public: + virtual ~IntIntToLong() {} + virtual int64 run(int, int) = 0; +#if !defined(SWIG) + std::function GetFunction() { + return [this](int i, int j) { return run(i, j); }; + } +#endif +}; + +class LongLongLongToLong { + public: + virtual ~LongLongLongToLong() {} + virtual int64 run(int64, int64, int64) = 0; +#if !defined(SWIG) + std::function GetFunction() { + return [this](int64 i, int64 j, int64 k) { return run(i, j, k); }; + } +#endif +}; + +class LongToBoolean { + public: + virtual ~LongToBoolean() {} + virtual bool run(int64) = 0; +#if !defined(SWIG) + std::function GetFunction() { + return [this](int64 i) { return run(i); }; + } +#endif +}; + +class VoidToString { + public: + virtual ~VoidToString() {} + virtual std::string run() = 0; +#if !defined(SWIG) + std::function GetFunction() { + return [this]() { return run(); }; + } +#endif +}; + +class VoidToBoolean { + public: + virtual ~VoidToBoolean() {} + virtual bool run() = 0; +#if !defined(SWIG) + std::function GetFunction() { + return [this]() { return run(); }; + } +#endif +}; + +class LongLongLongToBoolean { + public: + virtual ~LongLongLongToBoolean() {} + virtual bool run(int64 i, int64 j, int64 k) = 0; +#if !defined(SWIG) + std::function GetFunction() { + return [this](int64 i, int64 j, int64 k) { return run(i, j, k); }; + } +#endif +}; + +class LongToVoid { + public: + virtual ~LongToVoid() {} + virtual void run(int64 i) = 0; +#if !defined(SWIG) + std::function GetFunction() { + return [this](int64 i) { run(i); }; + } +#endif +}; + +class VoidToVoid { + public: + virtual ~VoidToVoid() {} + virtual void run() = 0; +#if !defined(SWIG) + std::function GetFunction() { + return [this]() { run(); }; + } +#endif +}; +} // namespace swig_util +} // namespace operations_research +#endif // OR_TOOLS_UTIL_FUNCTIONS_SWIG_HELPERS_H_ diff --git a/src/util/functions_swig_test_helpers.h b/src/util/functions_swig_test_helpers.h new file mode 100644 index 0000000000..7b1e86f4fb --- /dev/null +++ b/src/util/functions_swig_test_helpers.h @@ -0,0 +1,54 @@ +// 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. + +#ifndef OR_TOOLS_UTIL_FUNCTIONS_SWIG_TEST_HELPERS_H_ +#define OR_TOOLS_UTIL_FUNCTIONS_SWIG_TEST_HELPERS_H_ + +// These are simple static methods to test std::function in the swig wrapper +// tests. +// +// NOTE: It was simpler to use static methods of a public class rather than +// simple static methods; because the Java wrapping of the latter made them hard +// to find (whereas the class methods are easy to find). + +#include +#include "base/integral_types.h" + +namespace operations_research { +class FunctionSwigTestHelpers { + public: + static std::string NoOpVoidToString(std::function fun) { return fun(); } + + static int64 NoOpInt64ToInt64(std::function fun, int64 x) { + return fun(x); + } + + static int64 NoOpInt64PairToInt64(std::function fun, + int64 x, int64 y) { + return fun(x, y); + } + + static int64 NoOpInt64TripleToInt64( + std::function fun, int64 x, int64 y, + int64 z) { + return fun(x, y, z); + } + + static bool NoOpInt64ToBool(std::function fun, int64 x) { + return fun(x); + } + + static bool NoOpVoidToBool(std::function fun) { return fun(); } +}; +} // namespace operations_research +#endif // OR_TOOLS_UTIL_FUNCTIONS_SWIG_TEST_HELPERS_H_ diff --git a/src/util/graph_export.cc b/src/util/graph_export.cc index e259ec210d..f866b32bd1 100644 --- a/src/util/graph_export.cc +++ b/src/util/graph_export.cc @@ -14,7 +14,7 @@ #include "util/graph_export.h" -#include "base/unique_ptr.h" +#include #include "base/logging.h" #include "base/macros.h" diff --git a/src/util/java/functions.swig b/src/util/java/functions.swig new file mode 100644 index 0000000000..88339b556c --- /dev/null +++ b/src/util/java/functions.swig @@ -0,0 +1,216 @@ +// 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. + +// This file provides swig wrapping for some specialization of std::function +// parameters. Currently, swig does not support much of C++11 features, and +// especially not the std::function. + +// Java callers will need to use a specific "type" of callbacks: they must +// specialize one of the existing generic callback classes defined in +// ../functions_swig_helpers.h (which are SWIG-wrapped to Java in a +// straightforward way). See the examples. + +%include "base/base.swig" + +%include "std_common.i" +%include "std_string.i" + +%{ +#include +#include "base/integral_types.h" +#include "util/functions_swig_helpers.h" +%} + + +#define PARENTHIZE(ReturnType, Args...) ReturnType(Args) +#define CAT3(a, b, c) a ## b ## c + +// The C preprocessor macros below use some tricks that make them work only if +// the actual C preprocessor expands them (not the SWIG preprocessor). +%{ +// NAMES(int64, bool, int) expands to: , i0, i1, i2 +#define NAMES_0 +#define NAMES_1 i0 +#define NAMES_2 i0, i1 +#define NAMES_3 i0, i1, i2 +#define NAMES(num) NAMES_ ## num + +// INSERT_NAMES(int64, bool, int) expands to: int64 i0, bool i1, int i2 +#define INSERT_NAMES_0() +#define INSERT_NAMES_1(arg0) arg0 i0 +#define INSERT_NAMES_2(arg0, arg1) arg0 i0, arg1 i1 +#define INSERT_NAMES_3(arg0, arg1, arg2) arg0 i0, arg1 i1, arg2 i2 +#define INSERT_NAMES(num) INSERT_NAMES_ ## num + +// Abbreviation of the java type corresponding to the given CType. +// Eg. JAVA_ABBREV(int64) expands to "J". +#define JAVA_ABBREV_int64 "J" +#define JAVA_ABBREV_int "I" +#define JAVA_ABBREV_bool "Z" +#define JAVA_ABBREV(x) JAVA_ABBREV_ ## x + +// ABBREV(int64, bool, int64) expands to: JZJ +#define ABBREV_0() +#define ABBREV_1(arg1) JAVA_ABBREV(arg1) +#define ABBREV_2(arg1, arg2) JAVA_ABBREV(arg1) JAVA_ABBREV(arg2) +#define ABBREV_3(arg1, arg2, arg3) ABBREV_2(arg1, arg2) JAVA_ABBREV(arg3) +#define ABBREV(num) ABBREV_ ## num +%} + +// See WRAP_STD_FUNCTION_JAVA below to understand why we need the __Unused__ +// argument. +%define WRAP_STD_FUNCTION_JAVA_AUX(ClassPath, ClassName, CppClass, + ReturnType, JavaReturnType, NumArgs, + __Unused__, Args...) +// The macro expansions can be hard to follow, so we show an example of the +// expected macro expansion with: ReturnType=int64, Args=int64, bool. +// EXPANSION EXAMPLE: "int64(int64, bool)". +%typemap(in) std::function { + jclass object_class = jenv->FindClass(ClassPath ClassName); + if (nullptr == object_class) return $null; + jmethodID method_id = jenv->GetStaticMethodID( + object_class, "getCPtr", "(L" ClassPath ClassName ";)J"); + assert(method_id != nullptr); + operations_research::swig_util::CppClass* const fun = + reinterpret_cast( + jenv->CallStaticLongMethod(object_class, method_id, $input)); + // EXPANSION EXAMPLE: "int64 i0, bool i1". + $1 = [fun](INSERT_NAMES(NumArgs)(Args)) { + // EXPANSION EXAMPLE: "i0, i1". + return fun->run(NAMES(NumArgs)); + }; +} + +// These 3 typemaps tell SWIG what JNI and Java types to use +%typemap(jni) std::function "jobject" +%typemap(jtype) std::function ClassName +%typemap(jstype) std::function ClassName + +// This typemap handles the conversion of the jtype to jstype typemap type +// and vice versa +%typemap(javain) std::function "$javainput" +%enddef + +// NUM_ARGS_MINUS_1(Guard, 4, "Hello", -1) = 3; etc. This generic macro works +// with 1 to 4 args (variadic macros with a total of zero arguments don't work). +#define NUM_ARGS_MINUS_1(Args...) NUM_ARGS_AUX(Args, 3, 2, 1, 0) +#define NUM_ARGS_AUX(_1, _2, _3, _4, N, Args...) N + +#define FIRST_ARG(x, Args...) x + +// We make the wrapper even more convenient to use. See usage below. +// +// Note: the variadic 'Args...' actually contains the 'JavaReturnType' argument, +// then the (possibly empty) list of argument types of the function. We do this +// because despite what the SWIG documentation claims, variadic macros don't +// work well when their number of arguments is zero. +%define WRAP_STD_FUNCTION_JAVA(CppClass, Package, ReturnType, Args...) +WRAP_STD_FUNCTION_JAVA_AUX(Package, "CppClass", CppClass, ReturnType, + FIRST_ARG(Args), NUM_ARGS_MINUS_1(Args), Args) +%enddef + +// --------- VoidToString --------- + +%typemap(in) std::function { + jclass object_class = + jenv->FindClass("com/google/ortools/util/VoidToString"); + if (nullptr == object_class) return $null; + jmethodID method_id = + jenv->GetStaticMethodID(object_class, "getCPtr", + "(Lcom/google/ortools/util/VoidToString;)J"); + assert(method_id != nullptr); + operations_research::swig_util::VoidToString* const fun = + reinterpret_cast( + jenv->CallStaticLongMethod(object_class, method_id, $input)); + $1 = [fun]() { + return fun->run(); + }; +} + +// These 3 typemaps tell SWIG what JNI and Java types to use +%typemap(jni) std::function "jobject" +%typemap(jtype) std::function "VoidToString" +%typemap(jstype) std::function "VoidToString" + +// This typemap handles the conversion of the jstype to jtype typemap types +%typemap(javain) std::function "$javainput" + + +// --------- VoidToVoid --------- + +%typemap(in) std::function { + jclass object_class = + jenv->FindClass("com/google/ortools/util/VoidToVoid"); + if (nullptr == object_class) return $null; + jmethodID method_id = + jenv->GetStaticMethodID(object_class, "getCPtr", + "(Lcom/google/ortools/util/VoidToVoid;)J"); + assert(method_id != nullptr); + operations_research::swig_util::VoidToVoid* const fun = + reinterpret_cast( + jenv->CallStaticLongMethod(object_class, method_id, $input)); + $1 = [fun]() { fun->run(); }; +} + +// These 3 typemaps tell SWIG what JNI and Java types to use +%typemap(jni) std::function "jobject" +%typemap(jtype) std::function "VoidToVoid" +%typemap(jstype) std::function "VoidToVoid" + +// This typemap handles the conversion of the jstype to jtype typemap types +%typemap(javain) std::function "$javainput" + +// --------- VoidToVoid --------- + +%typemap(in) std::function { + jclass object_class = + jenv->FindClass("com/google/ortools/util/LongToVoid"); + if (nullptr == object_class) return $null; + jmethodID method_id = + jenv->GetStaticMethodID(object_class, "getCPtr", + "(Lcom/google/ortools/util/LongToVoid;)J"); + assert(method_id != nullptr); + operations_research::swig_util::LongToVoid* const fun = + reinterpret_cast( + jenv->CallStaticLongMethod(object_class, method_id, $input)); + $1 = [fun](int64 i) { fun->run(i); }; +} + +// These 3 typemaps tell SWIG what JNI and Java types to use +%typemap(jni) std::function "jobject" +%typemap(jtype) std::function "LongToVoid" +%typemap(jstype) std::function "LongToVoid" + +// This typemap handles the conversion of the jstype to jtype typemap types +%typemap(javain) std::function "$javainput" + +// directors + +%module(directors="1") operations_research_swig_util + +// --------- Include the swig helpers file to create the director classes ------ +// We cannot use %ignoreall/%unignoreall as this is not compatible with nested +// swig files. + +%feature("director") operations_research::swig_util::IntIntToLong; +%feature("director") operations_research::swig_util::LongLongLongToBoolean; +%feature("director") operations_research::swig_util::LongLongLongToLong; +%feature("director") operations_research::swig_util::LongLongToLong; +%feature("director") operations_research::swig_util::LongToBoolean; +%feature("director") operations_research::swig_util::LongToLong; +%feature("director") operations_research::swig_util::LongToVoid; +%feature("director") operations_research::swig_util::VoidToBoolean; +%feature("director") operations_research::swig_util::VoidToString; +%feature("director") operations_research::swig_util::VoidToVoid; + +%include "util/functions_swig_helpers.h" diff --git a/src/util/piecewise_linear_function.h b/src/util/piecewise_linear_function.h index 91fc463dcf..02a9650f60 100644 --- a/src/util/piecewise_linear_function.h +++ b/src/util/piecewise_linear_function.h @@ -20,7 +20,7 @@ #define OR_TOOLS_UTIL_PIECEWISE_LINEAR_FUNCTION_H_ #include -#include "base/unique_ptr.h" +#include #include #include diff --git a/src/util/python/functions.swig b/src/util/python/functions.swig new file mode 100644 index 0000000000..3f1c2b531b --- /dev/null +++ b/src/util/python/functions.swig @@ -0,0 +1,184 @@ +// 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. + +// This file provides swig wrapping for some specialization of std::function +// parameters. Currently, swig does not support much of C++11 features, and +// especially not the std::function. + +// Usage and tests in pywrapfunctions_test.py + +%include "base/base.swig" + +%{ + +#include + +// Wrap std::function + +static std::string PyFunctionVoidToString(PyObject* pyfunc) { + PyObject* pyresult = PyObject_CallFunctionObjArgs(pyfunc, nullptr); + std::string result; + if (!pyresult) { + PyErr_SetString(PyExc_RuntimeError, + "std::function invocation failed."); + } else { + result = PyString_AsString(pyresult); + Py_DECREF(pyresult); + } + return result; +} +%} + +%typecheck(SWIG_TYPECHECK_POINTER) std::function { + $1 = PyCallable_Check($input); +} + +%typemap(in) std::function { + $1 = [$input]() { return PyFunctionVoidToString($input); }; +} + +// Wrap std::function + +%{ +static int64 PyFunctionInt64ToInt64(PyObject* pyfunc, int64 i) { + // () needed to force creation of one-element tuple + PyObject* pyresult = PyEval_CallFunction(pyfunc, "(l)", static_cast(i)); + int64 result = 0; + if (!pyresult) { + PyErr_SetString(PyExc_RuntimeError, + "std::function invocation failed."); + } else { + result = PyInt_AsLong(pyresult); + Py_DECREF(pyresult); + } + return result; +} +%} + +%typecheck(SWIG_TYPECHECK_POINTER) std::function { + $1 = PyCallable_Check($input); +} + +%typemap(in) std::function { + $1 = [$input](int64 index) { return PyFunctionInt64ToInt64($input, index); }; +} + +// Wrap std::function + +%{ +static int64 PyFunctionInt64Int64ToInt64(PyObject* pyfunc, int64 i, int64 j) { + PyObject* pyresult = PyEval_CallFunction(pyfunc, "ll", static_cast(i), + static_cast(j)); + int64 result = 0; + if (!pyresult) { + PyErr_SetString(PyExc_RuntimeError, + "std::function invocation failed."); + } else { + result = PyInt_AsLong(pyresult); + Py_DECREF(pyresult); + } + return result; +} +%} + +%typecheck(SWIG_TYPECHECK_POINTER) std::function { + $1 = PyCallable_Check($input); +} + +%typemap(in) std::function { + $1 = [$input](int64 i, int64 j) { + return PyFunctionInt64Int64ToInt64($input, i, j); + }; +} + +// Wrap std::function + +%{ +static int64 PyFunctionInt64Int64Int64ToInt64(PyObject* pyfunc, + int64 i, int64 j, int64 k) { + PyObject* pyresult = PyEval_CallFunction(pyfunc, "lll", static_cast(i), + static_cast(j), + static_cast(k)); + int64 result = 0; + if (!pyresult) { + PyErr_SetString( + PyExc_RuntimeError, + "std::function invocation failed."); + } else { + result = PyInt_AsLong(pyresult); + Py_DECREF(pyresult); + } + return result; +} +%} + +%typecheck(SWIG_TYPECHECK_POINTER) std::function { + $1 = PyCallable_Check($input); +} + +%typemap(in) std::function { + $1 = [$input](int64 i, int64 j, int64 k) { + return PyFunctionInt64Int64Int64ToInt64($input, i, j, k); + }; +} + +// Wrap std::function + +%{ +static bool PyFunctionInt64ToBool(PyObject* pyfunc, int64 i) { + // () needed to force creation of one-element tuple + PyObject* pyresult = PyEval_CallFunction(pyfunc, "(l)", static_cast(i)); + bool result = 0; + if (!pyresult) { + PyErr_SetString(PyExc_RuntimeError, + "std::function invocation failed."); + } else { + result = PyObject_IsTrue(pyresult); + Py_DECREF(pyresult); + } + return result; +} +%} + +%typecheck(SWIG_TYPECHECK_POINTER) std::function { + $1 = PyCallable_Check($input); +} + +%typemap(in) std::function { + $1 = [$input](int64 index) { return PyFunctionInt64ToBool($input, index); }; +} + +// Wrap std::function + +%{ +static bool PyFunctionVoidToBool(PyObject* pyfunc) { + PyObject* pyresult = PyObject_CallFunctionObjArgs(pyfunc, nullptr); + bool result = false; + if (!pyresult) { + PyErr_SetString(PyExc_RuntimeError, + "std::function invocation failed."); + } else { + result = PyObject_IsTrue(pyresult); + Py_DECREF(pyresult); + } + return result; +} +%} + +%typecheck(SWIG_TYPECHECK_POINTER) std::function { + $1 = PyCallable_Check($input); +} + +%typemap(in) std::function { + $1 = [$input]() { return PyFunctionVoidToBool($input); }; +} diff --git a/src/util/zvector.h b/src/util/zvector.h index d131b2903c..761477b1c7 100644 --- a/src/util/zvector.h +++ b/src/util/zvector.h @@ -24,7 +24,7 @@ #include #include #include -#include "base/unique_ptr.h" +#include #include "base/integral_types.h" #include "base/logging.h"